Project import
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..12c29fe
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,110 @@
+#
+# Copyright (c) 2010-2011 Nest, Inc.
+# All rights reserved.
+#
+# This document is the property of Nest. It is considered
+# confidential and proprietary information.
+#
+# This document may not be reproduced or transmitted in any form,
+# in whole or in part, without the express written permission of
+# Nest.
+#
+# Description:
+# This file is the makefile for c-kermit, a combined serial and
+# network communication software package.
+#
+
+BuildConfigSpecialized := No
+BuildProductSpecialized := No
+
+include pre.mak
+
+PackageName := ckermit
+
+PackageExtension := tar.gz
+PackageSeparator := -
+
+PackagePatchArgs :=
+
+PackageArchive := $(PackageName).$(PackageExtension)
+PackageSourceDir := $(PackageName)$(PackageSeparator)$(PackageVersion)
+
+PackageBuildMakefile = $(call GenerateBuildPaths,makefile)
+
+CleanPaths += $(PackageLicenseFile)
+
+NCursesDir := sw/tps/ncurses
+NCursesIncDir := $(call GenerateResultPaths,$(NCursesDir),usr/include)
+NCursesLibDir := $(call GenerateResultPaths,$(NCursesDir),usr/lib)
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/COPYING.TXT: source
+
+$(PackageLicenseFile): $(PackageSourceDir)/COPYING.TXT
+ $(copy-result)
+
+# Extract the source from the archive and apply patches, if any.
+
+$(PackageSourceDir): $(PackageArchive) $(PackagePatchPaths)
+ $(create-directory)
+ $(call expand-archive,$(PackageArchive),$(@))
+ $(Verbose)touch $(@)
+ $(call patch-directory,$(@),$(PackagePatchArgs),$(PackagePatchPaths))
+
+# Prepare the sources.
+
+.PHONY: source
+source: | $(PackageSourceDir)
+
+# Patch the sources, if necessary.
+
+.PHONY: patch
+patch: source
+
+# Generate the package build makefile.
+
+$(PackageBuildMakefile): | $(PackageSourceDir) $(BuildDirectory)
+ $(call create-links,$(CURDIR)/$(PackageSourceDir),$(BuildDirectory))
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: source $(PackageBuildMakefile)
+
+# Build the source.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+.PHONY: build
+build: configure
+ $(Verbose)unset MAKEFLAGS && \
+ $(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+ INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+ CC="$(CC)" CXX="$(CXX)" AR=$(AR) NM=$(NM) RANLIB=$(RANLIB) STRIP=$(STRIP) \
+ NCURSES_CPPFLAGS=$(call ToolGenerateIncludeArgument,$(NCursesIncDir)) \
+ NCURSES_LDFLAGS=$(call ToolGenerateLibraryPathArgument,$(NCursesLibDir)) \
+ linux-cross
+
+# Stage the build to a temporary installation area.
+#
+# We have to unset MAKEFLAGS since they confuse the package build otherwise.
+
+.PHONY: stage
+stage: build | $(ResultDirectory)
+ $(Verbose)unset MAKEFLAGS && \
+ $(MAKE) $(JOBSFLAG) -C $(BuildDirectory) \
+ INSTALL="$(INSTALL) $(INSTALLFLAGS)" \
+ DESTDIR=$(ResultDirectory) \
+ prefix=/usr \
+ MANDIR="" \
+ install
+
+clean:
+ $(Verbose)$(RM) $(RMFLAGS) -r $(PackageSourceDir)
+ $(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+ $(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/ckermit-8.0.211/COPYING.TXT b/ckermit-8.0.211/COPYING.TXT
new file mode 100644
index 0000000..da5d39e
--- /dev/null
+++ b/ckermit-8.0.211/COPYING.TXT
@@ -0,0 +1,106 @@
+THE C-KERMIT 7.0 AND 8.0 LICENSE
+
+ Last update: Thu May 1 13:52:15 2003
+
+This is the new C-Kermit 7.0 and 8.0 license. The intention is to allow
+C-Kermit to be distributed with "free" operating systems such as GNU/Linux,
+FreeBSD, NetBSD, OpenBSD, The Hurd, etc, even when the distributions
+themselves (such as Red Hat or Caldera) might be sold and/or might include
+applications that are not free, and yet still require a license to include
+C-Kermit in or with "non-free" products such as commercial OS's, commercial
+software packages, embedded systems, and hardware (other than general-purpose
+computers preloaded with "free" operating systems), since these licenses
+furnish a large portion of the Kermit Project's funding.
+
+There have been some questions about the provision in Clause (A) that:
+
+ The
+ C-Kermit source code may not be changed without the consent of the
+ Kermit Project, which will not be unreasonably withheld (this is
+ simply a matter of keeping a consistent and supportable code base).
+
+The intention of this clause is primarily to make sure that anybody who
+makes modifications sends them back to us, since we are the ones have to
+support C-Kermit, and so we can carry them through to future releases (so
+you don't have to make the same changes again and again).
+
+Secondarily it is to protect Columbia University in the unlikely event of
+modifications made with deliberate intent to offend or cause damage.
+
+Any redistributor of C-Kermit under Clause (A) below should rest assured
+there is no intention of preventing them from constructing a distribution in
+the appropriate format (RPM or whatever) for their product or from issuing
+any patches required for their products; we simply want to be informed so we
+can maintain a consistent code base and a solid, supportable software
+package. We are happy to work with any redistributor an any issues that
+concern them. If you have questions, send them to kermit@columbia.edu.
+
+Note: All changes to this file since 1 January 2000 (the C-Kermit 7.0
+release date) are above; the license itself has not changed, except to
+update the most recent copyright date.
+
+(Begin)
+
+Copyright (C) 1985, 2003,
+ The Trustees of Columbia University in the City of New York.
+ All rights reserved.
+
+PERMISSIONS:
+
+The C-Kermit software may be obtained directly from the Kermit Project at
+Columbia University (or from any source explicitly licensed by the Kermit
+Project or implicitly licensed by Clause (A) below) by any individual for
+his or her OWN USE, and by any company or other organization for its own
+INTERNAL DISTRIBUTION and use, including installation on servers that are
+accessed by customers or clients, WITHOUT EXPLICIT LICENSE.
+
+Conditions for REDISTRIBUTION are as follows:
+
+(A) The C-Kermit software, in source and/or binary form, may be
+ included WITHOUT EXPLICIT LICENSE in distributions of OPERATING
+ SYSTEMS that have OSI (Open Source Initiative, www.opensource.org)
+ approved licenses, even if non-Open-Source applications (but not
+ operating systems) are included in the same distribution. Such
+ distributions include, but are not limited to, CD-ROM, FTP site,
+ Web site, or preinstalled software on a new GENERAL-PURPOSE
+ computer, as long as the primary character of the distribution is
+ an Open Source operating system with accompanying utilities. The
+ C-Kermit source code may not be changed without the consent of the
+ Kermit Project, which will not be unreasonably withheld (this is
+ simply a matter of keeping a consistent and supportable code base).
+
+(B) Inclusion of C-Kermit software in whole or in part, in any form, in
+ or with any product not covered by Clause (A), or its distribution
+ by any commercial enterprise to its actual or potential customers
+ or clients except as in Clause (A), requires a license from the
+ Kermit Project, Columbia University; contact kermit@columbia.edu.
+
+The name of Columbia University may not be used to endorse or promote
+products derived from or including the C-Kermit software without specific
+prior written permission.
+
+DISCLAIMER:
+
+ THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
+ TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS
+ FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF
+ COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT
+ BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,
+ OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR
+ IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS
+ HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL
+ INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN
+ THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY
+ AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING
+ ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.
+
+The above copyright notice, permissions notice, and disclaimer may not be
+removed, altered, or obscured and shall be included in all copies of the
+C-Kermit software. The Trustees of Columbia University in the City of
+New York reserve the right to revoke this permission if any of the terms
+of use set forth above are breached.
+
+(End)
diff --git a/ckermit-8.0.211/ck_crp.c b/ckermit-8.0.211/ck_crp.c
new file mode 100644
index 0000000..c35afd0
--- /dev/null
+++ b/ckermit-8.0.211/ck_crp.c
@@ -0,0 +1,5590 @@
+char *ckcrpv = "Encryption Engine, 8.0.114, 9 Oct 2003";
+/*
+ C K _ C R P . C - Cryptography for C-Kermit"
+
+ Copyright (C) 1998, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+
+ Author:
+ Jeffrey E Altman (jaltman@secure-endpoints.com)
+ Secure Endpoints Inc., New York City
+*/
+
+#define CK_CRP_C
+#ifdef CK_DES
+#ifdef CK_SSL
+#ifndef LIBDES
+#define LIBDES
+#endif /* LIBDES */
+#endif /* CK_SSL */
+#endif /* CK_DES */
+
+#ifdef CRYPT_DLL
+#define CK_AUTHENTICATION
+#define CK_ENCRYPTION
+#define CK_DES
+#define CK_CAST
+#ifndef LIBDES
+#define LIBDES
+#endif /* LIBDES */
+
+#define TELCMDS /* to define name array */
+#define TELOPTS /* to define name array */
+#define ENCRYPT_NAMES
+#endif /* CRYPT_DLL */
+
+#include "ckcsym.h"
+#include "ckcdeb.h"
+#include "ckcnet.h"
+
+#ifdef DEBUG
+#undef DEBUG
+#endif /* DEBUG */
+
+#ifdef CK_AUTHENTICATION
+#ifdef CK_ENCRYPTION
+#define ENCRYPTION
+#ifdef CK_DES
+#define DES_ENCRYPTION
+#endif /* CK_DES */
+#ifdef CK_CAST
+#define CAST_ENCRYPTION
+#endif /* CK_CAST */
+#ifdef COMMENT
+#define CAST_EXPORT_ENCRYPTION
+#endif /* COMMENT */
+#endif /* CK_ENCRYPTION */
+#endif /* CK_AUTHENTICATION */
+
+#ifdef CK_ENCRYPTION
+
+#include "ckucmd.h" /* For struct keytab definition */
+#include "ckuath.h"
+#include "ckuat2.h"
+#ifdef MIT_CURRENT
+#include <krb5.h>
+#endif /* MIT_CURRENT */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef OS2
+#include <stdarg.h>
+#ifdef OS2ONLY
+#include <os2.h>
+#endif /* OS2ONLY */
+#include "ckosyn.h"
+#else /* OS2 */
+static char * tmpstring = NULL;
+#endif /* OS2 */
+
+#ifndef CAST_OR_EXPORT
+#ifdef CAST_ENCRYPTION
+#define CAST_OR_EXPORT
+#endif /* CAST_ENCRYPTION */
+#ifdef CAST_EXPORT_ENCRYPTION
+#define CAST_OR_EXPORT
+#endif /* CAST_EXPORT_ENCRYPTION */
+#endif /* CAST_OR_EXPORT */
+
+#ifdef CRYPT_DLL
+int cmd_quoting = 0;
+
+#ifndef TELOPT_MACRO
+int
+telopt_index(opt) int opt; {
+ if ( opt >= 0 && opt <= TELOPT_SEND_URL )
+ return(opt);
+ else if ( opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT )
+ return(opt-89);
+ else
+ return(NTELOPTS);
+}
+
+int
+telopt_ok(opt) int opt; {
+ return((opt >= TELOPT_BINARY && opt <= TELOPT_SEND_URL) ||
+ (opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT));
+}
+
+CHAR *
+telopt(opt) int opt; {
+ if ( telopt_ok(opt) )
+ return(telopts[telopt_index(opt)]);
+ else
+ return("UNKNOWN");
+}
+#endif /* TELOPT_MACRO */
+
+static int (*p_ttol)(char *,int)=NULL;
+static int (*p_dodebug)(int,char *,char *,long)=NULL;
+static int (*p_dohexdump)(char *,char *,int)=NULL;
+static void (*p_tn_debug)(char *)=NULL;
+static int (*p_vscrnprintf)(char *, ...)=NULL;
+static void * p_k5_context=NULL;
+static unsigned long (*p_reqtelmutex)(unsigned long)=NULL;
+static unsigned long (*p_reltelmutex)(void)=NULL;
+
+unsigned long
+RequestTelnetMutex(unsigned long x)
+{
+ if ( p_reqtelmutex )
+ return p_reqtelmutex(x);
+ return 0;
+}
+
+unsigned long
+ReleaseTelnetMutex(void)
+{
+ if ( p_reltelmutex )
+ return p_reltelmutex();
+ return 0;
+}
+
+int
+ttol(char * s, int n)
+{
+ if ( p_ttol )
+ return(p_ttol(s,n));
+ else
+ return(-1);
+}
+
+int
+dodebug(int flag, char * s1, char * s2, long n)
+{
+ if ( p_dodebug )
+ return(p_dodebug(flag,s1,s2,n));
+ else
+ return(-1);
+}
+
+int
+dohexdump( char * s1, char * s2, int n )
+{
+ if ( p_dohexdump )
+ p_dohexdump(s1,s2,n);
+ return(0);
+}
+
+void
+tn_debug( char * s )
+{
+ if ( p_tn_debug )
+ p_tn_debug(s);
+}
+
+static char myprtfstr[4096];
+int
+Vscrnprintf(const char * format, ...) {
+ int i, len, rc=0;
+ char *cp;
+ va_list ap;
+
+ va_start(ap, format);
+#ifdef NT
+ rc = _vsnprintf(myprtfstr, sizeof(myprtfstr)-1, format, ap);
+#else /* NT */
+ rc = vsprintf(myprtfstr, format, ap);
+#endif /* NT */
+ va_end(ap);
+
+ if ( p_vscrnprintf )
+ return(p_vscrnprintf(myprtfstr));
+ else
+ return(-1);
+}
+
+int
+#ifdef CK_ANSIC
+tn_hex(CHAR * buf, int buflen, CHAR * data, int datalen)
+#else /* CK_ANSIC */
+tn_hex(buf, buflen, data, datalen)
+ CHAR * buf;
+ int buflen;
+ CHAR * data;
+ int datalen;
+#endif /* CK_ANSIC */
+{
+ int i = 0, j = 0, k = 0;
+ CHAR tmp[8];
+#ifdef COMMENT
+ int was_hex = 1;
+
+ for (k=0; k < datalen; k++) {
+ if (data[k] < 32 || data[k] >= 127) {
+ sprintf(tmp,"%s%02X ",was_hex?"":"\" ",data[k]);
+ was_hex = 1;
+ } else {
+ sprintf(tmp,"%s%c",was_hex?"\"":"",data[k]);
+ was_hex = 0;
+ }
+ ckstrncat(buf,tmp,buflen);
+ }
+ if (!was_hex)
+ ckstrncat(buf,"\" ",buflen);
+#else /* COMMENT */
+ if (datalen <= 0 || data == NULL)
+ return(0);
+
+ for (i = 0; i < datalen; i++) {
+ ckstrncat(buf,"\r\n ",buflen);
+ for (j = 0 ; (j < 16); j++) {
+ if ((i + j) < datalen)
+ sprintf(tmp,
+ "%s%02x ",
+ (j == 8 ? "| " : ""),
+ (CHAR) data[i + j]
+ );
+ else
+ sprintf(tmp,
+ "%s ",
+ (j == 8 ? "| " : "")
+ );
+ ckstrncat(buf,tmp,buflen);
+ }
+ ckstrncat(buf," ",buflen);
+ for (k = 0; (k < 16) && ((i + k) < datalen); k++) {
+ sprintf(tmp,
+ "%s%c",
+ (k == 8 ? " " : ""),
+ isprint(data[i + k]) ? data[i + k] : '.'
+ );
+ ckstrncat(buf,tmp,buflen);
+ }
+ i += j - 1;
+ } /* end for */
+ ckstrncat(buf,"\r\n ",buflen);
+#endif /* COMMENT */
+ return(strlen(buf));
+}
+
+#ifdef COMMENT
+#define ttol dll_ttol
+#define dodebug dll_dodebug
+#define dohexdump dll_dohexdump
+#define tn_debug dll_tn_debug
+#define Vscrnprintf dll_vscrnprintf
+#endif /* COMMENT */
+
+char tn_msg[TN_MSG_LEN], hexbuf[TN_MSG_LEN]; /* from ckcnet.c */
+int deblog=1, debses=1, tn_deb=1;
+#else /* CRYPT_DLL */
+extern char tn_msg[], hexbuf[]; /* from ckcnet.c */
+extern int deblog, debses, tn_deb;
+#ifdef MIT_CURRENT
+extern krb5_context k5_context;
+#endif /* MIT_CURRENT */
+#endif /* CRYPT_DLL */
+
+#ifdef LIBDES
+#ifndef UNIX
+#define des_new_random_key des_random_key
+#define des_set_random_generator_seed des_random_seed
+#endif /* UNIX */
+#define des_fixup_key_parity des_set_odd_parity
+#ifdef OPENSSL_097
+#define OPENSSL_ENABLE_OLD_DES_SUPPORT
+#include <openssl/des.h>
+#endif /* OPENSSL_097 */
+#else /* LIBDES */
+#ifdef UNIX
+#define des_set_random_generator_seed(x) des_init_random_number_generator(x)
+#endif /* UNIX */
+#ifdef OS2
+#define des_new_random_key ck_des_new_random_key
+#define des_set_random_generator_seed ck_des_set_random_generator_seed
+#define des_key_sched ck_des_key_sched
+#define des_ecb_encrypt ck_des_ecb_encrypt
+#define des_string_to_key ck_des_string_to_key
+#define des_fixup_key_parity ck_des_fixup_key_parity
+#endif /* OS2 */
+#endif /* LIBDES */
+
+#ifdef CK_DES
+/* This code comes from Eric Young's libdes package and is not part */
+/* of the standard MIT DES library that is part of Kerberos. However, */
+/* it is extremely useful. So we add it here. */
+
+
+/* Weak and semi week keys as take from
+ * %A D.W. Davies
+ * %A W.L. Price
+ * %T Security for Computer Networks
+ * %I John Wiley & Sons
+ * %D 1984
+ * Many thanks to smb@ulysses.att.com (Steven Bellovin) for the reference
+ * (and actual cblock values).
+ */
+#define NUM_WEAK_KEY 16
+static Block weak_keys[NUM_WEAK_KEY]={
+ /* weak keys */
+ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
+ {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
+ {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F},
+ {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0},
+ /* semi-weak keys */
+ {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE},
+ {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
+ {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
+ {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
+ {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
+ {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
+ {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
+ {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
+ {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
+ {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
+ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
+ {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}};
+
+int
+ck_des_is_weak_key(key)
+Block key;
+{
+ int i;
+
+ for (i=0; i<NUM_WEAK_KEY; i++) {
+ /* Added == 0 to comparision, I obviously don't run
+ * this section very often :-(, thanks to
+ * engineering@MorningStar.Com for the fix
+ * eay 93/06/29
+ * Another problem, I was comparing only the first 4
+ * bytes, 97/03/18 */
+ if (memcmp(weak_keys[i],key,sizeof(Block)) == 0)
+ return(1);
+ }
+ return(0);
+}
+
+#ifdef UNIX
+#ifdef LIBDES
+/* These functions are not part of Eric Young's DES library */
+/* _unix_time_gmt_unixsec */
+/* _des_set_random_generator_seed */
+/* _des_fixup_key_parity (added in 0.9.5) */
+/* _des_new_random_key */
+#include <sys/time.h>
+
+unsigned long
+unix_time_gmt_unixsec (usecptr)
+ unsigned long *usecptr;
+{
+ struct timeval now;
+
+ (void) gettimeofday (&now, (struct timezone *)0);
+ if (usecptr)
+ *usecptr = now.tv_usec;
+ return now.tv_sec;
+}
+
+void
+des_set_random_generator_seed(Block B)
+{
+ des_random_seed(B);
+ return;
+}
+
+#ifdef COMMENT
+/* added to openssl in 0.9.5 */
+void
+des_fixup_key_parity(Block B)
+{
+ des_set_odd_parity(B);
+ return;
+}
+#endif /* COMMENT */
+int
+des_new_random_key(Block B)
+{
+ int rc=0;
+ rc = des_random_key(B);
+ return(rc);
+}
+
+#endif /* LIBDES */
+#endif /* UNIX */
+#endif /* CK_DES */
+
+/*
+ * Copyright (c) 1991, 1993
+ * 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.
+ */
+
+/* based on @(#)encrypt.c 8.1 (Berkeley) 6/4/93 */
+
+/*
+ * Copyright (C) 1990 by the Massachusetts Institute of Technology
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <stdio.h>
+
+/*
+ * These function pointers point to the current routines
+ * for encrypting and decrypting data.
+ */
+static VOID (*encrypt_output) P((unsigned char *, int));
+static int (*decrypt_input) P((int));
+
+#ifdef DEBUG
+static int encrypt_debug_mode = 1;
+static int encrypt_verbose = 1;
+#else
+static int encrypt_verbose = 1;
+static int encrypt_debug_mode = 0;
+#endif
+
+static char dbgbuf [16384];
+
+static int decrypt_mode = 0;
+static int encrypt_mode = 0;
+static int autoencrypt = 1;
+static int autodecrypt = 1;
+static int havesessionkey = 0;
+
+static kstream EncryptKSGlobalHack = NULL;
+static int EncryptType = ENCTYPE_ANY;
+
+#define typemask(x) ((x) > 0 ? 1 << ((x)-1) : 0)
+
+static long i_support_encrypt =
+ typemask(ENCTYPE_DES_CFB64) | typemask(ENCTYPE_DES_OFB64);
+static long i_support_decrypt =
+ typemask(ENCTYPE_DES_CFB64) | typemask(ENCTYPE_DES_OFB64);
+static long i_wont_support_encrypt = 0;
+static long i_wont_support_decrypt = 0;
+#define I_SUPPORT_ENCRYPT (i_support_encrypt & ~i_wont_support_encrypt)
+#define I_SUPPORT_DECRYPT (i_support_decrypt & ~i_wont_support_decrypt)
+
+static long remote_supports_encrypt = 0;
+static long remote_supports_decrypt = 0;
+
+/* Make sure that this list is in order of algorithm strength */
+/* as it determines the search order for selecting specific */
+/* encryption choices. All CFB modes must come before OFB modes. */
+static Encryptions encryptions[] = {
+#ifdef DES_ENCRYPTION
+ { "DES3_CFB64",
+ ENCTYPE_DES3_CFB64,
+ des3_cfb64_encrypt,
+ des3_cfb64_decrypt,
+ des3_cfb64_init,
+ des3_cfb64_start,
+ des3_cfb64_is,
+ des3_cfb64_reply,
+ des3_cfb64_session,
+ des3_cfb64_keyid,
+ NULL },
+#endif /* DES_ENCRYPTION */
+#ifdef CAST_ENCRYPTION
+#ifndef CAST_EXPORT_ENCRYPTION
+ { "CAST128_CFB64", ENCTYPE_CAST128_CFB64,
+ cast_cfb64_encrypt,
+ cast_cfb64_decrypt,
+ cast_cfb64_init,
+ cast_cfb64_start,
+ cast_cfb64_is,
+ cast_cfb64_reply,
+ cast_cfb64_session,
+ cast_cfb64_keyid,
+ NULL },
+#endif
+#endif
+#ifdef DES_ENCRYPTION
+ { "DES_CFB64",
+ ENCTYPE_DES_CFB64,
+ cfb64_encrypt,
+ cfb64_decrypt,
+ cfb64_init,
+ cfb64_start,
+ cfb64_is,
+ cfb64_reply,
+ cfb64_session,
+ cfb64_keyid,
+ NULL },
+#endif /* DES_ENCRYPTION */
+#if defined (CAST_EXPORT_ENCRYPTION) || defined(CAST_ENCRYPTION)
+ { "CAST5_40_CFB64", ENCTYPE_CAST5_40_CFB64,
+ castexp_cfb64_encrypt,
+ castexp_cfb64_decrypt,
+ castexp_cfb64_init,
+ castexp_cfb64_start,
+ castexp_cfb64_is,
+ castexp_cfb64_reply,
+ castexp_cfb64_session,
+ castexp_cfb64_keyid,
+ NULL },
+#endif /* CAST_ENCRYPTION */
+#ifdef DES_ENCRYPTION
+ { "DES3_OFB64",
+ ENCTYPE_DES3_OFB64,
+ des3_ofb64_encrypt,
+ des3_ofb64_decrypt,
+ des3_ofb64_init,
+ des3_ofb64_start,
+ des3_ofb64_is,
+ des3_ofb64_reply,
+ des3_ofb64_session,
+ des3_ofb64_keyid,
+ NULL },
+#endif /* DES_ENCRYPTION */
+#ifdef CAST_ENCRYPTION
+#ifndef CAST_EXPORT_ENCRYPTION
+ { "CAST128_OFB64", ENCTYPE_CAST128_OFB64,
+ cast_ofb64_encrypt,
+ cast_ofb64_decrypt,
+ cast_ofb64_init,
+ cast_ofb64_start,
+ cast_ofb64_is,
+ cast_ofb64_reply,
+ cast_ofb64_session,
+ cast_ofb64_keyid,
+ NULL },
+#endif
+#endif
+#ifdef DES_ENCRYPTION
+ { "DES_OFB64",
+ ENCTYPE_DES_OFB64,
+ ofb64_encrypt,
+ ofb64_decrypt,
+ ofb64_init,
+ ofb64_start,
+ ofb64_is,
+ ofb64_reply,
+ ofb64_session,
+ ofb64_keyid,
+ NULL },
+#endif /* DES_ENCRYPTION */
+#if defined (CAST_EXPORT_ENCRYPTION) || defined(CAST_ENCRYPTION)
+ { "CAST5_40_OFB64", ENCTYPE_CAST5_40_OFB64,
+ castexp_ofb64_encrypt,
+ castexp_ofb64_decrypt,
+ castexp_ofb64_init,
+ castexp_ofb64_start,
+ castexp_ofb64_is,
+ castexp_ofb64_reply,
+ castexp_ofb64_session,
+ castexp_ofb64_keyid,
+ NULL },
+#endif /* CAST_ENCRYPTION */
+ { 0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+int
+get_crypt_table( struct keytab ** pTable, int * pN )
+{
+ int i=0,n=0;
+
+ if ( *pTable )
+ {
+ for ( i=0 ; i < *pN ; i++ )
+ free( (*pTable)[i].kwd ) ;
+ free ( *pTable ) ;
+ }
+ *pTable = NULL;
+ *pN = 0;
+
+ /* How many encryption types do we have? */
+ while ( encryptions[n].name )
+ n++;
+
+ if ( n )
+ {
+ *pTable = malloc( sizeof(struct keytab) * (n+2) ) ;
+ if ( !(*pTable) )
+ return(0);
+
+#ifdef OS2
+ (*pTable)[0].kwd =strdup("automatic");
+#else /* OS2 */
+ makestr(&tmpstring,"automatic");
+ (*pTable)[0].kwd = tmpstring;
+ tmpstring = NULL;
+#endif /* OS2 */
+ (*pTable)[0].kwval = ENCTYPE_ANY;
+ (*pTable)[0].flgs = 0;
+#ifdef OS2
+ (*pTable)[1].kwd =strdup("none");
+#else /* OS2 */
+ makestr(&tmpstring,"none");
+ (*pTable)[1].kwd = tmpstring;
+ tmpstring = NULL;
+#endif /* OS2 */
+ (*pTable)[1].kwval = 999;
+ (*pTable)[1].flgs = 0;
+ (*pN) = 2;
+
+ for ( i=0 ; i < n ; i++ ) {
+ char * newstr = NULL, * p;
+ int newval = encryptions[i].type;
+ int j = 0, len = 0;
+
+#ifdef OS2
+ newstr = strdup(encryptions[i].name);
+ strlwr(newstr);
+#else /* OS2 */
+ makestr(&tmpstring,encryptions[i].name);
+ newstr = tmpstring;
+ tmpstring = NULL;
+ for (p = newstr; *p; p++) if (isupper(*p)) *p = tolower(*p);
+#endif /* OS2 */
+
+ for (j = 0; j < (*pN); j++) {
+ int tempval = 0;
+ char * tempstr = NULL;
+
+ if ( strcmp( (*pTable)[j].kwd, newstr ) > 0 )
+ {
+ tempval = (*pTable)[j].kwval;
+ tempstr = (*pTable)[j].kwd;
+ (*pTable)[j].kwd = newstr ;
+ (*pTable)[j].kwval = newval;
+ newval = tempval;
+ newstr = tempstr;
+ (*pTable)[j].flgs = 0;
+ }
+ }
+ (*pTable)[*pN].kwd = newstr ;
+ (*pTable)[*pN].kwval = newval;
+ (*pTable)[*pN].flgs = 0 ;
+ (*pN)++ ;
+ }
+ } else {
+ *pTable = malloc( sizeof(struct keytab) * 2 ) ;
+ if ( !(*pTable) )
+ return(0);
+
+#ifdef OS2
+ (*pTable)[0].kwd =strdup("automatic");
+#else /* OS2 */
+ makestr(&tmpstring,"automatic");
+ (*pTable)[0].kwd = tmpstring;
+ tmpstring = NULL;
+#endif /* OS2 */
+ (*pTable)[0].kwval = ENCTYPE_ANY;
+ (*pTable)[0].flgs = 0;
+#ifdef OS2
+ (*pTable)[1].kwd =strdup("none");
+#else /* OS2 */
+ makestr(&tmpstring,"none");
+ (*pTable)[1].kwd = tmpstring;
+ tmpstring = NULL;
+#endif /* OS2 */
+ (*pTable)[1].kwval = 999;
+ (*pTable)[1].flgs = 0;
+ (*pN) = 2;
+ }
+ return(*pN);
+}
+
+static unsigned char str_send[64] = { IAC, SB, TELOPT_ENCRYPTION,
+ ENCRYPT_SUPPORT };
+static unsigned char str_suplen = 0;
+static unsigned char str_start[72] = { IAC, SB, TELOPT_ENCRYPTION };
+static unsigned char str_end[] = { IAC, SB, TELOPT_ENCRYPTION, 0, IAC, SE };
+
+_PROTOTYP(int encrypt_request_end, (VOID));
+_PROTOTYP(int encrypt_request_start, (VOID));
+_PROTOTYP(int encrypt_enc_keyid, (unsigned char *, int));
+_PROTOTYP(int encrypt_dec_keyid, (unsigned char *, int));
+_PROTOTYP(int encrypt_support, (unsigned char *, int));
+_PROTOTYP(int encrypt_start, (unsigned char *, int));
+_PROTOTYP(int encrypt_end, (VOID));
+
+_PROTOTYP(int encrypt_ks_stream,(struct kstream_data_block *, /* output */
+ struct kstream_data_block *)); /* input */
+
+_PROTOTYP(int decrypt_ks_stream,(struct kstream_data_block *, /* output */
+ struct kstream_data_block *)); /* input */
+
+int
+#ifdef CK_ANSIC
+encrypt_ks_stream(struct kstream_data_block *i,
+ struct kstream_data_block *o)
+#else
+encrypt_ks_stream(i,o)
+ struct kstream_data_block *i; struct kstream_data_block *o;
+#endif
+{
+ /*
+ * this is really quite bogus, since it does an in-place encryption...
+ */
+ if (encrypt_output) {
+ encrypt_output(i->ptr, i->length);
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+#ifdef CK_ANSIC
+decrypt_ks_stream(struct kstream_data_block *i,
+ struct kstream_data_block *o)
+#else
+decrypt_ks_stream(i,o)
+ struct kstream_data_block *i; struct kstream_data_block *o;
+#endif
+{
+ unsigned int len;
+ /*
+ * this is really quite bogus, since it does an in-place decryption...
+ */
+ if (decrypt_input) {
+ for (len = 0 ; len < i->length ; len++)
+ ((unsigned char *)i->ptr)[len]
+ = decrypt_input(((unsigned char *)i->ptr)[len]);
+ return 1;
+ }
+ return 0;
+}
+
+int
+#ifdef CK_ANSIC
+decrypt_ks_hack(unsigned char *buf, int cnt)
+#else
+decrypt_ks_hack(buf,cnt) unsigned char *buf; int cnt;
+#endif
+{
+ int len;
+ /*
+ * this is really quite bogus, since it does an in-place decryption...
+ */
+ for (len = 0 ; len < cnt ; len++)
+ buf[len] = decrypt_input(buf[len]);
+
+#ifdef DEBUG
+ hexdump("decrypt ks hack", buf, cnt);
+#endif
+ return 1;
+}
+
+
+/*
+ * parsedat[0] == the suboption we might be negotiating,
+ */
+int
+#ifdef CK_ANSIC
+encrypt_parse(unsigned char *parsedat, int end_sub)
+#else
+encrypt_parse(parsedat,end_sub) unsigned char *parsedat; int end_sub;
+#endif
+{
+ int rc = 0;
+
+ switch(parsedat[1]) {
+ case ENCRYPT_START:
+ rc = encrypt_start(parsedat + 2, end_sub - 2);
+ break;
+ case ENCRYPT_END:
+ rc = encrypt_end();
+ break;
+ case ENCRYPT_SUPPORT:
+ rc = encrypt_support(parsedat + 2, end_sub - 2);
+ break;
+ case ENCRYPT_REQSTART:
+ rc = encrypt_request_start();
+ break;
+ case ENCRYPT_REQEND:
+ /*
+ * We can always send an REQEND so that we cannot
+ * get stuck encrypting. We should only get this
+ * if we have been able to get in the correct mode
+ * anyhow.
+ */
+ rc = encrypt_request_end();
+ break;
+ case ENCRYPT_IS:
+ rc = encrypt_is(parsedat + 2, end_sub - 2);
+ break;
+ case ENCRYPT_REPLY:
+ rc = encrypt_reply(parsedat + 2, end_sub - 2);
+ break;
+ case ENCRYPT_ENC_KEYID:
+ rc = encrypt_enc_keyid(parsedat + 2, end_sub - 2);
+ break;
+ case ENCRYPT_DEC_KEYID:
+ rc = encrypt_dec_keyid(parsedat + 2, end_sub - 2);
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+ return(rc);
+}
+
+/* XXX */
+Encryptions *
+#ifdef CK_ANSIC
+findencryption(int type)
+#else
+findencryption(type) int type;
+#endif
+{
+ Encryptions *ep = encryptions;
+
+ if (!(I_SUPPORT_ENCRYPT & remote_supports_decrypt & typemask(type)))
+ return(0);
+ while (ep->type && ep->type != type)
+ ++ep;
+ return(ep->type ? ep : 0);
+}
+
+Encryptions *
+#ifdef CK_ANSIC
+finddecryption(int type)
+#else
+finddecryption(type) int type;
+#endif
+{
+ Encryptions *ep = encryptions;
+
+ if (!(I_SUPPORT_DECRYPT & remote_supports_encrypt & typemask(type)))
+ return(0);
+ while (ep->type && ep->type != type)
+ ++ep;
+ return(ep->type ? ep : 0);
+}
+
+#define MAXKEYLEN 64
+
+static struct key_info {
+ unsigned char keyid[MAXKEYLEN];
+ int keylen;
+ int dir;
+ int *modep;
+ Encryptions *(*getcrypt)();
+} ki[2] = {
+ { { 0 }, 0, DIR_ENCRYPT, &encrypt_mode, findencryption },
+ { { 0 }, 0, DIR_DECRYPT, &decrypt_mode, finddecryption },
+};
+
+VOID
+#ifdef CK_ANSIC
+encrypt_init(kstream iks, int type)
+#else
+encrypt_init(iks, type) kstream iks; int type;
+#endif
+{
+ Encryptions *ep = encryptions;
+
+ i_support_encrypt = i_support_decrypt = 0;
+ remote_supports_encrypt = remote_supports_decrypt = 0;
+ i_wont_support_encrypt = i_wont_support_decrypt = 0;
+ encrypt_mode = 0;
+ decrypt_mode = 0;
+ encrypt_output = NULL;
+ decrypt_input = NULL;
+ ki[0].keylen = 0;
+ memset(ki[0].keyid,0,MAXKEYLEN);
+ ki[1].keylen = 0;
+ memset(ki[1].keyid,0,MAXKEYLEN);
+ havesessionkey = 0;
+ autoencrypt = 1;
+ autodecrypt = 1;
+
+ EncryptKSGlobalHack = iks;
+ EncryptType = type;
+
+ str_send[0] = IAC;
+ str_send[1] = SB;
+ str_send[2] = TELOPT_ENCRYPTION;
+ str_send[3] = ENCRYPT_SUPPORT;
+ str_suplen = 4;
+
+ while (ep->type) {
+ if ( EncryptType == ENCTYPE_ANY ||
+ EncryptType == ep->type ) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>I will support %s\n",
+ ENCTYPE_NAME(ep->type)); /* safe */
+ debug(F110,"encrypt_init",dbgbuf,0);
+ }
+#endif
+ i_support_encrypt |= typemask(ep->type);
+ i_support_decrypt |= typemask(ep->type);
+ if ((i_wont_support_decrypt & typemask(ep->type)) == 0)
+ if ((str_send[str_suplen++] = ep->type) == IAC)
+ str_send[str_suplen++] = IAC;
+ }
+ if (ep->init)
+ (*ep->init)(0);
+ ++ep;
+ }
+ str_send[str_suplen++] = IAC;
+ str_send[str_suplen++] = SE;
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_send_support(VOID)
+#else
+encrypt_send_support()
+#endif
+{
+ Encryptions *ep = encryptions;
+
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return;
+#endif /* CK_SSL */
+
+ str_send[0] = IAC;
+ str_send[1] = SB;
+ str_send[2] = TELOPT_ENCRYPTION;
+ str_send[3] = ENCRYPT_SUPPORT;
+ str_suplen = 4;
+
+ while (ep->type) {
+ if ( EncryptType == ENCTYPE_ANY ||
+ EncryptType == ep->type ) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>I will support %s\n",
+ ENCTYPE_NAME(ep->type)); /* safe */
+ debug(F110,"encrypt_send_support",dbgbuf,0);
+ }
+#endif
+ if ((i_wont_support_decrypt & typemask(ep->type)) == 0)
+ if ((str_send[str_suplen++] = ep->type) == IAC)
+ str_send[str_suplen++] = IAC;
+ }
+ ++ep;
+ }
+ str_send[str_suplen++] = IAC;
+ str_send[str_suplen++] = SE;
+
+ /*
+ * If the user has requested that decryption start
+ * immediatly, then send a "REQUEST START" before
+ * we negotiate the type.
+ */
+ if (autodecrypt)
+ encrypt_send_request_start();
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s SUPPORT ",
+ TELOPT(TELOPT_ENCRYPTION)); /* safe */
+ for ( i=4;i<str_suplen-2;i++ ) {
+ if ( str_send[i] == IAC ) {
+ ckstrncat(tn_msg,"IAC ",TN_MSG_LEN);
+ i++;
+ }
+ ckstrncat(tn_msg,ENCTYPE_NAME(str_send[i]),TN_MSG_LEN);
+ ckstrncat(tn_msg," ",TN_MSG_LEN);
+ }
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_send, str_suplen);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+
+ str_suplen = 0;
+}
+
+/*
+ * Called when ENCRYPT SUPPORT is received.
+ */
+int
+#ifdef CK_ANSIC
+encrypt_support(unsigned char *_typelist, int _cnt)
+#else
+encrypt_support(_typelist, _cnt) unsigned char * _typelist; int _cnt;
+#endif
+{
+ register int type, use_type = 0;
+ unsigned char * typelist = _typelist;
+ int cnt = _cnt;
+ Encryptions *ep;
+
+ debug(F111,"encrypt_support","cnt",cnt);
+
+ /*
+ * Forget anything the other side has previously told us.
+ */
+ remote_supports_decrypt = 0;
+
+ while (cnt-- > 0) {
+ type = *typelist++;
+ if ( EncryptType == ENCTYPE_ANY ||
+ EncryptType == type ) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Remote supports %s (%d)\n",
+ ENCTYPE_NAME(type), type); /* safe */
+ debug(F110,"encrypt_support",dbgbuf,0);
+ }
+#endif
+ if ((type < ENCTYPE_CNT) &&
+ (I_SUPPORT_ENCRYPT & typemask(type))) {
+ remote_supports_decrypt |= typemask(type);
+ if (use_type == 0)
+ use_type = type;
+ }
+ }
+ }
+ if (use_type) {
+ ep = findencryption(use_type);
+ if (!ep) {
+ debug(F111,"encrypt_support","findencryption == NULL",use_type);
+ return(-1);
+ }
+ type = ep->start ? (*ep->start)(DIR_ENCRYPT, 0) : 0;
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>(*ep->start)() %s returned %d (%s)\n",
+ ENCTYPE_NAME(use_type), type,
+ ENCRYPT_NAME(type)); /* safe */
+ debug(F110,"encrypt_support",dbgbuf,0);
+ }
+#endif
+ if (type < 0) {
+ debug(F111,"encrypt_support","type < 0",type);
+ return(-1);
+ }
+ encrypt_mode = use_type;
+ if (type == 0)
+ encrypt_start_output(use_type);
+ debug(F111,"encrypt_support","success",type);
+ return(0);
+ }
+ debug(F111,"encrypt_support","failed",use_type);
+ return(-1);
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_is(unsigned char *data, int cnt)
+#else
+encrypt_is(data, cnt) unsigned char *data; int cnt;
+#endif /* CK_ANSIC */
+{
+ Encryptions *ep;
+ register int type, ret;
+
+ if (--cnt < 0)
+ return(-1);
+ type = *data++;
+ if (type < ENCTYPE_CNT)
+ remote_supports_encrypt |= typemask(type);
+ if (!(ep = finddecryption(type))) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>encrypt_is: "
+ "Can't find type %s (%d) for initial negotiation\n",
+ ENCTYPE_NAME_OK(type)
+ ? ENCTYPE_NAME(type) : "(unknown)",
+ type); /* safe */
+ debug(F110,"encrypt_is",dbgbuf,0);
+ }
+#endif
+ return(-1);
+ }
+ if (!ep->is) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>encrypt_is: "
+ "No initial negotiation needed for type %s (%d)\n",
+ ENCTYPE_NAME_OK(type)
+ ? ENCTYPE_NAME(type) : "(unknown)",
+ type); /* safe */
+ debug(F110,"encrypt_is",dbgbuf,0);
+ }
+#endif
+ ret = 0;
+ } else {
+ ret = (*ep->is)(data, cnt);
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, "encrypt_is: "
+ "(*ep->is)(%x, %d) returned %s(%d)\n", data, cnt,
+ (ret < 0) ? "FAIL " :
+ (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); /* safe */
+ debug(F110,"encrypt_is",dbgbuf,0);
+ }
+#endif
+ }
+ if (ret < 0) {
+ autodecrypt = 0;
+ return(-1);
+ } else {
+ decrypt_mode = type;
+ if (ret == 0 && autodecrypt) {
+ encrypt_send_request_start();
+ }
+ }
+ return(0);
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_reply(unsigned char *data, int cnt)
+#else
+encrypt_reply(data, cnt) unsigned char *data; int cnt;
+#endif
+{
+ Encryptions *ep;
+ register int ret, type;
+
+ if (--cnt < 0)
+ return(-1);
+ type = *data++;
+ if (!(ep = findencryption(type))) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf,
+ ">>>Can't find type %s (%d) for initial negotiation\n",
+ ENCTYPE_NAME_OK(type)
+ ? ENCTYPE_NAME(type) : "(unknown)",
+ type); /* safe */
+ debug(F110,"encrypt_reply",dbgbuf,0);
+ }
+#endif
+ return(-1);
+ }
+ if (!ep->reply) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>No initial negotiation needed for type %s (%d)\n",
+ ENCTYPE_NAME_OK(type)
+ ? ENCTYPE_NAME(type) : "(unknown)",
+ type); /* safe */
+ debug(F110,"encrypt_reply",dbgbuf,0);
+ }
+#endif
+ ret = 0;
+ } else {
+ ret = (*ep->reply)(data, cnt);
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, "(*ep->reply)(%x, %d) returned %s(%d)\n",
+ data, cnt,
+ (ret < 0) ? "FAIL " :
+ (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); /* safe */
+ debug(F110,"encrypt_reply",dbgbuf,0);
+ }
+#endif
+ }
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>encrypt_reply returned %d\n", ret); /* safe */
+ debug(F110,"encrypt_reply",dbgbuf,0);
+ }
+#endif
+ if (ret < 0) {
+ autoencrypt = 0;
+ return(-1);
+ } else {
+ encrypt_mode = type;
+ if (ret == 0 && autoencrypt)
+ encrypt_start_output(type);
+ }
+ return(0);
+}
+
+/*
+ * Called when a ENCRYPT START command is received.
+ */
+int
+#ifdef CK_ANSIC
+encrypt_start(unsigned char *data, int cnt)
+#else
+encrypt_start(data, cnt) unsigned char *data; int cnt;
+#endif
+{
+ Encryptions *ep;
+
+ if (!decrypt_mode) {
+ /*
+ * Something is wrong. We should not get a START
+ * command without having already picked our
+ * decryption scheme. Send a REQUEST-END to
+ * attempt to clear the channel...
+ */
+ encrypt_send_request_end();
+ printf("Authentication error!\n%s\n",
+ "Warning, Cannot decrypt input stream!!!");
+ return(-1);
+ }
+
+ if (ep = finddecryption(decrypt_mode)) {
+ if ( decrypt_input != ep->input ) {
+ decrypt_input = ep->input;
+ EncryptKSGlobalHack->decrypt = decrypt_ks_stream;
+ EncryptKSGlobalHack->decrypt_type = ep->type;
+
+ if (encrypt_verbose) {
+ sprintf(dbgbuf, "Input is now decrypted with type %s",
+ ENCTYPE_NAME(decrypt_mode)); /* safe */
+ debug(F110,"encrypt_start",dbgbuf,0);
+ printf("%s\n",dbgbuf);
+ }
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Start to decrypt input with type %s",
+ ENCTYPE_NAME(decrypt_mode)); /* safe */
+ debug(F110,"ck_crp",dbgbuf,0);
+ }
+#endif
+ }
+ } else {
+ char buf[1024];
+ sprintf(buf, "Warning, Cannot decrypt type %s (%d)!!!",
+ ENCTYPE_NAME_OK(decrypt_mode)
+ ? ENCTYPE_NAME(decrypt_mode) : "(unknown)",
+ decrypt_mode); /* safe */
+ printf("Authentication error!\n%s\n",buf);
+ encrypt_send_request_end();
+ return(-1);
+ }
+ return(0);
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_dont_support(int type)
+#else
+encrypt_dont_support(type) int type;
+#endif
+{
+ i_wont_support_encrypt |= typemask(type);
+ i_wont_support_decrypt |= typemask(type);
+ return(0);
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_session_key(Session_Key *key, int server)
+#else
+encrypt_session_key(key, server) Session_Key *key; int server;
+#endif
+{
+ Encryptions *ep = encryptions;
+
+ if (havesessionkey)
+ return(0);
+
+ havesessionkey = 1;
+
+ while (ep->type) {
+ debug(F111,"encrypt_session_key",ep->name,ep->type);
+ if (ep->session) {
+ if ((*ep->session)(key, server) < 0) {
+ i_wont_support_encrypt |= typemask(ep->type);
+ i_wont_support_decrypt |= typemask(ep->type);
+ }
+ }
+ ++ep;
+ }
+ debug(F111,"encrypt_session_key (done)",ep->name,ep->type);
+ return(0);
+}
+
+/*
+ * Called when ENCRYPT END is received.
+ */
+int
+#ifdef CK_ANSIC
+encrypt_end(VOID)
+#else
+encrypt_end()
+#endif
+{
+ decrypt_input = NULL;
+ EncryptKSGlobalHack->decrypt = NULL;
+ EncryptKSGlobalHack->decrypt_type = ENCTYPE_ANY;
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Input is back to clear text"); /* safe */
+ debug(F110,"encrypt_end",dbgbuf,0);
+ }
+#endif
+ if (encrypt_verbose) {
+ sprintf(dbgbuf, "Input is now clear text"); /* safe */
+ debug(F110,"encrypt_end",dbgbuf,0);
+ printf("%s\n",dbgbuf);
+ }
+ return(0);
+}
+
+/*
+ * Called when ENCRYPT REQUEST-END is received.
+ */
+int
+#ifdef CK_ANSIC
+encrypt_request_end(VOID)
+#else
+encrypt_request_end()
+#endif
+{
+ encrypt_send_end();
+ return(0);
+}
+
+/*
+ * Called when ENCRYPT REQUEST-START is received. If we receive
+ * this before a type is picked, then that indicates that the
+ * other side wants us to start encrypting data as soon as we
+ * can.
+ */
+int
+#ifdef CK_ANSIC
+encrypt_request_start(VOID)
+#else
+encrypt_request_start()
+#endif
+{
+ if (encrypt_mode != 0)
+ encrypt_start_output(encrypt_mode);
+ return(0);
+}
+
+static unsigned char str_keyid[(MAXKEYLEN*2)+5] = {
+ IAC, SB, TELOPT_ENCRYPTION
+};
+_PROTOTYP(int encrypt_keyid,(struct key_info *,unsigned char *,int));
+
+int
+#ifdef CK_ANSIC
+encrypt_enc_keyid(unsigned char *keyid, int len)
+#else
+encrypt_enc_keyid(keyid, len) unsigned char *keyid; int len;
+#endif
+{
+ return(encrypt_keyid(&ki[1], keyid, len));
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_dec_keyid(unsigned char *keyid, int len)
+#else
+encrypt_dec_keyid(keyid, len) unsigned char *keyid; int len;
+#endif /* CK_ANSIC */
+{
+ return(encrypt_keyid(&ki[0], keyid, len));
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len)
+#else
+encrypt_keyid(kp, keyid, len)
+ struct key_info *kp; unsigned char *keyid; int len;
+#endif
+{
+ Encryptions *ep;
+ int dir = kp->dir;
+ register int ret = 0;
+
+ if (!(ep = (*kp->getcrypt)(*kp->modep))) {
+ if (len == 0)
+ return(-1);
+ kp->keylen = 0;
+ } else if (len == 0 || len > MAXKEYLEN) {
+ /*
+ * Empty option or Key too long, indicates a failure.
+ */
+ if (kp->keylen == 0)
+ return(-1);
+ kp->keylen = 0;
+ if (ep->keyid)
+ (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen);
+
+ } else if ((len != kp->keylen) || (memcmp(keyid, kp->keyid, len) != 0)) {
+ /*
+ * Length or contents are different
+ */
+ kp->keylen = len;
+ memcpy(kp->keyid, keyid, len); /* length < MAXKEYLEN */
+ if (ep->keyid)
+ (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen);
+ } else {
+ if (ep->keyid)
+ ret = (*ep->keyid)(dir, kp->keyid, &kp->keylen);
+ if ((ret == 0) && (dir == DIR_ENCRYPT) && autoencrypt)
+ encrypt_start_output(*kp->modep);
+ return(0);
+ }
+
+ encrypt_send_keyid(dir, kp->keyid, kp->keylen, 0);
+ return(0);
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_send_keyid(int dir, unsigned char *keyid, int keylen, int saveit)
+#else
+encrypt_send_keyid(dir, keyid, keylen, saveit)
+ int dir; unsigned char *keyid; int keylen; int saveit;
+#endif
+{
+ unsigned char *strp;
+
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return(0);
+#endif /* CK_SSL */
+
+ str_keyid[3] = (dir == DIR_ENCRYPT)
+ ? ENCRYPT_ENC_KEYID : ENCRYPT_DEC_KEYID;
+ if (saveit && keylen <= MAXKEYLEN) {
+ struct key_info *kp = &ki[(dir == DIR_ENCRYPT) ? 0 : 1];
+ memcpy(kp->keyid, keyid, keylen);
+ kp->keylen = keylen;
+ }
+
+ for (strp = &str_keyid[4]; keylen > 0; --keylen) {
+ if ((*strp++ = *keyid++) == IAC)
+ *strp++ = IAC;
+ }
+ *strp++ = IAC;
+ *strp++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s %s ",
+ TELOPT(TELOPT_ENCRYPTION),
+ (dir == DIR_ENCRYPT) ? "ENC-KEYID" : "DEC-KEYID"); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&str_keyid[4],strp-str_keyid-2-4);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_keyid, strp - str_keyid);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ return(0);
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_auto(int on)
+#else
+encrypt_auto(on) int on;
+#endif
+{
+ if (on < 0)
+ autoencrypt ^= 1;
+ else
+ autoencrypt = on ? 1 : 0;
+}
+
+VOID
+#ifdef CK_ANSIC
+decrypt_auto(int on)
+#else
+decrypt_auto(on) int on;
+#endif
+{
+ if (on < 0)
+ autodecrypt ^= 1;
+ else
+ autodecrypt = on ? 1 : 0;
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_start_output(int type)
+#else
+encrypt_start_output(type) int type;
+#endif
+{
+ Encryptions *ep;
+ register unsigned char *p;
+ register int i;
+
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return;
+#endif /* CK_SSL */
+
+ if (!(ep = findencryption(type))) {
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Can't encrypt with type %s (%d)\n",
+ ENCTYPE_NAME_OK(type)
+ ? ENCTYPE_NAME(type) : "(unknown)",
+ type); /* safe */
+ debug(F110,"encrypt_start_output",dbgbuf,0);
+ }
+#endif
+ return;
+ }
+ if (ep->start) {
+ i = (*ep->start)(DIR_ENCRYPT, 0);
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Encrypt start: %s (%d) %s\n",
+ (i < 0) ? "failed" :
+ "initial negotiation in progress",
+ i, ENCTYPE_NAME(type)); /* safe */
+ debug(F110,"encrypt_start_output",dbgbuf,0);
+ }
+#endif
+ if (i)
+ return;
+ }
+
+ if ( encrypt_output != ep->output ) {
+ p = str_start;
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPTION;
+ *p++ = ENCRYPT_START;
+ for (i = 0; i < ki[0].keylen; ++i) {
+ if (( *p++ = ki[0].keyid[i]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s START ",
+ TELOPT(TELOPT_ENCRYPTION)); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&str_start[4],p-str_start-2-4);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_start, p - str_start);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+
+ /*
+ * If we are already encrypting in some mode, then
+ * encrypt the ring (which includes our request) in
+ * the old mode, mark it all as "clear text" and then
+ * switch to the new mode.
+ */
+ encrypt_output = ep->output;
+ EncryptKSGlobalHack->encrypt = encrypt_ks_stream;
+ EncryptKSGlobalHack->encrypt_type = type;
+ encrypt_mode = type;
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Started to encrypt output with type %s",
+ ENCTYPE_NAME(type)); /* safe */
+ debug(F110,"encrypt_start_output",dbgbuf,0);
+ }
+#endif
+ if (encrypt_verbose) {
+ sprintf(dbgbuf, "Output is now encrypted with type %s",
+ ENCTYPE_NAME(type)); /* safe */
+ debug(F110,"encrypt_start_output",dbgbuf,0);
+ printf("%s\n",dbgbuf);
+ }
+ }
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_send_end(VOID)
+#else
+encrypt_send_end()
+#endif
+{
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return;
+#endif /* CK_SSL */
+
+ if (!encrypt_output)
+ return;
+
+ str_end[0] = IAC;
+ str_end[1] = SB;
+ str_end[2] = TELOPT_ENCRYPTION;
+ str_end[3] = ENCRYPT_END;
+ str_end[4] = IAC;
+ str_end[5] = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s END IAC SE",
+ TELOPT(TELOPT_ENCRYPTION)); /* safe */
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_end, sizeof(str_end));
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+
+ encrypt_output = 0;
+ EncryptKSGlobalHack->encrypt = NULL;
+ EncryptKSGlobalHack->encrypt_type = ENCTYPE_ANY;
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Output is back to clear text"); /* safe */
+ debug(F110,"encrypt_send_end",dbgbuf,0);
+ }
+#endif
+ if (encrypt_verbose) {
+ sprintf(dbgbuf, "Output is now clear text"); /* safe */
+ debug(F110,"encrypt_send_end",dbgbuf,0);
+ printf("%s\n",dbgbuf);
+ }
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_send_request_start(VOID)
+#else
+encrypt_send_request_start()
+#endif
+{
+ register unsigned char *p;
+ register int i;
+
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return;
+#endif /* CK_SSL */
+
+ p = str_start;
+ *p++ = IAC;
+ *p++ = SB;
+ *p++ = TELOPT_ENCRYPTION;
+ *p++ = ENCRYPT_REQSTART;
+ for (i = 0; i < ki[1].keylen; ++i) {
+ if (( *p++ = ki[1].keyid[i]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s REQUEST-START ",
+ TELOPT(TELOPT_ENCRYPTION)); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&str_start[4],p-str_start-2-4);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_start, p - str_start);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Request input to be encrypted\n"); /* safe */
+ debug(F110,"encrypt_send_request_start",dbgbuf,0);
+ }
+}
+
+VOID
+#ifdef CK_ANSIC
+encrypt_send_request_end(VOID)
+#else
+encrypt_send_request_end()
+#endif
+{
+#ifdef CK_SSL
+ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+ return;
+#endif /* CK_SSL */
+
+ str_end[0] = IAC;
+ str_end[1] = SB;
+ str_end[2] = TELOPT_ENCRYPTION;
+ str_end[3] = ENCRYPT_REQEND;
+ str_end[4] = IAC;
+ str_end[5] = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,"TELNET SENT SB %s REQEND IAC SE",
+ TELOPT(TELOPT_ENCRYPTION)); /* safe */
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(str_end, sizeof(str_end));
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+
+ if (encrypt_debug_mode) {
+ sprintf(dbgbuf, ">>>Request input to be clear text\n"); /* safe */
+ debug(F110,"encrypt_send_request_end",dbgbuf,0);
+ }
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_is_encrypting(VOID)
+#else
+encrypt_is_encrypting()
+#endif
+{
+ if (encrypt_output)
+ return 1;
+ return 0;
+}
+
+int
+#ifdef CK_ANSIC
+encrypt_is_decrypting(VOID)
+#else
+encrypt_is_decrypting()
+#endif
+{
+ if (decrypt_input)
+ return 1;
+ return 0;
+}
+
+#ifdef DEBUG
+void
+encrypt_debug(mode)
+ int mode;
+{
+ encrypt_debug_mode = mode;
+}
+#endif
+
+#ifdef CK_DES
+/*-
+ * Copyright (c) 1991, 1993
+ * 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.
+ */
+
+/* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */
+
+#define CFB 0
+#define OFB 1
+
+#define NO_SEND_IV 1
+#define NO_RECV_IV 2
+#define NO_KEYID 4
+#define IN_PROGRESS (NO_SEND_IV|NO_RECV_IV|NO_KEYID)
+#define SUCCESS 0
+#define xFAILED -1
+
+Schedule test_sched;
+
+struct des_stinfo {
+ Block str_output;
+ Block str_feed;
+ Block str_iv;
+ Block str_ikey;
+#ifdef MIT_CURRENT
+ unsigned char str_keybytes[8];
+ krb5_keyblock str_key;
+#else /* MIT_CURRENT */
+ Schedule str_sched;
+ int str_index;
+#endif /* MIT_CURRENT */
+ int str_flagshift;
+};
+
+struct des_fb {
+#ifndef MIT_CURRENT
+ Block krbdes_key;
+ Schedule krbdes_sched;
+#endif /* MIT_CURRENT */
+ Block temp_feed;
+ unsigned char fb_feed[64];
+ int need_start;
+ int state[2];
+ int keyid[2];
+ int once;
+#ifdef MIT_CURRENT
+ int validkey;
+#endif /* MIT_CURRENT */
+ struct des_stinfo streams[2];
+};
+static struct des_fb des_fb[2];
+
+struct des3_stinfo {
+ Block str_output;
+ Block str_feed;
+ Block str_iv;
+ Block str_ikey[3];
+ Schedule str_sched[3];
+ int str_index;
+ int str_flagshift;
+};
+
+struct des3_fb {
+#ifndef MIT_CURRENT
+ Block krbdes_key[3];
+ Schedule krbdes_sched[3];
+#endif /* MIT_CURRENT */
+ Block temp_feed;
+ unsigned char fb_feed[64];
+ int need_start;
+ int state[2];
+ int keyid[2];
+ int once;
+#ifdef MIT_CURRENT
+ int validkey;
+#endif /* MIT_CURRENT */
+ struct des3_stinfo streams[2];
+};
+static struct des3_fb des3_fb[2];
+
+struct keyidlist {
+ char *keyid;
+ int keyidlen;
+ char *key;
+ int keylen;
+ int flags;
+} keyidlist [] = {
+ { "\0", 1, 0, 0, 0 }, /* default key of zero */
+ { 0, 0, 0, 0, 0 }
+};
+
+#define KEYFLAG_MASK 03
+
+#define KEYFLAG_NOINIT 00
+#define KEYFLAG_INIT 01
+#define KEYFLAG_OK 02
+#define KEYFLAG_BAD 03
+
+#define KEYFLAG_SHIFT 2
+
+#define SHIFT_VAL(a,b) (KEYFLAG_SHIFT*((a)+((b)*2)))
+
+#define FB64_IV 1
+#define FB64_IV_OK 2
+#define FB64_IV_BAD 3
+#define FB64_CHALLENGE 4
+#define FB64_RESPONSE 5
+
+void fb64_stream_iv P((Block, struct des_stinfo *));
+void fb64_init P((struct des_fb *));
+static int fb64_start P((struct des_fb *, int, int));
+int fb64_is P((unsigned char *, int, struct des_fb *));
+int fb64_reply P((unsigned char *, int, struct des_fb *));
+static int fb64_session P((Session_Key *, int, struct des_fb *));
+void fb64_stream_key P((Block, struct des_stinfo *));
+int fb64_keyid P((int, unsigned char *, int *, struct des_fb *));
+
+#ifdef MIT_CURRENT
+static void
+#ifdef CK_ANSIC
+ecb_encrypt(struct des_stinfo *stp, Block in, Block out)
+#else /* CKANSIC */
+ecb_encrypt(stp, in, out)
+ struct des_stinfo *stp;
+ Block in;
+ Block out;
+#endif /* CK_ANSIC */
+{
+ krb5_error_code code;
+ krb5_data din;
+ krb5_enc_data dout;
+
+ din.length = 8;
+ din.data = in;
+
+ dout.ciphertext.length = 8;
+ dout.ciphertext.data = out;
+ dout.enctype = ENCTYPE_UNKNOWN;
+
+#ifdef CRYPT_DLL
+ code = krb5_c_encrypt(*p_k5_context, &stp->str_key, 0, 0,
+ &din, &dout);
+#else /* CRYPT_DLL */
+ code = krb5_c_encrypt(k5_context, &stp->str_key, 0, 0,
+ &din, &dout);
+#endif /* CRYPT_DLL */
+ /* XXX I'm not sure what to do if this fails */
+ if (code)
+ com_err("libtelnet", code, "encrypting stream data");
+}
+#endif /* MIT_CURRENT */
+
+void
+cfb64_init(server)
+ int server;
+{
+ fb64_init(&des_fb[CFB]);
+ des_fb[CFB].fb_feed[4] = ENCTYPE_DES_CFB64;
+ des_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB);
+ des_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB);
+}
+
+void
+ofb64_init(server)
+ int server;
+{
+ fb64_init(&des_fb[OFB]);
+ des_fb[OFB].fb_feed[4] = ENCTYPE_DES_OFB64;
+ des_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB);
+ des_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB);
+}
+
+void
+fb64_init(fbp)
+ register struct des_fb *fbp;
+{
+ memset((void *)fbp, 0, sizeof(*fbp));
+ fbp->state[0] = fbp->state[1] = xFAILED;
+ fbp->fb_feed[0] = IAC;
+ fbp->fb_feed[1] = SB;
+ fbp->fb_feed[2] = TELOPT_ENCRYPTION;
+ fbp->fb_feed[3] = ENCRYPT_IS;
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ * 2: Not yet. Other things (like getting the key from
+ * Kerberos) have to happen before we can continue.
+ */
+int
+cfb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(fb64_start(&des_fb[CFB], dir, server));
+}
+int
+ofb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(fb64_start(&des_fb[OFB], dir, server));
+}
+
+static int
+fb64_start(fbp, dir, server)
+ struct des_fb *fbp;
+ int dir;
+ int server;
+{
+ int x;
+ unsigned char *p;
+ register int state;
+
+ switch (dir) {
+ case DIR_DECRYPT:
+ /*
+ * This is simply a request to have the other side
+ * start output (our input). He will negotiate an
+ * IV so we need not look for it.
+ */
+ state = fbp->state[dir-1];
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ break;
+
+ case DIR_ENCRYPT:
+ state = fbp->state[dir-1];
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ else if ((state & NO_SEND_IV) == 0)
+ break;
+
+#ifdef MIT_CURRENT
+ if (!fbp->validkey) {
+ fbp->need_start = 1;
+ break;
+ }
+#else /* MIT_CURRENT */
+ if (!VALIDKEY(fbp->krbdes_key)) {
+ fbp->need_start = 1;
+ break;
+ }
+#endif /* MIT_CURRENT */
+ state &= ~NO_SEND_IV;
+ state |= NO_RECV_IV;
+ /*
+ * Create a random feed and send it over.
+ */
+#ifdef MIT_CURRENT
+ {
+ krb5_data d;
+ krb5_error_code code;
+
+ d.data = fbp->temp_feed;
+ d.length = sizeof(fbp->temp_feed);
+
+#ifdef CRYPT_DLL
+ if (code = krb5_c_random_make_octets(*p_k5_context,&d))
+ return(xFAILED);
+#else /* CRYPT_DLL */
+ if (code = krb5_c_random_make_octets(k5_context,&d))
+ return(xFAILED);
+#endif /* CRYPT_DLL */
+ }
+
+#else /* MIT_CURRENT */
+ des_new_random_key(fbp->temp_feed);
+ des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed,
+ fbp->krbdes_sched, 1);
+#endif /* MIT_CURRENT */
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_IS;
+ p++;
+ *p++ = FB64_IV;
+ for (x = 0; x < sizeof(Block); ++x) {
+ if (( *p++ = fbp->temp_feed[x]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s IS %s FB64_IV ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ break;
+ default:
+ return(xFAILED);
+ }
+ return(fbp->state[dir-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+cfb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(fb64_is(data, cnt, &des_fb[CFB]));
+}
+
+int
+ofb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(fb64_is(data, cnt, &des_fb[OFB]));
+}
+
+int
+fb64_is(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct des_fb *fbp;
+{
+ unsigned char *p;
+ register int state = fbp->state[DIR_DECRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+#ifdef CK_SSL
+ if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+#endif /* CK_SSL */
+ switch (*data++) {
+ case FB64_IV:
+ if (cnt != sizeof(Block)) {
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("CFB64: initial vector failed on size\r\n");
+#endif
+ state = xFAILED;
+ goto failure;
+ }
+
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ printf("CFB64: initial vector received\r\n");
+ printf("Initializing Decrypt stream\r\n");
+ }
+#endif
+ fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]);
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_OK;
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s REPLY %s FB64_IV_OK ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ state = IN_PROGRESS;
+ break;
+
+ default:
+#if 0
+ if (encrypt_debug_mode) {
+ printf("Unknown option type: %d\r\n", *(data-1));
+ printf("\r\n");
+ }
+#endif
+ /* FALL THROUGH */
+ failure:
+ /*
+ * We failed. Send an FB64_IV_BAD option
+ * to the other side so it will know that
+ * things failed.
+ */
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_BAD;
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s REPLY %s FB64_IV_BAD ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ break;
+ }
+ return(fbp->state[DIR_DECRYPT-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+cfb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(fb64_reply(data, cnt, &des_fb[CFB]));
+}
+int
+ofb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(fb64_reply(data, cnt, &des_fb[OFB]));
+}
+
+
+int
+fb64_reply(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct des_fb *fbp;
+{
+ register int state = fbp->state[DIR_ENCRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+ switch (*data++) {
+ case FB64_IV_OK:
+ fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ state &= ~NO_RECV_IV;
+ encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1);
+ break;
+
+ case FB64_IV_BAD:
+ memset(fbp->temp_feed, 0, sizeof(Block));
+ fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ state = xFAILED;
+ break;
+
+ default:
+#if 0
+ if (encrypt_debug_mode) {
+ printf("Unknown option type: %d\r\n", data[-1]);
+ printf("\r\n");
+ }
+#endif
+ /* FALL THROUGH */
+ failure:
+ state = xFAILED;
+ break;
+ }
+ return(fbp->state[DIR_ENCRYPT-1] = state);
+}
+
+int
+cfb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(fb64_session(key, server, &des_fb[CFB]));
+}
+
+int
+ofb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(fb64_session(key, server, &des_fb[OFB]));
+}
+
+static int
+fb64_session(key, server, fbp)
+ Session_Key *key;
+ int server;
+ struct des_fb *fbp;
+{
+ int rc=0;
+ int use2keys;
+ struct des_stinfo * s_stream;
+ struct des_stinfo * c_stream;
+
+ if(server) {
+ s_stream = &fbp->streams[DIR_ENCRYPT-1];
+ c_stream = &fbp->streams[DIR_DECRYPT-1];
+ }
+ else {
+ s_stream = &fbp->streams[DIR_DECRYPT-1];
+ c_stream = &fbp->streams[DIR_ENCRYPT-1];
+ }
+
+ if (!key || key->length < sizeof(Block)) {
+ CHAR buf[80];
+ sprintf(buf,"Can't set DES session key (%d < %d)",
+ key ? key->length : 0, sizeof(Block)); /* safe */
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("%s\r\n",buf);
+#endif
+ debug(F110,"fb64_session",buf,0);
+ return(-1);
+ }
+ use2keys = (key->type == SK_DES ||
+ key->length < 2 * sizeof(Block)) ? 0 : 1;
+#ifdef MIT_CURRENT
+ if(use2keys) {
+ memcpy((void *) fbp->keybytes,
+ (void *) (key->data + sizeof(Block)), sizeof(Block));
+ des_fixup_key_parity(fbp->);
+ fb64_stream_key(fbp->krbdes_key, s_stream);
+ }
+
+ memcpy((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block));
+ if (key->type != SK_DES)
+ des_fixup_key_parity(fbp->krbdes_key);
+
+ if(!use2keys)
+ fb64_stream_key(fbp->krbdes_key, s_stream);
+ fb64_stream_key(fbp->krbdes_key, c_stream);
+ fbp->validkey = 1;
+
+ fb64_stream_key(key->data, &fbp->streams[DIR_ENCRYPT-1]);
+ fb64_stream_key(key->data, &fbp->streams[DIR_DECRYPT-1]);
+#else /* MIT_CURRENT */
+ if(use2keys) {
+ memcpy((void *) fbp->krbdes_key,
+ (void *) (key->data + sizeof(Block)), sizeof(Block));
+ des_fixup_key_parity(fbp->krbdes_key);
+ fb64_stream_key(fbp->krbdes_key, s_stream);
+ }
+
+ memcpy((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block));
+ if (key->type != SK_DES)
+ des_fixup_key_parity(fbp->krbdes_key);
+
+ if(!use2keys)
+ fb64_stream_key(fbp->krbdes_key, s_stream);
+ fb64_stream_key(fbp->krbdes_key, c_stream);
+
+ if (fbp->once == 0) {
+ des_set_random_generator_seed(fbp->krbdes_key);
+ fbp->once = 1;
+ }
+
+ memset(fbp->krbdes_sched,0,sizeof(Schedule));
+ hexdump("fb64_session_key",fbp->krbdes_key,8);
+
+ rc = des_key_sched(fbp->krbdes_key, fbp->krbdes_sched);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption\n");
+ debug(F110,"fb64_session_key",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\n");
+ debug(F110,"fb64_session_key",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\n");
+ debug(F110,"fb64_session_key",
+ "Key Schedule not created by encryption",0);
+ }
+
+ hexdump("fb64_session_key schedule",fbp->krbdes_sched,8*16);
+#endif /* MIT_CURRENT */
+ /*
+ * Now look to see if krbdes_start() was was waiting for
+ * the key to show up. If so, go ahead an call it now
+ * that we have the key.
+ */
+ if (fbp->need_start) {
+ fbp->need_start = 0;
+ fb64_start(fbp, DIR_ENCRYPT, server);
+ }
+ return(0);
+}
+
+/*
+ * We only accept a keyid of 0. If we get a keyid of
+ * 0, then mark the state as SUCCESS.
+ */
+int
+cfb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(fb64_keyid(dir, kp, lenp, &des_fb[CFB]));
+}
+
+int
+ofb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(fb64_keyid(dir, kp, lenp, &des_fb[OFB]));
+}
+
+int
+fb64_keyid(dir, kp, lenp, fbp)
+ int dir, *lenp;
+ unsigned char *kp;
+ struct des_fb *fbp;
+{
+ register int state = fbp->state[dir-1];
+
+ if (*lenp != 1 || (*kp != '\0')) {
+ *lenp = 0;
+ return(state);
+ }
+
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+
+ state &= ~NO_KEYID;
+
+ return(fbp->state[dir-1] = state);
+}
+
+#if 0
+void
+fb64_printsub(data, cnt, buf, buflen, type)
+ unsigned char *data, *buf, *type;
+ int cnt, buflen;
+{
+ char lbuf[64];
+ register int i;
+ char *cp;
+
+ buf[buflen-1] = '\0'; /* make sure it's NULL terminated */
+ buflen -= 1;
+
+ switch(data[2]) {
+ case FB64_IV:
+ sprintf(lbuf, "%s_IV", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_OK:
+ sprintf(lbuf, "%s_IV_OK", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_BAD:
+ sprintf(lbuf, "%s_IV_BAD", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_CHALLENGE:
+ sprintf(lbuf, "%s_CHALLENGE", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_RESPONSE:
+ sprintf(lbuf, "%s_RESPONSE", type);
+ cp = lbuf;
+ goto common;
+
+ default:
+ sprintf(lbuf, " %d (unknown)", data[2]);
+ cp = lbuf;
+ common:
+ for (; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ for (i = 3; i < cnt; i++) {
+ sprintf(lbuf, " %d", data[i]);
+ for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ }
+ break;
+ }
+}
+
+void
+cfb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ fb64_printsub(data, cnt, buf, buflen, "CFB64");
+}
+
+void
+ofb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ fb64_printsub(data, cnt, buf, buflen, "OFB64");
+}
+#endif
+
+void
+fb64_stream_iv(seed, stp)
+ Block seed;
+ register struct des_stinfo *stp;
+{
+ int rc=0;
+
+ memcpy(stp->str_iv, seed, sizeof(Block));
+ memcpy(stp->str_output, seed, sizeof(Block));
+
+ memset(stp->str_sched,0,sizeof(Schedule));
+
+ hexdump("fb64_stream_iv",stp->str_ikey,8);
+
+#ifndef MIT_CURRENT
+ rc = des_key_sched(stp->str_ikey, stp->str_sched);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption\r\n");
+ debug(F110,"fb64_stream_iv",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\r\n");
+ debug(F110,"fb64_stream_iv",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\r\n");
+ debug(F110,"fb64_stream_iv",
+ "Key Schedule not created by encryption",0);
+ }
+ hexdump("fb64_stream_iv schedule",stp->str_sched,8*16);
+#endif /* MIT_CURRENT */
+
+ stp->str_index = sizeof(Block);
+}
+
+void
+fb64_stream_key(key, stp)
+ Block key;
+ register struct des_stinfo *stp;
+{
+ int rc = 0;
+
+#ifdef MIT_CURRENT
+ memcpy(stp->str_keybytes, key, sizeof(Block));
+ stp->str_key.length = 8;
+ stp->str_key.contents = stp->str_keybytes;
+ /* the original version of this code uses des ecb mode, but
+ it only ever does one block at a time. cbc with a zero iv
+ is identical */
+ stp->str_key.enctype = ENCTYPE_DES_CBC_RAW;
+#else /* MIT_CURRENT */
+ memcpy(stp->str_ikey, key, sizeof(Block));
+
+ memset(stp->str_sched,0,sizeof(Schedule));
+
+ hexdump("fb64_stream_key",key,8);
+
+ rc = des_key_sched(key, stp->str_sched);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption\r\n");
+ debug(F110,"fb64_stream_key",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\r\n");
+ debug(F110,"fb64_stream_key",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\r\n");
+ debug(F110,"fb64_stream_key",
+ "Key Schedule not created by encryption",0);
+ }
+ hexdump("fb64_stream_key schedule",stp->str_sched,8*16);
+#endif /* MIT_CURRENT */
+
+ memcpy(stp->str_output, stp->str_iv, sizeof(Block));
+
+ stp->str_index = sizeof(Block);
+}
+
+/*
+ * DES 64 bit Cipher Feedback
+ *
+ * key --->+-----+
+ * +->| DES |--+
+ * | +-----+ |
+ * | v
+ * INPUT --(--------->(+)+---> DATA
+ * | |
+ * +-------------+
+ *
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = DES(iV, key)
+ * On = Dn ^ Vn
+ * V(n+1) = DES(On, key)
+ */
+
+void
+cfb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ register struct des_stinfo *stp = &des_fb[CFB].streams[DIR_ENCRYPT-1];
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef MIT_CURRENT
+ ecb_encrypt(stp, stp->str_output, b);
+#else /* MIT_CURRENT */
+ des_ecb_encrypt(stp->str_output, b, stp->str_sched, 1);
+#endif /* MIT_CURRENT */
+ memcpy(stp->str_feed,b,sizeof(Block));
+ index = 0;
+ }
+
+ /* On encryption, we store (feed ^ data) which is cypher */
+ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
+ s++;
+ index++;
+ }
+ stp->str_index = index;
+}
+
+int
+cfb64_decrypt(data)
+ int data;
+{
+ register struct des_stinfo *stp = &des_fb[CFB].streams[DIR_DECRYPT-1];
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef MIT_CURRENT
+ ecb_encrypt(stp, stp->str_output, b);
+#else /* MIT_CURRENT */
+ des_ecb_encrypt(stp->str_output, b, stp->str_sched, 1);
+#endif /* MIT_CURRENT */
+ memcpy(stp->str_feed, b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ /* On decryption we store (data) which is cypher. */
+ stp->str_output[index] = data;
+ return(data ^ stp->str_feed[index]);
+}
+
+/*
+ * DES 64 bit Output Feedback
+ *
+ * key --->+-----+
+ * +->| DES |--+
+ * | +-----+ |
+ * +-----------+
+ * v
+ * INPUT -------->(+) ----> DATA
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = DES(iV, key)
+ * V(n+1) = DES(Vn, key)
+ * On = Dn ^ Vn
+ */
+void
+ofb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ register struct des_stinfo *stp = &des_fb[OFB].streams[DIR_ENCRYPT-1];
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef MIT_CURRENT
+ ecb_encrypt(stp, stp->str_feed, b);
+#else /* MIT_CURRENT */
+ des_ecb_encrypt(stp->str_feed, b, stp->str_sched, 1);
+#endif /* MIT_CURRENT */
+ memcpy(stp->str_feed,b,sizeof(Block));
+ index = 0;
+ }
+ *s++ ^= stp->str_feed[index];
+ index++;
+ }
+ stp->str_index = index;
+}
+
+int
+ofb64_decrypt(data)
+ int data;
+{
+ register struct des_stinfo *stp = &des_fb[OFB].streams[DIR_DECRYPT-1];
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef MIT_CURRENT
+ ecb_encrypt(stp, stp->str_feed, b);
+#else /* MIT_CURRENT */
+ des_ecb_encrypt(stp->str_feed, b, stp->str_sched, 1);
+#endif /* MIT_CURRENT */
+ memcpy(stp->str_feed, b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ return(data ^ stp->str_feed[index]);
+}
+
+
+void des3_fb64_stream_iv P((Block, struct des3_stinfo *));
+void des3_fb64_init P((struct des3_fb *));
+static int des3_fb64_start P((struct des3_fb *, int, int));
+int des3_fb64_is P((unsigned char *, int, struct des3_fb *));
+int des3_fb64_reply P((unsigned char *, int, struct des3_fb *));
+static int des3_fb64_session P((Session_Key *, int, struct des3_fb *));
+void des3_fb64_stream_key P((Block *, struct des3_stinfo *));
+int des3_fb64_keyid P((int, unsigned char *, int *, struct des3_fb *));
+
+void
+des3_cfb64_init(server)
+ int server;
+{
+ des3_fb64_init(&des3_fb[CFB]);
+ des3_fb[CFB].fb_feed[4] = ENCTYPE_DES3_CFB64;
+ des3_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB);
+ des3_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB);
+}
+
+void
+des3_ofb64_init(server)
+ int server;
+{
+ des3_fb64_init(&des3_fb[OFB]);
+ des3_fb[OFB].fb_feed[4] = ENCTYPE_DES3_OFB64;
+ des3_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB);
+ des3_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB);
+}
+
+void
+des3_fb64_init(fbp)
+ register struct des3_fb *fbp;
+{
+ memset((void *)fbp, 0, sizeof(*fbp));
+ fbp->state[0] = fbp->state[1] = xFAILED;
+ fbp->fb_feed[0] = IAC;
+ fbp->fb_feed[1] = SB;
+ fbp->fb_feed[2] = TELOPT_ENCRYPTION;
+ fbp->fb_feed[3] = ENCRYPT_IS;
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ * 2: Not yet. Other things (like getting the key from
+ * Kerberos) have to happen before we can continue.
+ */
+int
+des3_cfb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(des3_fb64_start(&des3_fb[CFB], dir, server));
+}
+int
+des3_ofb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(des3_fb64_start(&des3_fb[OFB], dir, server));
+}
+
+static int
+des3_fb64_start(fbp, dir, server)
+ struct des3_fb *fbp;
+ int dir;
+ int server;
+{
+ int x;
+ unsigned char *p;
+ register int state;
+
+ switch (dir) {
+ case DIR_DECRYPT:
+ /*
+ * This is simply a request to have the other side
+ * start output (our input). He will negotiate an
+ * IV so we need not look for it.
+ */
+ state = fbp->state[dir-1];
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ break;
+
+ case DIR_ENCRYPT:
+ state = fbp->state[dir-1];
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ else if ((state & NO_SEND_IV) == 0)
+ break;
+
+ if (!VALIDKEY(fbp->krbdes_key[0]) ||
+ !VALIDKEY(fbp->krbdes_key[1]) ||
+ !VALIDKEY(fbp->krbdes_key[2]) ) {
+ fbp->need_start = 1;
+ break;
+ }
+ state &= ~NO_SEND_IV;
+ state |= NO_RECV_IV;
+ /*
+ * Create a random feed and send it over.
+ */
+ des_new_random_key(fbp->temp_feed);
+#ifdef LIBDES
+ des_ecb3_encrypt(fbp->temp_feed, fbp->temp_feed,
+ fbp->krbdes_sched[0],
+ fbp->krbdes_sched[1],
+ fbp->krbdes_sched[2],
+ 1);
+#else /* LIBDES */
+ des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed,
+ fbp->krbdes_sched[0], 1);
+ des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed,
+ fbp->krbdes_sched[1], 0);
+ des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed,
+ fbp->krbdes_sched[2], 1);
+#endif /* LIBDES */
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_IS;
+ p++;
+ *p++ = FB64_IV;
+ for (x = 0; x < sizeof(Block); ++x) {
+ if (( *p++ = fbp->temp_feed[x]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s IS %s FB64_IV ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ break;
+ default:
+ return(xFAILED);
+ }
+ return(fbp->state[dir-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+des3_cfb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(des3_fb64_is(data, cnt, &des3_fb[CFB]));
+}
+
+int
+des3_ofb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(des3_fb64_is(data, cnt, &des3_fb[OFB]));
+}
+
+int
+des3_fb64_is(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct des3_fb *fbp;
+{
+ unsigned char *p;
+ register int state = fbp->state[DIR_DECRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+#ifdef CK_SSL
+ if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+#endif /* CK_SSL */
+ switch (*data++) {
+ case FB64_IV:
+ if (cnt != sizeof(Block)) {
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("DES3_FB64: initial vector failed on size\r\n");
+#endif
+ state = xFAILED;
+ goto failure;
+ }
+
+#ifdef DEBUG
+ if (encrypt_debug_mode) {
+ printf("DES3_FB64: initial vector received\r\n");
+ printf("Initializing Decrypt stream\r\n");
+ }
+#endif
+ des3_fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]);
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_OK;
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s REPLY %s FB64_IV_OK ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ state = IN_PROGRESS;
+ break;
+
+ default:
+#if 0
+ if (encrypt_debug_mode) {
+ printf("Unknown option type: %d\r\n", *(data-1));
+ printf("\r\n");
+ }
+#endif
+ /* FALL THROUGH */
+ failure:
+ /*
+ * We failed. Send an FB64_IV_BAD option
+ * to the other side so it will know that
+ * things failed.
+ */
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_BAD;
+ *p++ = IAC;
+ *p++ = SE;
+
+ if (deblog || tn_deb || debses) {
+ int i;
+ sprintf(tn_msg,
+ "TELNET SENT SB %s REPLY %s FB64_IV_BAD ",
+ TELOPT(fbp->fb_feed[2]),
+ enctype_names[fbp->fb_feed[4]]); /* safe */
+ tn_hex(tn_msg,TN_MSG_LEN,&fbp->fb_feed[6],(p-fbp->fb_feed)-2-6);
+ ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN);
+ debug(F100,tn_msg,"",0);
+ if (tn_deb || debses) tn_debug(tn_msg);
+ }
+#ifdef OS2
+ RequestTelnetMutex( SEM_INDEFINITE_WAIT );
+#endif
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+#ifdef OS2
+ ReleaseTelnetMutex();
+#endif
+ break;
+ }
+ return(fbp->state[DIR_DECRYPT-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+int
+des3_cfb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(des3_fb64_reply(data, cnt, &des3_fb[CFB]));
+}
+int
+des3_ofb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(des3_fb64_reply(data, cnt, &des3_fb[OFB]));
+}
+
+
+int
+des3_fb64_reply(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct des3_fb *fbp;
+{
+ register int state = fbp->state[DIR_ENCRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+ switch (*data++) {
+ case FB64_IV_OK:
+ des3_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+ state &= ~NO_RECV_IV;
+ encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1);
+ break;
+
+ case FB64_IV_BAD:
+ memset(fbp->temp_feed, 0, sizeof(Block));
+ des3_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ state = xFAILED;
+ break;
+
+ default:
+#if 0
+ if (encrypt_debug_mode) {
+ printf("Unknown option type: %d\r\n", data[-1]);
+ printf("\r\n");
+ }
+#endif
+ /* FALL THROUGH */
+ failure:
+ state = xFAILED;
+ break;
+ }
+ return(fbp->state[DIR_ENCRYPT-1] = state);
+}
+
+int
+des3_cfb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(des3_fb64_session(key, server, &des3_fb[CFB]));
+}
+
+int
+des3_ofb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(des3_fb64_session(key, server, &des3_fb[OFB]));
+}
+
+static int
+des3_fb64_session(key, server, fbp)
+ Session_Key *key;
+ int server;
+ struct des3_fb *fbp;
+{
+ int rc=0,i=0;
+ int keys2use=0;
+ struct des3_stinfo * s_stream;
+ struct des3_stinfo * c_stream;
+
+ if(server) {
+ s_stream = &fbp->streams[DIR_ENCRYPT-1];
+ c_stream = &fbp->streams[DIR_DECRYPT-1];
+ }
+ else {
+ s_stream = &fbp->streams[DIR_DECRYPT-1];
+ c_stream = &fbp->streams[DIR_ENCRYPT-1];
+ }
+
+ keys2use = key->length / sizeof(Block);
+ if (!key || (key->type == SK_DES) || (keys2use < 2)) {
+ CHAR buf[80];
+ sprintf(buf,"Can't set 3DES session key (%d < %d)",
+ key ? key->length : 0, 2 * sizeof(Block)); /* safe */
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("%s\r\n",buf);
+#endif
+ debug(F110,"des3_fb64_session",buf,0);
+ return(-1);
+ }
+
+ debug(F111,"des3_fb64_session","keys2use",keys2use);
+ /* Compute the first set of keys / key order */
+ switch ( keys2use ) {
+ case 2:
+ memcpy((void *)fbp->krbdes_key[0], (void *)key->data, sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[1],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block));
+ break;
+ case 3:
+ default:
+ memcpy((void *)fbp->krbdes_key[0], (void *)key->data, sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[1],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[2],
+ (void *) (key->data + 2*sizeof(Block)), sizeof(Block));
+ break;
+ }
+ hexdump("des3_session_key key->data",key->data,sizeof(Block));
+ hexdump("des3_session_key fbp->krbdes_key[0]",
+ fbp->krbdes_key[0],
+ sizeof(Block)
+ );
+ if (fbp->once == 0) {
+ des_set_random_generator_seed(fbp->krbdes_key[0]);
+ fbp->once = 1;
+ }
+
+ for ( i=0;i<3;i++ )
+ des_fixup_key_parity(fbp->krbdes_key[i]);
+ des3_fb64_stream_key(fbp->krbdes_key, s_stream);
+
+
+ /* Compute the second set of keys / key order */
+ switch ( keys2use ) {
+ case 2:
+ memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[1], (void *)key->data, sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[2],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ break;
+ case 3:
+ memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[1],
+ (void *) (key->data + 2*sizeof(Block)), sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block));
+ break;
+ case 4:
+ memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[1],
+ (void *) (key->data + 3*sizeof(Block)), sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block));
+ break;
+ case 5:
+ memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)),
+ sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[1],
+ (void *) (key->data + 3*sizeof(Block)), sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[2],
+ (void *)(key->data + 4*sizeof(Block)), sizeof(Block));
+ break;
+ case 6:
+ memcpy((void *) fbp->krbdes_key[0],
+ (void *) (key->data + 3*sizeof(Block)), sizeof(Block));
+ memcpy((void *)fbp->krbdes_key[1],
+ (void *)(key->data + 4*sizeof(Block)), sizeof(Block));
+ memcpy((void *) fbp->krbdes_key[2],
+ (void *) (key->data + 5 *sizeof(Block)), sizeof(Block));
+ break;
+ }
+
+ for ( i=0;i<3;i++ )
+ des_fixup_key_parity(fbp->krbdes_key[i]);
+ des3_fb64_stream_key(fbp->krbdes_key, c_stream);
+
+ /* now use the second set of keys to build the default Key Schedule */
+ /* which is used for generating the IV. */
+ for ( i=0;i<3;i++ ) {
+ memset(fbp->krbdes_sched[i],0,sizeof(Schedule));
+
+ rc = des_key_sched(fbp->krbdes_key[i], fbp->krbdes_sched[i]);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption [DES3,%s]\r\n",
+ server?"server":"client");
+ debug(F110,"des3_fb64_stream_iv",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\r\n");
+ debug(F110,"des3_fb64_stream_iv",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\r\n");
+ debug(F110,"des3_fb64_stream_iv",
+ "Key Schedule not created by encryption",0);
+ }
+ hexdump("des3_fb64_session_key schedule",fbp->krbdes_sched[i],8*16);
+ }
+ /*
+ * Now look to see if krbdes_start() was was waiting for
+ * the key to show up. If so, go ahead an call it now
+ * that we have the key.
+ */
+ if (fbp->need_start) {
+ fbp->need_start = 0;
+ des3_fb64_start(fbp, DIR_ENCRYPT, server);
+ }
+ return(0);
+}
+
+/*
+ * We only accept a keyid of 0. If we get a keyid of
+ * 0, then mark the state as SUCCESS.
+ */
+int
+des3_cfb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(des3_fb64_keyid(dir, kp, lenp, &des3_fb[CFB]));
+}
+
+int
+des3_ofb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(des3_fb64_keyid(dir, kp, lenp, &des3_fb[OFB]));
+}
+
+int
+des3_fb64_keyid(dir, kp, lenp, fbp)
+ int dir, *lenp;
+ unsigned char *kp;
+ struct des3_fb *fbp;
+{
+ register int state = fbp->state[dir-1];
+
+ if (*lenp != 1 || (*kp != '\0')) {
+ *lenp = 0;
+ return(state);
+ }
+
+ if (state == xFAILED)
+ state = IN_PROGRESS;
+
+ state &= ~NO_KEYID;
+
+ return(fbp->state[dir-1] = state);
+}
+
+#if 0
+void
+des3_fb64_printsub(data, cnt, buf, buflen, type)
+ unsigned char *data, *buf, *type;
+ int cnt, buflen;
+{
+ char lbuf[64];
+ register int i;
+ char *cp;
+
+ buf[buflen-1] = '\0'; /* make sure it's NULL terminated */
+ buflen -= 1;
+
+ switch(data[2]) {
+ case FB64_IV:
+ sprintf(lbuf, "%s_IV", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_OK:
+ sprintf(lbuf, "%s_IV_OK", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_BAD:
+ sprintf(lbuf, "%s_IV_BAD", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_CHALLENGE:
+ sprintf(lbuf, "%s_CHALLENGE", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_RESPONSE:
+ sprintf(lbuf, "%s_RESPONSE", type);
+ cp = lbuf;
+ goto common;
+
+ default:
+ sprintf(lbuf, " %d (unknown)", data[2]);
+ cp = lbuf;
+ common:
+ for (; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ for (i = 3; i < cnt; i++) {
+ sprintf(lbuf, " %d", data[i]);
+ for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ }
+ break;
+ }
+}
+
+void
+des3_cfb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ des3_fb64_printsub(data, cnt, buf, buflen, "CFB64");
+}
+
+void
+des3_ofb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ des3_fb64_printsub(data, cnt, buf, buflen, "OFB64");
+}
+#endif
+
+void
+des3_fb64_stream_iv(seed, stp)
+ Block seed;
+ register struct des3_stinfo *stp;
+{
+ int rc=0, i = 0;;
+
+ memcpy(stp->str_iv, seed, sizeof(Block));
+ memcpy(stp->str_output, seed, sizeof(Block));
+ for ( i=0;i<3;i++ ) {
+ memset(stp->str_sched[i],0,sizeof(Schedule));
+
+ hexdump("des3_fb64_stream_iv",stp->str_ikey[i],8);
+
+ rc = des_key_sched(stp->str_ikey[i], stp->str_sched[i]);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption [DES3 iv]\r\n");
+ debug(F110,"des3_fb64_stream_iv",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\r\n");
+ debug(F110,"des3_fb64_stream_iv",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\r\n");
+ debug(F110,"des3_fb64_stream_iv",
+ "Key Schedule not created by encryption",0);
+ }
+ hexdump("des3_fb64_stream_iv schedule",stp->str_sched[i],8*16);
+ }
+ stp->str_index = sizeof(Block);
+}
+
+void
+des3_fb64_stream_key(key, stp)
+ Block * key;
+ register struct des3_stinfo *stp;
+{
+ int rc = 0, i = 0;
+
+ for ( i=0;i<3;i++ ) {
+ memcpy(stp->str_ikey[i], key[i], sizeof(Block));
+
+ memset(stp->str_sched[i],0,sizeof(Schedule));
+
+ hexdump("des3_fb64_stream_key",key[i],8);
+
+ rc = des_key_sched(key[i], stp->str_sched[i]);
+ if ( rc == -1 ) {
+ printf("?Invalid DES key specified for encryption [DES3 key]\r\n");
+ debug(F110,"des3_fb64_stream_key",
+ "invalid DES Key specified for encryption",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified for encryption\r\n");
+ debug(F110,"des3_fb64_stream_key",
+ "weak DES Key specified for encryption",0);
+ } else if ( rc != 0 ) {
+ printf("?Key Schedule not created by encryption\r\n");
+ debug(F110,"des3_fb64_stream_key",
+ "Key Schedule not created by encryption",0);
+ }
+ hexdump("des3_fb64_stream_key schedule",stp->str_sched[i],8*16);
+ }
+
+ memcpy(stp->str_output, stp->str_iv, sizeof(Block));
+ stp->str_index = sizeof(Block);
+}
+
+/*
+ * DES3 64 bit Cipher Feedback
+ *
+ * key1 key2 key3
+ * | | |
+ * v v v
+ * +-------+ +-------+ +-------+
+ * +->| DES-e |->| DES-d |->| DES-e |-- +
+ * | +-------+ +-------+ +-------+ |
+ * | v
+ * INPUT --(-------------------------------->(+)+---> DATA
+ * | |
+ * +------------------------------------+
+ *
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = DES-e(DES-d(DES-e(iV, key1),key2),key3)
+ * On = Dn ^ Vn
+ * V(n+1) = DES-e(DES-d(DES-e(On, key1),key2),key3)
+ */
+
+void
+des3_cfb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ register struct des3_stinfo *stp = &des3_fb[CFB].streams[DIR_ENCRYPT-1];
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef LIBDES
+ des_ecb3_encrypt(stp->str_output, b, stp->str_sched[0],
+ stp->str_sched[1], stp->str_sched[2], 1);
+#else /* LIBDES */
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[0], 1);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[1], 0);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[2], 1);
+#endif /* LIBDES */
+ memcpy(stp->str_feed,b,sizeof(Block));
+ index = 0;
+ }
+
+ /* On encryption, we store (feed ^ data) which is cypher */
+ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
+ s++;
+ index++;
+ }
+ stp->str_index = index;
+}
+
+int
+des3_cfb64_decrypt(data)
+ int data;
+{
+ register struct des3_stinfo *stp = &des3_fb[CFB].streams[DIR_DECRYPT-1];
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef LIBDES
+ des_ecb3_encrypt(stp->str_output, b, stp->str_sched[0],
+ stp->str_sched[1], stp->str_sched[2], 1);
+#else /* LIBDES */
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[0], 1);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[1], 0);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[2], 1);
+#endif /* LIBDES */
+ memcpy(stp->str_feed, b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ /* On decryption we store (data) which is cypher. */
+ stp->str_output[index] = data;
+ return(data ^ stp->str_feed[index]);
+}
+
+/*
+ * DES3 64 bit Output Feedback
+ *
+ *
+ * key1 key2 key3
+ * | | |
+ * v v v
+ * +-------+ +-------+ +-------+
+ * +->| DES-e |->| DES-d |->| DES-e |-- +
+ * | +-------+ +-------+ +-------+ |
+ * +------------------------------------+
+ * v
+ * INPUT ------------------------------------->(+) ----> DATA
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = DES-e(DES-d(DES-e(iV, key1),key2),key3)
+ * V(n+1) = DES-e(DES-d(DES-e(Vn, key1),key2),key3)
+ * On = Dn ^ Vn
+ */
+void
+des3_ofb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ register struct des3_stinfo *stp = &des3_fb[OFB].streams[DIR_ENCRYPT-1];
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef LIBDES
+ des_ecb3_encrypt(stp->str_feed, b, stp->str_sched[0],
+ stp->str_sched[1], stp->str_sched[2], 1);
+#else /* LIBDES */
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[0], 1);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[1], 0);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[2], 1);
+#endif /* LIBDES */
+ memcpy(stp->str_feed,b,sizeof(Block));
+ index = 0;
+ }
+ *s++ ^= stp->str_feed[index];
+ index++;
+ }
+ stp->str_index = index;
+}
+
+int
+des3_ofb64_decrypt(data)
+ int data;
+{
+ register struct des3_stinfo *stp = &des3_fb[OFB].streams[DIR_DECRYPT-1];
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+#ifdef LIBDES
+ des_ecb3_encrypt(stp->str_feed, b, stp->str_sched[0],
+ stp->str_sched[1], stp->str_sched[2], 1);
+#else /* LIBDES */
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[0], 1);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[1], 0);
+ des_ecb_encrypt(stp->str_output, b,
+ stp->str_sched[2], 1);
+#endif /* LIBDES */
+ memcpy(stp->str_feed, b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ return(data ^ stp->str_feed[index]);
+}
+#endif /* CK_DES */
+
+#ifdef CK_CAST
+/*-
+ * Copyright (c) 1991, 1993
+ * 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.
+ */
+
+/*
+ * Copyright (c) 1997 Stanford University
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+
+/*
+ * cast.h
+ * Author: Tom Wu
+ *
+ * Type and function declarations for CAST.
+ */
+
+#ifndef _CAST_H_
+#define _CAST_H_
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#else
+#define P(x) ()
+#endif /* __STDC__ */
+#endif /* P */
+
+#ifndef LITTLE_ENDIAN
+#ifndef BIG_ENDIAN
+#ifndef WORDS_BIGENDIAN
+#define LITTLE_ENDIAN 1
+#endif /* WORDS_BIGENDIAN */
+#endif /* BIG_ENDIAN */
+#endif /* LITTLE_ENDIAN */
+
+typedef unsigned int uint32; /* Must be 32 bits */
+typedef uint32 * uint32p;
+typedef unsigned char uint8;
+typedef uint8 * uint8p;
+
+typedef struct {
+ struct CastSubkeyPair {
+ uint32 Km;
+ uint32 Kr;
+ } K[16];
+ int ksize;
+} CastKeySched;
+
+/*
+ * cast*_key_sched(schedule, key)
+ *
+ * Initializes the CAST key schedule "schedule" according to the given key.
+ * The different setup routines accept different length keys:
+ *
+ * ck_cast5_40_key_sched: 40-bit/5-byte (12 round) keys
+ * ck_cast5_64_key_sched: 64-bit/8-byte (12 round) keys
+ * ck_cast5_80_key_sched: 80-bit/10-byte (12 round) keys
+ * ck_cast128_key_sched: 128-bit/16-byte (16 round) keys
+ */
+
+extern void ck_cast5_40_key_sched P((CastKeySched *, uint8 *));
+extern void ck_cast5_64_key_sched P((CastKeySched *, uint8 *));
+extern void ck_cast5_80_key_sched P((CastKeySched *, uint8 *));
+extern void ck_cast128_key_sched P((CastKeySched *, uint8 *));
+
+/*
+ * ck_cast_ecb_encrypt(output, input, schedule, mode)
+ * ck_cast_ecb_crypt(data, schedule, mode)
+ *
+ * Encrypts the 64-bit "input" according to the CAST key schedule
+ * "schedule" and places the result in "output". If "mode" is 0,
+ * ck_cast_ecb_encrypt will encrypt, otherwise it will decrypt.
+ * "Output" and "input" can point to the same memory, in which case
+ * en/decryption will be performed in place.
+ *
+ * ck_cast_ecb_crypt accepts input in the form of an array of two
+ * 32-bit words and performs encryption/decryption in place.
+ */
+
+extern void ck_cast_ecb_encrypt P((uint8 *, uint8 *, CastKeySched *, int));
+extern void ck_cast_ecb_crypt P((uint32 *, CastKeySched *, int));
+
+#endif /* CAST_H */
+
+extern encrypt_debug_mode;
+
+#define CFB_40 0
+#define OFB_40 1
+#ifdef CAST_EXPORT_ENCRYPTION
+#define FB_CNT 2
+#else
+#define CFB_128 2
+#define OFB_128 3
+#define FB_CNT 4
+#endif
+
+#define NO_SEND_IV 1
+#define NO_RECV_IV 2
+#define NO_KEYID 4
+#define IN_PROGRESS (NO_SEND_IV|NO_RECV_IV|NO_KEYID)
+#define SUCCESS 0
+#define cFAILED -1
+
+
+struct cast_fb {
+ Block temp_feed;
+ unsigned char fb_feed[64];
+ int key_isset;
+ int need_start;
+ int state[2];
+ struct cast_stinfo {
+ Block str_output;
+ Block str_feed;
+ Block str_iv;
+ CastKeySched str_sched;
+ int str_index;
+ } streams[2];
+};
+
+static struct cast_fb cast_fb[FB_CNT];
+
+#define FB64_IV 1
+#define FB64_IV_OK 2
+#define FB64_IV_BAD 3
+
+
+static void cast_fb64_stream_iv P((Block, struct cast_stinfo *));
+static void cast_fb64_init P((struct cast_fb *));
+static int cast_fb64_start P((struct cast_fb *, int, int));
+static int cast_fb64_is P((unsigned char *, int, struct cast_fb *));
+static int cast_fb64_reply P((unsigned char *, int, struct cast_fb *));
+static int cast_fb64_session P((Session_Key *, int, struct cast_fb *, int));
+static void cast_fb64_stream_key P((Block, struct cast_stinfo *, int));
+static int cast_fb64_keyid P((int, unsigned char *, int *, struct cast_fb *));
+static void _cast_cfb64_encrypt P((unsigned char *,int, struct cast_stinfo *));
+static int _cast_cfb64_decrypt P((int, struct cast_stinfo *));
+static void _cast_ofb64_encrypt P((unsigned char *,int, struct cast_stinfo *));
+static int _cast_ofb64_decrypt P((int, struct cast_stinfo *));
+
+#ifndef CAST_EXPORT_ENCRYPTION
+void
+cast_cfb64_init(server)
+ int server;
+{
+ cast_fb64_init(&cast_fb[CFB_128]);
+ cast_fb[CFB_128].fb_feed[4] = ENCTYPE_CAST128_CFB64;
+}
+
+void
+cast_ofb64_init(server)
+ int server;
+{
+ cast_fb64_init(&cast_fb[OFB_128]);
+ cast_fb[OFB_128].fb_feed[4] = ENCTYPE_CAST128_OFB64;
+}
+#endif
+
+void
+castexp_cfb64_init(server)
+ int server;
+{
+ cast_fb64_init(&cast_fb[CFB_40]);
+ cast_fb[CFB_40].fb_feed[4] = ENCTYPE_CAST5_40_CFB64;
+}
+
+void
+castexp_ofb64_init(server)
+ int server;
+{
+ cast_fb64_init(&cast_fb[OFB_40]);
+ cast_fb[OFB_40].fb_feed[4] = ENCTYPE_CAST5_40_OFB64;
+}
+
+static void
+cast_fb64_init(fbp)
+ register struct cast_fb *fbp;
+{
+ memset((void *)fbp, 0, sizeof(*fbp));
+ fbp->key_isset = 0;
+ fbp->state[0] = fbp->state[1] = cFAILED;
+ fbp->fb_feed[0] = IAC;
+ fbp->fb_feed[1] = SB;
+ fbp->fb_feed[2] = TELOPT_ENCRYPTION;
+ fbp->fb_feed[3] = ENCRYPT_IS;
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ * 2: Not yet. Other things (like getting the key from
+ * Kerberos) have to happen before we can continue.
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(cast_fb64_start(&cast_fb[CFB_128], dir, server));
+}
+
+int
+cast_ofb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(cast_fb64_start(&cast_fb[OFB_128], dir, server));
+}
+#endif
+
+int
+castexp_cfb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(cast_fb64_start(&cast_fb[CFB_40], dir, server));
+}
+
+int
+castexp_ofb64_start(dir, server)
+ int dir;
+ int server;
+{
+ return(cast_fb64_start(&cast_fb[OFB_40], dir, server));
+}
+
+static int
+cast_fb64_start(fbp, dir, server)
+ struct cast_fb *fbp;
+ int dir;
+ int server;
+{
+ Block b;
+ int x;
+ unsigned char *p;
+ register int state;
+
+ switch (dir) {
+ case DIR_DECRYPT:
+ /*
+ * This is simply a request to have the other side
+ * start output (our input). He will negotiate an
+ * IV so we need not look for it.
+ */
+ state = fbp->state[dir-1];
+ if (state == cFAILED)
+ state = IN_PROGRESS;
+ break;
+
+ case DIR_ENCRYPT:
+ state = fbp->state[dir-1];
+ if (state == cFAILED)
+ state = IN_PROGRESS;
+ else if ((state & NO_SEND_IV) == 0)
+ break;
+
+ if (!fbp->key_isset) {
+ fbp->need_start = 1;
+ break;
+ }
+ state &= ~NO_SEND_IV;
+ state |= NO_RECV_IV;
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("Creating new feed\r\n");
+#endif
+ /*
+ * Create a random feed and send it over.
+ */
+ ck_cast_ecb_encrypt(fbp->temp_feed, fbp->temp_feed,
+ &fbp->streams[dir-1].str_sched, 0);
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_IS;
+ p++;
+ *p++ = FB64_IV;
+ for (x = 0; x < sizeof(Block); ++x) {
+ if ((*p++ = fbp->temp_feed[x]) == IAC)
+ *p++ = IAC;
+ }
+ *p++ = IAC;
+ *p++ = SE;
+
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+ break;
+ default:
+ return(cFAILED);
+ }
+ return(fbp->state[dir-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_is(data, cnt, &cast_fb[CFB_128]));
+}
+
+int
+cast_ofb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_is(data, cnt, &cast_fb[OFB_128]));
+}
+#endif
+
+int
+castexp_cfb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_is(data, cnt, &cast_fb[CFB_40]));
+}
+
+int
+castexp_ofb64_is(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_is(data, cnt, &cast_fb[OFB_40]));
+}
+
+static int
+cast_fb64_is(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct cast_fb *fbp;
+{
+ int x;
+ unsigned char *p;
+ Block b;
+ register int state = fbp->state[DIR_DECRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+#ifdef CK_SSL
+ if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows)
+#endif /* CK_SSL */
+ switch (*data++) {
+ case FB64_IV:
+ if (cnt != sizeof(Block)) {
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("FB64: initial vector failed on size\r\n");
+#endif
+ state = cFAILED;
+ goto failure;
+ }
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("FB64: initial vector received\r\n");
+
+ if (encrypt_debug_mode)
+ printf("Initializing Decrypt stream\r\n");
+#endif
+ cast_fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]);
+
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_OK;
+ *p++ = IAC;
+ *p++ = SE;
+
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+ state = IN_PROGRESS;
+ break;
+
+ default:
+ /* unknown option type */
+ /* FALL THROUGH */
+ failure:
+ /*
+ * We failed. Send an FB64_IV_BAD option
+ * to the other side so it will know that
+ * things failed.
+ */
+ p = fbp->fb_feed + 3;
+ *p++ = ENCRYPT_REPLY;
+ p++;
+ *p++ = FB64_IV_BAD;
+ *p++ = IAC;
+ *p++ = SE;
+
+ ttol(fbp->fb_feed, p - fbp->fb_feed);
+ break;
+ }
+ return(fbp->state[DIR_DECRYPT-1] = state);
+}
+
+/*
+ * Returns:
+ * -1: some error. Negotiation is done, encryption not ready.
+ * 0: Successful, initial negotiation all done.
+ * 1: successful, negotiation not done yet.
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_reply(data, cnt, &cast_fb[CFB_128]));
+}
+
+int
+cast_ofb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_reply(data, cnt, &cast_fb[OFB_128]));
+}
+#endif
+
+int
+castexp_cfb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_reply(data, cnt, &cast_fb[CFB_40]));
+}
+
+int
+castexp_ofb64_reply(data, cnt)
+ unsigned char *data;
+ int cnt;
+{
+ return(cast_fb64_reply(data, cnt, &cast_fb[OFB_40]));
+}
+
+static int
+cast_fb64_reply(data, cnt, fbp)
+ unsigned char *data;
+ int cnt;
+ struct cast_fb *fbp;
+{
+ int x;
+ unsigned char *p;
+ Block b;
+ register int state = fbp->state[DIR_ENCRYPT-1];
+
+ if (cnt-- < 1)
+ goto failure;
+
+ switch (*data++) {
+ case FB64_IV_OK:
+ cast_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ if (state == cFAILED)
+ state = IN_PROGRESS;
+ state &= ~NO_RECV_IV;
+ encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1);
+ break;
+
+ case FB64_IV_BAD:
+ memset(fbp->temp_feed, 0, sizeof(Block));
+ cast_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]);
+ state = cFAILED;
+ break;
+
+ default:
+#if 0
+ if (encrypt_debug_mode) {
+ printf("Unknown option type: %d\r\n", data[-1]);
+ printd(data, cnt);
+ printf("\r\n");
+ }
+#endif
+ /* FALL THROUGH */
+ failure:
+ state = cFAILED;
+ break;
+ }
+ return(fbp->state[DIR_ENCRYPT-1] = state);
+}
+
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(cast_fb64_session(key, server, &cast_fb[CFB_128], 1));
+}
+
+int
+cast_ofb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(cast_fb64_session(key, server, &cast_fb[OFB_128], 1));
+}
+#endif
+
+int
+castexp_cfb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(cast_fb64_session(key, server, &cast_fb[CFB_40], 0));
+}
+
+int
+castexp_ofb64_session(key, server)
+ Session_Key *key;
+ int server;
+{
+ return(cast_fb64_session(key, server, &cast_fb[OFB_40], 0));
+}
+
+#define CAST128_KEYLEN 16 /* 128 bits */
+#define CAST5_40_KEYLEN 5 /* 40 bits */
+
+static int
+cast_fb64_session(key, server, fbp, fs)
+ Session_Key *key;
+ int server;
+ struct cast_fb *fbp;
+ int fs;
+{
+ int klen;
+ unsigned char * kptr;
+
+ if(fs)
+ klen = CAST128_KEYLEN;
+ else
+ klen = CAST5_40_KEYLEN;
+
+ if (!key || key->length < klen) {
+ CHAR buf[80];
+ sprintf(buf,"Can't set CAST session key (%d < %d)",
+ key ? key->length : 0, klen); /* safe */
+#ifdef DEBUG
+ if (encrypt_debug_mode)
+ printf("%s\r\n",buf);
+#endif
+ debug(F110,"cast_fb64_session",buf,0);
+ return(cFAILED);
+ }
+ if(key->length < 2 * klen)
+ kptr = key->data;
+ else
+ kptr = key->data + klen;
+
+ if(server) {
+ cast_fb64_stream_key(kptr, &fbp->streams[DIR_ENCRYPT-1], fs);
+ cast_fb64_stream_key(key->data, &fbp->streams[DIR_DECRYPT-1], fs);
+ }
+ else {
+ cast_fb64_stream_key(kptr, &fbp->streams[DIR_DECRYPT-1], fs);
+ cast_fb64_stream_key(key->data, &fbp->streams[DIR_ENCRYPT-1], fs);
+ }
+
+ /* Stuff leftovers into the feed */
+ if(key->length >= 2 * klen + sizeof(Block))
+ memcpy(fbp->temp_feed, key->data + 2 * klen, sizeof(Block));
+ else {
+#ifdef COMMENT
+ /* This is a better way of erasing the password */
+ /* but we do not want to link in libsrp */
+ t_random(fbp->temp_feed, sizeof(Block));
+#else
+ memset(fbp->temp_feed, 0, sizeof(Block));
+#endif
+ }
+
+ fbp->key_isset = 1;
+ /*
+ * Now look to see if cast_fb64_start() was was waiting for
+ * the key to show up. If so, go ahead an call it now
+ * that we have the key.
+ */
+ if (fbp->need_start) {
+ fbp->need_start = 0;
+ cast_fb64_start(fbp, DIR_ENCRYPT, server);
+ }
+ return(0);
+}
+
+/*
+ * We only accept a keyid of 0. If we get a keyid of
+ * 0, then mark the state as SUCCESS.
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[CFB_128]));
+}
+
+int
+cast_ofb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[OFB_128]));
+}
+#endif
+
+int
+castexp_cfb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[CFB_40]));
+}
+
+int
+castexp_ofb64_keyid(dir, kp, lenp)
+ int dir, *lenp;
+ unsigned char *kp;
+{
+ return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[OFB_40]));
+}
+
+static int
+cast_fb64_keyid(dir, kp, lenp, fbp)
+ int dir, *lenp;
+ unsigned char *kp;
+ struct cast_fb *fbp;
+{
+ register int state = fbp->state[dir-1];
+
+ if (*lenp != 1 || (*kp != '\0')) {
+ *lenp = 0;
+ return(state);
+ }
+
+ if (state == cFAILED)
+ state = IN_PROGRESS;
+
+ state &= ~NO_KEYID;
+
+ return(fbp->state[dir-1] = state);
+}
+
+static void
+cast_fb64_printsub(data, cnt, buf, buflen, type)
+ unsigned char *data, *buf, *type;
+ int cnt, buflen;
+{
+ char lbuf[64];
+ register int i;
+ char *cp;
+
+ buf[buflen-1] = '\0'; /* make sure it's NULL terminated */
+ buflen -= 1;
+
+ switch(data[2]) {
+ case FB64_IV:
+ sprintf(lbuf, "%s_IV", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_OK:
+ sprintf(lbuf, "%s_IV_OK", type);
+ cp = lbuf;
+ goto common;
+
+ case FB64_IV_BAD:
+ sprintf(lbuf, "%s_IV_BAD", type);
+ cp = lbuf;
+ goto common;
+
+ default:
+ sprintf(lbuf, " %d (unknown)", data[2]);
+ cp = lbuf;
+ common:
+ for (; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ for (i = 3; i < cnt; i++) {
+ sprintf(lbuf, " %d", data[i]);
+ for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++)
+ buflen--;
+ }
+ break;
+ }
+}
+
+void
+cast_cfb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ cast_fb64_printsub(data, cnt, buf, buflen, "CFB64");
+}
+
+void
+cast_ofb64_printsub(data, cnt, buf, buflen)
+ unsigned char *data, *buf;
+ int cnt, buflen;
+{
+ cast_fb64_printsub(data, cnt, buf, buflen, "OFB64");
+}
+
+static void
+cast_fb64_stream_iv(seed, stp)
+ Block seed;
+ register struct cast_stinfo *stp;
+{
+ memcpy((void *)stp->str_iv, (void *)seed, sizeof(Block));
+ memcpy((void *)stp->str_output, (void *)seed, sizeof(Block));
+
+ stp->str_index = sizeof(Block);
+}
+
+static void
+cast_fb64_stream_key(key, stp, fs)
+ unsigned char * key;
+ register struct cast_stinfo *stp;
+ int fs;
+{
+#ifndef CAST_EXPORT_ENCRYPTION
+ if(fs)
+ ck_cast128_key_sched(&stp->str_sched, key);
+ else
+#endif
+ ck_cast5_40_key_sched(&stp->str_sched, key);
+
+ memcpy((void *)stp->str_output, (void *)stp->str_iv, sizeof(Block));
+
+ stp->str_index = sizeof(Block);
+}
+
+/*
+ * CAST 64 bit Cipher Feedback
+ *
+ * key --->+------+
+ * +->| CAST |--+
+ * | +------+ |
+ * | v
+ * INPUT --(---------->(+)+---> DATA
+ * | |
+ * +--------------+
+ *
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = CAST(iV, key)
+ * On = Dn ^ Vn
+ * V(n+1) = CAST(On, key)
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+void
+cast_cfb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ _cast_cfb64_encrypt(s, c, &cast_fb[CFB_128].streams[DIR_ENCRYPT-1]);
+}
+#endif
+
+void
+castexp_cfb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ _cast_cfb64_encrypt(s, c, &cast_fb[CFB_40].streams[DIR_ENCRYPT-1]);
+}
+
+static void
+_cast_cfb64_encrypt(s, c, stp)
+ register unsigned char *s;
+ int c;
+ register struct cast_stinfo *stp;
+{
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+ ck_cast_ecb_encrypt(b, stp->str_output, &stp->str_sched, 0);
+ memcpy((void *)stp->str_feed, (void *)b, sizeof(Block));
+ index = 0;
+ }
+
+ /* On encryption, we store (feed ^ data) which is cypher */
+ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s);
+ s++;
+ index++;
+ }
+ stp->str_index = index;
+}
+
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_cfb64_decrypt(data)
+ int data;
+{
+ return _cast_cfb64_decrypt(data, &cast_fb[CFB_128].streams[DIR_DECRYPT-1]);
+}
+#endif
+
+int
+castexp_cfb64_decrypt(data)
+ int data;
+{
+ return _cast_cfb64_decrypt(data, &cast_fb[CFB_40].streams[DIR_DECRYPT-1]);
+}
+
+static int
+_cast_cfb64_decrypt(data, stp)
+ int data;
+ register struct cast_stinfo *stp;
+{
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+ ck_cast_ecb_encrypt(b, stp->str_output, &stp->str_sched, 0);
+ memcpy((void *)stp->str_feed, (void *)b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ /* On decryption we store (data) which is cypher. */
+ stp->str_output[index] = data;
+ return(data ^ stp->str_feed[index]);
+}
+
+/*
+ * CAST 64 bit Output Feedback
+ *
+ * key --->+------+
+ * +->| CAST |--+
+ * | +------+ |
+ * +------------+
+ * v
+ * INPUT --------->(+) ----> DATA
+ *
+ * Given:
+ * iV: Initial vector, 64 bits (8 bytes) long.
+ * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt).
+ * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output.
+ *
+ * V0 = CAST(iV, key)
+ * V(n+1) = CAST(Vn, key)
+ * On = Dn ^ Vn
+ */
+#ifndef CAST_EXPORT_ENCRYPTION
+void
+cast_ofb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ _cast_ofb64_encrypt(s, c, &cast_fb[OFB_128].streams[DIR_ENCRYPT-1]);
+}
+#endif
+
+void
+castexp_ofb64_encrypt(s, c)
+ register unsigned char *s;
+ int c;
+{
+ _cast_ofb64_encrypt(s, c, &cast_fb[OFB_40].streams[DIR_ENCRYPT-1]);
+}
+
+static void
+_cast_ofb64_encrypt(s, c, stp)
+ register unsigned char *s;
+ int c;
+ register struct cast_stinfo *stp;
+{
+ register int index;
+
+ index = stp->str_index;
+ while (c-- > 0) {
+ if (index == sizeof(Block)) {
+ Block b;
+ ck_cast_ecb_encrypt(b, stp->str_feed, &stp->str_sched, 0);
+ memcpy((void *)stp->str_feed, (void *)b, sizeof(Block));
+ index = 0;
+ }
+ *s++ ^= stp->str_feed[index];
+ index++;
+ }
+ stp->str_index = index;
+}
+
+#ifndef CAST_EXPORT_ENCRYPTION
+int
+cast_ofb64_decrypt(data)
+ int data;
+{
+ return _cast_ofb64_decrypt(data, &cast_fb[OFB_128].streams[DIR_DECRYPT-1]);
+}
+#endif
+
+int
+castexp_ofb64_decrypt(data)
+ int data;
+{
+ return _cast_ofb64_decrypt(data, &cast_fb[OFB_40].streams[DIR_DECRYPT-1]);
+}
+
+static int
+_cast_ofb64_decrypt(data, stp)
+ int data;
+ register struct cast_stinfo *stp;
+{
+ int index;
+
+ if (data == -1) {
+ /*
+ * Back up one byte. It is assumed that we will
+ * never back up more than one byte. If we do, this
+ * may or may not work.
+ */
+ if (stp->str_index)
+ --stp->str_index;
+ return(0);
+ }
+
+ index = stp->str_index++;
+ if (index == sizeof(Block)) {
+ Block b;
+ ck_cast_ecb_encrypt(b, stp->str_feed, &stp->str_sched, 0);
+ memcpy((void *)stp->str_feed, (void *)b, sizeof(Block));
+ stp->str_index = 1; /* Next time will be 1 */
+ index = 0; /* But now use 0 */
+ }
+
+ return(data ^ stp->str_feed[index]);
+}
+
+/*
+ * Copyright (c) 1997 Stanford University
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * cast.c
+ * Author: Tom Wu
+ *
+ * An implementation of the CAST-128 encryption algorithm, as
+ * specified in RFC 2144.
+ */
+
+/* The first four S-boxes are for encryption/decryption */
+
+static uint32 S1[] = {
+ 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3,
+ 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675,
+ 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059,
+ 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+ 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b,
+ 0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de,
+ 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159,
+ 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+ 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f,
+ 0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165,
+ 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, 0x0c6e4f38,
+ 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+ 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493,
+ 0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a,
+ 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb,
+ 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+ 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14,
+ 0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6,
+ 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8,
+ 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+ 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495,
+ 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e,
+ 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426,
+ 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324,
+ 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98,
+ 0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f,
+ 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,0x7b5a41f0, 0xd37cfbad,
+ 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d,
+ 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464,
+ 0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a,
+ 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,0x3f04442f, 0x6188b153,
+ 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
+ 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274,
+ 0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755,
+ 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,0x580304f0, 0xca042cf1,
+ 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+ 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1,
+ 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79,
+ 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,0x474d6ad7, 0x7c0c5e5c,
+ 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+ 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff,
+ 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d,
+ 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
+};
+
+static uint32 S2[] = {
+ 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a,
+ 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba,
+ 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605,
+ 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+ 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b,
+ 0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4,
+ 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083,
+ 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+ 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f,
+ 0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d,
+ 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e,
+ 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+ 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366,
+ 0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4,
+ 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064,
+ 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+ 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6,
+ 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709,
+ 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364,
+ 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+ 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b,
+ 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9,
+ 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c,
+ 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+ 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741,
+ 0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab,
+ 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b,
+ 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+ 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa,
+ 0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8,
+ 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028,
+ 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+ 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6,
+ 0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b,
+ 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1,
+ 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+ 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb,
+ 0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea,
+ 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d,
+ 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+ 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e,
+ 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef,
+ 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
+};
+
+static uint32 S3[] = {
+ 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b,
+ 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae,
+ 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9,
+ 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+ 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd,
+ 0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e,
+ 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264,
+ 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+ 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e,
+ 0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f,
+ 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e,
+ 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+ 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790,
+ 0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504,
+ 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e,
+ 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+ 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8,
+ 0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d,
+ 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240,
+ 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+ 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c,
+ 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15,
+ 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788,
+ 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+ 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa,
+ 0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392,
+ 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f,
+ 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+ 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae,
+ 0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67,
+ 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9,
+ 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+ 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888,
+ 0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d,
+ 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2,
+ 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+ 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2,
+ 0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce,
+ 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d,
+ 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+ 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00,
+ 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5,
+ 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
+};
+
+static uint32 S4[] = {
+ 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57,
+ 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120,
+ 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd,
+ 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+ 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe,
+ 0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701,
+ 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801,
+ 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+ 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1,
+ 0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746,
+ 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3,
+ 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+ 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c,
+ 0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c,
+ 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16,
+ 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+ 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7,
+ 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327,
+ 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002,
+ 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+ 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7,
+ 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031,
+ 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff,
+ 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+ 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035,
+ 0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69,
+ 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec,
+ 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+ 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e,
+ 0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3,
+ 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6,
+ 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+ 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f,
+ 0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091,
+ 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6,
+ 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+ 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2,
+ 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367,
+ 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda,
+ 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+ 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6,
+ 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e,
+ 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
+};
+
+/* Encrypt/decrypt one 64-bit block of data */
+
+void
+ck_cast_ecb_encrypt(out, in, sched, mode)
+ uint8p out;
+ uint8p in;
+ CastKeySched * sched;
+ int mode; /* zero means encrypt */
+{
+ uint32 t[2];
+
+#ifdef LITTLE_ENDIAN
+ t[0] = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3];
+ t[1] = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | in[7];
+#else
+ t[0] = *(uint32p) in;
+ t[1] = *(uint32p) (in + 4);
+#endif
+
+ ck_cast_ecb_crypt(t, sched, mode);
+
+#ifdef LITTLE_ENDIAN
+ out[0] = (t[0] >> 24) & 0xff;
+ out[1] = (t[0] >> 16) & 0xff;
+ out[2] = (t[0] >> 8) & 0xff;
+ out[3] = t[0] & 0xff;
+ out[4] = (t[1] >> 24) & 0xff;
+ out[5] = (t[1] >> 16) & 0xff;
+ out[6] = (t[1] >> 8) & 0xff;
+ out[7] = t[1] & 0xff;
+#else
+ *(uint32p) out = t[0];
+ *(uint32p) (out + 4) = t[1];
+#endif
+}
+
+void
+ck_cast_ecb_crypt(data, sched, mode)
+ uint32p data;
+ CastKeySched * sched;
+ int mode;
+{
+ register uint32 L, R, temp;
+ register struct CastSubkeyPair * kp;
+ register uint8p Ia, Ib, Ic, Id;
+ uint32 I;
+
+#ifdef LITTLE_ENDIAN
+ Id = (uint8p) &I;
+ Ic = Id + 1;
+ Ib = Ic + 1;
+ Ia = Ib + 1;
+#else
+ Ia = (uint8p) &I;
+ Ib = Ia + 1;
+ Ic = Ib + 1;
+ Id = Ic + 1;
+#endif
+
+ L = data[0];
+ R = data[1];
+
+#define type0(left,right) \
+ temp = kp->Km + right;\
+ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\
+ left ^= ((S1[*Ia] ^ S2[*Ib]) - S3[*Ic]) + S4[*Id];
+
+#define type1(left,right) \
+ temp = kp->Km ^ right;\
+ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\
+ left ^= ((S1[*Ia] - S2[*Ib]) + S3[*Ic]) ^ S4[*Id];
+
+#define type2(left,right) \
+ temp = kp->Km - right;\
+ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\
+ left ^= ((S1[*Ia] + S2[*Ib]) ^ S3[*Ic]) - S4[*Id];
+
+ if(mode) {
+#ifndef CAST_EXPORT_ENCRYPTION
+ if(sched->ksize > 10) {
+ kp = &sched->K[15];
+ type0(L, R); --kp;
+ type2(R, L); --kp;
+ type1(L, R); --kp;
+ type0(R, L); --kp;
+ }
+ else
+#endif
+ kp = &sched->K[11];
+ type2(L, R); --kp;
+ type1(R, L); --kp;
+ type0(L, R); --kp;
+ type2(R, L); --kp;
+ type1(L, R); --kp;
+ type0(R, L); --kp;
+ type2(L, R); --kp;
+ type1(R, L); --kp;
+ type0(L, R); --kp;
+ type2(R, L); --kp;
+ type1(L, R); --kp;
+ type0(R, L);
+ }
+ else {
+ kp = &sched->K[0];
+ type0(L, R); ++kp;
+ type1(R, L); ++kp;
+ type2(L, R); ++kp;
+ type0(R, L); ++kp;
+ type1(L, R); ++kp;
+ type2(R, L); ++kp;
+ type0(L, R); ++kp;
+ type1(R, L); ++kp;
+ type2(L, R); ++kp;
+ type0(R, L); ++kp;
+ type1(L, R); ++kp;
+ type2(R, L); ++kp;
+#ifndef CAST_EXPORT_ENCRYPTION
+ if(sched->ksize > 10) {
+ type0(L, R); ++kp;
+ type1(R, L); ++kp;
+ type2(L, R); ++kp;
+ type0(R, L);
+ }
+#endif
+ }
+
+ data[0] = R;
+ data[1] = L;
+}
+
+/* The last four S-boxes are for key schedule setup */
+
+static uint32 S5[] = {
+ 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5,
+ 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00,
+ 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, 0xe6a2e77f, 0xf0c720cd,
+ 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff,
+ 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb,
+ 0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725,
+ 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, 0xf2f3f763, 0x68af8040,
+ 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7,
+ 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2,
+ 0x2261be02, 0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec,
+ 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, 0xfe38d399,
+ 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774,
+ 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966,
+ 0xdfdd55bc, 0x29de0655, 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468,
+ 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9,
+ 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910,
+ 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616,
+ 0xf24766e3, 0x8eca36c1, 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4,
+ 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419,
+ 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049,
+ 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9,
+ 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6,
+ 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, 0x0ab378d5, 0xd951fb0c,
+ 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be,
+ 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715,
+ 0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6,
+ 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, 0x76f0ae02, 0x083be84d,
+ 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4,
+ 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba,
+ 0x9cad9010, 0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487,
+ 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, 0x175683f4,
+ 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5,
+ 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c,
+ 0x1ad2fff3, 0x8c25404e, 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78,
+ 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87,
+ 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801,
+ 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110,
+ 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58,
+ 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3,
+ 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20,
+ 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d,
+ 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55,
+ 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4
+};
+
+static uint32 S6[] = {
+ 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4,
+ 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9,
+ 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, 0x33f14961, 0xc01937bd,
+ 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367,
+ 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f,
+ 0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c,
+ 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, 0xfd41197e, 0x9305a6b0,
+ 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3,
+ 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941,
+ 0x2c0e636a, 0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d,
+ 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, 0xaa928223,
+ 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9,
+ 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6,
+ 0x9a69a02f, 0x68818a54, 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a,
+ 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7,
+ 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc,
+ 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89,
+ 0xfd339fed, 0xb87834bf, 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be,
+ 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0,
+ 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f,
+ 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4,
+ 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853,
+ 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, 0x36f73523, 0x4cfb6e87,
+ 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f,
+ 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585,
+ 0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751,
+ 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, 0xbf32679d, 0xd45b5b75,
+ 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13,
+ 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283,
+ 0x3cc2acfb, 0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459,
+ 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, 0x74719eef,
+ 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891,
+ 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0,
+ 0xbc60b42a, 0x953498da, 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb,
+ 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200,
+ 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084,
+ 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf,
+ 0x3a479c3a, 0x5302da25, 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b,
+ 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869,
+ 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5,
+ 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb,
+ 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454,
+ 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f
+};
+
+static uint32 S7[] = {
+ 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912,
+ 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82,
+ 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, 0xa05fbcf6, 0xcd4181e9,
+ 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43,
+ 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4,
+ 0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9,
+ 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, 0x107789be, 0xb3b2e9ce,
+ 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516,
+ 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7,
+ 0xd0d854c0, 0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e,
+ 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, 0xe1a5c06e,
+ 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756,
+ 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9,
+ 0xc6e6fa14, 0xbae8584a, 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b,
+ 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3,
+ 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688,
+ 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c,
+ 0x16746233, 0x3c034c28, 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802,
+ 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778,
+ 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7,
+ 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be,
+ 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858,
+ 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, 0xf28ebfb0, 0xf5b9c310,
+ 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a,
+ 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476,
+ 0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df,
+ 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, 0xf22b017d, 0xa4173f70,
+ 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962,
+ 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a,
+ 0x058745b9, 0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07,
+ 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, 0x7154c24c,
+ 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c,
+ 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e,
+ 0xe4f2dfa6, 0x693ed285, 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378,
+ 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e,
+ 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be,
+ 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301,
+ 0xcfd2a87f, 0x60aeb767, 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2,
+ 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4,
+ 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914,
+ 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021,
+ 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada,
+ 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3
+};
+
+static uint32 S8[] = {
+ 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b,
+ 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174,
+ 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, 0xde9adeb1, 0x0a0cc32c,
+ 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd,
+ 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7,
+ 0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164,
+ 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, 0x12a8ddec, 0xfdaa335d,
+ 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862,
+ 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8,
+ 0x57e8726e, 0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6,
+ 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, 0x2998df04,
+ 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e,
+ 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38,
+ 0x424f7618, 0x35856039, 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8,
+ 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354,
+ 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42,
+ 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160,
+ 0x7895cda5, 0x859c15a5, 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab,
+ 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2,
+ 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225,
+ 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98,
+ 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441,
+ 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, 0xa842eedf, 0xfdba60b4,
+ 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054,
+ 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5,
+ 0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c,
+ 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, 0x77853b53, 0x37effcb5,
+ 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c,
+ 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b,
+ 0xc4248289, 0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4,
+ 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, 0xe98ea084,
+ 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101,
+ 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a,
+ 0xe0779695, 0xf9c17a8f, 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf,
+ 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77,
+ 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a,
+ 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f,
+ 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819,
+ 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3,
+ 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c,
+ 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1,
+ 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d,
+ 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e
+};
+
+/* Initialize a key schedule from a 128-bit key */
+
+static void
+cast_key_sched(sched, key)
+ CastKeySched * sched;
+ uint8p key;
+{
+ uint8p x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xA, xB, xC, xD, xE, xF;
+ uint8p z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, zA, zB, zC, zD, zE, zF;
+ uint32 X03, X47, X8B, XCF, Z03, Z47, Z8B, ZCF;
+
+#ifdef LITTLE_ENDIAN
+ x3 = (uint8p) &X03; x2 = x3 + 1; x1 = x2 + 1; x0 = x1 + 1;
+ x7 = (uint8p) &X47; x6 = x7 + 1; x5 = x6 + 1; x4 = x5 + 1;
+ xB = (uint8p) &X8B; xA = xB + 1; x9 = xA + 1; x8 = x9 + 1;
+ xF = (uint8p) &XCF; xE = xF + 1; xD = xE + 1; xC = xD + 1;
+ z3 = (uint8p) &Z03; z2 = z3 + 1; z1 = z2 + 1; z0 = z1 + 1;
+ z7 = (uint8p) &Z47; z6 = z7 + 1; z5 = z6 + 1; z4 = z5 + 1;
+ zB = (uint8p) &Z8B; zA = zB + 1; z9 = zA + 1; z8 = z9 + 1;
+ zF = (uint8p) &ZCF; zE = zF + 1; zD = zE + 1; zC = zD + 1;
+#else
+ x0 = (uint8p) &X03; x1 = x0 + 1; x2 = x1 + 1; x3 = x2 + 1;
+ x4 = (uint8p) &X47; x5 = x4 + 1; x6 = x5 + 1; x7 = x6 + 1;
+ x8 = (uint8p) &X8B; x9 = x8 + 1; xA = x9 + 1; xB = xA + 1;
+ xC = (uint8p) &XCF; xD = xC + 1; xE = xD + 1; xF = xE + 1;
+ z0 = (uint8p) &Z03; z1 = z0 + 1; z2 = z1 + 1; z3 = z2 + 1;
+ z4 = (uint8p) &Z47; z5 = z4 + 1; z6 = z5 + 1; z7 = z6 + 1;
+ z8 = (uint8p) &Z8B; z9 = z8 + 1; zA = z9 + 1; zB = zA + 1;
+ zC = (uint8p) &ZCF; zD = zC + 1; zE = zD + 1; zF = zE + 1;
+#endif
+
+#ifdef LITTLE_ENDIAN
+ *x0 = key[0];
+ *x1 = key[1];
+ *x2 = key[2];
+ *x3 = key[3];
+ *x4 = key[4];
+ *x5 = key[5];
+ *x6 = key[6];
+ *x7 = key[7];
+ *x8 = key[8];
+ *x9 = key[9];
+ *xA = key[10];
+ *xB = key[11];
+ *xC = key[12];
+ *xD = key[13];
+ *xE = key[14];
+ *xF = key[15];
+#else
+ X03 = *(uint32p) key;
+ X47 = *(uint32p) (key + 4);
+ X8B = *(uint32p) (key + 8);
+ XCF = *(uint32p) (key + 12);
+#endif
+
+ /* First half of key schedule */
+
+ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8];
+ Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA];
+ Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9];
+ ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB];
+
+ sched->K[0].Km = S5[*z8] ^ S6[*z9] ^ S7[*z7] ^ S8[*z6] ^ S5[*z2];
+ sched->K[1].Km = S5[*zA] ^ S6[*zB] ^ S7[*z5] ^ S8[*z4] ^ S6[*z6];
+ sched->K[2].Km = S5[*zC] ^ S6[*zD] ^ S7[*z3] ^ S8[*z2] ^ S7[*z9];
+ sched->K[3].Km = S5[*zE] ^ S6[*zF] ^ S7[*z1] ^ S8[*z0] ^ S8[*zC];
+
+ X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0];
+ X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2];
+ X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1];
+ XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3];
+
+ sched->K[4].Km = S5[*x3] ^ S6[*x2] ^ S7[*xC] ^ S8[*xD] ^ S5[*x8];
+ sched->K[5].Km = S5[*x1] ^ S6[*x0] ^ S7[*xE] ^ S8[*xF] ^ S6[*xD];
+ sched->K[6].Km = S5[*x7] ^ S6[*x6] ^ S7[*x8] ^ S8[*x9] ^ S7[*x3];
+ sched->K[7].Km = S5[*x5] ^ S6[*x4] ^ S7[*xA] ^ S8[*xB] ^ S8[*x7];
+
+ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8];
+ Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA];
+ Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9];
+ ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB];
+
+ sched->K[8].Km = S5[*z3] ^ S6[*z2] ^ S7[*zC] ^ S8[*zD] ^ S5[*z9];
+ sched->K[9].Km = S5[*z1] ^ S6[*z0] ^ S7[*zE] ^ S8[*zF] ^ S6[*zC];
+ sched->K[10].Km = S5[*z7] ^ S6[*z6] ^ S7[*z8] ^ S8[*z9] ^ S7[*z2];
+ sched->K[11].Km = S5[*z5] ^ S6[*z4] ^ S7[*zA] ^ S8[*zB] ^ S8[*z6];
+
+ X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0];
+ X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2];
+ X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1];
+ XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3];
+
+ sched->K[12].Km = S5[*x8] ^ S6[*x9] ^ S7[*x7] ^ S8[*x6] ^ S5[*x3];
+ sched->K[13].Km = S5[*xA] ^ S6[*xB] ^ S7[*x5] ^ S8[*x4] ^ S6[*x7];
+ sched->K[14].Km = S5[*xC] ^ S6[*xD] ^ S7[*x3] ^ S8[*x2] ^ S7[*x8];
+ sched->K[15].Km = S5[*xE] ^ S6[*xF] ^ S7[*x1] ^ S8[*x0] ^ S8[*xD];
+
+ /* Second half of key schedule - just like first half */
+
+ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8];
+ Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA];
+ Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9];
+ ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB];
+
+ sched->K[0].Kr = (S5[*z8] ^ S6[*z9] ^ S7[*z7] ^ S8[*z6] ^ S5[*z2]) & 0x1f;
+ sched->K[1].Kr = (S5[*zA] ^ S6[*zB] ^ S7[*z5] ^ S8[*z4] ^ S6[*z6]) & 0x1f;
+ sched->K[2].Kr = (S5[*zC] ^ S6[*zD] ^ S7[*z3] ^ S8[*z2] ^ S7[*z9]) & 0x1f;
+ sched->K[3].Kr = (S5[*zE] ^ S6[*zF] ^ S7[*z1] ^ S8[*z0] ^ S8[*zC]) & 0x1f;
+
+ X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0];
+ X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2];
+ X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1];
+ XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3];
+
+ sched->K[4].Kr = (S5[*x3] ^ S6[*x2] ^ S7[*xC] ^ S8[*xD] ^ S5[*x8]) & 0x1f;
+ sched->K[5].Kr = (S5[*x1] ^ S6[*x0] ^ S7[*xE] ^ S8[*xF] ^ S6[*xD]) & 0x1f;
+ sched->K[6].Kr = (S5[*x7] ^ S6[*x6] ^ S7[*x8] ^ S8[*x9] ^ S7[*x3]) & 0x1f;
+ sched->K[7].Kr = (S5[*x5] ^ S6[*x4] ^ S7[*xA] ^ S8[*xB] ^ S8[*x7]) & 0x1f;
+
+ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8];
+ Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA];
+ Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9];
+ ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB];
+
+ sched->K[8].Kr = (S5[*z3] ^ S6[*z2] ^ S7[*zC] ^ S8[*zD] ^ S5[*z9]) & 0x1f;
+ sched->K[9].Kr = (S5[*z1] ^ S6[*z0] ^ S7[*zE] ^ S8[*zF] ^ S6[*zC]) & 0x1f;
+ sched->K[10].Kr = (S5[*z7] ^ S6[*z6] ^ S7[*z8] ^ S8[*z9] ^ S7[*z2]) & 0x1f;
+ sched->K[11].Kr = (S5[*z5] ^ S6[*z4] ^ S7[*zA] ^ S8[*zB] ^ S8[*z6]) & 0x1f;
+
+ X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0];
+ X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2];
+ X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1];
+ XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3];
+
+ sched->K[12].Kr = (S5[*x8] ^ S6[*x9] ^ S7[*x7] ^ S8[*x6] ^ S5[*x3]) & 0x1f;
+ sched->K[13].Kr = (S5[*xA] ^ S6[*xB] ^ S7[*x5] ^ S8[*x4] ^ S6[*x7]) & 0x1f;
+ sched->K[14].Kr = (S5[*xC] ^ S6[*xD] ^ S7[*x3] ^ S8[*x2] ^ S7[*x8]) & 0x1f;
+ sched->K[15].Kr = (S5[*xE] ^ S6[*xF] ^ S7[*x1] ^ S8[*x0] ^ S8[*xD]) & 0x1f;
+}
+
+/* Initialize with a full-strength 128-bit key */
+
+#ifndef CAST_EXPORT_ENCRYPTION
+void
+ck_cast128_key_sched(sched, key)
+ CastKeySched * sched;
+ uint8 * key;
+{
+ sched->ksize = 16;
+ cast_key_sched(sched, key);
+}
+#endif
+
+/* Handle reduced-keysize variants */
+
+static void
+cast5_key_sched(sched, key, sz)
+ CastKeySched * sched;
+ uint8 * key;
+ int sz;
+{
+ uint8 buf[16];
+
+ sched->ksize = sz;
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, key, sz);
+ cast_key_sched(sched, buf);
+}
+
+/* 40, 64, and 80-bit keys - all use 12 rounds */
+
+void
+ck_cast5_40_key_sched(sched, key)
+ CastKeySched * sched;
+ uint8 * key;
+{
+ cast5_key_sched(sched, key, 5);
+}
+
+#ifndef CAST_EXPORT_ENCRYPTION
+void
+ck_cast5_64_key_sched(sched, key)
+ CastKeySched * sched;
+ uint8 * key;
+{
+ cast5_key_sched(sched, key, 8);
+}
+
+void
+ck_cast5_80_key_sched(sched, key)
+ CastKeySched * sched;
+ uint8 * key;
+{
+ cast5_key_sched(sched, key, 10);
+}
+#endif /* CAST_EXPORT_ENCRYPTION */
+#endif /* CK_CAST */
+
+#ifdef CRYPT_DLL
+static char *
+ck_crypt_dll_version()
+{
+ return(ckcrpv);
+}
+
+int
+crypt_dll_init( struct _crypt_dll_init * init )
+{
+#ifdef LIBDES
+ extern int des_check_key;
+ extern void libdes_dll_init(struct _crypt_dll_init *);
+ des_check_key = 1;
+#endif /* LIBDES */
+
+ if ( init->version >= 1 ) {
+ p_ttol = init->p_ttol;
+ p_dodebug = init->p_dodebug;
+ p_dohexdump = init->p_dohexdump;
+ p_tn_debug = init->p_tn_debug;
+ p_vscrnprintf = init->p_vscrnprintf;
+ if ( init->version == 1 )
+ return(1);
+ }
+ if ( init->version >= 2 ) {
+ /* This is a k5_context but we don't want to include krb5.h */
+ p_k5_context = (void *) init->p_k5_context;
+ if ( init->version == 2 )
+ return(1);
+ }
+ if ( init->version >= 3 ) {
+ init->p_install_funcs("encrypt_parse",encrypt_parse);
+ init->p_install_funcs("encrypt_init",encrypt_init);
+ init->p_install_funcs("encrypt_session_key",encrypt_session_key);
+ init->p_install_funcs("encrypt_send_request_start",
+ encrypt_send_request_start
+ );
+ init->p_install_funcs("encrypt_request_start",encrypt_request_start);
+ init->p_install_funcs("encrypt_send_request_end",
+ encrypt_send_request_end
+ );
+ init->p_install_funcs("encrypt_request_end",encrypt_request_end);
+ init->p_install_funcs("encrypt_send_end",encrypt_send_end);
+ init->p_install_funcs("encrypt_send_support",encrypt_send_support);
+ init->p_install_funcs("encrypt_is_encrypting",encrypt_is_encrypting);
+ init->p_install_funcs("encrypt_is_decrypting",encrypt_is_decrypting);
+ init->p_install_funcs("get_crypt_table",get_crypt_table);
+ init->p_install_funcs("des_is_weak_key",ck_des_is_weak_key);
+ libdes_dll_init(init);
+ if (init->version == 3)
+ return(1);
+ }
+ if ( init->version >= 4 ) {
+ init->p_install_funcs("crypt_dll_version",ck_crypt_dll_version);
+ if (init->version == 4)
+ return(1);
+ }
+
+ if ( init->version >= 5 ) {
+ p_reqtelmutex = init->p_reqtelmutex;
+ p_reltelmutex = init->p_reltelmutex;
+ if (init->version == 5)
+ return(1);
+ }
+
+ if ( init->version >= 6 ) {
+ init->p_install_funcs("encrypt_dont_support",encrypt_dont_support);
+ if ( init->version == 6 )
+ return(1);
+ /* when adding new versions; migrate the next two lines */
+ init->version = 6;
+ return(1);
+ }
+ return(0);
+}
+
+#undef malloc
+#undef realloc
+#undef free
+#undef strdup
+
+static void
+fatal(char *msg) {
+ if (!msg) msg = "";
+
+ printf(msg);
+ exit(1); /* Exit indicating failure */
+}
+
+void *
+kmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0) {
+ fatal("kmalloc: zero size");
+ }
+ ptr = malloc(size);
+ if (ptr == NULL) {
+ fatal("kmalloc: out of memory");
+ }
+ return ptr;
+}
+
+void *
+krealloc(void *ptr, size_t new_size)
+{
+ void *new_ptr;
+
+ if (new_size == 0) {
+ fatal("krealloc: zero size");
+ }
+ if (ptr == NULL)
+ new_ptr = malloc(new_size);
+ else
+ new_ptr = realloc(ptr, new_size);
+ if (new_ptr == NULL) {
+ fatal("krealloc: out of memory");
+ }
+ return new_ptr;
+}
+
+void
+kfree(void *ptr)
+{
+ if (ptr == NULL) {
+ printf("kfree: NULL pointer given as argument");
+ return;
+ }
+ free(ptr);
+}
+
+char *
+kstrdup(const char *str)
+{
+ size_t len;
+ char *cp;
+
+ if (str == NULL) {
+ fatal("kstrdup: NULL pointer given as argument");
+ }
+ len = strlen(str) + 1;
+ cp = kmalloc(len);
+ if (cp)
+ memcpy(cp, str, len);
+ return cp;
+}
+#endif /* CRYPT_DLL */
+#endif /* CK_ENCRYPTION */
diff --git a/ckermit-8.0.211/ck_des.c b/ckermit-8.0.211/ck_des.c
new file mode 100644
index 0000000..c534652
--- /dev/null
+++ b/ckermit-8.0.211/ck_des.c
@@ -0,0 +1,98 @@
+/*
+ C K _ D E S . C - libDES interface for Kermit 95"
+
+ Copyright (C) 1998, 2001, Trustees of Columbia University in the City of New
+ York. The C-Kermit software may not be, in whole or in part, licensed or
+ sold for profit as a software product itself, nor may it be included in or
+ distributed with commercial products or otherwise distributed by commercial
+ concerns to their clients or customers without written permission of the
+ Office of Kermit Development and Distribution, Columbia University. This
+ copyright notice must not be removed, altered, or obscured.
+
+ Author:
+ Jeffrey E Altman (jaltman@secure-endpoints.com)
+*/
+
+/*
+ This file contains wrappers so that the following functions will be imported
+ into the k95crypt.dll/k2crypt.dll files in such a form that they can be
+ re-exported to k95.exe/k2.exe. This subset of the DES library is needed to
+ provide DES based Kerberos authentication.
+*/
+
+
+#ifdef LIBDES
+/* The following is specific to my installation, but since I'm the only one */
+/* that uses this file ... */
+#include "ckcdeb.h"
+#include "ckuath.h"
+#define CK_DES_C
+#include "ckuat2.h"
+#ifdef NT
+#ifdef _M_ALPHA
+#include <c:\srp\des\des.h>
+#else
+#include <c:\src\srp\des\des.h>
+#endif
+#else
+#include <c:\srp\des\des.h>
+#endif
+
+int
+libdes_random_key(des_cblock B)
+{
+ des_random_key(B);
+ return(0);
+}
+
+void
+libdes_random_seed(des_cblock B)
+{
+ des_random_seed(B);
+}
+
+void
+libdes_key_sched(des_cblock * B, des_key_schedule S)
+{
+ des_key_sched(B,S);
+}
+
+void
+libdes_ecb_encrypt(des_cblock * B1, des_cblock * B2, des_key_schedule S, int n)
+{
+ des_ecb_encrypt(B1,B2,S,n);
+}
+
+int
+libdes_string_to_key(char * s, des_cblock * B)
+{
+ des_string_to_key(s,B);
+ return(0);
+}
+
+void
+libdes_fixup_key_parity(des_cblock * B)
+{
+ des_set_odd_parity(B);
+}
+
+void
+libdes_pcbc_encrypt(des_cblock *input, des_cblock *output, long length,
+ des_key_schedule schedule, des_cblock *ivec, int enc)
+{
+ des_pcbc_encrypt(input,output,length,schedule,ivec,enc);
+}
+
+void
+libdes_dll_init(struct _crypt_dll_init * init)
+{
+ init->p_install_funcs("libdes_random_key",libdes_random_key);
+ init->p_install_funcs("libdes_random_seed",libdes_random_seed);
+ init->p_install_funcs("libdes_key_sched",libdes_key_sched);
+ init->p_install_funcs("libdes_ecb_encrypt",libdes_ecb_encrypt);
+ init->p_install_funcs("libdes_string_to_key",libdes_string_to_key);
+ init->p_install_funcs("libdes_fixup_key_parity",libdes_fixup_key_parity);
+ init->p_install_funcs("libdes_pcbc_encrypt",libdes_pcbc_encrypt);
+
+}
+#endif /* LIBDES */
diff --git a/ckermit-8.0.211/ck_ssl.c b/ckermit-8.0.211/ck_ssl.c
new file mode 100644
index 0000000..7b53770
--- /dev/null
+++ b/ckermit-8.0.211/ck_ssl.c
@@ -0,0 +1,4242 @@
+char *cksslv = "SSL/TLS support, 8.0.221, 26 Feb 2004";
+/*
+ C K _ S S L . C -- OpenSSL Interface for C-Kermit
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+
+ Author: Jeffrey E Altman (jaltman@secure-endpoints.com)
+ Secure Endpoints Inc., New York City
+
+ Provides:
+
+ . Telnet Auth SSL option compatible with Tim Hudson's hack.
+ . Telnet START_TLS option
+ . Configuration of certificate and key files
+ . Certificate verification and revocation list checks
+ . Client certificate to user id routine
+
+ Note: This code is written to be compatible with OpenSSL 0.9.6[abcdefgh]
+ and 0.9.7 beta 5.
+ It will also compile with version 0.9.5 although that is discouraged
+ due to security weaknesses in that release.
+*/
+
+#include "ckcsym.h"
+#include "ckcdeb.h"
+
+#ifdef CK_SSL
+#include "ckcnet.h"
+#include "ckuath.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef UNIX
+#include <netinet/in.h>
+#ifndef FREEBSD4
+#include <arpa/inet.h>
+#endif /* FREEBSD4 */
+#endif /* UNIX */
+
+#ifdef DEC_TCPIP
+#include <time.h>
+#include <inet.h>
+#endif /* DEC_TCPIP */
+
+#ifdef OS2
+extern char exedir[];
+#ifdef NT
+char * GetAppData(int);
+#endif
+#endif /* OS2 */
+
+static int ssl_installed = 1;
+#endif /* CK_SSL */
+int
+ck_ssh_is_installed()
+{
+#ifdef SSHBUILTIN
+#ifdef SSLDLL
+#ifdef NT
+ extern HINSTANCE hCRYPTO;
+#else /* NT */
+ extern HMODULE hCRYPTO;
+#endif /* NT */
+ debug(F111,"ck_ssh_is_installed","hCRYPTO",hCRYPTO);
+ return(ssl_installed && (hCRYPTO != NULL));
+#else /* SSLDLL */
+ return(ssl_installed);
+#endif /* SSLDLL */
+#else
+ return 0;
+#endif
+}
+
+int
+#ifdef CK_ANSIC
+ck_ssleay_is_installed(void)
+#else
+ck_ssleay_is_installed()
+#endif
+{
+#ifdef CK_SSL
+#ifdef SSLDLL
+#ifdef NT
+ extern HINSTANCE hSSL, hCRYPTO;
+#else /* NT */
+ extern HMODULE hSSL, hCRYPTO;
+#endif /* NT */
+ debug(F111,"ck_ssleay_is_installed","hSSL",hSSL);
+ debug(F111,"ck_ssleay_is_installed","hCRYPTO",hCRYPTO);
+ return(ssl_installed && (hSSL != NULL) && (hCRYPTO != NULL));
+#else /* SSLDLL */
+ return(ssl_installed);
+#endif /* SSLDLL */
+#else /* CK_SSL */
+ return(0);
+#endif /* CK_SSL */
+}
+
+#ifdef CK_SSL
+#include "ckcker.h"
+#include "ckucmd.h" /* For struct keytab */
+#include "ckctel.h"
+#include "ck_ssl.h"
+#ifdef UNIX
+#include <pwd.h> /* Password file for home directory */
+#endif /* UNIX */
+#ifdef OS2
+#include <process.h>
+#endif /* OS2 */
+#ifdef OS2ONLY
+#include "ckotcp.h"
+#endif /* OS2ONLY */
+
+#ifdef SSLDLL
+int ssl_finished_messages = 0;
+#else /* SSLDLL */
+#ifdef OPENSSL_VERSION_NUMBER
+int ssl_finished_messages = (OPENSSL_VERSION_NUMBER >= 0x0090581fL);
+#else
+!ERROR This module requires OpenSSL 0.9.5a or higher
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* SSLDLL */
+
+static int auth_ssl_valid = 0;
+static char *auth_ssl_name = 0; /* this holds the oneline name */
+char ssl_err[SSL_ERR_BFSZ]="";
+
+BIO *bio_err=NULL;
+X509_STORE *crl_store = NULL;
+
+#ifndef NOFTP
+#ifndef SYSFTP
+SSL *ssl_ftp_con = NULL;
+SSL_CTX *ssl_ftp_ctx = NULL;
+SSL *ssl_ftp_data_con = NULL;
+int ssl_ftp_active_flag = 0;
+int ssl_ftp_data_active_flag = 0;
+#endif /* SYSFTP */
+#endif /* NOFTP */
+
+#ifndef NOHTTP
+SSL *tls_http_con = NULL;
+SSL_CTX *tls_http_ctx = NULL;
+int tls_http_active_flag = 0;
+int ssl_http_initialized = 0;
+#endif /* NOHTTP */
+
+SSL_CTX *ssl_ctx = NULL;
+SSL *ssl_con = NULL;
+int ssl_debug_flag = 0;
+int ssl_verbose_flag = 0;
+int ssl_only_flag = 0;
+int ssl_active_flag = 0;
+int ssl_verify_flag = SSL_VERIFY_PEER;
+int ssl_certsok_flag = 0;
+char *ssl_rsa_cert_file = NULL;
+char *ssl_rsa_cert_chain_file = NULL;
+char *ssl_rsa_key_file = NULL;
+char *ssl_dsa_cert_file = NULL;
+char *ssl_dsa_cert_chain_file = NULL;
+char *ssl_dh_key_file = NULL;
+char *ssl_crl_file = NULL;
+char *ssl_crl_dir = NULL;
+char *ssl_verify_file = NULL;
+char *ssl_verify_dir = NULL;
+char *ssl_dh_param_file = NULL;
+char *ssl_cipher_list = NULL;
+char *ssl_rnd_file = NULL;
+
+SSL_CTX *tls_ctx = NULL;
+SSL *tls_con = NULL;
+int tls_only_flag = 0;
+int tls_active_flag = 0;
+
+int ssl_initialized = 0;
+int ssl_verify_depth = -1; /* used to track depth in verify routines */
+
+/* compile this set to 1 to negotiate SSL/TLS but not actually start it */
+int ssl_dummy_flag=0;
+
+extern int inserver;
+extern int debses;
+extern int accept_complete;
+extern char szHostName[], szUserNameRequested[], szUserNameAuthenticated[];
+
+_PROTOTYP(int X509_to_user,(X509 *, char *, int));
+
+int
+#ifdef CK_ANSIC
+ssl_server_verify_callback(int ok, X509_STORE_CTX * ctx)
+#else /* CK_ANSIC */
+ssl_server_verify_callback(ok, ctx)
+int ok;
+X509_STORE_CTX *ctx;
+#endif /* CK_ANSIC */
+{
+ static char *saved_subject=NULL;
+ char *subject=NULL, *issuer=NULL;
+ int depth,error;
+ X509 *xs = NULL;
+
+ if ( ssl_certsok_flag )
+ return(1);
+
+ error=X509_STORE_CTX_get_error(ctx);
+ depth=X509_STORE_CTX_get_error_depth(ctx);
+ xs=X509_STORE_CTX_get_current_cert(ctx);
+
+ if (depth==0) {
+ /* clear things */
+ if (saved_subject!=NULL) {
+ free(saved_subject);
+ saved_subject=NULL;
+ }
+ if (auth_ssl_name!=NULL) {
+ free(auth_ssl_name);
+ auth_ssl_name=NULL;
+ }
+ }
+
+
+ if (ssl_debug_flag && !inserver) {
+ printf("ssl:server_verify_callback:depth=%d ok=%d err=%d-%s\r\n",
+ depth,ok,error,X509_verify_cert_error_string(error));
+ }
+
+ /* first thing is to have a meaningful name for the current
+ * certificate that is being verified ... and if we cannot
+ * determine that then something is seriously wrong!
+ */
+ makestr(&subject,
+ (char *)X509_NAME_oneline(X509_get_subject_name(xs),NULL,0));
+ makestr(&issuer,
+ (char *)X509_NAME_oneline(X509_get_issuer_name(xs),NULL,0));
+ if (!subject || !subject[0] || !issuer || !issuer[0]) {
+ ok = 0;
+ goto return_time;
+ }
+
+ if (ssl_verbose_flag && !inserver && depth != ssl_verify_depth) {
+ printf("[%d] Certificate Subject:\r\n%s\r\n",depth,subject);
+ printf("[%d] Certificate Issuer:\r\n%s\r\n",depth,issuer);
+ ssl_verify_depth = depth;
+ }
+
+ /* make sure that the certificate that has been presented */
+ /* has not been revoked (if we have been given a CRL. */
+ ok = ssl_verify_crl(ok, ctx);
+
+ /* if we have any form of error in secure mode we reject the connection */
+ if (error!=X509_V_OK) {
+ if (inserver) {
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ cksyslog(SYSLG_LI, 0,
+ "X.509 Certificate verify failure",
+ (char *) subject,
+ (char *)X509_verify_cert_error_string(error)
+ );
+ }
+#endif /* CKSYSLOG */
+
+ } else {
+ if ( ssl_verify_flag &
+ (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT))
+ printf("Error: ");
+ else
+ printf("Warning: ");
+ switch (error) {
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ printf("Certificate is self signed.\r\n");
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ printf("Certificate has expired.\r\n");
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ printf(
+ "Certificate issuer's certificate isn't available locally.\r\n");
+ break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ printf("Unable to verify leaf signature.\r\n");
+ break;
+ case X509_V_ERR_CERT_REVOKED:
+ printf("Certificate revoked.\r\n");
+ break;
+ default:
+ printf("Error %d while verifying certificate.\r\n",
+ ctx->error);
+ break;
+ }
+ }
+ ok = !(ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+ } else {
+ /* if we got all the way to the top of the tree then
+ * we *can* use this certificate for a username to
+ * match ... in all other cases we must not!
+ */
+ auth_ssl_name = saved_subject;
+ saved_subject = NULL;
+ }
+
+ return_time:
+
+ /* save the name if at least the first level is okay */
+ if (depth == 0 && ok)
+ makestr(&saved_subject,subject);
+
+ /* clean up things */
+ if (subject!=NULL)
+ free(subject);
+ if (issuer!=NULL)
+ free(issuer);
+
+ return ok;
+}
+
+int
+#ifdef CK_ANSIC
+ssl_client_verify_callback(int ok, X509_STORE_CTX * ctx)
+#else
+ssl_client_verify_callback(ok, ctx)
+int ok;
+X509_STORE_CTX *ctx;
+#endif
+{
+ char subject[256]="", issuer[256]="";
+ int depth, error, len;
+ X509 *xs;
+
+ xs=X509_STORE_CTX_get_current_cert(ctx);
+ error=X509_STORE_CTX_get_error(ctx);
+ depth=X509_STORE_CTX_get_error_depth(ctx);
+
+ if ( ssl_debug_flag )
+ printf("ssl:client_verify_callback:depth=%d ok=%d err=%d-%s\r\n",
+ depth,ok,error,X509_verify_cert_error_string(error));
+
+ if ( ssl_certsok_flag ) {
+ ok = 1;
+ }
+
+ /* first thing is to have a meaningful name for the current
+ * certificate that is being verified ... and if we cannot
+ * determine that then something is seriously wrong!
+ */
+#ifdef XN_FLAG_SEP_MULTILINE
+ X509_NAME_print_ex(bio_err,X509_get_subject_name(xs),4,
+ XN_FLAG_SEP_MULTILINE);
+ len = BIO_read(bio_err,subject,256);
+ subject[len < 256 ? len : 255] = '\0';
+ if (!subject[0]) {
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ uq_ok("X.509 Subject Name unavailable", ssl_err, 1, NULL, 0);
+ ok=0;
+ goto return_time;
+ }
+
+ X509_NAME_print_ex(bio_err,X509_get_issuer_name(xs),4,
+ XN_FLAG_SEP_MULTILINE);
+ len = BIO_read(bio_err,issuer,256);
+ issuer[len < 256 ? len : 255] = '\0';
+ if (!issuer[0]) {
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ uq_ok("X.509 Issuer Name unavailable", ssl_err, 1, NULL, 0);
+ ok=0;
+ goto return_time;
+ }
+#else /* XN_FLAG_SEP_MULTILINE */
+ X509_NAME_oneline(X509_get_subject_name(xs),subject,256);
+ if (!subject[0]) {
+ int len;
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ uq_ok("X.509 Subject Name unavailable", ssl_err, 1, NULL, 0);
+ ok=0;
+ goto return_time;
+ }
+
+ X509_NAME_oneline(X509_get_issuer_name(xs),issuer,256);
+ if (!issuer[0]) {
+ int len;
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ uq_ok("X.509 Issuer Name unavailable", ssl_err, 1, NULL, 0);
+ ok=0;
+ goto return_time;
+ }
+#endif /* XN_FLAG_SEP_MULTILINE */
+
+ if (ssl_verbose_flag && depth != ssl_verify_depth) {
+ printf("[%d] Certificate Subject:\r\n%s\r\n",depth,subject);
+ printf("[%d] Certificate Issuer:\r\n%s\r\n",depth,issuer);
+ ssl_verify_depth = depth;
+ }
+
+ ok = ssl_verify_crl(ok, ctx);
+
+ if ( !ok ) {
+ char prefix[1024];
+ /* if the server is using a self signed certificate then
+ * we need to decide if that is good enough for us to
+ * accept ...
+ */
+
+ switch ( error ) {
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ /* make 100% sure that in secure more we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ckmakxmsg(prefix,1024,
+ "Error: Server has a self-signed certificate\n",
+ "[",ckitoa(depth),"] Certificate Subject=\n",subject,
+ "\n[",ckitoa(depth),"] Certificate Issuer=\n",issuer,
+ NULL,NULL,NULL);
+
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ ckmakxmsg(prefix,1024,
+ "Warning: Server has a self-signed certificate\n",
+ "[",ckitoa(depth),"] Certificate Subject=\n",subject,
+ "\n[",ckitoa(depth),"] Certificate Issuer=\n",issuer,
+ NULL,NULL,NULL);
+
+ ok = uq_ok(prefix,
+ "Continue? (Y/N) ",
+ 3, NULL, 0);
+ if ( ok < 0 )
+ ok = 0;
+ goto return_time;
+ }
+ }
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ /* make 100% sure that in secure more we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ckmakxmsg(prefix,1024,
+ "Error: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Issuer=\n",issuer,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ ckmakxmsg(prefix,1024,
+ "Warning: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Issuer=\n",issuer,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0);
+ goto return_time;
+ }
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ int len;
+ /* make 100% sure that in secure more we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ASN1_TIME_print(bio_err,X509_get_notBefore(xs));
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ ckmakxmsg(prefix,1024,
+ "Error: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ "\nnotBefore=",ssl_err,
+ NULL,NULL,NULL,NULL,NULL,NULL);
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ int len;
+ ASN1_TIME_print(bio_err,X509_get_notBefore(xs));
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ ckmakxmsg(prefix,1024,
+ "Warning: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ "\n notBefore=",ssl_err,
+ NULL,NULL,NULL,NULL,NULL,NULL);
+ ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0);
+ }
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ int len;
+ /* make 100% sure that in secure more we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ASN1_TIME_print(bio_err,X509_get_notAfter(xs));
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+
+ ckmakxmsg(prefix,1024,
+ "Error: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ "\n notAfter=",ssl_err,
+ NULL,NULL,NULL,NULL,NULL,NULL);
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ int len;
+ ASN1_TIME_print(bio_err,X509_get_notAfter(xs));
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ ckmakxmsg(prefix,1024,
+ "Warning: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ "\n notAfter=",ssl_err,
+ NULL,NULL,NULL,NULL,NULL,NULL);
+ ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0);
+ }
+ break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ /*
+ * When an SSL server sends its certificates to the client there
+ * are two" conventions": one is to send the complete certificate
+ * chain and the other is to send the whole chain apart from the
+ * root.
+ *
+ * You don't usually need the root because the root is normally
+ * stored and trusted locally.
+ *
+ * So if you get the whole chain it will complain about the self
+ * signed certificate whereas if the root is missing it says it
+ * can't find the issuer certificate.
+ */
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ /* make 100% sure that in secure more we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ckmakxmsg(prefix,1024,
+ "Error: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Issuer=\n",issuer,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ ckmakxmsg(prefix,1024,
+ "Warning: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Issuer=\n",issuer,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0);
+#ifdef NT
+ if (ok) {
+ /* if the user decides to accept the certificate
+ * offer to store it for future connections in
+ * the user's private store
+ */
+ ok = uq_ok(
+ "Do you wish to store the certificate to verify future connections?",
+ "Continue (Y/N)", 3, NULL, 0);
+ if (ok)
+ ck_X509_save_cert_to_user_store(xs);
+ }
+#endif /* NT */
+ }
+ break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_OUT_OF_MEM:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_CERT_REVOKED:
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ default:
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ /* make 100% sure that in secure mode we drop the
+ * connection if the server does not have a
+ * real certificate!
+ */
+ ckmakxmsg(prefix,1024,
+ "Error: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ uq_ok(prefix, "Rejecting Connection", 1, NULL, 0);
+
+ /* sometimes it is really handy to be able to debug things
+ * and still get a connection!
+ */
+ if (ssl_debug_flag) {
+ printf("SSL: debug -> ignoring cert required!\r\n");
+ ok=1;
+ } else {
+ ok=0;
+ }
+ goto return_time;
+ } else if (ssl_verify_flag != SSL_VERIFY_NONE) {
+ ckmakxmsg(prefix,1024,
+ "Warning: ",
+ (char *)X509_verify_cert_error_string(error),
+ "\nCertificate Subject=\n",subject,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0);
+ }
+ break;
+ }
+ }
+
+ return_time:
+ if ( ssl_debug_flag )
+ printf("ssl:client_verify_callback => ok: %d\r\n",ok);
+ return ok;
+}
+
+VOID
+#ifdef CK_ANSIC
+ssl_client_info_callback(const SSL *s, int where, int ret)
+#else
+ssl_client_info_callback(s,where,ret)
+const SSL *s;
+int where;
+int ret;
+#endif /* CK_ANSIC */
+{
+ if (inserver || !ssl_debug_flag)
+ return;
+
+ switch ( where ) {
+ case SSL_CB_CONNECT_LOOP:
+ printf("SSL_connect:%s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ break;
+ case SSL_CB_CONNECT_EXIT:
+ if (ret == 0) {
+ printf("SSL_connect:failed in %s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ } else if (ret < 0) {
+ printf("SSL_connect:error in %s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ }
+ break;
+ case SSL_CB_ACCEPT_LOOP:
+ printf("SSL_accept:%s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ break;
+ case SSL_CB_ACCEPT_EXIT:
+ if (ret == 0) {
+ printf("SSL_accept:failed in %s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ } else if (ret < 0) {
+ printf("SSL_accept:error in %s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ }
+ break;
+ case SSL_CB_READ_ALERT:
+ printf("SSL_read_alert\r\n");
+ break;
+ case SSL_CB_WRITE_ALERT:
+ printf("SSL_write_alert\r\n");
+ break;
+ case SSL_CB_HANDSHAKE_START:
+ printf("SSL_handshake:%s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ break;
+ case SSL_CB_HANDSHAKE_DONE:
+ printf("SSL_handshake:%s %s\r\n",
+ SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s));
+ break;
+ }
+}
+
+#ifdef USE_CERT_CB
+/* Return 1, client cert is available */
+/* Return 0, no client cert is available */
+/* Return -1, callback must be called again. SSL_want_x509_lookup() == 1 */
+int
+#ifdef CK_ANSIC
+ssl_client_cert_callback(SSL * s, X509 ** x509, EVP_PKEY ** pkey)
+#else /* CK_ANSIC */
+ssl_client_cert_callback(s, x509, pkey)
+ SSL * s;
+ X509 ** x509;
+ EVP_PKEY ** pkey;
+#endif /* CK_ANSIC */
+{
+ if ( ssl_debug_flag ) {
+ const char * cipher_list=SSL_get_cipher(s);
+ printf("ssl_client_cert_callback called (%s)\r\n",
+ cipher_list?cipher_list:"UNKNOWN");
+ }
+#ifdef COMMENT
+ if ( s == tls_con ) {
+ if (tls_load_certs(tls_cts,tls_con,0)) {
+ *x509 = SSL_get_certificate(s);
+ *pkey = SSL_get_privatekey(s);
+ return(1);
+ }
+ } else if ( s == ssl_con ) {
+ if (tls_load_certs(ssl_ctx,ssl_con,0)) {
+ *x509 = SSL_get_certificate(s);
+ *pkey = SSL_get_privatekey(s);
+ return(1);
+ }
+ }
+ return(0);
+#else /* COMMENT */
+ return(0);
+#endif /* COMMENT */
+}
+#endif /* USE_CERT_CB */
+
+#ifndef MS_CALLBACK
+#define MS_CALLBACK
+#endif /* MS_CALLBACK */
+
+static RSA MS_CALLBACK *
+#ifdef CK_ANSIC
+tmp_rsa_cb(SSL * s, int export, int keylength)
+#else /* CK_ANSIC */
+tmp_rsa_cb(s,export,keylength)
+SSL *s;
+int export;
+int keylength;
+#endif /* CK_ANSIC */
+{
+ static RSA *rsa_tmp=NULL;
+ extern int quiet;
+
+#ifndef NO_RSA
+ if (rsa_tmp == NULL)
+ {
+ if (ssl_debug_flag)
+ printf("Generating temporary (%d bit) RSA key...\r\n",keylength);
+
+ rsa_tmp=RSA_generate_key(keylength,RSA_F4,NULL,NULL);
+
+ if (ssl_debug_flag)
+ printf("\r\n");
+ }
+#else /* NO_RSA */
+ if (ssl_debug_flag)
+ printf("Unable to generate temporary RSA key...\r\n");
+#endif
+ return(rsa_tmp);
+}
+
+
+#ifndef NO_DH
+static unsigned char dh512_p[]={
+ 0xE9,0x4E,0x3A,0x64,0xFA,0x65,0x5F,0xA6,0x44,0xC7,0xFC,0xF1,
+ 0x16,0x8B,0x11,0x11,0x7A,0xF0,0xB2,0x49,0x80,0x56,0xA3,0xF8,
+ 0x0F,0x7D,0x01,0x68,0x5D,0xF6,0x8A,0xEA,0x8C,0xDD,0x01,0xDC,
+ 0x43,0x18,0xE0,0xC4,0x89,0x80,0xE6,0x2D,0x44,0x77,0x45,0xFD,
+ 0xBA,0xFC,0x43,0x35,0x12,0xC0,0xED,0x32,0xD3,0x16,0xEF,0x51,
+ 0x09,0x44,0xA2,0xDB,
+};
+static unsigned char dh512_g[]={
+ 0x05,
+};
+
+static unsigned char dh768_p[]={
+ 0x8B,0x2A,0x8C,0x6C,0x0F,0x87,0xC7,0x34,0xEE,0x2E,0xFB,0x60,
+ 0x94,0xB3,0xBF,0x95,0xBA,0x84,0x74,0x86,0xEA,0xE0,0xA4,0x33,
+ 0xE0,0x8F,0x7C,0x79,0x5C,0x62,0xE2,0x91,0xC5,0x6D,0x68,0xB9,
+ 0x6C,0x5E,0x4E,0x94,0x0C,0x8E,0x56,0x8E,0xEB,0x98,0x7C,0x6E,
+ 0x0E,0xF2,0xD5,0xAA,0x22,0x27,0x3F,0x0F,0xAF,0x10,0xB5,0x0B,
+ 0x16,0xCC,0x05,0x27,0xBB,0x58,0x6D,0x61,0x4B,0x2B,0xAB,0xDC,
+ 0x6A,0x15,0xBC,0x36,0x75,0x4D,0xEC,0xAB,0xFA,0xB6,0xE1,0xB1,
+ 0x13,0x70,0xD8,0x77,0xCD,0x5E,0x51,0x77,0x81,0x0D,0x77,0x43,
+};
+static unsigned char dh768_g[]={
+ 0x05,
+};
+
+static unsigned char dh1024_p[]={
+ 0xA4,0x75,0xCF,0x35,0x00,0xAF,0x3C,0x17,0xCE,0xB0,0xD0,0x52,
+ 0x43,0xA0,0x0E,0xFA,0xA2,0xC9,0xBE,0x0B,0x76,0x7A,0xD9,0x2E,
+ 0xF4,0x97,0xAC,0x02,0x24,0x69,0xF6,0x36,0x4F,0xAB,0xCC,0x43,
+ 0xC1,0x74,0xFF,0xA3,0xD4,0x04,0x0F,0x11,0x2B,0x6D,0x8C,0x47,
+ 0xC9,0xCF,0x40,0x93,0x9B,0x7D,0x1E,0x52,0x85,0xB2,0x17,0x55,
+ 0x9C,0xF2,0x41,0x02,0x2A,0x9D,0x5F,0x24,0x22,0xC6,0x04,0xC4,
+ 0xAB,0x92,0x6D,0xC7,0xC8,0xF3,0x41,0x58,0x6C,0x86,0xFD,0xB8,
+ 0x0F,0x2D,0xDD,0xBF,0xA8,0x40,0x0C,0x58,0xC8,0xF2,0x3F,0x18,
+ 0xEF,0xF1,0x93,0x3E,0xBA,0x16,0x41,0xBE,0x32,0x6C,0xC5,0x63,
+ 0xFF,0x8A,0x02,0x3D,0xAC,0xD5,0x5A,0x49,0x64,0x34,0x14,0x2E,
+ 0xFB,0x2E,0xE7,0x39,0x1A,0x0F,0x3C,0x33,
+};
+static unsigned char dh1024_g[]={
+ 0x05,
+};
+
+static unsigned char dh1536_p[]={
+ 0xA3,0x2B,0x75,0x0E,0x7B,0x31,0x82,0xCA,0xF2,0xFC,0xF3,0x3D,
+ 0xCE,0x5F,0xCD,0x5B,0x95,0xF6,0x2F,0xA4,0x5D,0x08,0x26,0xD2,
+ 0x5F,0xC0,0x3F,0xC5,0xD8,0xA2,0xFE,0x83,0x26,0xBC,0xEB,0x7D,
+ 0xF0,0x4E,0xD2,0xA6,0xBB,0x3C,0x88,0x63,0xCE,0x98,0xDE,0x08,
+ 0xE2,0xE1,0xAF,0xE2,0x38,0xA8,0xFA,0x68,0x76,0x8D,0xBF,0xDF,
+ 0xBB,0x30,0x15,0xFE,0xBD,0x22,0xCC,0x03,0x4E,0x5E,0x33,0xA3,
+ 0x6D,0xD6,0x68,0x12,0x97,0x17,0x4B,0xB5,0x84,0x5F,0x5F,0xA3,
+ 0x5C,0x2F,0xA4,0x10,0xC1,0xAD,0xBF,0xAC,0x30,0xCA,0x47,0x64,
+ 0x63,0xFE,0xEE,0xEE,0xA1,0x64,0x73,0x70,0xAA,0xF9,0xFE,0xC6,
+ 0xAD,0x5E,0xF6,0xF3,0x9C,0xDF,0x34,0x53,0x34,0x72,0xA6,0xA4,
+ 0xBB,0x81,0x5A,0x43,0x41,0xFD,0x41,0x05,0x5B,0x77,0x7B,0x84,
+ 0x03,0xFA,0x8A,0xFA,0xF7,0x8E,0x0F,0xCB,0x51,0xA2,0xB8,0x45,
+ 0xFF,0x59,0x42,0xEF,0xCF,0xF6,0x25,0x37,0xE2,0x6D,0xFF,0x69,
+ 0x11,0xF5,0x77,0x59,0x79,0x1C,0x5F,0x05,0xFC,0x7A,0x65,0x81,
+ 0x03,0x4A,0x78,0xC6,0xE9,0x48,0x73,0xF6,0x10,0xBC,0x99,0x1C,
+ 0xEE,0x44,0x2F,0x8B,0x70,0xCA,0xA8,0xB6,0x02,0x83,0x3E,0x0B,
+};
+static unsigned char dh1536_g[]={
+ 0x05,
+};
+
+static unsigned char dh2048_p[]={
+ 0xFA,0x4E,0xE4,0x3B,0xFA,0xC1,0x87,0xDD,0xE7,0xC6,0x8B,0xE6,
+ 0x13,0x85,0xBC,0x9B,0x2B,0x8B,0x5B,0x46,0xBB,0x8B,0x86,0x6D,
+ 0xD7,0xB6,0xD5,0x49,0xC5,0x54,0xF2,0x3E,0xD2,0x39,0x64,0x9B,
+ 0x0E,0x33,0x39,0x8F,0xFA,0xFA,0xD9,0x78,0xED,0x34,0x82,0x29,
+ 0x37,0x58,0x4D,0x5D,0x40,0xCB,0x69,0xE3,0x8A,0x9F,0x17,0x0C,
+ 0x01,0x23,0x6B,0x05,0x01,0xAF,0x33,0xDE,0xDF,0x1A,0xBB,0x7B,
+ 0x6A,0x9F,0xD8,0xED,0x8D,0x5E,0x44,0x19,0x5B,0xE0,0xB6,0x23,
+ 0xF9,0x7A,0x96,0x6E,0x94,0x33,0x31,0x49,0xBA,0x84,0xD5,0x12,
+ 0xD7,0x6D,0xDC,0x35,0x54,0x64,0xA3,0xD8,0x04,0x26,0xC5,0xAF,
+ 0x7F,0xE3,0xFE,0x6F,0xBE,0xD5,0x17,0x72,0x4B,0xA6,0xD0,0xA7,
+ 0x5F,0x18,0xF5,0xF0,0x2D,0x11,0x9A,0xF6,0xD5,0x3B,0x6C,0x61,
+ 0x3C,0x6F,0x8E,0x09,0x4F,0x2C,0xE1,0x26,0x06,0x51,0xB3,0x19,
+ 0x85,0x85,0x13,0xF9,0xC2,0x6E,0x80,0x28,0x9E,0x8A,0xA0,0x01,
+ 0x46,0xD1,0x85,0x44,0x8C,0xE6,0xEE,0x7E,0x1E,0x17,0x3D,0xBA,
+ 0x54,0xFF,0xE8,0x0E,0xDD,0x51,0xF3,0x74,0x7F,0x0D,0x0B,0xAB,
+ 0xCA,0x84,0x8D,0x24,0x5D,0x56,0xD4,0x47,0x02,0xFC,0x93,0x9F,
+ 0xAE,0x9B,0x5C,0xDB,0x63,0xEB,0x65,0x01,0x38,0xC2,0x7B,0x30,
+ 0x1E,0x17,0x1C,0x75,0xF5,0x16,0x3B,0x4F,0x5F,0x41,0x32,0xB5,
+ 0xFF,0x9E,0x61,0xFD,0xD2,0x62,0x6E,0xFD,0x8A,0x28,0x93,0x59,
+ 0x2D,0x70,0x14,0x4D,0xE1,0x86,0xD5,0x90,0xB4,0xDF,0x72,0x71,
+ 0xE0,0xB4,0xD0,0xD6,0x82,0x3A,0x4A,0x04,0x58,0x32,0x0B,0xD3,
+ 0x51,0x13,0x32,0x63,
+};
+static unsigned char dh2048_g[]={
+ 0x02,
+};
+
+static DH *
+get_dh512()
+{
+ DH *dh=NULL;
+
+ if ((dh=DH_new()) == NULL)
+ return(NULL);
+ dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
+ dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return(NULL);
+ return(dh);
+}
+
+static DH *
+get_dh768()
+{
+ DH *dh=NULL;
+
+ if ((dh=DH_new()) == NULL)
+ return(NULL);
+ dh->p=BN_bin2bn(dh768_p,sizeof(dh768_p),NULL);
+ dh->g=BN_bin2bn(dh768_g,sizeof(dh768_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return(NULL);
+ return(dh);
+}
+
+static DH *
+get_dh1024()
+{
+ DH *dh=NULL;
+
+ if ((dh=DH_new()) == NULL)
+ return(NULL);
+ dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL);
+ dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return(NULL);
+ return(dh);
+}
+
+static DH *
+get_dh1536()
+{
+ DH *dh=NULL;
+
+ if ((dh=DH_new()) == NULL)
+ return(NULL);
+ dh->p=BN_bin2bn(dh1536_p,sizeof(dh1536_p),NULL);
+ dh->g=BN_bin2bn(dh1536_g,sizeof(dh1536_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return(NULL);
+ return(dh);
+}
+
+static DH *
+get_dh2048()
+{
+ DH *dh=NULL;
+
+ if ((dh=DH_new()) == NULL)
+ return(NULL);
+ dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
+ dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ return(NULL);
+ return(dh);
+}
+#endif /* NO_DH */
+
+static DH MS_CALLBACK *
+#ifdef CK_ANSIC
+tmp_dh_cb(SSL * s, int export, int keylength)
+#else /* CK_ANSIC */
+tmp_dh_cb(s,export,keylength)
+SSL *s;
+int export;
+int keylength;
+#endif /* CK_ANSIC */
+{
+ static DH *dh_tmp=NULL;
+ BIO *bio=NULL;
+ extern int quiet;
+
+#ifndef NO_DH
+ if (dh_tmp == NULL)
+ {
+ if (ssl_dh_param_file &&
+ (bio=BIO_new_file(ssl_dh_param_file,"r")) != NULL)
+ dh_tmp=PEM_read_bio_DHparams(bio,NULL,NULL,NULL);
+ if (bio != NULL)
+ BIO_free(bio);
+
+ if ( dh_tmp == NULL ) {
+ if ( keylength < 768 )
+ dh_tmp = get_dh512();
+ else if ( keylength < 1024 )
+ dh_tmp = get_dh768();
+ else if ( keylength < 1536 )
+ dh_tmp = get_dh1024();
+ else if ( keylength < 2048 )
+ dh_tmp = get_dh1536();
+ else
+ dh_tmp = get_dh2048();
+ }
+ }
+#else /* NO_DH */
+ if (ssl_debug_flag)
+ printf("DH not supported...\r\n");
+#endif /* NO_DH */
+ return(dh_tmp);
+}
+
+static void
+ssl_display_comp(SSL * ssl)
+{
+ if ( !ck_ssleay_is_installed() )
+ return;
+
+ if (ssl == NULL)
+ return;
+
+ if (ssl->expand == NULL || ssl->expand->meth == NULL)
+ printf("Compression: None\r\n");
+ else {
+ printf("Compression: %s\r\n",ssl->expand->meth->name);
+ }
+}
+
+int
+#ifdef CK_ANSIC
+ssl_display_connect_details(SSL * ssl_con, int server, int verbose)
+#else /* CK_ANSIC */
+ssl_display_connect_details(ssl_con,server,verbose)
+SSL *ssl_con;
+int server;
+int verbose;
+#endif /* CK_ANSIC */
+{
+ X509 *peer;
+ SSL_CIPHER * cipher;
+ const char *cipher_list;
+ char buf[512]="";
+
+ if ( !ck_ssleay_is_installed() )
+ return(0);
+
+ if ( inserver && !tn_deb )
+ return(0);
+
+ /* the cipher list *can* be NULL ... useless but it happens! */
+ cipher = SSL_get_current_cipher(ssl_con);
+ cipher_list = SSL_CIPHER_get_name(cipher);
+ SSL_CIPHER_description(cipher,buf,sizeof(buf));
+ if (cipher_list==NULL)
+ cipher_list="<NULL>";
+ printf("[TLS - %s",buf);
+ ssl_display_comp(ssl_con);
+
+ if ( server ) {
+ cipher_list=SSL_get_shared_ciphers(ssl_con,buf,512);
+ if (cipher_list==NULL)
+ cipher_list="<NULL>";
+ printf("[TLS - shared ciphers=%s]\r\n",
+ cipher_list);
+ }
+ if ( server || tn_deb ) {
+ peer=SSL_get_peer_certificate(ssl_con);
+ if (peer != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(peer),buf,512);
+ printf("[TLS - subject=%s]\r\n",buf);
+ X509_NAME_oneline(X509_get_issuer_name(peer),buf,512);
+ printf("[TLS - issuer=%s]\r\n",buf);
+ /* X509_free(peer); */
+ } else if (!tls_is_krb5(0)) {
+ if ( !sstelnet && !tcp_incoming ) {
+ printf("[TLS - No certificate provided.]\r\n");
+ printf(
+ "[TLS - The identity of the host could not be verified.]\r\n");
+ }
+ }
+ }
+ return(0);
+}
+
+/*
+ * Use SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *, void * userdata)
+ * to set the value of the userdata. We are going to use it to store the
+ * prompt.
+ */
+
+int
+#ifdef CK_ANSIC
+ssl_passwd_callback(char *buf, int len, int rwflag, VOID * userdata)
+#else /* CK_ANSIC */
+ssl_passwd_callback(buf,len,rwflag,userdata)
+ char * buf; int len; int rwflag; VOID *userdata;
+#endif /* CK_ANSIC */
+{
+ extern char pwbuf[];
+ extern int pwflg, pwcrypt;
+ int ok;
+ char *prompt=NULL;
+
+ if ( pwbuf[0] && pwflg ) {
+ int n;
+ n = ckstrncpy(buf,pwbuf,len);
+#ifdef OS2
+ if ( pwcrypt )
+ ck_encrypt((char *)buf);
+#endif /* OS2 */
+ return(n);
+ }
+
+ if ( userdata == NULL )
+ prompt="Enter certificate passphrase: ";
+ else
+ prompt=(char*)userdata;
+ ok = uq_txt(NULL,prompt,2,NULL,buf,len,NULL,DEFAULT_UQ_TIMEOUT);
+ return(ok > 0 ? strlen(buf) : 0);
+}
+
+
+/* Attempts to load certificate data into the TLS context structures */
+/* Returns 1 on success; 0 on failure */
+int
+tls_load_certs(SSL_CTX * ctx, SSL * con, int server)
+{
+ int rc = 1;
+ extern int quiet;
+
+ if ( !ck_ssleay_is_installed() )
+ return(0);
+
+ debug(F111,"tls_load_certs","SSL_CTX",ctx);
+ debug(F111,"tls_load_certs","SSL",con);
+ debug(F111,"tls_load_certs","server",server);
+
+ if ( con ) {
+ if (ssl_rsa_cert_file) {
+ if ( ssl_debug_flag )
+ printf("Loading RSA certificate into SSL\r\n");
+
+ rc = SSL_use_certificate_file(con, ssl_rsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ {
+ if ( !quiet || ssl_debug_flag )
+ printf("Error loading certificate from %s\r\n",
+ ssl_rsa_cert_file);
+ } else {
+ if (!ssl_rsa_key_file || !ssl_rsa_key_file[0])
+ makestr(&ssl_rsa_key_file,ssl_rsa_cert_file);
+
+ rc = SSL_use_PrivateKey_file(con, ssl_rsa_key_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ rc = SSL_use_PrivateKey_file(con, ssl_rsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ {
+ if ( !quiet || ssl_debug_flag )
+ printf("Error loading key from %s\r\n",
+ ssl_rsa_key_file);
+ } else {
+ rc = SSL_check_private_key(con);
+ if (!rc)
+ {
+ if ( ssl_debug_flag )
+ printf(
+ "Private key does not match the certificate public key\r\n");
+ }
+ }
+ }
+ }
+
+ if (ssl_dsa_cert_file) {
+ if ( ssl_debug_flag )
+ printf("Loading DSA certificate into SSL\r\n");
+
+ rc = SSL_use_certificate_file(con, ssl_dsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ {
+ if ( ssl_debug_flag ) {
+ printf("Error loading certificate from %s\r\n",
+ ssl_dsa_cert_file);
+ }
+ } else {
+ if (!ssl_dh_key_file || !ssl_dh_key_file[0])
+ makestr(&ssl_dh_key_file,ssl_dsa_cert_file);
+ rc = SSL_use_PrivateKey_file(con, ssl_dh_key_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ rc = SSL_use_PrivateKey_file(con, ssl_dsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ {
+ if ( !quiet || ssl_debug_flag ) {
+ printf("Error loading key from %s\r\n",
+ ssl_dh_key_file);
+ }
+ } else {
+ rc = SSL_check_private_key(con);
+ if (!rc)
+ {
+ if ( ssl_debug_flag )
+ printf(
+ "Private key does not match the certificate public key\n");
+ }
+ }
+ }
+ }
+ } else {
+ if (ssl_rsa_cert_file) {
+ if ( ssl_debug_flag )
+ printf("Loading RSA certificate into SSL\r\n");
+
+ rc = SSL_CTX_use_certificate_file(ctx, ssl_rsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ {
+ if ( !quiet || ssl_debug_flag )
+ printf("Error loading certificate from %s\r\n",
+ ssl_rsa_cert_file);
+ } else {
+ if (!ssl_rsa_key_file || !ssl_rsa_key_file[0])
+ makestr(&ssl_rsa_key_file,ssl_rsa_cert_file);
+
+ rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_rsa_key_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_rsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc) {
+ if ( ssl_debug_flag )
+ printf("Error loading key from %s\r\n",ssl_rsa_key_file);
+ } else {
+ rc = SSL_CTX_check_private_key(ctx);
+ if (!rc) {
+ if ( ssl_debug_flag )
+ printf(
+ "Private key does not match the certificate public key\r\n");
+ }
+ }
+ }
+ }
+ if (ssl_dsa_cert_file) {
+ if ( ssl_debug_flag )
+ printf("Loading DSA certificate into SSL\r\n");
+
+ rc = SSL_CTX_use_certificate_file(ctx, ssl_dsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc) {
+ if ( ssl_debug_flag ) {
+ printf("Error loading certificate from %s\r\n",
+ ssl_dsa_cert_file);
+ }
+ } else {
+ if (!ssl_dh_key_file || !ssl_dh_key_file[0])
+ makestr(&ssl_dh_key_file,ssl_dsa_cert_file);
+ rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_dh_key_file,
+ X509_FILETYPE_PEM);
+ if (!rc)
+ rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_dsa_cert_file,
+ X509_FILETYPE_PEM);
+ if (!rc) {
+ if ( ssl_debug_flag )
+ printf("Error loading key from %s\r\n",ssl_dh_key_file);
+ } else {
+ rc = SSL_CTX_check_private_key(ctx);
+ if (!rc) {
+ if ( ssl_debug_flag )
+ printf(
+ "Private key does not match the certificate public key\n");
+ }
+ }
+ }
+ }
+ }
+
+ if (ssl_rsa_cert_chain_file && server) {
+ int skip1st = 0;
+ if (ssl_debug_flag)
+ printf("Loading RSA Certificate Chain into SSL\r\n");
+ if (!ckstrcmp(ssl_rsa_cert_chain_file,ssl_rsa_cert_file,-1,
+#ifdef OS2
+ 0
+#else
+ 1
+#endif /* OS2 */
+ ))
+ skip1st = 1;
+ rc = SSL_CTX_use_certificate_chain_file(ctx,ssl_rsa_cert_chain_file);
+ if (!rc && ssl_debug_flag)
+ printf("Error loading RSA Certificate Chain into SSL\r\n");
+ }
+ if (ssl_dsa_cert_chain_file && server) {
+ int skip1st = 0;
+ if (ssl_debug_flag)
+ printf("Loading DSA Certificate Chain into SSL\r\n");
+ if (!ckstrcmp(ssl_dsa_cert_chain_file,ssl_dsa_cert_file,-1,
+#ifdef OS2
+ 0
+#else
+ 1
+#endif /* OS2 */
+ ))
+ skip1st = 1;
+ rc = SSL_CTX_use_certificate_chain_file(ctx,ssl_dsa_cert_chain_file);
+ if (!rc && ssl_debug_flag)
+ printf("Error loading DSA Certificate Chain into SSL\r\n");
+ }
+ return(rc);
+}
+
+VOID
+#ifdef CK_ANSIC
+ssl_once_init(void)
+#else
+ssl_once_init()
+#endif /* CK_ANSIC */
+{
+ COMP_METHOD * cm;
+
+ if ( !ck_ssleay_is_installed() )
+ return;
+
+ debug(F111,"Kermit built for OpenSSL",OPENSSL_VERSION_TEXT,SSLEAY_VERSION_NUMBER);
+#ifndef OS2ONLY
+ debug(F111,"OpenSSL Library",SSLeay_version(SSLEAY_VERSION),
+ SSLeay());
+ debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_BUILT_ON),0);
+ debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_CFLAGS),0);
+ debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_PLATFORM),0);
+
+ /* The following test is suggested by Richard Levitte */
+ if (((OPENSSL_VERSION_NUMBER ^ SSLeay()) & 0xffffff0f)
+#ifdef OS2
+ || ckstrcmp(OPENSSL_VERSION_TEXT,(char *)SSLeay_version(SSLEAY_VERSION),-1,1)
+#endif /* OS2 */
+ ) {
+ ssl_installed = 0;
+ debug(F111,"OpenSSL Version does not match. Built with",
+ SSLeay_version(SSLEAY_VERSION),SSLEAY_VERSION_NUMBER);
+ printf("?OpenSSL libraries do not match required version.");
+ printf(" SSL\\TLS support disabled\r\n\r\n");
+ bleep(BP_FAIL);
+#ifdef SSLDLL
+ ck_ssl_unloaddll();
+ ck_crypto_unloaddll();
+#endif /* SSLDLL */
+ return;
+ }
+#endif /* OS2ONLY */
+
+ /* init things so we will get meaningful error messages
+ * rather than numbers
+ */
+ SSL_load_error_strings();
+
+#ifdef SSHBUILTIN
+ OPENSSL_add_all_algorithms_noconf();
+#else
+ /* SSL_library_init() only loads those ciphers needs for SSL */
+ /* These happen to be a similar set to those required for SSH */
+ /* but they are not a complete set of ciphers provided by the */
+ /* crypto library. */
+ SSL_library_init();
+#endif /* SSHBUILTIN */
+
+#ifdef ZLIB
+ cm = COMP_zlib();
+ if (cm != NULL && cm->type != NID_undef) {
+ SSL_COMP_add_compression_method(0xe0, cm); /* EAY's ZLIB ID */
+ }
+#endif /* ZLIB */
+ cm = COMP_rle();
+ if (cm != NULL && cm->type != NID_undef)
+ SSL_COMP_add_compression_method(0xe1, cm); /* EAY's RLE ID */
+
+ /* Ensure the Random number generator has enough entropy */
+ if ( !RAND_status() ) {
+ char buffer[256]="";
+ char randombytes[256];
+ int rc1 = -1, rc2 = 1; /* assume failure and success */
+
+ debug(F110,"ssl_once_init","!RAND_status()",0);
+
+ if ( ssl_rnd_file == NULL ) {
+ debug(F110,"ssl_rnd_file","ssl_rnd_file is NULL",0);
+ RAND_file_name(buffer,256);
+ if ( buffer[0] )
+ makestr(&ssl_rnd_file, buffer);
+ else
+ makestr(&ssl_rnd_file,".rnd");
+ }
+ debug(F110,"ssl_rnd_file",ssl_rnd_file,0);
+
+ rc1 = RAND_egd(ssl_rnd_file);
+ debug(F111,"ssl_once_init","RAND_egd()",rc1);
+ if ( rc1 <= 0 ) {
+ rc2 = RAND_load_file(ssl_rnd_file, -1);
+ debug(F111,"ssl_once_init","RAND_load_file()",rc1);
+ }
+
+ if ( rc1 <= 0 && !rc2 )
+ {
+ time_t t = time(NULL);
+ int tlen = sizeof(time_t);
+ int pid = getpid();
+ int plen = sizeof(int);
+ int n;
+#ifndef RAND_MAX
+#define RAND_MAX 0x7FFF
+#endif
+ debug(F110,"ssl_once_init","calling RAND_seed()",0);
+
+ RAND_seed((unsigned char *)&t, tlen);
+ RAND_seed((unsigned char *)&pid, plen);
+
+ srand((unsigned int)t);
+ sprintf(buffer, "%.0f", (((double)(rand()%RAND_MAX)/RAND_MAX)*
+ (sizeof(randombytes)-128-1)));
+ n = (atoi(buffer)+1)%(sizeof(randombytes)-128-1);
+ RAND_seed(randombytes, 128);
+ }
+
+ if ( !RAND_status() ) {
+ debug(F110,"ssl_once_init","Unable to initialize PRNG",0);
+ printf(" Unable to load 'random state'\n");
+ printf(" SSL and TLS are unavailble.\n");
+ printf(" Use SET AUTH SSL RANDOM-FILE <file> command to provide random data.\n");
+ printf(" Specified file will be overwritten with new random data after use.\n");
+ return;
+ }
+
+ if ( ssl_rnd_file ) {
+ int rc = RAND_write_file(ssl_rnd_file);
+ debug(F111,"ssl_once_init","RAND_write_file()",rc);
+ }
+ }
+
+#ifdef NT
+ // Initialize additional OID types for use when saving certs to a file
+ OBJ_create("2.99999.3","SET.ex3","SET x509v3 extension 3");
+#endif /* NT */
+
+ /* make sure we have somewhere we can log errors to */
+ bio_err=BIO_new(BIO_s_mem());
+
+ debug(F100,"ssl_once_init() complete","",0);
+}
+
+int
+#ifdef CK_ANSIC
+ssl_tn_init(int mode)
+#else
+ssl_tn_init(mode) int mode;
+#endif /* CK_ANSIC */
+{
+#ifdef KRB5
+ extern char * k5_keytab;
+ extern char * krb5_d_srv;
+#endif /* KRB5 */
+ static int last_ssl_mode = -1;
+ SSL * ssl_conx=NULL, * tls_conx=NULL;
+
+ ssl_initialized = 0;
+
+ if ( !ck_ssleay_is_installed() )
+ return(0);
+
+ debug(F111,"ssl_tn_init","mode",mode);
+
+ if (ssl_debug_flag)
+ printf("SSL_DEBUG_FLAG on\r\n");
+
+ if (last_ssl_mode != mode) {
+ if (ssl_ctx) {
+ SSL_CTX_free(ssl_ctx);
+ ssl_ctx = NULL;
+ }
+ if (tls_ctx) {
+ SSL_CTX_free(tls_ctx);
+ tls_ctx = NULL;
+ }
+ }
+
+ if ( (last_ssl_mode != mode) || !ssl_ctx || !tls_ctx ) {
+ if ( mode == SSL_CLIENT ) {
+ ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method());
+ /* This can fail because we do not have RSA available */
+ if ( !ssl_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv23_client_method failed",0);
+ ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method());
+ }
+ if ( !ssl_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv3_client_method failed",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+#ifndef COMMENT
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_client_method());
+#else /* COMMENT */
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method());
+ /* This can fail because we do not have RSA available */
+ if ( !tls_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv23_client_method failed",0);
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method());
+ }
+#endif /* COMMENT */
+ if ( !tls_ctx ) {
+ debug(F110,"ssl_tn_init","TLSv1_client_method failed",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+#ifdef USE_CERT_CB
+ SSL_CTX_set_client_cert_cb(ssl_ctx,ssl_client_cert_callback);
+ SSL_CTX_set_client_cert_cb(tls_ctx,ssl_client_cert_callback);
+#endif /* USE_CERT_CB */
+ } else if (mode == SSL_SERVER) {
+ /* We are a server */
+ ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
+ /* This can fail because we do not have RSA available */
+ if ( !ssl_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv23_server_method failed",0);
+ ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_server_method());
+ }
+ if ( !ssl_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv3_server_method failed",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+#ifdef COMMENT
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_server_method());
+#else /* COMMENT */
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method());
+ /* This can fail because we do not have RSA available */
+ if ( !tls_ctx ) {
+ debug(F110,"ssl_tn_init","SSLv23_server_method failed",0);
+ tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_server_method());
+ }
+#endif /* COMMENT */
+ if ( !tls_ctx ) {
+ debug(F110,"ssl_tn_init","TLSv1_server_method failed",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+ } else /* Unknown mode */
+ return(0);
+
+ if ( !inserver ) {
+ SSL_CTX_set_default_passwd_cb(ssl_ctx,
+ (pem_password_cb *)ssl_passwd_callback);
+ SSL_CTX_set_default_passwd_cb(tls_ctx,
+ (pem_password_cb *)ssl_passwd_callback);
+ }
+
+ /* for SSL switch on all the interoperability and bug
+ * workarounds so that we will communicate with people
+ * that cannot read poorly written specs :-)
+ * for TLS be sure to prevent use of SSLv2
+ */
+ SSL_CTX_set_options(ssl_ctx,SSL_OP_ALL|SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(tls_ctx,
+ SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA);
+
+ SSL_CTX_set_info_callback(ssl_ctx,ssl_client_info_callback);
+ SSL_CTX_set_info_callback(tls_ctx,ssl_client_info_callback);
+
+#ifndef COMMENT
+ /* Set the proper caching mode */
+ if ( mode == SSL_SERVER ) {
+ SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_SERVER);
+ SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_SERVER);
+ } else {
+ SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_CLIENT);
+ SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_CLIENT);
+ }
+ SSL_CTX_set_session_id_context(ssl_ctx,(CHAR *)"1",1);
+ SSL_CTX_set_session_id_context(tls_ctx,(CHAR *)"2",1);
+#else /* COMMENT */
+ SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_OFF);
+#endif /* COMMENT */
+ }
+
+ /* The server uses defaults for the certificate files. */
+ /* The client does not. */
+ if (mode == SSL_SERVER) {
+ char cert_filepath[1024];
+ const char * defdir = NULL;
+ DH * dh = NULL;
+
+ defdir = getenv("SSL_CERT_DIR");
+ if ( !defdir ) {
+#ifdef OS2
+ defdir = exedir;
+#else /* OS2 */
+ defdir = X509_get_default_cert_dir();
+#endif /* OS2 */
+ debug(F110,"ssl_tn_init - setting default directory to",defdir,0);
+ }
+ if ( !defdir )
+ defdir = "";
+
+ if (!ssl_rsa_cert_file) {
+ /* we need to know the fullpath to the location of the
+ * certificate that we will be running with as we cannot
+ * be sure of the cwd when we are launched
+ */
+ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-rsa.pem");
+ if (zchki(cert_filepath) > 0)
+ makestr(&ssl_rsa_cert_file,cert_filepath);
+ }
+ if (ssl_rsa_cert_file && !ssl_rsa_key_file) {
+ /* we need to know the fullpath to the location of the
+ * certificate that we will be running with as we cannot
+ * be sure of the cwd when we are launched
+ */
+ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-rsa-key.pem");
+ if (zchki(cert_filepath) > 0)
+ makestr(&ssl_rsa_key_file,cert_filepath);
+ }
+ if (!ssl_dsa_cert_file) {
+ /* we need to know the fullpath to the location of the
+ * certificate that we will be running with as we cannot
+ * be sure of the cwd when we are launched
+ */
+ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-dsa.pem");
+ if (zchki(cert_filepath) > 0)
+ makestr(&ssl_dsa_cert_file,cert_filepath);
+ }
+ if (ssl_dsa_cert_file && !ssl_dh_key_file) {
+ /* we need to know the fullpath to the location of the
+ * certificate that we will be running with as we cannot
+ * be sure of the cwd when we are launched
+ */
+ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-dsa-key.pem");
+ if (zchki(cert_filepath) > 0)
+ makestr(&ssl_dh_key_file,cert_filepath);
+ }
+ if (!ssl_crl_dir) {
+ /* we need to know the fullpath to the location of the
+ * certificate that we will be running with as we cannot
+ * be sure of the cwd when we are launched
+ */
+ sprintf(cert_filepath,"%s/crl",defdir);
+ if (zchki(cert_filepath) > 0)
+ makestr(&ssl_crl_dir,cert_filepath);
+ }
+
+ if (ssl_only_flag && !tls_load_certs(ssl_ctx,ssl_con,1)) {
+ debug(F110,"ssl_tn_init","Unable to load SSL certs",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+ if (tls_only_flag && !tls_load_certs(tls_ctx,tls_con,1)) {
+ debug(F110,"ssl_tn_init","Unable to load TLS certs",0);
+ last_ssl_mode = -1;
+ return(0);
+ }
+
+ if ( (last_ssl_mode != mode) || !ssl_ctx || !tls_ctx ) {
+ /* we may require a temp 512 bit RSA key because of the
+ * wonderful way export things work ... if so we generate
+ * one now!
+ */
+
+ SSL_CTX_set_tmp_rsa_callback(ssl_ctx, tmp_rsa_cb);
+ SSL_CTX_set_tmp_dh_callback( ssl_ctx, tmp_dh_cb);
+ SSL_CTX_set_tmp_rsa_callback(tls_ctx, tmp_rsa_cb);
+ SSL_CTX_set_tmp_dh_callback( tls_ctx, tmp_dh_cb);
+
+ dh = tmp_dh_cb(NULL,0,512);
+ SSL_CTX_set_tmp_dh(ssl_ctx,dh);
+ SSL_CTX_set_tmp_dh(tls_ctx,dh);
+
+ /* The following code is only called if we are using a
+ * certificate with an RSA public key and where the
+ * certificate has a key length less than 512 bits or is
+ * marked for signing only. This is so we can support
+ * the greatest legal privacy level with exportable clients.
+ */
+
+ if (SSL_CTX_need_tmp_RSA(ssl_ctx) ||
+ SSL_CTX_need_tmp_RSA(tls_ctx))
+ {
+ RSA *rsa;
+
+ if ( ssl_debug_flag )
+ printf("Generating temp (512 bit) RSA key ...\r\n");
+ rsa=RSA_generate_key(512,RSA_F4,NULL,NULL);
+ if ( ssl_debug_flag )
+ printf("Generation of temp (512 bit) RSA key done\r\n");
+
+ if (SSL_CTX_need_tmp_RSA(ssl_ctx)) {
+ if (!SSL_CTX_set_tmp_rsa(ssl_ctx,rsa)) {
+ if ( ssl_debug_flag )
+ printf(
+ "Failed to assign generated temp RSA key to SSL!\r\n");
+ }
+ }
+ if (SSL_CTX_need_tmp_RSA(tls_ctx)) {
+ if (!SSL_CTX_set_tmp_rsa(tls_ctx,rsa)) {
+ if ( ssl_debug_flag )
+ printf(
+ "Failed to assign generated temp RSA key to TLS!\r\n");
+ }
+ }
+ RSA_free(rsa);
+ if ( ssl_debug_flag )
+ printf("Assigned temp (512 bit) RSA key\r\n");
+ }
+ }
+ }
+
+ /* make sure we will find certificates in the standard
+ * location ... otherwise we don't look anywhere for
+ * these things which is going to make client certificate
+ * exchange rather useless :-)
+ * In OS2, default values for ssl_verify_file and ssl_verify_path.
+ */
+
+#ifdef OS2
+#ifdef NT
+ {
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (isdir(path) &&
+ SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init certificate verify dir",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification Directory: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,NULL,path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/certs",NULL,NULL);
+ if (isdir(path) &&
+ SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init certificate verify dir",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification Directory: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,NULL,path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/certs",NULL,NULL);
+ if (isdir(path) &&
+ SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init certificate verify dir",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification Directory: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,NULL,path);
+ }
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init certificate verify file",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification File: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,path,NULL);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_certs.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init certificate verify file",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification File: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,path,NULL);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_certs.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init certificate verify file",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification File: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,path,NULL);
+ }
+ }
+#else /* NT */
+ {
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (isdir(path) &&
+ SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init certificate verify dir",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification Directory: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,NULL,path);
+ }
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init certificate verify file",path,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification File: %s\r\n",path);
+ SSL_CTX_load_verify_locations(ssl_ctx,path,NULL);
+ }
+ }
+#endif /* NT */
+#else /* OS2 */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ SSL_CTX_set_default_verify_paths(tls_ctx);
+#endif /* OS2 */
+
+ if (ssl_verify_file) {
+ if (zchki(ssl_verify_file) > 0 &&
+ SSL_CTX_load_verify_locations(tls_ctx,ssl_verify_file,NULL) == 1) {
+ debug(F110,"ssl_tn_init certificate verify file",ssl_verify_file,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification File: %s\r\n",ssl_verify_file);
+ SSL_CTX_load_verify_locations(ssl_ctx,ssl_verify_file,NULL);
+ }
+ }
+ if (ssl_verify_dir && isdir(ssl_verify_dir)) {
+ if (SSL_CTX_load_verify_locations(tls_ctx,NULL,ssl_verify_dir) == 1) {
+ debug(F110,"ssl_tn_init certificate verify dir",ssl_verify_dir,0);
+ if (ssl_debug_flag)
+ printf(" Certificate Verification Directory: %s\r\n",ssl_verify_dir);
+ SSL_CTX_load_verify_locations(ssl_ctx,NULL,ssl_verify_dir);
+ }
+ }
+ if (mode == SSL_SERVER) {
+ SSL_CTX_set_verify(ssl_ctx,
+ ssl_verify_flag?ssl_verify_flag|SSL_VERIFY_CLIENT_ONCE:0,
+ ssl_server_verify_callback);
+ SSL_CTX_set_verify(tls_ctx,
+ ssl_verify_flag?ssl_verify_flag|SSL_VERIFY_CLIENT_ONCE:0,
+ ssl_server_verify_callback);
+ } else {
+ SSL_CTX_set_verify(ssl_ctx,ssl_verify_flag,
+ ssl_client_verify_callback);
+ SSL_CTX_set_verify(tls_ctx,ssl_verify_flag,
+ ssl_client_verify_callback);
+ }
+
+ /* Free the existing CRL Store */
+ if (crl_store) {
+ X509_STORE_free(crl_store);
+ crl_store = NULL;
+ }
+
+ /* set up the new CRL Store */
+ crl_store = X509_STORE_new();
+ if (crl_store) {
+#ifdef OS2
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
+ if (isdir(path) &&
+ X509_STORE_load_locations(crl_store,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init crl dir",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL Directory: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/crls",NULL,NULL);
+ if (isdir(path) &&
+ X509_STORE_load_locations(crl_store,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init crl dir",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL Directory: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/crls",NULL,NULL);
+ if (isdir(path) &&
+ X509_STORE_load_locations(crl_store,NULL,path) == 1) {
+ debug(F110,"ssl_tn_init crl dir",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL Directory: %s\r\n",path);
+ }
+#endif /* NT */
+
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ X509_STORE_load_locations(crl_store,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init crl file",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL File: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_crls.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ X509_STORE_load_locations(crl_store,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init crl file",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL File: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_crls.pem",NULL,NULL);
+ if (zchki(path) > 0 &&
+ X509_STORE_load_locations(crl_store,path,NULL) == 1) {
+ debug(F110,"ssl_tn_init crl file",path,0);
+ if (ssl_debug_flag)
+ printf(" CRL File: %s\r\n",path);
+ }
+#endif /* NT */
+#endif /* OS2 */
+
+ if (ssl_crl_file || ssl_crl_dir) {
+ if (ssl_crl_file && zchki(ssl_crl_file) > 0 &&
+ X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 1) {
+ debug(F110,"ssl_tn_init crl file",ssl_crl_file,0);
+ if (ssl_debug_flag)
+ printf(" CRL File: %s\r\n",ssl_crl_file);
+ }
+ if (ssl_crl_dir && isdir(ssl_crl_dir) &&
+ X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 1) {
+ debug(F110,"ssl_tn_init crl dir",ssl_crl_dir,0);
+ if (ssl_debug_flag)
+ printf(" CRL Directory: %s\r\n",ssl_crl_dir);
+ }
+ }
+#ifndef OS2
+ else {
+ X509_STORE_set_default_paths(crl_store);
+ }
+#endif /* OS2 */
+ }
+
+#ifndef COMMENT
+ ssl_conx = ssl_con;
+ ssl_con=(SSL *)SSL_new(ssl_ctx);
+ if ( !ssl_con ) {
+ debug(F110,"ssl_tn_init","SSL_new(ssl_con) failed",0);
+ last_ssl_mode = -1;
+ ssl_con = ssl_conx;
+ return(0);
+ }
+ if (ssl_conx) {
+ if ( mode == SSL_CLIENT ) {
+ SSL_set_session(ssl_con, SSL_get_session(ssl_conx));
+ }
+#ifdef SSL_KRB5
+ if (ssl_conx->kssl_ctx) {
+ kssl_ctx_free(ssl_conx->kssl_ctx);
+ ssl_conx->kssl_ctx = NULL;
+ }
+#endif /* SSL_KRB5 */
+ SSL_free(ssl_conx);
+ ssl_conx = NULL;
+ }
+ tls_conx = tls_con;
+ tls_con=(SSL *)SSL_new(tls_ctx);
+ if ( !tls_con ) {
+ debug(F110,"ssl_tn_init","SSL_new(tls_con) failed",0);
+ last_ssl_mode = -1;
+ tls_con = tls_conx;
+ return(0);
+ }
+ if (tls_conx) {
+ if ( mode == SSL_CLIENT )
+ SSL_set_session(tls_con, SSL_get_session(tls_conx));
+#ifdef SSL_KRB5
+ if (tls_conx->kssl_ctx) {
+ kssl_ctx_free(tls_conx->kssl_ctx);
+ tls_conx->kssl_ctx = NULL;
+ }
+#endif /* SSL_KRB5 */
+ SSL_free(tls_conx);
+ tls_conx = NULL;
+ }
+#else /* COMMENT */
+ /* I don't know why this does not work to reuse the connection. */
+ if ( ssl_con ) {
+ SSL_clear(ssl_con);
+ SSL_set_session(ssl_con,NULL);
+ SSL_set_accept_state(ssl_con) ;
+ } else {
+ ssl_con=(SSL *)SSL_new(ssl_ctx);
+ if (!ssl_con) {
+ debug(F110,"ssl_tn_init","SSL_new(ssl_ctx) failed",0);
+ last_ssl_mode = -1;
+ ssl_con = ssl_conx;
+ return(0);
+ }
+ }
+
+ if ( tls_con ) {
+ SSL_clear(tls_con);
+ SSL_set_session(tls_con,NULL);
+ SSL_set_accept_state(tls_con) ;
+ } else {
+ tls_con=(SSL *)SSL_new(tls_ctx);
+ if ( !tls_con ) {
+ debug(F110,"ssl_tn_init","SSL_new(tls_ctx) failed",0);
+ last_ssl_mode = -1;
+ tls_con = tls_conx;
+ return(0);
+ }
+ }
+#endif /* COMMENT */
+
+#ifdef SSL_KRB5
+#ifndef KRB5_SERVICE_NAME
+#define KRB5_SERVICE_NAME "host"
+#endif
+
+ if (ssl_con->kssl_ctx == NULL)
+ ssl_con->kssl_ctx = kssl_ctx_new();
+ if (tls_con->kssl_ctx == NULL)
+ tls_con->kssl_ctx = kssl_ctx_new();
+ if (mode == SSL_SERVER) {
+ if (ssl_con->kssl_ctx != NULL)
+ kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_KEYTAB, k5_keytab);
+ if (tls_con->kssl_ctx != NULL)
+ kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_KEYTAB, k5_keytab);
+ } else {
+ if (ssl_con->kssl_ctx != NULL)
+ kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_SERVER, szHostName);
+ if (tls_con->kssl_ctx != NULL)
+ kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_SERVER, szHostName);
+ }
+ kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_SERVICE,
+ krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME);
+ kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_SERVICE,
+ krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME);
+#endif /* SSL_KRB5 */
+
+ if (ssl_cipher_list) {
+ SSL_set_cipher_list(ssl_con,ssl_cipher_list);
+ SSL_set_cipher_list(tls_con,ssl_cipher_list);
+ } else {
+ char * p;
+ if (p = getenv("SSL_CIPHER")) {
+ SSL_set_cipher_list(ssl_con,p);
+ SSL_set_cipher_list(tls_con,p);
+ } else {
+ SSL_set_cipher_list(ssl_con,DEFAULT_CIPHER_LIST);
+ SSL_set_cipher_list(tls_con,DEFAULT_CIPHER_LIST);
+ }
+ }
+
+ ssl_verify_depth = -1;
+
+ if ( ssl_debug_flag )
+ printf("SSL/TLS init done!\r\n");
+
+ ssl_initialized = 1;
+ last_ssl_mode = mode;
+ debug(F110,"ssl_tn_init","done",0);
+ return(1);
+}
+
+#ifndef NOHTTP
+int
+#ifdef CK_ANSIC
+ssl_http_init(char * hostname)
+#else
+ssl_http_init(hostname) char * hostname;
+#endif /* CK_ANSIC */
+{
+#ifdef KRB5
+ extern char * k5_keytab;
+ extern char * krb5_d_srv;
+#endif /* KRB5 */
+ SSL * tls_conx=NULL;
+
+ ssl_http_initialized = 0;
+
+ if ( !ck_ssleay_is_installed() )
+ return(0);
+ debug(F110,"ssl_http_init",hostname,0);
+
+ if (ssl_debug_flag)
+ printf("SSL_DEBUG_FLAG on\r\n");
+
+ if (!tls_http_ctx ) {
+#ifdef COMMENT
+ /* too many web servers still do not support TLSv1 */
+ tls_http_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_client_method());
+#else /* COMMENT */
+ tls_http_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method());
+ /* This can fail because we do not have RSA available */
+ if ( !tls_http_ctx ) {
+ debug(F110,"ssl_http_init","SSLv23_client_method failed",0);
+ tls_http_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method());
+ }
+#endif /* COMMENT */
+ if ( !tls_http_ctx ) {
+ debug(F110,"ssl_http_init","TLSv1_client_method failed",0);
+ return(0);
+ }
+#ifdef USE_CERT_CB
+ SSL_CTX_set_client_cert_cb(tls_http_ctx,ssl_client_cert_callback);
+#endif /* USE_CERT_CB */
+ }
+
+ SSL_CTX_set_default_passwd_cb(tls_http_ctx,
+ (pem_password_cb *)ssl_passwd_callback);
+
+ /* for SSL switch on all the interoperability and bug
+ * workarounds so that we will communicate with people
+ * that cannot read poorly written specs :-)
+ * for TLS be sure to prevent use of SSLv2
+ */
+ SSL_CTX_set_options(tls_http_ctx,
+ SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA);
+
+ SSL_CTX_set_info_callback(tls_http_ctx,ssl_client_info_callback);
+
+#ifndef COMMENT
+ SSL_CTX_set_session_cache_mode(tls_http_ctx,SSL_SESS_CACHE_CLIENT);
+ SSL_CTX_set_session_id_context(tls_http_ctx,(CHAR *)"3",1);
+#else /* COMMENT */
+ SSL_CTX_set_session_cache_mode(tls_http_ctx,SSL_SESS_CACHE_OFF);
+#endif /* COMMENT */
+
+ /* make sure we will find certificates in the standard
+ * location ... otherwise we don't look anywhere for
+ * these things which is going to make client certificate
+ * exchange rather useless :-)
+ */
+
+#ifdef OS2
+#ifdef NT
+ {
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+ }
+#else /* NT */
+ {
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+ }
+#endif /* NT */
+#else /* OS2 */
+ SSL_CTX_set_default_verify_paths(tls_http_ctx);
+#endif /* OS2 */
+
+ if (ssl_verify_file &&
+ SSL_CTX_load_verify_locations(tls_http_ctx,ssl_verify_file,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load ssl_verify_file",ssl_verify_file,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
+ }
+ if (ssl_verify_dir &&
+ SSL_CTX_load_verify_locations(tls_http_ctx,NULL,ssl_verify_dir) == 0) {
+ debug(F110,"ssl_http_init unable to load ssl_verify_dir",ssl_verify_dir,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
+ }
+
+ SSL_CTX_set_verify(tls_http_ctx,ssl_verify_flag,
+ ssl_client_verify_callback);
+
+ /* Free the existing CRL Store */
+ if (crl_store) {
+ X509_STORE_free(crl_store);
+ crl_store = NULL;
+ }
+
+ /* set up the new CRL Store */
+ crl_store = X509_STORE_new();
+ if (crl_store) {
+#ifdef OS2
+ char path[CKMAXPATH];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ssl_http_init unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+#endif /* NT */
+
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+#endif /* NT */
+#endif /* OS2 */
+
+ if (ssl_crl_file || ssl_crl_dir) {
+ if (ssl_crl_file &&
+ X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
+ debug(F110,"ssl_http_init unable to load ssl_crl_file",ssl_crl_file,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
+ }
+ if (ssl_crl_dir &&
+ X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
+ debug(F110,"ssl_http_init unable to load ssl_crl_dir",ssl_crl_dir,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
+ }
+ } else {
+ X509_STORE_set_default_paths(crl_store);
+ }
+ }
+
+#ifndef COMMENT
+ tls_conx = tls_http_con;
+ tls_http_con=(SSL *)SSL_new(tls_http_ctx);
+ if ( !tls_http_con ) {
+ debug(F110,"ssl_http_init","SSL_new(tls_http_con) failed",0);
+ tls_http_con = tls_conx;
+ return(0);
+ }
+ if (tls_conx) {
+ SSL_set_session(tls_http_con, SSL_get_session(tls_conx));
+#ifdef SSL_KRB5
+ if (tls_conx->kssl_ctx) {
+ kssl_ctx_free(tls_conx->kssl_ctx);
+ tls_conx->kssl_ctx = NULL;
+ }
+#endif /* SSL_KRB5 */
+ SSL_free(tls_conx);
+ tls_conx = NULL;
+ }
+#else /* COMMENT */
+ /* I don't know why this does not work to reuse the connection. */
+ if ( tls_http_con ) {
+ SSL_clear(tls_http_con);
+ SSL_set_session(tls_http_con,NULL);
+ SSL_set_accept_state(tls_http_con) ;
+ } else {
+ tls_http_con=(SSL *)SSL_new(tls_http_ctx);
+ if ( !tls_http_con ) {
+ debug(F110,"ssl_http_init","SSL_new(tls_http_ctx) failed",0);
+ tls_http_con = tls_conx;
+ return(0);
+ }
+ }
+#endif /* COMMENT */
+
+#ifdef SSL_KRB5
+#ifndef KRB5_SERVICE_NAME
+#define KRB5_SERVICE_NAME "host"
+#endif
+
+ if (tls_http_con->kssl_ctx == NULL)
+ tls_http_con->kssl_ctx = kssl_ctx_new();
+ if (tls_http_con->kssl_ctx != NULL)
+ kssl_ctx_setstring(tls_http_con->kssl_ctx, KSSL_SERVER, hostname);
+
+ kssl_ctx_setstring(tls_http_con->kssl_ctx, KSSL_SERVICE,
+ krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME);
+#endif /* SSL_KRB5 */
+
+ if (ssl_cipher_list)
+ SSL_set_cipher_list(tls_http_con,ssl_cipher_list);
+ else {
+ char * p;
+ if (p = getenv("SSL_CIPHER")) {
+ SSL_set_cipher_list(tls_http_con,p);
+ } else {
+ SSL_set_cipher_list(tls_http_con,DEFAULT_CIPHER_LIST);
+ }
+ }
+
+ ssl_verify_depth = -1;
+
+ if ( ssl_debug_flag )
+ printf("SSL/TLS init done!\r\n");
+
+ ssl_http_initialized = 1;
+ return(1);
+}
+#endif /* NOHTTP */
+
+char *
+ssl_get_dNSName(ssl) SSL * ssl;
+{
+ static char *dns = NULL;
+ X509 *server_cert = NULL;
+ int i;
+ X509_EXTENSION *ext = NULL;
+ STACK_OF(GENERAL_NAME) *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+
+ if ( dns ) {
+ free(dns);
+ dns = NULL;
+ }
+
+ if (server_cert = SSL_get_peer_certificate(ssl)) {
+ if ((i = X509_get_ext_by_NID(server_cert, NID_subject_alt_name, -1))<0)
+ return NULL;
+ if (!(ext = X509_get_ext(server_cert, i)))
+ return NULL;
+ X509V3_add_standard_extensions();
+ if (!(ialt = X509V3_EXT_d2i(ext)))
+ return NULL;
+ for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) {
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ if (gen->type == GEN_DNS) {
+ if(!gen->d.ia5 || !gen->d.ia5->length)
+ break;
+ dns = malloc(gen->d.ia5->length + 1);
+ if (dns) {
+ memcpy(dns, gen->d.ia5->data, gen->d.ia5->length);
+ dns[gen->d.ia5->length] = 0;
+ }
+ break;
+ }
+ }
+ X509V3_EXT_cleanup();
+ }
+cleanup:
+ if (ialt) sk_GENERAL_NAME_free(ialt);
+ if (server_cert) X509_free(server_cert);
+ return dns;
+}
+
+char *
+ssl_get_commonName(ssl) SSL * ssl;
+{
+ static char name[256];
+ int err;
+ X509 *server_cert;
+
+ if (server_cert = SSL_get_peer_certificate(ssl)) {
+ err = X509_NAME_get_text_by_NID(X509_get_subject_name(server_cert),
+ NID_commonName, name, sizeof(name));
+ X509_free(server_cert);
+ }
+ if (err > 0)
+ return name;
+ else
+ return NULL;
+}
+
+char *
+ssl_get_issuer_name(ssl) SSL * ssl;
+{
+ static char name[256];
+ X509 *server_cert;
+
+ name[0] = '\0';
+ if (server_cert = SSL_get_peer_certificate(ssl)) {
+ X509_NAME_oneline(X509_get_issuer_name(server_cert),name,sizeof(name));
+ X509_free(server_cert);
+ return name;
+ }
+ else {
+#ifdef COMMENT
+ fprintf(stderr, "Warning: No certificate from server!\r\n");
+#endif /* COMMENT */
+ return NULL;
+ }
+}
+
+char *
+ssl_get_subject_name(ssl) SSL * ssl;
+{
+ static char name[256];
+ X509 *server_cert;
+
+ name[0] = '\0';
+ if (server_cert = SSL_get_peer_certificate(ssl)) {
+ X509_NAME_oneline(X509_get_subject_name(server_cert),name,sizeof(name));
+ X509_free(server_cert);
+ return name;
+ }
+ else
+ return NULL;
+}
+
+#ifdef COMMENT
+#ifdef CK_SSL
+ && !(ck_ssleay_is_installed() &&
+ (tls_active_flag || ssl_active_flag) &&
+ ssl_anonymous_cipher(tls_active_flag?tls_con:ssl_con))
+#endif /* CK_SSL */
+
+int
+ssl_anonymous_cipher(ssl) SSL * ssl;
+{
+ X509 * cert;
+
+ if (sstelnet)
+ cert = SSL_get_certificate(ssl);
+ else
+ cert = SSL_get_peer_certificate(ssl);
+
+ if ( cert ) {
+ X509_free(cert);
+ return 0;
+ }
+ return 1;
+}
+#endif /* COMMENT */
+
+/*
+ This one is (very much!) based on work by
+ Ralf S. Engelschall <rse@engelschall.com>.
+ Comments by Ralf.
+*/
+int
+ssl_verify_crl(int ok, X509_STORE_CTX *ctx)
+{
+ X509_OBJECT obj;
+ X509_NAME *subject = NULL;
+ X509_NAME *issuer = NULL;
+ X509 *xs = NULL;
+ X509_CRL *crl = NULL;
+ X509_REVOKED *revoked = NULL;
+ X509_STORE_CTX * store_ctx = NULL;
+ long serial;
+ BIO *bio = NULL;
+ int i, n, rc;
+ char *cp;
+ char *cp2;
+
+ /*
+ * Unless a revocation store for CRLs was created we
+ * cannot do any CRL-based verification, of course.
+ */
+ if (!crl_store)
+ return ok;
+
+ store_ctx = X509_STORE_CTX_new();
+ if ( !store_ctx )
+ return(ok);
+
+ /*
+ * Determine certificate ingredients in advance
+ */
+ xs = X509_STORE_CTX_get_current_cert(ctx);
+ subject = X509_get_subject_name(xs);
+ issuer = X509_get_issuer_name(xs);
+
+ /*
+ * OpenSSL provides the general mechanism to deal with CRLs but does not
+ * use them automatically when verifying certificates, so we do it
+ * explicitly here. We will check the CRL for the currently checked
+ * certificate, if there is such a CRL in the store.
+ *
+ * We come through this procedure for each certificate in the certificate
+ * chain, starting with the root-CA's certificate. At each step we've to
+ * both verify the signature on the CRL (to make sure it's a valid CRL)
+ * and it's revocation list (to make sure the current certificate isn't
+ * revoked). But because to check the signature on the CRL we need the
+ * public key of the issuing CA certificate (which was already processed
+ * one round before), we've a little problem. But we can both solve it and
+ * at the same time optimize the processing by using the following
+ * verification scheme (idea and code snippets borrowed from the GLOBUS
+ * project):
+ *
+ * 1. We'll check the signature of a CRL in each step when we find a CRL
+ * through the _subject_ name of the current certificate. This CRL
+ * itself will be needed the first time in the next round, of course.
+ * But we do the signature processing one round before this where the
+ * public key of the CA is available.
+ *
+ * 2. We'll check the revocation list of a CRL in each step when
+ * we find a CRL through the _issuer_ name of the current certificate.
+ * This CRLs signature was then already verified one round before.
+ *
+ * This verification scheme allows a CA to revoke its own certificate as
+ * well, of course.
+ */
+
+ /*
+ * Try to retrieve a CRL corresponding to the _subject_ of
+ * the current certificate in order to verify it's integrity.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL);
+ rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, &obj);
+ X509_STORE_CTX_cleanup(store_ctx);
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Verify the signature on this CRL
+ */
+ if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
+ fprintf(stderr, "Invalid signature on CRL!\n");
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+ X509_OBJECT_free_contents(&obj);
+ X509_STORE_CTX_free(store_ctx);
+ return 0;
+ }
+
+ /*
+ * Check date of CRL to make sure it's not expired
+ */
+ i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+ if (i == 0) {
+ fprintf(stderr, "Found CRL has invalid nextUpdate field.\n");
+ X509_STORE_CTX_set_error(ctx,
+ X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ X509_OBJECT_free_contents(&obj);
+ X509_STORE_CTX_free(store_ctx);
+ return 0;
+ }
+ if (i < 0) {
+ fprintf(stderr,
+"Found CRL is expired - revoking all certificates until you get updated CRL.\n"
+ );
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+ X509_OBJECT_free_contents(&obj);
+ X509_STORE_CTX_free(store_ctx);
+ return 0;
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+
+ /*
+ * Try to retrieve a CRL corresponding to the _issuer_ of
+ * the current certificate in order to check for revocation.
+ */
+ memset((char *)&obj, 0, sizeof(obj));
+ X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL);
+ rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, &obj);
+ X509_STORE_CTX_free(store_ctx); /* calls X509_STORE_CTX_cleanup() */
+ crl = obj.data.crl;
+ if (rc > 0 && crl != NULL) {
+ /*
+ * Check if the current certificate is revoked by this CRL
+ */
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+ for (i = 0; i < n; i++) {
+ revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+ if (ASN1_INTEGER_cmp(revoked->serialNumber,
+ X509_get_serialNumber(xs)) == 0) {
+
+ serial = ASN1_INTEGER_get(revoked->serialNumber);
+ cp = X509_NAME_oneline(issuer, NULL, 0);
+ free(cp);
+
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+ X509_OBJECT_free_contents(&obj);
+ return 0;
+ }
+ }
+ X509_OBJECT_free_contents(&obj);
+ }
+ return ok;
+}
+
+char *
+tls_userid_from_client_cert(ssl) SSL * ssl;
+{
+ static char cn[256];
+ static char *r = cn;
+ int err;
+ X509 *client_cert;
+
+ if (client_cert = SSL_get_peer_certificate(ssl)) {
+ /* call the custom function */
+ err = X509_to_user(client_cert, cn, sizeof(cn));
+ X509_free(client_cert);
+ if (err)
+ return r = NULL;
+ else
+ return r;
+ }
+ else
+ return r = NULL;
+}
+
+unsigned char **
+tls_get_SAN_objs(SSL * ssl, int type)
+/* returns NULL or an array of malloc'ed objects of type `type' from the server's
+ * subjectAltName, remember to free() them all!
+ */
+{
+#define NUM_SAN_OBJS 64
+ static unsigned char *objs[NUM_SAN_OBJS];
+ unsigned char **rv = NULL;
+ X509 *server_cert = NULL;
+ int i, j;
+ X509_EXTENSION *ext = NULL;
+ STACK_OF(GENERAL_NAME) *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+
+ memset(objs, 0, sizeof(objs));
+ if (server_cert = SSL_get_peer_certificate(ssl)) {
+ if ((i = X509_get_ext_by_NID(server_cert, NID_subject_alt_name, -1)) < 0)
+ goto eject;
+ if (!(ext = X509_get_ext(server_cert, i)))
+ goto eject;
+ X509V3_add_standard_extensions();
+ if (!(ialt = X509V3_EXT_d2i(ext)))
+ goto eject;
+ rv = objs;
+ for (i = 0, j = 0; i < sk_GENERAL_NAME_num(ialt) && j < NUM_SAN_OBJS - 2; i++) {
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ /* The use of V_ASN1_CONTEXT_SPECIFIC is because OpenSSL 0.9.6 defined its
+ * types | V_ASN1_CONTEXT_SPECIFIC. 0.9.7 does not. In case, we are built
+ * with one and linked to the other we use this hack.
+ */
+ if ((gen->type | V_ASN1_CONTEXT_SPECIFIC) == (type | V_ASN1_CONTEXT_SPECIFIC)) {
+ if(!gen->d.ia5 || !gen->d.ia5->length)
+ break;
+ objs[j] = malloc(gen->d.ia5->length + 1);
+ if (objs[j]) {
+ memcpy(objs[j], gen->d.ia5->data, gen->d.ia5->length);
+ objs[j][gen->d.ia5->length] = 0;
+ j++;
+ }
+ }
+ }
+ X509V3_EXT_cleanup();
+ }
+eject:
+ if (ialt) sk_GENERAL_NAME_free(ialt);
+ if (server_cert) X509_free(server_cert);
+ return rv;
+}
+
+
+static int
+dNSName_cmp(const char *host, const char *dNSName)
+{
+ int c1 = 0, c2 = 0, num_comp, rv = -1;
+ char *p, *p1, *p2, *host_copy=NULL, *dNSName_copy=NULL;
+
+ /* first we count the number of domain name components in both parameters.
+ * they should be equal many, or it's not a match
+ */
+ p = (char *) host;
+ while (p = strstr(p, ".")) {
+ c1++;
+ p++;
+ }
+ p = (char *) dNSName;
+ while (p = strstr(p, ".")) {
+ c2++;
+ p++;
+ }
+ if (c1 != c2)
+ return -1;
+ num_comp = c1;
+
+ makestr(&host_copy,host);
+ makestr(&dNSName_copy,dNSName);
+ if (host_copy == NULL || dNSName_copy == NULL)
+ goto eject;
+ /* make substrings by replacing '.' with '\0' */
+ p = dNSName_copy;
+ while (p = strstr(p, ".")) {
+ *p = '\0';
+ p++;
+ }
+ p = host_copy;
+ while (p = strstr(p, ".")) {
+ *p = '\0';
+ p++;
+ }
+
+ /* compare each component */
+ p1 = host_copy;
+ p2 = dNSName_copy;
+ for (; num_comp; num_comp--) {
+ if (!ckmatch(p2, p1,0,1))
+ /* failed match */
+ goto eject;
+ p1 += strlen(p1) + 1;
+ p2 += strlen(p2) + 1;
+ }
+ /* match ok */
+ rv = 0;
+
+ eject:
+ if (dNSName_copy) free(dNSName_copy);
+ if (host_copy) free(host_copy);
+ return rv;
+}
+
+
+
+static int
+show_hostname_warning(char *s1, char *s2)
+{
+ char prefix[1024];
+ int ok = 1;
+ ckmakxmsg(prefix,1024,
+ "Warning: Hostname (\"", s1,
+ "\") does not match server's certificate (\"", s2, "\")",
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ if (ssl_verify_flag)
+ ok = uq_ok(prefix,
+ "Continue? (Y/N) ",
+ 3, NULL, 0);
+ else if (ssl_verbose_flag)
+ printf(prefix);
+ return(ok);
+}
+
+#ifndef HPUX10
+#ifndef HPUX1100
+#ifndef SCO_OSR505
+#ifndef OpenBSD
+#ifndef FREEBSD4
+#ifndef LINUX
+#ifndef AIX41
+#ifndef UW7
+#ifndef SOLARIS9
+#ifndef SOLARIS8
+#ifndef SOLARIS7
+#ifdef DEC_TCPIP
+#define inet_aton INET_ATON
+#endif /* DEC_TCPIP */
+static int
+inet_aton(char * ipaddress, struct in_addr * ia) {
+ struct stringarray * q;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } dummy;
+
+ q = cksplit(1,0,ipaddress,".","0123456789abcdefACDEF",8,0,0);
+ if (q->a_size == 4) {
+ dummy.b[0] = atoi(q->a_head[1]);
+ dummy.b[1] = atoi(q->a_head[2]);
+ dummy.b[2] = atoi(q->a_head[3]);
+ dummy.b[3] = atoi(q->a_head[4]);
+ ia->s_addr = dummy.l;
+ return(ia->s_addr != 0);
+ }
+ return(0);
+}
+#endif /* SOLARIS7 */
+#endif /* SOLARIS8 */
+#endif /* SOLARIS9 */
+#endif /* UW7 */
+#endif /* AIX41 */
+#endif /* LINUX */
+#endif /* FREEBSD4 */
+#endif /* OpenBSD */
+#endif /* SCO_OSR505 */
+#endif /* HPUX1100 */
+#endif /* HPUX10 */
+
+int
+ssl_check_server_name(SSL * ssl, char * hostname)
+/* returns 0 if hostname and server's cert matches, else -1 */
+{
+ char * commonName;
+ unsigned char ** dNSName;
+ unsigned char ** ipAddress;
+ struct in_addr ia;
+ int rv;
+
+ if (ssl_verbose_flag && !inserver) {
+ if (dNSName = tls_get_SAN_objs(ssl,GEN_DNS)) {
+ int i = 0;
+ for (i = 0; dNSName[i]; i++) {
+ printf("Certificate[0] altSubjectName DNS=%s\r\n",dNSName[i]);
+ free(dNSName[i]);
+ }
+ }
+ if (ipAddress = tls_get_SAN_objs(ssl,GEN_IPADD)) {
+ int i = 0;
+ char *server_ip;
+ struct in_addr ia;
+
+ for (i = 0; ipAddress[i]; i++) {
+ if (ipAddress[i]) {
+ ia.s_addr = *(unsigned long *)ipAddress[i];
+ server_ip = inet_ntoa(ia);
+ printf("Certificate[0] altSubjectName IPAddr=%s\r\n",server_ip);
+ }
+ free(ipAddress[i]);
+ }
+ /* ipAddress points to a static - don't free */
+ }
+ if (dNSName = tls_get_SAN_objs(ssl,GEN_EMAIL)) {
+ int i = 0;
+ for (i = 0; dNSName[i]; i++) {
+ printf("Certificate[0] altSubjectName Email=%s\r\n",dNSName[i]);
+ free(dNSName[i]);
+ }
+ }
+ if (dNSName = tls_get_SAN_objs(ssl,GEN_URI)) {
+ int i = 0;
+ for (i = 0; dNSName[i]; i++) {
+ printf("Certificate[0] altSubjectName URI=%s\r\n",dNSName[i]);
+ free(dNSName[i]);
+ }
+ }
+ if (dNSName = tls_get_SAN_objs(ssl,GEN_OTHERNAME)) {
+ int i = 0;
+ for (i = 0; dNSName[i]; i++) {
+ printf("Certificate[0] altSubjectName Other=%s\r\n",dNSName[i]);
+ free(dNSName[i]);
+ }
+ }
+ }
+
+ /* first we check if `hostname' is in fact an ip address */
+ if (inet_aton(hostname, &ia)) {
+ ipAddress = tls_get_SAN_objs(ssl,GEN_IPADD);
+ if (ipAddress) {
+ int i = 0;
+ char *server_ip = "UNKNOWN";
+
+ for (i = 0; ipAddress[i]; i++)
+ if (*(unsigned long *)ipAddress[i] == ia.s_addr)
+ return 0;
+
+ if (ipAddress[i - 1]) {
+ ia.s_addr = *(unsigned long *)ipAddress[i - 1];
+ server_ip = inet_ntoa(ia);
+ }
+ rv = show_hostname_warning(hostname, server_ip) ? 0 : -1;
+ for (i = 0; ipAddress[i]; i++)
+ free(ipAddress[i]);
+ } else {
+ rv = show_hostname_warning(hostname, "NO IP IN CERT") ? 0 : -1;
+ }
+ return(rv);
+ }
+
+ /* look for dNSName(s) in subjectAltName in the server's certificate */
+ dNSName = tls_get_SAN_objs(ssl,GEN_DNS);
+ if (dNSName) {
+ int i = 0;
+ for (i = 0; dNSName[i]; i++) {
+ if (!dNSName_cmp(hostname,(char *)dNSName[i]))
+ return 0;
+ }
+ rv = show_hostname_warning(hostname,
+ (char *)((dNSName[i - 1] == NULL) ?
+ (char *)"UNKNOWN" : (char *)dNSName[i - 1]))
+ ? 0 : -1;
+ for (i = 0; dNSName[i]; i++)
+ free(dNSName[i]);
+ return rv;
+ } else if ((commonName = ssl_get_commonName(ssl))) {
+ /* so the server didn't have any dNSName's, check the commonName */
+ if (!dNSName_cmp(hostname, commonName))
+ return 0;
+ else
+ return (show_hostname_warning(hostname, commonName) ? 0 : -1);
+ }
+ return -1;
+}
+
+/* Is 'user' authorized to access the system without a login */
+int
+tls_is_user_valid(SSL * ssl, const char *user)
+{
+ X509 *client_cert;
+ int r = 0;
+
+ if ( !ssl || !user || !user[0] )
+ return(0);
+
+ if (!(client_cert = SSL_get_peer_certificate(ssl)))
+ return 0;
+
+ /* Use user supplied function */
+ r = X509_userok(client_cert,user);
+
+ X509_free(client_cert);
+ return r;
+}
+
+int
+tls_is_anon(int x)
+{
+ char buf[128];
+ SSL_CIPHER * cipher;
+ SSL * ssl = NULL;
+
+ switch ( x ) {
+#ifndef NOFTP
+#ifndef SYSFTP
+ case 1: /* ftp command */
+ if ( ssl_ftp_active_flag )
+ ssl = ssl_ftp_con;
+ else
+ return(0);
+ break;
+ case 2: /* ftp data */
+ if ( ssl_ftp_data_active_flag )
+ ssl = ssl_ftp_data_con;
+ else
+ return(0);
+ break;
+#endif /* SYSFTP */
+#endif /* NOFTP */
+ default:
+ if (tls_active_flag)
+ ssl = tls_con;
+ else if (ssl_active_flag)
+ ssl = ssl_con;
+ else
+ return(0);
+ }
+
+ cipher = SSL_get_current_cipher(ssl);
+ if (SSL_CIPHER_description(cipher,buf,sizeof(buf))) {
+ if (ckindex("Au=None",buf,0,0,0) != 0)
+ return(1); /* anonymous */
+ return(0); /* known */
+ } else {
+ /* could not get cipher description. Assume anonymous */
+ return(1);
+ }
+}
+
+int
+tls_is_krb5(int x)
+{
+ char buf[128];
+ SSL_CIPHER * cipher;
+ SSL * ssl = NULL;
+
+ switch ( x ) {
+#ifndef NOFTP
+#ifndef SYSFTP
+ case 1: /* ftp command */
+ if ( ssl_ftp_active_flag )
+ ssl = ssl_ftp_con;
+ else
+ return(0);
+ break;
+ case 2: /* ftp data */
+ if ( ssl_ftp_data_active_flag )
+ ssl = ssl_ftp_data_con;
+ else
+ return(0);
+ break;
+#endif /* SYSFTP */
+#endif /* NOFTP */
+#ifndef NOHTTP
+ case 3:
+ if ( tls_http_active_flag )
+ ssl = tls_http_con;
+ break;
+#endif /* NOHTTP */
+ default:
+ if (tls_active_flag)
+ ssl = tls_con;
+ else if (ssl_active_flag)
+ ssl = ssl_con;
+ else
+ return(0);
+ }
+
+ cipher = SSL_get_current_cipher(ssl);
+ if (cipher && SSL_CIPHER_description(cipher,buf,sizeof(buf))) {
+ if (ckindex("Au=KRB5",buf,0,0,0) != 0)
+ return(1); /* krb5 */
+ }
+ return(0); /* not */
+}
+
+int
+ssl_get_client_finished(char *buf, int count)
+{
+#ifdef NO_GET_FINISHED
+ return(0);
+#else
+ if (sstelnet || tcp_incoming) {
+ return(SSL_get_peer_finished(ssl_active_flag?ssl_con:tls_con,
+ buf,count));
+ } else {
+ return(SSL_get_finished(ssl_active_flag?ssl_con:tls_con,
+ buf,count));
+ }
+#endif /* NO_GET_FINISHED */
+}
+
+int
+ssl_get_server_finished(char *buf, int count)
+{
+#ifdef NO_GET_FINISHED
+ return(0);
+#else
+ if (sstelnet || tcp_incoming) {
+ return(SSL_get_finished(ssl_active_flag?ssl_con:tls_con,
+ buf,count));
+ } else {
+ return(SSL_get_peer_finished(ssl_active_flag?ssl_con:tls_con,
+ buf,count));
+ }
+#endif /* NO_GET_FINISHED */
+}
+
+
+#ifdef CK_AUTHENTICATION
+int
+#ifdef CK_ANSIC
+ssl_reply(int how, unsigned char *data, int cnt)
+#else
+ssl_reply(how,data,cnt) int how; unsigned char *data; int cnt;
+#endif
+{
+ char * str=NULL;
+
+ data += 4; /* Point to status byte */
+ cnt -= 4;
+
+ if(cnt-- < 1) {
+ auth_finished(AUTH_REJECT);
+ return AUTH_FAILURE;
+ }
+
+ switch(*data++) {
+ case SSL_ACCEPT:
+ if (tn_deb || debses)
+ tn_debug("[SSL - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - handshake starting]\r\n");
+ debug(F110,"ssl_reply","[SSL - handshake starting]",0);
+
+ /* right ... now we drop into the SSL library */
+ if (!ssl_only_flag) {
+ if (ssl_dummy_flag) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - Dummy Connected]");
+ else if ( ssl_verbose_flag ) {
+ printf("[SSL - Dummy Connected]\r\n");
+ }
+ debug(F110,"ssl_reply","[SSL - Dummy Connected]",0);
+ auth_finished(AUTH_UNKNOWN);
+ accept_complete = 1;
+ return AUTH_SUCCESS;
+ }
+
+ if (SSL_connect(ssl_con) <= 0) {
+ int len;
+ if (tn_deb || debses) {
+ tn_debug("[SSL - FAILED]");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ printf(ssl_err);
+ } else if ( ssl_verbose_flag ) {
+ printf("[SSL - FAILED]\r\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ printf(ssl_err);
+ }
+ debug(F110,"ssl_reply","[SSL - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ ttclos(0);
+ return AUTH_FAILURE;
+ } else {
+ if (tn_deb || debses)
+ tn_debug("[SSL - OK]");
+ else if ( ssl_verbose_flag ) {
+ printf("[SSL - OK]\r\n");
+ }
+ debug(F110,"ssl_reply","[SSL - OK]",0);
+
+ ssl_active_flag = 1;
+ ssl_display_connect_details(ssl_con,0,ssl_verbose_flag);
+ }
+ }
+ auth_finished(AUTH_UNKNOWN);
+ accept_complete = 1;
+ break;
+
+ case SSL_REJECT:
+ if (tn_deb || debses) {
+ tn_debug(
+ "[SSL - failed to switch on SSL - trying plaintext login]");
+ } else if ( ssl_verbose_flag ) {
+ printf("[SSL - failed to switch on SSL]\r\n");
+ printf("Trying plaintext login:\r\n");
+ }
+ debug(F110,"ssl_reply","[SSL - failed to switch on SSL]",0);
+ auth_finished(AUTH_REJECT);
+ return AUTH_FAILURE;
+
+ default:
+ return AUTH_FAILURE;
+ }
+ return AUTH_SUCCESS;
+}
+
+int
+#ifdef CK_ANSIC
+ssl_is(unsigned char *data, int cnt)
+#else
+ssl_is(data,cnt) unsigned char *data; int cnt;
+#endif
+{
+ if ((cnt -= 4) < 1)
+ return AUTH_FAILURE;
+
+ data += 4;
+ switch(*data++) {
+ case SSL_START:
+ /* server starts the SSL stuff now ... */
+ if (!ssl_only_flag) {
+ if ( !tls_load_certs(ssl_ctx,ssl_con,1) ) {
+ auth_finished(AUTH_REJECT);
+ return AUTH_FAILURE;
+ }
+
+ if (tn_deb || debses)
+ tn_debug("[SSL - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - handshake starting]\r\n");
+ debug(F110,"ssl_is","[SSL - handshake starting]",0);
+
+ SendSSLAuthSB(SSL_ACCEPT, (void *)0, 0);
+
+ auth_ssl_valid = 1;
+
+ if (ssl_dummy_flag) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - Dummy Connected]");
+ else if ( ssl_verbose_flag ) {
+ printf("[SSL - Dummy Connected]\r\n");
+ }
+ debug(F110,"ssl_is","[SSL - Dummy Connected]",0);
+ accept_complete = 1;
+ auth_finished(AUTH_UNKNOWN);
+ return AUTH_SUCCESS;
+ }
+
+ if (SSL_accept(ssl_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[SSL - SSL_accept error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+ else if ( ssl_verbose_flag )
+ printf("[SSL - SSL_accept error]\r\n");
+
+ debug(F110,"ssl_is",errbuf,0);
+
+ auth_finished(AUTH_REJECT);
+ ttclos(0);
+ return AUTH_FAILURE;
+ }
+
+ if (tn_deb || debses)
+ tn_debug("[SSL - OK]");
+ else if ( ssl_verbose_flag ) {
+ printf("[SSL - OK]\r\n");
+ }
+ debug(F110,"ssl_is","[SSL - OK]",0);
+ ssl_active_flag = 1;
+ ssl_display_connect_details(ssl_con,1,ssl_verbose_flag);
+
+ /* now check to see that we got exactly what we
+ * wanted from the caller ... if a certificate is
+ * required then we make 100% sure that we were
+ * given one during the handshake (as it is an optional
+ * part of SSL)
+ */
+
+#ifdef SSL_KRB5
+ if ( tls_is_krb5(0) ) {
+ if (ssl_con->kssl_ctx->client_princ)
+ debug(F110,"ssl_is KRB5",ssl_con->kssl_ctx->client_princ,0);
+ } else
+#endif /* SSL_KRB5 */
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ X509 * peer = SSL_get_peer_certificate(ssl_con);
+ if (peer == NULL) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - peer check failed]");
+ else if (ssl_debug_flag)
+ printf("[SSL - peer check failed]\r\n");
+ debug(F110,"ssl_is","[SSL - peer check failed]",0);
+
+ /* LOGGING REQUIRED HERE! */
+ auth_finished(AUTH_REJECT);
+ return AUTH_FAILURE;
+ }
+ }
+ auth_finished(AUTH_UNKNOWN);
+ accept_complete = 1;
+ }
+ break;
+
+ default:
+ SendSSLAuthSB(SSL_REJECT, (void *) "Unknown option received", -1);
+ if (tn_deb || debses)
+ tn_debug("[SSL - Unknown option received]");
+ else
+ printf("Unknown SSL option %d\r\n", data[-1]);
+ debug(F111,"ssl_is","[SSL - Unknown option received]",data[-1]);
+ auth_ssl_valid = 0;
+ auth_finished(AUTH_REJECT);
+ return(AUTH_FAILURE);
+ }
+ return AUTH_SUCCESS;
+}
+
+#endif /* CK_AUTHENTICATION */
+
+int
+ck_tn_tls_negotiate(VOID)
+{
+ X509 * peer = NULL;
+ char str[256], *uid=NULL;
+ extern int sstelnet;
+
+ if ( !ck_ssleay_is_installed() )
+ return(-1);
+
+ if (sstelnet) {
+ /* server starts the TLS stuff now ... */
+ if (!tls_only_flag) {
+ if ( !tls_load_certs(tls_ctx,tls_con,1) ) {
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - handshake starting]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - handshake starting]",0);
+
+ if (ssl_dummy_flag) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - Dummy Connected]");
+ else if ( ssl_verbose_flag ) {
+ printf("[TLS - Dummy Connected]\r\n");
+ }
+ debug(F110,"ck_tn_tls_negotiate","[TLS - Dummy Connected]",0);
+ accept_complete = 1;
+ auth_finished(AUTH_REJECT);
+ return 0;
+ }
+
+ if (SSL_accept(tls_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[TLS - SSL_accept error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+ else if ( ssl_verbose_flag )
+ printf("[TLS - SSL_accept error]\r\n");
+
+ debug(F110,"ck_tn_tls_negotiate",errbuf,0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - OK]");
+ else if ( ssl_verbose_flag ) {
+ printf("[TLS - OK]\r\n");
+ }
+
+ debug(F110,"ck_tn_tls_negotiate","[TLS - OK]",0);
+ tls_active_flag = 1;
+ ssl_display_connect_details(tls_con,1,ssl_verbose_flag);
+
+
+#ifdef SSL_KRB5
+ if ( tls_is_krb5(0) ) {
+ if (tls_con->kssl_ctx->client_princ) {
+ char *p;
+ ckstrncpy(szUserNameAuthenticated,
+ tls_con->kssl_ctx->client_princ,
+ UIDBUFLEN);
+ ckstrncpy(szUserNameRequested,
+ tls_con->kssl_ctx->client_princ,
+ UIDBUFLEN);
+ for ( p = szUserNameRequested; *p ; p++ ) {
+ if ( *p == '@' || *p == '/' ) {
+ *p = '\0';
+ break;
+ }
+ }
+ } else {
+ szUserNameRequested[0] = '\0';
+ szUserNameAuthenticated[0] = '\0';
+ }
+#ifdef CK_LOGIN
+ if (zvuser(szUserNameRequested))
+ auth_finished(AUTH_VALID);
+ else
+#endif /* CK_LOGIN */
+ auth_finished(AUTH_USER);
+ } else
+#endif /* SSL_KRB5 */
+ {
+ /* now check to see that we got exactly what we
+ * wanted from the caller ... if a certificate is
+ * required then we make 100% sure that we were
+ * given one during the handshake (as it is an optional
+ * part of TLS)
+ */
+ peer=SSL_get_peer_certificate(tls_con);
+ if (peer == NULL) {
+ debug(F100,"SSL_get_peer_certificate() == NULL","",0);
+ auth_finished(AUTH_REJECT);
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - peer check failed]");
+ else if (ssl_debug_flag) {
+ printf("[TLS - peer check failed]\r\n");
+ }
+ debug(F110,
+ "ck_tn_tls_negotiate",
+ "[TLS - peer check failed]",
+ 0
+ );
+ /* LOGGING REQUIRED HERE! */
+ return -1;
+ }
+ } else {
+ debug(F100,"SSL_get_peer_certificate() != NULL","",0);
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+ NID_commonName,str,
+ 256
+ );
+ if ( ssl_verbose_flag )
+ printf("[TLS - commonName=%s]\r\n",str);
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+#ifndef NID_x500UniqueIdentifier
+ NID_uniqueIdentifier,
+#else
+ NID_x500UniqueIdentifier,
+#endif
+ str,
+ 256
+ );
+ if ( ssl_verbose_flag )
+ printf("[TLS - uniqueIdentifier=%s]\r\n",str);
+
+ /* Try to determine user name */
+ uid = tls_userid_from_client_cert(tls_con);
+ if ( uid ) {
+ /* This code is very questionable.
+ * How should it behave?
+ * The client has presented a certificate that
+ * contains a username. We have validated the
+ * certificate but we do not automatically
+ * log the user in unless there is a .tlslogin
+ * file.
+ */
+
+ ckstrncpy(szUserNameRequested,uid,UIDBUFLEN);
+#ifdef CK_LOGIN
+ if (zvuser(uid))
+ auth_finished(AUTH_VALID);
+ else
+#endif /* CK_LOGIN */
+ auth_finished(AUTH_USER);
+ }
+ else {
+ szUserNameRequested[0] = '\0';
+ auth_finished(AUTH_REJECT);
+ }
+ }
+ }
+ }
+ } else {
+ char * str=NULL;
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - handshake starting]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - handshake starting]",0);
+
+ /* right ... now we drop into the SSL library */
+ if (!tls_only_flag) {
+ char *subject=NULL, *issuer=NULL, *commonName=NULL, *dNSName=NULL;
+
+ if (ssl_dummy_flag) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - Dummy Connected]");
+ else if ( ssl_verbose_flag ) {
+ printf("[TLS - Dummy Connected]\r\n");
+ }
+ debug(F110,"ck_tn_tls_negotiate","[TLS - Dummy Connected]",0);
+ auth_finished(AUTH_REJECT);
+ accept_complete = 1;
+ return 0;
+ }
+
+#ifndef USE_CERT_CB
+ if (!tls_load_certs(tls_ctx,tls_con,0))
+ return(-1);
+#endif /* USE_CERT_CB */
+ if (SSL_connect(tls_con) <= 0) {
+ int len;
+ if (tn_deb || debses) {
+ tn_debug("[TLS - FAILED]");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ printf(ssl_err);
+ } else if ( ssl_verbose_flag ) {
+ printf("[TLS - FAILED]\r\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ printf(ssl_err);
+ }
+ debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+
+ tls_active_flag = 1;
+ if ( !ssl_certsok_flag && (ssl_verify_flag & SSL_VERIFY_PEER)
+ && !tls_is_krb5(0)) {
+ char prmpt[1024];
+ subject = ssl_get_subject_name(tls_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ } else {
+ int ok;
+ ok = uq_ok("Warning: Server didn't provide a certificate",
+ "Continue? (Y/N)", 3, NULL, 0);
+ if (!ok) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+ } else if (ssl_check_server_name(tls_con, szHostName)) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+
+ if ( ssl_debug_flag && ssl_finished_messages) {
+ char msg[32];
+ int i, len=32;
+ extern char tn_msg[], hexbuf[];
+
+ tn_msg[0] = '\0';
+ len = ssl_get_client_finished(msg,len);
+ if ( len > 0 ) {
+ for ( i=0;i<len;i++ ) {
+ sprintf(hexbuf,"%02X ",msg[i]);
+ ckstrncat(tn_msg,hexbuf,TN_MSG_LEN);
+ }
+ printf("TLS client finished: %s\r\n",tn_msg);
+ }
+ tn_msg[0] = '\0';
+ len = ssl_get_server_finished(msg,len);
+ if ( len > 0 ) {
+ for ( i=0;i<len;i++ ) {
+ sprintf(hexbuf,"%02X ",msg[i]);
+ ckstrncat(tn_msg,hexbuf,TN_MSG_LEN);
+ }
+ printf("TLS server finished: %s\r\n",tn_msg);
+ }
+ }
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - OK]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - OK]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - OK]",0);
+
+ ssl_display_connect_details(tls_con,0,ssl_verbose_flag);
+ }
+ auth_finished(AUTH_REJECT);
+ }
+ accept_complete = 1;
+ auth_ssl_valid = 1;
+ return(0);
+}
+
+int
+ck_ssl_incoming(fd) int fd;
+{
+ /* if we are not running in debug then any error
+ * stuff from SSL debug *must* not go down
+ * the socket (which 0,1,2 are all pointing to by
+ * default)
+ */
+
+ int timo = 2000;
+
+ if ( !ck_ssleay_is_installed() )
+ return(-1);
+
+ /* do the SSL stuff now ... before we play with pty's */
+ SSL_set_fd(ssl_con,fd);
+ SSL_set_fd(tls_con,fd);
+
+ if (tls_only_flag) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - handshake starting]\r\n");
+ debug(F110,"ck_ssl_incoming","[TLS - handshake starting]",0);
+
+ /* hmm ... only when running talking to things like
+ * https servers should we hit this code and then
+ * we really don't care *who* we talk to :-)
+ */
+ if (SSL_accept(tls_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[TLS - SSL_accept error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+ else if ( ssl_verbose_flag )
+ printf("[TLS - SSL_accept error]\r\n");
+
+ debug(F110,"ck_ssl_incoming",errbuf,0);
+ return(-1);
+ } else {
+ if (tn_deb || debses)
+ tn_debug("[TLS - OK]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - OK]\r\n");
+ debug(F110,"ck_ssl_incoming","[TLS - OK]",0);
+ tls_active_flag = 1;
+ }
+ } else if (ssl_only_flag) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - handshake starting]\r\n");
+ debug(F110,"ck_ssl_incoming","[SSL - handshake starting]",0);
+
+ /* hmm ... only when running talking to things like
+ * https servers should we hit this code and then
+ * we really don't care *who* we talk to :-)
+ */
+ if (SSL_accept(ssl_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[SSL - SSL_accept error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+ else if ( ssl_verbose_flag )
+ printf("[SSL - SSL_accept error]\r\n");
+
+ debug(F110,"ck_ssl_incoming",errbuf,0);
+ return(-1);
+ } else {
+ if (tn_deb || debses)
+ tn_debug("[SSL - OK]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - OK]\r\n");
+ debug(F110,"ssl_is","[SSL - OK]",0);
+ ssl_active_flag = 1;
+ }
+ }
+ if (ssl_active_flag || tls_active_flag) {
+ X509 *peer;
+ char str[256], *uid=NULL;
+
+ /* now check to see that we got exactly what we
+ * wanted from the caller ... if a certificate is
+ * required then we make 100% sure that we were
+ * given on during the handshake (as it is an optional
+ * part of SSL and TLS)
+ */
+
+ if ( tls_active_flag ) {
+ peer=SSL_get_peer_certificate(tls_con);
+ } else if ( ssl_active_flag ) {
+ peer=SSL_get_peer_certificate(ssl_con);
+ }
+
+ if (peer == NULL) {
+ debug(F100,"SSL_get_peer_certificate() == NULL","",0);
+ auth_finished(AUTH_REJECT);
+
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ if (tn_deb || debses)
+ tn_debug("[SSL/TLS - peer check failed]");
+ else if (ssl_debug_flag) {
+ printf("[SSL/TLS - peer check failed]\r\n");
+ }
+ debug(F110,
+ "ck_tn_tls_negotiate",
+ "[SSL/TLS - peer check failed]",
+ 0
+ );
+ /* LOGGING REQUIRED HERE! */
+ return -1;
+ }
+
+ } else {
+ debug(F100,"SSL_get_peer_certificate() != NULL","",0);
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+ NID_commonName,str,
+ 256
+ );
+ printf("[TLS - commonName=%s]\r\n",str);
+
+ X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
+#ifndef NID_x500UniqueIdentifier
+ NID_uniqueIdentifier,
+#else
+ NID_x500UniqueIdentifier,
+#endif
+ str,256
+ );
+ printf("[TLS - uniqueIdentifier=%s]\r\n",str);
+
+ /* Try to determine user name */
+ uid = tls_userid_from_client_cert(tls_con);
+ if ( uid ) {
+ /* This code is very questionable.
+ * How should it behave?
+ * The client has presented a certificate that
+ * contains a username. We have validated the
+ * certificate but we do not automatically
+ * log the user in unless there is a .tlslogin
+ * file.
+ */
+
+ ckstrncpy(szUserNameRequested,uid,UIDBUFLEN);
+#ifdef CK_LOGIN
+ if (zvuser(uid))
+ auth_finished(AUTH_VALID);
+ else
+#endif /* CK_LOGIN */
+ auth_finished(AUTH_USER);
+ }
+ else {
+ szUserNameRequested[0] = '\0';
+ auth_finished(AUTH_REJECT);
+ }
+ }
+ }
+ return(0); /* success */
+}
+
+int
+ck_ssl_outgoing(fd) int fd;
+{
+ int timo = 2000;
+
+ if ( !ck_ssleay_is_installed() )
+ return(-1);
+
+ /* bind in the network descriptor */
+ SSL_set_fd(ssl_con,fd);
+ SSL_set_fd(tls_con,fd);
+
+ /* If we are doing raw TLS then start it now ... */
+ if (tls_only_flag) {
+#ifndef USE_CERT_CB
+ if (!tls_load_certs(tls_ctx,tls_con,0)) {
+ debug(F110,"ck_ssl_outgoing","tls_load_certs() failed",0);
+ return(-1);
+ }
+#endif /* USE_CERT_CB */
+ if (tn_deb || debses)
+ tn_debug("[TLS - handshake starting]");
+ else if (ssl_verbose_flag)
+ printf("[TLS - handshake starting]\r\n");
+ debug(F110,"ck_ssl_outgoing","[TLS - handshake starting]",0);
+ if (SSL_connect(tls_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[TLS - SSL_connect error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,"ck_ssl_outgoing","[TLS - FAILED]",0);
+ netclos();
+ return(-1);
+ } else {
+ tls_active_flag = 1;
+ if ( !ssl_certsok_flag && (ssl_verify_flag & SSL_VERIFY_PEER) &&
+ !tls_is_krb5(0) ) {
+ char *subject = ssl_get_subject_name(tls_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0);
+
+ auth_finished(AUTH_REJECT);
+ return -1;
+ } else {
+ char prmpt[1024];
+ int ok;
+ ok = uq_ok("Warning: Server didn't provide a certificate",
+ "Continue? (Y/N)", 3, NULL, 0);
+ if (!ok) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+ } else if (ssl_check_server_name(tls_con, szHostName)) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+
+ printf("[TLS - OK]\r\n");
+ if (tn_deb || debses)
+ tn_debug("[TLS - OK]");
+ debug(F110,"ck_ssl_outgoing","[TLS - OK]",0);
+ ssl_display_connect_details(tls_con,0,ssl_verbose_flag);
+ }
+ }
+ /* if we are doing raw SSL then start it now ... */
+ else if (ssl_only_flag) {
+#ifndef USE_CERT_CB
+ if (!tls_load_certs(ssl_ctx,ssl_con,0))
+ return(-1);
+#endif /* USE_CERT_CB */
+ if (tn_deb || debses)
+ tn_debug("[SSL - handshake starting]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - handshake starting]\r\n");
+ debug(F110,"ck_ssl_outgoing","[SSL - handshake starting]",0);
+ if (SSL_connect(ssl_con) <= 0) {
+ if ( ssl_debug_flag ) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[SSL - SSL_connect error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+ printf("%s\r\n",errbuf);
+ }
+ if (tn_deb || debses)
+ tn_debug("[SSL - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - FAILED]\r\n");
+ debug(F110,"ck_ssl_outgoing","[SSL - FAILED]",0);
+ return(-1);
+ } else {
+ ssl_active_flag = 1;
+
+ if ( !ssl_certsok_flag && (ssl_verify_flag & SSL_VERIFY_PEER) &&
+ !tls_is_krb5(0)) {
+ char *subject = ssl_get_subject_name(ssl_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ {
+ if (tn_deb || debses)
+ tn_debug("[SSL - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - FAILED]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[SSL - FAILED]",0);
+
+ auth_finished(AUTH_REJECT);
+ return -1;
+ } else {
+ char prmpt[1024];
+ int ok;
+ ok = uq_ok("Warning: Server didn't provide a certificate",
+ "Continue? (Y/N)", 3, NULL, 0);
+ if (!ok) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[SSL - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+ } else if (ssl_check_server_name(ssl_con, szHostName)) {
+ if (tn_deb || debses)
+ tn_debug("[SSL - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[SSL - FAILED]\r\n");
+ debug(F110, "ck_tn_tls_negotiate","[SSL - FAILED]",0);
+ auth_finished(AUTH_REJECT);
+ return -1;
+ }
+ }
+
+ printf("[SSL - OK]\r\n");
+ if (tn_deb || debses)
+ tn_debug("[SSL - OK]");
+ debug(F110,"ck_ssl_outgoing","[SSL - OK]",0);
+ ssl_display_connect_details(ssl_con,0,ssl_verbose_flag);
+ }
+ }
+ return(0); /* success */
+}
+
+#ifndef NOHTTP
+int
+ck_ssl_http_client(fd, hostname) int fd; char * hostname;
+{
+ int timo = 2000;
+
+ if ( !ck_ssleay_is_installed() )
+ return(-1);
+
+ /* bind in the network descriptor */
+ SSL_set_fd(tls_http_con,fd);
+
+ /* If we are doing raw TLS then start it now ... */
+ if (1) {
+#ifndef USE_CERT_CB
+ if (!tls_load_certs(tls_http_ctx,tls_http_con,0)) {
+ debug(F110,"ck_ssl_http_client","tls_load_certs() failed",0);
+ return(-1);
+ }
+#endif /* USE_CERT_CB */
+ if (tn_deb || debses)
+ tn_debug("[TLS - handshake starting]");
+ else if (ssl_verbose_flag)
+ printf("[TLS - handshake starting]\r\n");
+ debug(F110,"ck_ssl_outgoing","[TLS - handshake starting]",0);
+ if (SSL_connect(tls_http_con) <= 0) {
+ char errbuf[1024];
+
+ sprintf(errbuf,"[TLS - SSL_connect error: %s",
+ ERR_error_string(ERR_get_error(),NULL));
+
+ if (tn_deb || debses)
+ tn_debug(errbuf);
+ else if ( ssl_debug_flag )
+ printf("%s\r\n",errbuf);
+
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,"ck_ssl_http_client","[TLS - FAILED]",0);
+ http_close();
+ return(-1);
+ } else {
+ tls_http_active_flag = 1;
+ if ( !ssl_certsok_flag && (ssl_verify_flag & SSL_VERIFY_PEER) &&
+ !tls_is_krb5(3) ) {
+ char *subject = ssl_get_subject_name(tls_http_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ return -1;
+ } else {
+ char prmpt[1024];
+ int ok;
+ ok = uq_ok("Warning: Server didn't provide a certificate",
+ "Continue? (Y/N)", 3, NULL, 0);
+ if (!ok) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ return -1;
+ }
+ }
+ } else if (ssl_check_server_name(tls_http_con, hostname)) {
+ if (tn_deb || debses)
+ tn_debug("[TLS - FAILED]");
+ else if ( ssl_verbose_flag )
+ printf("[TLS - FAILED]\r\n");
+ debug(F110,
+ "ck_tn_tls_negotiate","[TLS - FAILED]",0);
+ return -1;
+ }
+ }
+
+ printf("[TLS - OK]\r\n");
+ if (tn_deb || debses)
+ tn_debug("[TLS - OK]");
+ debug(F110,"ck_ssl_outgoing","[TLS - OK]",0);
+ ssl_display_connect_details(tls_http_con,0,ssl_verbose_flag);
+ }
+ }
+ return(0); /* success */
+}
+#endif /* NOHTTP */
+int
+ck_ssl_renegotiate_ciphers()
+{
+
+ if ( !ck_ssleay_is_installed() )
+ return(0);
+
+ if ( !sstelnet )
+ return(0);
+
+ if ( ssl_active_flag )
+ return SSL_renegotiate(ssl_con);
+ else if ( tls_active_flag )
+ return SSL_renegotiate(tls_con);
+ return(0);
+}
+
+#ifdef NT
+int
+ck_X509_save_cert_to_user_store(X509 *cert)
+{
+#ifdef X509V3_EXT_DUMP_UNKNOWN
+ char path[CKMAXPATH];
+ char hash[16];
+ char * GetAppData(int);
+ BIO * out=NULL;
+
+ if ( cert == NULL )
+ return(0);
+
+ sprintf(hash,"%08lx",X509_subject_name_hash(cert));
+ ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/certs/",
+ hash,".0");
+
+
+ out=BIO_new(BIO_s_file());
+ if (out == NULL)
+ {
+ ERR_print_errors(bio_err);
+ return(0);
+ }
+ if (BIO_write_filename(out,path) <= 0) {
+ perror(path);
+ return(0);
+ }
+
+ X509_print_ex(out, cert, XN_FLAG_SEP_MULTILINE, X509V3_EXT_DUMP_UNKNOWN);
+ if (!PEM_write_bio_X509(out,cert)) {
+ BIO_printf(bio_err,"unable to write certificate\n");
+ ERR_print_errors(bio_err);
+ BIO_free_all(out);
+ return(0);
+ }
+ BIO_free_all(out);
+ return(1);
+#else /* X509V3_EXT_DUMP_UNKNOWN */
+ return(0);
+#endif /* X509V3_EXT_DUMP_UNKNOWN */
+}
+#endif /* NT */
+
+#ifndef OS2
+/* The following function should be replaced by institution specific */
+/* code that will convert an X509 cert structure to a userid for the */
+/* purposes of client to host login. The example code included */
+/* simply returns the UID field of the Subject if it exists. */
+
+/* X509_to_user() returns 0 if valid userid in 'userid', else -1 */
+int
+X509_to_user(X509 *peer_cert, char *userid, int len)
+{
+#ifdef X509_UID_TO_USER
+ /* BEGIN EXAMPLE */
+ int err;
+
+ if (!(peer_cert && userid) || len <= 0)
+ return -1;
+
+ userid[0] = '\0';
+ debug(F110,"X509_to_user() subject",
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),NULL,0),0);
+
+ /* userid is in cert subject /UID */
+ err = X509_NAME_get_text_by_NID(X509_get_subject_name(peer_cert),
+#ifndef NID_x500UniqueIdentifier
+ NID_uniqueIdentifier,
+#else
+ NID_x500UniqueIdentifier,
+#endif
+ userid, len);
+
+ debug(F111,"X509_to_user() userid",userid,err);
+ if (err > 0)
+ return 0;
+
+ /* END EXAMPLE */
+#else /* X509_UID_TO_USER */
+#ifdef X509_SUBJECT_ALT_NAME_TO_USER
+ /* BEGIN EXAMPLE */
+ int i;
+ X509_EXTENSION *ext = NULL;
+ STACK_OF(GENERAL_NAME) *ialt = NULL;
+ GENERAL_NAME *gen = NULL;
+ char email[256];
+
+ if (!(peer_cert && userid) || len <= 0)
+ return -1;
+
+ userid[0] = '\0';
+ email[0] = '\0';
+ debug(F110,"X509_to_user() subject",
+ X509_NAME_oneline(X509_get_subject_name(peer_cert),NULL,0),0);
+
+ if ((i = X509_get_ext_by_NID(peer_cert, NID_subject_alt_name, -1))<0)
+ return -1;
+ if (!(ext = X509_get_ext(peer_cert, i)))
+ return -1;
+ X509V3_add_standard_extensions();
+ if (!(ialt = X509V3_EXT_d2i(ext)))
+ return -1;
+ for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) {
+ gen = sk_GENERAL_NAME_value(ialt, i);
+ if (gen->type == GEN_DNS) {
+ if(!gen->d.ia5 || !gen->d.ia5->length)
+ break;
+ if ( gen->d.ia5->length + 1 > sizeof(email) ) {
+ goto cleanup;
+ }
+ memcpy(email, gen->d.ia5->data, gen->d.ia5->length);
+ email[gen->d.ia5->length] = 0;
+ break;
+ }
+ }
+ cleanup:
+ X509V3_EXT_cleanup();
+ if (ialt)
+ sk_GENERAL_NAME_free(ialt);
+
+ debug(F110,"X509_to_user() email",email,0);
+
+ if ( email[0] ) {
+ char * domain = NULL;
+
+ /* Find domain */
+ for ( i=0 ; email[i] ; i++ ) {
+ if ( email[i] == '@' ) {
+ email[i] = '\0';
+ domain = &email[i+1];
+ break;
+ }
+ }
+
+ if ( domain ) {
+ /* XXX - Put code to Verify domain here */
+
+ if ( /* domain is okay */ 1 )
+ ckstrncpy(userid,email,len);
+ }
+ }
+
+ return(userid[0] ? 0 : -1);
+ /* END EXAMPLE */
+#endif /* X509_SUBJECT_ALT_NAME_TO_USER */
+#endif /* X509_UID_TO_USER */
+ return -1;
+}
+
+/* The following function should be replaced by institution specific */
+/* code that will determine whether or not the combination of the */
+/* provided X509 certificate and username is valid for automatic */
+/* login. Whereas X509_to_user() is used to provide authentication */
+/* of the user, the X509_userok() function is used to provide */
+/* authorization. The certificate passed into X509_userok() does */
+/* need to map to a userid; nor would the userid it would map to */
+/* need to match the userid provided to the function. There are */
+/* numerous circumstances in which it is beneficial to have the ability */
+/* for multiple users to gain access to a common account such as */
+/* 'root' on Unix; or a class account on a web server. In Unix we */
+/* implement this capability with the ~userid/.tlslogin file which */
+/* a list of X509 certificates which may be used to access the */
+/* account 'userid'. */
+
+/* X509_to_user() returns 0 if access is denied; 1 is access is permitted */
+int
+X509_userok(X509 * peer_cert, const char * userid)
+{
+#ifndef VMS
+ /* check if clients cert is in "user"'s ~/.tlslogin file */
+ char buf[512];
+ int r = 0;
+ FILE *fp;
+ struct passwd *pwd;
+ X509 *file_cert;
+
+ if ( peer_cert == NULL )
+ return(0);
+
+ if (!(pwd = getpwnam(userid)))
+ return 0;
+ if (strlen(pwd->pw_dir) > 500)
+ return(0);
+ sprintf(buf, "%s/.tlslogin", pwd->pw_dir);
+
+ if (!(fp = fopen(buf, "r")))
+ return 0;
+ while (!r && (file_cert = PEM_read_X509(fp, NULL, NULL, NULL))) {
+ if (!ASN1_STRING_cmp(peer_cert->signature, file_cert->signature))
+ r = 1;
+ X509_free(file_cert);
+ }
+ fclose(fp);
+ return(r);
+#else /* VMS */
+ /* Need to implement an appropriate function for VMS */
+ return(0);
+#endif /* VMS */
+}
+#endif /* OS2 */
+#endif /* CK_SSL */
diff --git a/ckermit-8.0.211/ck_ssl.h b/ckermit-8.0.211/ck_ssl.h
new file mode 100644
index 0000000..b4dd8a3
--- /dev/null
+++ b/ckermit-8.0.211/ck_ssl.h
@@ -0,0 +1,147 @@
+/*
+ C K _ S S L . H -- OpenSSL Interface Header for C-Kermit
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+
+ Author: Jeffrey E Altman (jaltman@secure-endpoints.com)
+ Secure Endpoints Inc., New York City
+*/
+
+#ifdef CK_SSL
+#ifndef CK_ANSIC
+#define NOPROTO
+#endif /* CK_ANSIC */
+
+#ifdef COMMENT /* Not for C-Kermit 7.1 */
+#ifdef KRB5
+#ifndef NOSSLK5
+#ifndef SSL_KRB5
+#define SSL_KRB5
+#endif /* SSL_KRB5 */
+#endif /* NOSSLK5 */
+#endif /* KRB5 */
+#endif /* COMMENT */
+
+#ifdef OS2
+#ifndef ZLIB
+#define ZLIB
+#endif /* ZLIB */
+#endif /* OS2 */
+
+#ifdef ZLIB
+#include <openssl/comp.h>
+#endif /* ZLIB */
+/* We place the following to avoid loading openssl/mdc2.h since it
+ * relies on the OpenSSL des.h. Since we do not need the MDC2
+ * definitions there is no reason to have it included by openssl/evp.h
+ */
+#define OPENSSL_NO_MDC2
+#include <openssl/des.h>
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <openssl/rand.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/bn.h>
+#include <openssl/blowfish.h>
+#include <openssl/dh.h>
+#include <openssl/rc4.h>
+#include <openssl/cast.h>
+#include <openssl/dsa.h>
+#include <openssl/rsa.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#ifdef SSL_KRB5
+#include <openssl/kssl.h>
+#endif /* SSL_KRB5 */
+
+extern BIO *bio_err;
+extern SSL *ssl_con;
+extern SSL_CTX *ssl_ctx;
+extern int ssl_debug_flag;
+extern int ssl_only_flag;
+extern int ssl_active_flag;
+extern int ssl_verify_flag;
+extern int ssl_verbose_flag;
+extern int ssl_certsok_flag;
+extern int ssl_dummy_flag;
+extern int ssl_verify_depth;
+
+extern char *ssl_rsa_cert_file;
+extern char *ssl_rsa_cert_chain_file;
+extern char *ssl_rsa_key_file;
+extern char *ssl_dsa_cert_file;
+extern char *ssl_dsa_cert_chain_file;
+extern char *ssl_dh_key_file;
+extern char *ssl_cipher_list;
+extern char *ssl_crl_file;
+extern char *ssl_crl_dir;
+extern char *ssl_verify_file;
+extern char *ssl_verify_dir;
+extern char *ssl_dh_param_file;
+extern char *ssl_rnd_file;
+
+extern SSL_CTX *tls_ctx;
+extern SSL *tls_con;
+extern int tls_only_flag;
+extern int tls_active_flag;
+extern int x509_cert_valid;
+extern X509_STORE *crl_store;
+
+#ifndef NOHTTP
+extern SSL_CTX *tls_http_ctx;
+extern SSL *tls_http_con;
+extern int tls_http_active_flag;
+#endif /* NOHTTP */
+
+extern int ssl_initialized;
+
+_PROTOTYP(VOID ssl_once_init,(void));
+_PROTOTYP(int ssl_tn_init,(int));
+_PROTOTYP(int ssl_http_init,(char *));
+_PROTOTYP(int ck_ssl_http_client,(int,char *));
+_PROTOTYP(int ssl_display_connect_details,(SSL *,int,int));
+_PROTOTYP(int ssl_server_verify_callback,(int, X509_STORE_CTX *));
+_PROTOTYP(int ssl_client_verify_callback,(int, X509_STORE_CTX *));
+_PROTOTYP(int ssl_reply,(int, unsigned char *, int));
+_PROTOTYP(int ssl_is,(unsigned char *, int));
+_PROTOTYP(int ck_ssl_incoming,(int));
+_PROTOTYP(int ck_ssl_outgoing,(int));
+_PROTOTYP(int tls_is_user_valid,(SSL *, const char *));
+_PROTOTYP(char * ssl_get_dnsName,(SSL *));
+_PROTOTYP(char * ssl_get_commonName,(SSL *));
+_PROTOTYP(char * ssl_get_issuer_name,(SSL *));
+_PROTOTYP(char * ssl_get_subject_name,(SSL *));
+_PROTOTYP(int ssl_get_client_finished,(char *, int));
+_PROTOTYP(int ssl_get_server_finished,(char *, int));
+_PROTOTYP(int ssl_passwd_callback,(char *, int, int, VOID *));
+_PROTOTYP(VOID ssl_client_info_callback,(const SSL *,int, int));
+_PROTOTYP(int ssl_anonymous_cipher,(SSL * ssl));
+_PROTOTYP(int tls_load_certs,(SSL_CTX * ctx, SSL * con, int server));
+_PROTOTYP(int ssl_verify_crl,(int, X509_STORE_CTX *));
+_PROTOTYP(int tls_is_krb5,(int));
+_PROTOTYP(int X509_userok,(X509 *,const char *));
+_PROTOTYP(int ck_X509_save_cert_to_user_store,(X509 *));
+#ifdef OS2
+#include "ckosslc.h"
+#include "ckossl.h"
+#endif /* OS2 */
+
+#define SSL_CLIENT 0
+#define SSL_SERVER 1
+#define SSL_HTTP 2
+
+#define SSL_ERR_BFSZ 4096
+
+#ifdef SSL_KRB5
+#define DEFAULT_CIPHER_LIST "HIGH:MEDIUM:LOW:+KRB5:+ADH:+EXP"
+#else
+#define DEFAULT_CIPHER_LIST "HIGH:MEDIUM:LOW:+ADH:+EXP"
+#endif /* SSL_KRB5 */
+#endif /* CK_SSL */
diff --git a/ckermit-8.0.211/ckaaaa.txt b/ckermit-8.0.211/ckaaaa.txt
new file mode 100644
index 0000000..efa83a5
--- /dev/null
+++ b/ckermit-8.0.211/ckaaaa.txt
@@ -0,0 +1,385 @@
+ckaaaa.txt 10 Apr 2004
+
+ C-KERMIT VERSION 8.0.211
+ OVERVIEW OF FILES
+
+ Communications software for UNIX and (Open)VMS.
+
+ And in former versions also for:
+ Stratus VOS, AOS/VS, QNX,
+ Plan 9, OS-9, Apollo Aegis, and the Commodore Amiga.
+ The Apple Macintosh, the Atari ST.
+
+ The Kermit Project - Columbia University
+
+ http://www.columbia.edu/kermit/ - kermit@columbia.edu
+
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+
+
+DOCUMENTATION
+
+ C-Kermit is documented in the book "Using C-Kermit", Second Edition, by
+ Frank da Cruz and Christine M. Gianone, Digital Press, ISBN 1-55558-164-1,
+ supplementated by Web-based updates for C-Kermit 7.0 and 8.0.
+
+PLATFORMS
+ Security
+ Name Included Last Updated
+
+ Unix Yes 8.0.211 10 Apr 2004
+ (Open)VMS No 8.0.208 10 Apr 2004
+ Windows (K95) Yes 8.0.208 14 Mar 2003 (K95 2.1)
+ OS/2 (K95) Yes 8.0.208 14 Mar 2003 (K95 2.1)
+ DG AOS/VS No 7.0.196 1 Jan 2000
+ Stratus VOS No 7.0.196 1 Jan 2000
+ Bell Plan 9 No 7.0.196 1 Jan 2000
+ Microware OS-9 No 7.0.196 1 Jan 2000
+ Commodore Amiga No 7.0.196 1 Jan 2000
+ Macintosh No 5A(190) 16 Aug 1994 (Mac Kermit 0.991)
+ Atari ST No 5A(189) 30 Jun 1993
+
+QUICK START FOR FTP USERS
+
+ If you have a Web browser, go to:
+
+ http://www.columbia.edu/kermit/ckermit.html
+
+ And take it from there. Otherwise...
+
+ The definitive FTP source for Kermit software is kermit.columbia.edu.
+ Kermit software obtained from other FTP sites is not necessarily complete
+ or up to date, and may have been modified.
+
+C-Kermit for UNIX computers that have a C compiler and 'make' program:
+
+ Directory kermit/archives, binary mode, file cku211.tar.Z or cku211.tar.gz
+
+ This is a compressed tar archive of UNIX C-Kermit source code, makefile, and
+ other files. It unpacks into its current directory, so download it into a
+ fresh directory. Transfer in binary mode, uncompress (or gunzip), untar (tar
+ xvf cku211.tar), and then give the appropriate "make" command to build for
+ your UNIX system; read the comments in the makefile and ckuins.txt for
+ further info.
+
+C-Kermit for VMS:
+
+ If you have VMS UNZIP, get the file kermit/archives/ckv211.zip in binary
+ mode, unzip, and build with CKVKER.COM.
+
+Others: In the kermit/f or kermit/test directories under the appropriate
+prefixes, explained below.
+
+
+INSTALLATION
+
+Installation procedures depend on the system. Please read the CK?INS.TXT,
+if any, file for your system (?=U for UNIX, V for VMS, etc). Please note
+the naming and placement for the initialization files:
+
+ CKERMIT.INI
+ The standard initialization file. Please leave it as is unless you
+ know what you are doing and (if you are changing it or replacing it
+ for others to use) you are prepared to support it. Rename this file
+ to .kermrc in UNIX, OS-9, BeBox, or Plan 9. In Stratus VOS, rename
+ it ckermit.ini (lowercase). On multiuser systems, it goes either in the
+ (or EACH) user's home (login) directory, or else in a common shared
+ place if C-Kermit has been configured to look in that place (see
+ ckccfg.txt for details).
+
+ CKERMOD.INI
+ A *sample* customization file. On multiuser OS's, a copy of this file
+ goes in each user's home directory, and then each user edits it to suit
+ her needs and preferences; e.g. by defining macros for their common
+ connections.
+
+ DIALING DIRECTORIES
+ Dialing directory files can be system-wide, per-group, or per-user, or
+ any combination. For example, there can be a corporate wide directory
+ shared by all users, a supplemental directory for each division or
+ department, and a personal directory for each user. Simply be sure the
+ dialing directory files are identified a SET DIAL DIRECTORY command in
+ the user's (or the system-wide) C-Kermit initialization file, or in the
+ environment variable (logical name, symbol) K_DIAL_DIRECTORY. (The
+ standard initialization file looks by default in the user's home or login
+ directory.) When installing C-Kermit on multiuser platforms from which
+ users will dial out, you can also set environment variables for area
+ code, country code, and the various dialing prefixes as described on page
+ 478 of "Using C-Kermit" (second edition), so users don't have to worry
+ about defining these items themselves. Network directories and service
+ directories can also be set up in a similar manner.
+
+ DOCUMENTATION
+ In UNIX, the general C-Kermit man page (or one of the versions tailored
+ for a specific platform, like HP-UX or Solaris) should be installed in
+ the appropriate place. In VMS, the VMS help topic (CKVKER.HLP) should
+ be installed as described in CKVINS.TXT. Plain-text documentation such
+ as CKERMIT2.TXT should be put in whatever place people are accustomed
+ to looking.
+
+FILES AND FILE NAMING CONVENTIONS
+
+C-Kermit is a family of Kermit programs for many different computer systems.
+The program shares a common set of system-independent file transfer protocol
+modules, written in the C language. System-dependent operations are collected
+into system-specific modules for each system.
+
+C-Kermit file names all start with the letters "CK", followed by a single
+letter indicating the subgroup. When referring to these files in the UNIX,
+AOS/VS, or VOS environments, use lowercase letters, rather than the uppercase
+letters shown here. Subgroups:
+
+ _: Security/Authentication/Encryption code, possibly regulated by law
+ a: General descriptive material and documentation
+ b: BOO file encoders and decoders (obsolete)
+ c: All platforms with C compilers
+ d: Data General AOS/VS
+ e: Reserved for "ckermit" files, like CKERMIT.INI, CKERMIT80.TXT
+ f: (reserved)
+ g: (reserved)
+ h: (reserved)
+ i: Commodore Amiga (Intuition)
+ j: (unused)
+ k: (unused)
+ l: Stratus VOS
+ m: Macintosh with Mac OS
+ n: Microsoft Windows NT
+ o: OS/2 and/or Microsoft Windows 95/98/ME/NT/2000/XP/...
+ p: Bell Labs Plan 9
+ q: (reserved)
+ r: DEC PDP-11 with RSTS/E (reserved)
+ s: Atari ST GEMDOS (last supported in version 5A(189))
+ t: DEC PDP-11 with RT-11 (reserved)
+ u: UNIX or environments with UNIX-like C libraries
+ v: VMS and OpenVMS
+ w: Wart (Lex-like preprocessor, used with all systems)
+ x: (reserved)
+ y: (reserved)
+ z: (reserved)
+ 0-3: (reserved)
+ 4: IBM AS/400 (reserved)
+ 5-8: (reserved)
+ 9: Microware OS-9
+
+Examples:
+
+ ckaaaa.txt - This file
+ ckufio.c - File i/o for UNIX
+ ckstio.c - Communications i/o for the Atari ST
+ makefile - makefile for building UNIX C-Kermit
+ ckpker.mk - makefile for building Plan 9 C-Kermit
+ ckvker.com - build procedure for VMS C-Kermit
+
+IMPORTANT FILES (use lowercase names on UNIX, VOS, or AOS/VS):
+
+ ckaaaa.txt - This file (overview of the C-Kermit files).
+ For system-specific distributions, this will normally
+ be replaced by a system-specific READ.ME file.
+
+ ckermit70.txt - Updates: Supplement to "Using C-Kermit", 2nd Ed, for 7.0.
+ ckermit80.txt - Updates: Supplement to "Using C-Kermit", 2nd Ed, for 8.0.
+ ckututor.txt - C-Kermit Tutorial for Unix (plain text)
+ ckcbwr.txt - "Beware file" (limitations, known bugs, hints), general.
+ ckermit.ini - Standard initialization file (rename to .kermrc in UNIX, OS-9)
+ ckermod.ini - Sample customization file (rename to .mykermrc in UNIX, OS-9)
+
+The following can be found at the Kermit FTP site:
+
+ ckermit.kdd - Sample dialing directory file (rename to .kdd in UNIX, OS-9)
+ ckermit.knd - Sample dialing directory file (rename to .knd in UNIX, OS-9)
+ ckermit.ksd - Sample services directory file (rename to .ksd in UNIX, OS-9)
+ ckedemo.ksc - Demonstration macros from "Using C-Kermit"
+ ckepage.ksc - Ditto
+ ckevt.ksc - Ditto
+
+UNIX-specific files:
+
+ ckuins.txt - UNIX-specific installation instructions.
+ ckubwr.txt - UNIX-specific beware file.
+ ckuker.nr - "man page" for UNIX.
+
+VMS-specific files:
+
+ ckvins.txt - VMS-specific installation instructions.
+ ckvbwr.txt - VMS-specific beware file
+ ckvker.hlp - VMS C-Kermit HELP topic (needs updating).
+
+DG AOS/VS-specific files:
+
+ ckdins.txt - Data General AOS/VS C-Kermit installation instructions
+ ckdbwr.txt - AOS/VS "beware" file
+ ckd*.cli - Procedures for building AOS/VS C-Kermit
+
+The following files are of interest mainly to programmers and historians
+(find them at the Kermit ftp site):
+
+ ckcker.ann - Release announcements.
+ ckccfg.txt - Configuration information (feature selection), general.
+ ckcplm.txt - Program logic manual (for programmers).
+ ckc211.txt - Program update history for edit 201-211.
+ ckc200.txt - Program update history for edit 198-200 (big)
+ ckc197.txt - Program update history for edit 195-197 (big)
+ ckc190.txt - Program update history for edits 189-190 (big).
+ ckc188.txt - Program update history, edits 179-188 (big).
+ ckc178.txt - Program edit history, 5A edits through 178 (very big).
+ ckcv4f.txt - Program edit history, version 4F.
+ ckcv4e.txt - Program edit history, version 4E.
+
+BINARIES
+
+If you have FTP access to kermit.columbia.edu (also known as
+kermit.cc.columbia.edu, ftp.cc.columbia.edu), you can also retrieve various
+C-Kermit binaries from the directory kermit/bin/ck*.*, or more conventiently
+from the web page:
+
+ http://www.columbia.edu/kermit/ck80binaries.html
+
+Test versions would be in kermit/test/bin/ck*.*. Be sure to transfer these
+files in binary mode. The READ.ME file in that directory explains what's
+what.
+
+SOURCE FILES
+
+The source files for the UNIX version (all UNIX versions) are available in
+kermit/archives/ckuNNN.tar.Z, approximately 1MB in size. Transfer this file
+in binary mode. This is a compressed tar archive. There is also a gzip'd
+version, cku211.tar.gz. To get the binary tar archive:
+
+ mkdir kermit (at shell prompt, make a Kermit directory)
+ cd kermit (make it your current directory)
+
+ ftp kermit.columbia.edu (make an ftp connection)
+ user: anonymous (log in as user "anonymous", lower case!)
+ password: (use your email id as a password)
+ cd kermit/archives (go to the archives directory)
+ type binary (specify binary file transfer)
+ get cku211.tar.Z (get the tar archive) (or get cku192.tar.gz)
+ bye (disconnect and exit from ftp)
+
+ uncompress cku211.tar.Z (at the shell prompt, uncompress the archive)
+ tar xvf cku211.tar (extract the files from the tar archive)
+ make xxx (build C-Kermit for your system)
+
+(where "xxx" is the makefile entry appropriate for your system.)
+
+All C-Kermit source and other text files are also kept separately in the
+kermit/f directory. The files necessary to build a particular implementation
+of C-Kermit are listed in the appropriate makefile or equivalent:
+
+ UNIX: makefile (or rename ckuker.mak to makefile)
+ 2.11 BSD: ckubs2.mak (rename to makefile), ckustr.sed
+ Plan 9: ckpker.mk (rename to mkfile)
+ Macintosh: ckmker.mak (rename to kermit.make, use MPW C 3.2)
+ VMS: CKVKER.COM (DCL) (and optionally also CKVKER.MMS)
+ or CKVOLD.COM (for VMS 4.x)
+ Amiga: CKIKER.MAK (Aztec C) or CKISAS.MAK (SAS C)
+ Atari ST: CKSKER.MAK
+ OS-9: ck9ker.mak or ck9ker.gcc
+ AOS/VS: ckdmak.cli, ckdcc.cli, ckdlnk.cli
+Stratus VOS: cklmak.cm
+
+Minimal source files for building selected versions (these patterns get all
+the files you need, and in some cases maybe a few extra):
+
+ UNIX: ck[cuw]*.[cwh] (including QNX, Plan 9, and BeBox)
+ UNIX: ck[cuw_]*.[cwh] (Unix with security modules)
+ VMS: ck[cuwv]*.[cwh]
+ Mac: ck[cuwm]*.[cwhr]
+ AOS/VS: ck[cuwd]*.[cwh]
+ VOS: ck[cwhl]*.[cwh]
+ Amiga: ck[cuwi]*.[cwh]
+ Atari: ck[cuws]*.[cwh]
+ OS-9: ck[cuw9]*.[cwha]
+
+For a detailed, specific source file list for this C-Kermit release, see the
+file ckcxxx.txt, where xxx is the current C-Kermit edit number, such as 211.
+
+Finally, here is a more detailed description of the C-Kermit file naming
+conventions. A C-Kermit filename has the form:
+
+ CK<system><what>.<type>
+
+where:
+
+<system> is described earlier in this file;
+
+<type> is the file type (use lowercase on UNIX, VOS, or AOS/VS):
+
+ c: C language source
+ h: Header file for C language source
+ w: Wart preprocessor source, converted by Wart (or Lex) to a C program
+ r: Macintosh resource file (8-bit text)
+ a: Assembler source
+
+ txt: Plain text.
+ nr: Nroff/Troff text formatter source for UNIX "man page"
+ mss: Scribe text formatter source
+ ps: Typeset material to be printed on a PostScript printer
+ hlp: A VMS Help topic
+
+ ini: Initialization file
+ ksc: A Kermit Script to be executed by the TAKE command
+ kdd: A Kermit Dialing Directory
+ knd: A Kermit Network Directory
+ ksd: A Kermit Services Directory
+
+ mak: A Makefile or other build procedure (often needs renaming)
+ com: (VMS only) a DCL command procedure
+ cli: (AOS/VS only) a command procedure
+ cmd: (OS/2 only) a Rexx command procedure
+
+ boo: "boo"-encoded executable program, decode with CKBUNB program.
+ hex: "hex"-encoded executable program, decode with CKVDEH program (VMS only).
+ hqx: BinHex'd Macintosh Kermit program, decode with BinHex version 4.0.
+ uue: A uuencoded binary file, decode with uudecode or (DG only) CKDECO.
+
+ def: An OS/2 linker definitions file.
+ sh: A UNIX shell script.
+ sed: A UNIX sed (editor) script.
+ str: A file of character strings extracted from C-Kermit (BSD 2.1x only).
+
+<what> is mnemonic (up to 3 characters) for what's in the file:
+
+NOTE: After C-Kermit 6.0, text filetypes such as .DOC and .HLP were changed
+to .TXT to avoid confusion in Windows-based Web browsers, which would
+otherwise mistake them for Microsoft Word or Windows Help documents.
+
+ aaa: A "read-me" file, like this one
+ ins: Installation instructions or procedures
+ bwr: "Beware" file -- things to watch out for, hints and tips
+ plm: Program Logic Manual
+ ker: General C-Kermit definitions, information, documentation
+
+ nnn: Digits: C-Kermit edit number (e.g. cku211.tar.gz)
+ cmd: Command parsing
+ con: CONNECT command
+ cns: CONNECT command (UNIX only - version that uses select(), not fork())
+ deb: Debug/Transaction Log formats, Typedefs
+ dia: Modem/Dialer control
+ fio: System-depdendent File I/O
+ fns: Protocol support functions
+ fn2: More protocol support functions (and FN3, ...)
+ lib: Common library routines module
+ mai: Main program
+ net: Network i/o module
+ pro: Protocol
+ scr: SCRIPT command
+ tel: Telnet protocol module
+ tio: System-dependent communications i/o & control and interrupt handing
+ sig: Signal handling module
+ usr: Interactive/script user interface
+ us2: More user interface (mainly help text)
+ us3: Still more user interface (and USR4, USR5, USR6, USR7)
+ usx: Common user interface functions
+ usy: Command-line parsing
+ xla: Character set translation module
+ uni: Unicode support
+ pty: Pseudoterminal support
+ mdb: Malloc-debugging module (not included in real builds)
+ str: Strings module (only for 2.xBSD)
+
+(End of ckaaaa.txt)
diff --git a/ckermit-8.0.211/ckc211.txt b/ckermit-8.0.211/ckc211.txt
new file mode 100644
index 0000000..1886725
--- /dev/null
+++ b/ckermit-8.0.211/ckc211.txt
@@ -0,0 +1,3595 @@
+C-KERMIT CHANGE LOG (Changes since 8.0.200 of 12 Dec 2001)
+
+Chronological order: Go to the bottom to find the newest edits.
+
+---8.0.200---
+
+Known bugs (+ = fixed after release):
+
+ + 1. tilde_expand() can call getcwd() with NULL arg.
+ + 2. getexedir() called too early (fatal in combination with (1)).
+ + 3. Kermit "get blah" where blah is a symlink; server refuses to send it.
+ Should not do this if GET not recursive.
+ ? 4. Dave Sneddon's report about VMS fore/background confusion.
+ + 5. FTP GET path/file doesn't work - path not stripped - but MGET works.
+ + 6. IRIX 5.3 compilation problems (have patches from Marcus Herbert)
+ X 7. Filename completion bug (see below) (deferred).
+ + 8. QNX6 herald and other problems.
+
+-------------
+
+Merged Jeff's changes, 20 Dec 2001:
+
+ . Changed all occurrences of "ttnproto == NP_TELNET" to "IS_TELNET()" to
+ account for the difference between SSH and Telnet. ckuscr.c,
+ ckuus[3457].c, ckcnet.h, ckcfns.c, ckudia.c, ckutio.c, ckucon.c, ckucns.c.
+
+ . Moved SSH pty failure warnings. ckuusr.c.
+
+ . Security adjustments to FTP module, plus fix an error message. ckcftp.c.
+
+ . Adjustment of some security-related #ifdefs. ckcdeb.h, ckuus2.c, ckctel.c.
+
+ . Guard against calling getpwnam() with a NULL arg in tilde_expand() ckufio.c.
+
+ . Moved getexedir() call to later, where it's safe. ckcmai.c.
+
+Added SSH ADD and many SSH SET commands from Jeff's spec. Fixed SHOW SSH
+to not dump core if variables weren't set. ckcker.h, ckuus[r3].c, 20 Dec 2001.
+
+C-Kermit in server mode, client says "get foo" where foo is a symlink.
+Server says "no files meet selection criteria" instead of sending the file.
+It should only refuse to follow symlinks if it's a recursive get. Fixed
+in sgetinit(): ckcpro.w, 21 Dec 2001.
+
+More work on SSH and SET/SHOW SSH commands. ckuus[r3].c, 21 Dec 2001.
+
+Undid Jeff's replacement of the SSH pseudoterminal allocation failure
+message, because now it comes out any time an SSH command has to be
+reparsed (in the non-SSHBUILTIN case). ckuusr.c, 21 Dec 2001.
+
+More SSH and SET SSH command work back & forth with Jeff, plus Jeff added
+SET HOST /NET:SSH. ckcmai.c, ckuus[r37].c, ckcdeb.h, ckuusr.h, 22 Dec 2001.
+
+Added SSH OPEN switches. ckuusr.c, 22 Dec 2001.
+
+Added SSH CLEAR, HELP SSH, and HELP SET SSH. ckuus[r2].c, 23 Dec 2001.
+
+From Jeff:
+ . SET TCP commands now apply to SSH
+ . SSH V2 REKEY and FORWRD-{LOCAL,REMOTE}-PORT commands now implemented
+ . Missing DLLs automatically disable appropriate authentication mechanisms.
+ckuusr.c ckcnet.c ckuus3.c ckcmai.c ckcnet.h ckuus4.c, 26 Dec 2001.
+
+From Jeff:
+ . Remove SET SSH KEEPALIVES.
+ . Add help text for SSH AGENT { ADD, DELETE, LIST }.
+ckuus[23].c, 28 Dec 2001.
+
+Added parsing for SSH AGENT { ADD, DELETE, LIST }. ckuusr.c, 28 Dec 2001.
+
+From Jeff:
+ . Fixed a crash that can happen when making an SSH connection.
+ . Filled in SSH AGENT actions.
+ . Changed default for strict host key check (to ASK) and help text.
+ . uploaded new binaries include ~kermit/os2test/beta/ssh-agent.exe
+ . Read man ssh-agent on ftp.kermit.columbia.edu for details on what it does.
+ckuus[r23].c, 28 Dec 2001.
+
+"ftp get path/filename" didn't work; the FTP client did not strip the path
+from the local copy of the filename when doing a GET, even though it did
+for MGET. Diagnosis: in doftpget(), the "if (!getone && !skipthis)" statement
+lacked an "else" part for the getone case. ckcftp.c, 28 Dec 2001.
+
+A while back Jeff reported that in FTP MGET, if you cancel a file with 'x',
+all the rest of the files arrive truncated to 0 bytes. I tried this on both
+Unix and Windows and couldn't reproduce it.
+
+In the last-minute flurry to release C-Kermit 8.0, I thought I noticed the FTP
+client failing to update the fullscreen file-transfer display. But it seems
+to work right, at least in Unix. When downloading a big file with FTP, all
+the display fields are updated as expected. But smaller files might go by too
+fast for the display to do anything. HOWEVER, in K95 the file transfer
+display does not update itself until the end of the file, even if the file
+takes a long time to transfer. This happens in both the Console and GUI
+versions. A thread thing? (Jeff says no.) Yet the same display works fine
+on Telnet connections.
+
+In IRIX 5.3, the select()-based CONNECT module had to include <sys/time.h>
+or else it blew up with "struct timeval" unknown. Since there already was
+a SYSTIMEH CFLAG, I added the #include within #ifdef SYSTIMEH..#endif and
+rebuilt with KFLAGS=-DSYSTIMEH, only to discover that the irix5* targets
+didn't bother to propogate KFLAGS. Fixed in ckucns.c, makefile, 30 Dec 2001.
+
+Increased IRIX5x Olimit from 2400 to 3000 because of ckuus[34].c. Added
+-ansi, since (Marcus Herbert reported) we were not actually getting ANSI-C
+compilation even though CK_ANSIC was defined. But now that we are, we get
+warnings in <netinet/tcp.h>, which is included by ckcnet.h:
+
+ bit-field 'th_off' type required to be int, unsigned int, or signed int.
+ (3.5.2.1(30))
+ u_char th_off:4,
+ ------ ^
+Tough. makefile, 30 Dec 2001.
+
+But adding -ansi to the IRIX 5x targets also make compilation bomb whenever we
+referenced fdopen() or popen(), which evidently don't have prototypes in any
+of the header files. Luckily we already have CFLAGS for this occasion too:
+DCLFDOPEN and DCLPOPEN. Added these to the irix51 target. Also had to copy
+the fdopen()-popen() prototype section to ckuusx.c, which has a new reference
+to fdopen() in a workaround for the curses console buffering bug. makefile,
+ckuusx.c, 30 Dec 2001.
+
+The QNX6 version did not receive a proper herald (it announced itself as
+"unknown version". Reshuffled #ifdefs in ckuver.h, added display of QNX6
+and NEUTRINO symbols to ckuus5.c, 30 Dec 2001.
+
+Lucas Hart sent in a patch for the VMS problem. Apparently it was even worse
+than Dave Sneddon had reported: 8.0 couldn't run at all under Batch. ckvtio.c,
+31 Dec 2001.
+
+A major obstacle to the usability of the FTP client is that certain commands
+don't behave as FTP users expect: CD, DIR, DELETE, MKDIR, etc, which are local
+rather remote, and there are no LCD (etc), USER, or ACCOUNT commands. We
+could fix this by adding an FTP command-language personality, but file
+management commands can also be remote or local on connections to Kermit
+servers too. So:
+
+SET LOCUS { LOCAL, REMOTE, AUTO }
+ Sets the locus for unprefixed file management commands.
+ When LOCAL, a REMOTE (or R) prefix is required for
+ to send file management commands to a remote server (e.g. RCD, RDIR).
+ When REMOTE, an L prefix is required to issue local file management
+ commands (e.g. LCD, LDIR). The word LOCAL can't be used as a prefix
+ since it is used for declaring local variables.
+
+This applies to all types of connections, and thus is orthogonal to SET
+GET-PUT-REMOTE, which selects between Kermit and FTP for remote file-transfer
+and management commands.
+
+The default LOCUS is AUTO, which means we switch to REMOTE whenever an FTP
+connection is made, and to LOCAL whenever a non-FTP connection is made,
+and switch back accordingly whenever a connnection is closed.
+
+Implementation (31 Dec 2001):
+ . None of this is compiled if LOCUS is not defined.
+ . Added XYLOCUS (SET LOCUS) and LOCUS definitions: ckuusr.h.
+ . Override by defining NOLOCUS (which inhibits definition of LOCUS).
+ . Added LOCUS to SET keyword table: ckuusr.c.
+ . Added locus & autolocus variables: ckuusr.c.
+ . Added SET LOCUS parsing and variable setting: ckuus3.c.
+ . Added display of LOCUS setting to SHOW COMMAND: ckuus5.c.
+ . Added automatic locus setting to setlin(): ckuus7.c.
+ . Added automatic locus setting to ftpopen() and ftpclose(): ckcftp.c.
+
+How to catch all the places where a Kermit connection is closed? Turns out
+we've done this before, when we added the connection log. So I made
+dologend() take care of locus switching. But dologend() was not compiled in
+if certain symbols were defined, such as NOLOCAL, or not defined, such as
+CKLOGDIAL. So I (a) rearranged the #ifdefs so that even if these would
+otherwise have obliviated dologend(), now they leave a piece of it for
+locus-setting; (b) moved the prototype out of #ifdefs; and (c) took all calls
+to it out of #ifdefs. ckcker.h, ckcfn2.c, ckcmai.c, ckucns.c, ckucon.c,
+ckuus[r347x].c, 31 Dec 2001.
+
+Added locus checking to the following commands: DIRECTORY, CD/CWD, CDUP,
+DELETE, PWD, MKDIR, RMDIR, RENAME. ckuusr.c, 31 Dec 2001.
+
+Added LDIRECTORY, LCD/LCWD, LCDUP, LDELETE, LPWD, LMKDIR, LRMDIR,
+LRENAME. ckuusr.[ch], 31 Dec 2001.
+
+Added USER and ACCOUNT commands, which are the same as FTP USER and FTP
+ACCOUNT. ckuusr.[ch], ckcftp.c, 31 Dec 2001.
+
+Since automatic locus switching could be a big surprise for most people, I
+printed message any time it changed. ckcftp.c, ckuus[37].c, 31 Dec 2001.
+
+Added help text for the new L commands and filled in missing HELP text for
+SET GET-PUT-REMOTE, CDUP, MKDIR, and RMDIR. ckuus2.c, 31 Dec 2001.
+
+Changed help text of CD, DIR, etc, for LOCUS. Changed the help text for
+RCD, RPWD, RDEL, RDIR, etc, to mention that they also work with FTP servers.
+Updated HELP REMOTE for this too. ckuus2.c, 31 Dec 2001.
+
+Made sure code builds with NOLOCAL, NOLOGDIAL, and NOLOCUS (it does).
+
+The IKSD command, when given with a /USER: switch, sends the user ID to the
+IKSD. But the SET HOST /USER: command does not, when making a connection to a
+Kermit service. This makes it impossible to script IKSD interactions using
+only client commands. Furthermore, even if you include a /PASSWORD switch
+with the IKSD command, it does not send the password. I added code near the
+bottom of setlin() to do this. If we have a connection to a Kermit service
+and a /USER: switch was given, then we attempt a REMOTE LOGIN. If a
+/PASSWORD: switch was not given then if the username is "ftp" or "anonymous",
+we automatically supply a password of user@host; otherwise we prompt for a
+password. If a /USER: switch was not given, it acts like before. It all
+works, but it might not be the best way (or place) to do it. setlin():
+ckuus7.c, 31 Dec 2001.
+
+ NOTE: The above change doesn't help with IKSD /USER:anonymous,
+ the server prompts for password anyway, not sure why.
+
+ NOTE 2: What about secure authentication? We have to test to see
+ if user was already authenticated before sending the login packet.
+
+Added /opt/kermit and /opt/kermit/doc to info_dir[] list (for Solaris).
+ckuus5.c, 31 Dec 2001.
+
+From Jeff: new Help text for SET TERM FONT (K95 GUI). ckuus2.c, 1 Jan 2002.
+
+More work on help text for file management commands -- e.g. we can't lump
+the L-commands together with the unprefixed ones; they need separate entries.
+Also: added missing HELP REMOTE PWD, improved the default case (in which
+help text had been omitted for a valid command). ckuus2.c, 1 Jan 2002.
+
+It seems VMS C-Kermit was pretty much ignoring the -B (force background) and
+-z (force foreground) command-line options. Fixed in congm(): ckvtio.c,
+1 Jan 2002.
+
+Tested the SET LOCUS business with VMS C-Kermit, which does not have a
+built-in FTP client. Of course in this case there is no automatic locus
+switching, but SET LOCUS REMOTE works nicely on IKSD connections.
+
+From Jeff:
+ . #ifdef adjustments for LOCUS changes.
+ . SSH KEY CREATE /TYPE:SRP.
+ . Fix \v(serial) to not be 8N2 by default if speed is 0.
+ . Don't let doexit() run if sysinit() hasn't been called first.
+ckuus[r247x].c, 2 Jan 2002.
+
+Made SET BACKGROUND { ON, OFF } do exactly the same as -B and -z options.
+ckuus3.c, 2 Jan 2002.
+
+Updated user-visible copyright dates to 2002 (but still need to do all the
+source-module comments). ckcmai.c, ckuus[25].c, 2 Jan 2002.
+
+Rearranged #include <sys/time.h> in ckucns.c that was done for IRIX 5.3,
+to avoid conflicts in SV/68 R3v6. 3 Jan 2002.
+
+From Dave Sneddon: Code changes in VMS sysinit() and congm() to work around
+problems in batch, SPAWN'd, etc, and change CTTNAM from TT: to SYS$INPUT:.
+ckcdeb.h, ckvtio.c, 3 Jan 2002.
+
+From Jeff:
+ . Fixed typo in definition of CTTNAM for VMS. ckcdeb.h
+ . Moved macro definitions for SSHBUILTIN from ckuus3.c to ckuusr.h
+ so they can be referenced in ckuus7.c
+ . Added SSH functionality to SET HOST:
+ SET HOST /NET:SSH /CONNECT hostname [port] /switches
+ . Fixed SET NET TYPE so it won't reject SSH if SSH is installed.
+ . Changes to allow IKSD to continue functioning. Somehow this minor change
+ to ckcmai.c got lost in one of the back and forth exchanges.
+ . HELP TEXT for UCS2 kverb
+ . Fix a problem in K95 where multiple threads could be attempting to
+ send a telnet negotiation simultaneously.
+ckcmai.c ckcdeb.h ckuus2.c ckuus3.c ckuusr.c ckuusr.h ckuus7.c ckctel.c
+ck_crp.c ckuat2.h ckuath.c, 4 Jan 2002.
+
+From Jeff:
+
+ Peter Runestig complaining that the Telnet Forward X code was corrupting
+ data. This resulted in a very thorough examination of the telnet module
+ code and a discovery of some rather significant problems. The root of the
+ problems is the lack of thread safety. To correct this problem the
+ following was done.
+
+ All code (regardless of module) which outputs telnet commands is placed
+ into a mutex region to ensure that competing output threads do not result
+ in interleaving their output. This could happen for instance when the
+ forward-x thread is forwarding data and the user changes the window size
+ or sends an AYT or BREAK. Next the buffer used for input and output
+ processing were identical. This means that output data could be treated
+ as input or vice versa. Ugh....
+
+ I also spent some more time cleaning up setlin(). Mostly reorganizing the
+ code into single if (...) blocks so that breaking it up will be easier.
+
+ckctel.c ckuus7.c, 4 Jan 2002.
+
+Updated internal copyright notices. All modules, 5 Jan 2002.
+
+From Jeff:
+ More of same, plus new makefile target and changes from Spike Gronim
+ for freebsd44+srp+openssl.
+ckcdeb.h ckcnet.c ckctel.c ckuus7.c ck_ssl.c makefile, 5 Jan 2002.
+
+Some minor updates and fixes to SSH and SET SSH help text.
+ckuus2.c, 6 Jan 2002.
+
+Added SET RGB-COLORS for GUI. ckuusr.[ch], ckuus3.c, 6 Jan 2002.
+
+From Jeff: More Telnet changes, Debug semaphores for K95, etc: ckcdeb.h,
+ckuusr.h, ckuus[r35x].c, ckctel.[ch], ckuath.c, 7 Jan 2002.
+
+Added --xpos:n --ypos:n, SET GUI WINDOW POSITION x y, and changed SET
+RGB-COLORS to SET GUI RGBCOLOR. Action needs to be filled in (in setguiwin()
+in ckuus3.c), and gui_xpos and gui_ypos need to be defined in cko???.c.
+ckuusr.h, ckuus[r3y].c, 7 Jan 2002.
+
+Added --fontname:name --fontsize:name (and facename as synonym for fontname).
+ckuusr.h, ckuus[7y].c, 7 Jan 2002.
+
+Moved GUI (not OS/2) SET TERM FONT code in ckuus7.c to its own routine,
+setguifont(), in ckuus3.c, and made GUI SET TERM FONT call this routine,
+and also made SET GUI FONT call the same routine. ckuus[37].c, 7 Jan 2002.
+
+Added --termtype:, --height:, --width:, --user:. Also added symbols for
+--telnet:, --ssh:, --ftp:, --[remote-]charset, and --password:, but didn't
+fill them in. --password: is probably not a good idea (but we allow it for
+FTP); the others involve a lot of code-shuffling and reconciliation, which
+I'll try to do when I get a chance (especially the connection ones, which
+can be done as part of the setlin() restructuring). ckuusr.h, ckuusy.c,
+8 Jan 2002.
+
+Also I tried commenting out the #ifndef KUI..#endif's around SET TERMINAL
+CHARACTER-SET (easier said than done because a crucial #endif was mislabeled).
+Let's see if it compiles & works... ckuus7.c, 8 Jan 2002
+
+Added FTP [ OPEN ] /NOINIT, meaning don't send REST, STRU, and MODE commands
+upon making an FTP connection. This allows connection to servers that close
+the connection (or worse) when given these commands (e.g. Linux 2.4 TUX 2.0
+FTP server). ckcftp.c, 8 Jan 2002.
+
+Looked at adding caller ID support for the ANSWER command:
+
+ . SET ANSWER CALLER-ID { ON, OFF }
+ . SET ANSWER RINGS <number>
+ . \v(callid_xxx) xxx = { date, time, name, nmbr, mesg }
+ . CKD_CID modem capability
+ . Set CKD_CID for modems that have it.
+ . A quick survey shows:
+ - USR V.90: No (but Jeff says some USRs have it).
+ - V.250: No
+ - Lucent Venus: No
+ - USR: #CID=1 (the ones that have it -- X2?)
+ - Diamond Supra: #CID=1
+ - Rockwell 56K: #CID=1
+ - PCTEL: #CID=1
+ - Zoltrix: +VCID=1
+ - Conexant: +VCID=1
+ . Since there are different commands to enable caller ID reporting,
+ we need a new field in struct MDMINF.
+ . SHOW MODEM and SHOW DIAL would need updating.
+ . etc etc...
+
+This is all way too much for now so I just did the setting of the \v(callid_*)
+variables. These are reset at the beginning of an ANSWER command, and then
+set by the ANSWER command if they come in; thus they persist from the time
+they are collected until another ANSWER command is given. To take advantage
+of autoanswer, the user has to enable it in the modem (all the modems I found
+that support it have it disabled by default), and also has to set the number
+of rings to at least 2. This can be done with (depending on the modem):
+
+ set modem command autoanswer on ATS0=2#CID=1\{13}
+ set modem command autoanswer on ATS0=2+VCID=1\{13}
+
+and undone with:
+
+ set modem command autoanswer on ATS0=1#CID=0\{13}
+ set modem command autoanswer on ATS0=1+VCID=0\{13}
+
+The variables can be accessed only after the call is answered. Therefore the
+only way to refuse a call is to answer it, inspect the variables, and then
+hang it up if desired. Future Kermit releases can do this more nicely (as
+sketched out above.) Also while I was in the dialing code, I added result
+code VCON (= VOICE), used by several of the newer modems. These changes are
+untested. The SET ANSWER command is written but commented out. ckuusr.h,
+ckcker.h, ckuus[r3].c, ckudia.c, 8 Jan 2002.
+
+From Jeff: fixes to --termtype:, --height:, --width:, --user:, and filling in
+of --rcharset:, which required extracting code from settrm() into a separate
+parse-method-independent remote character-set setting routine. ckuus[7y].c,
+8 Jan 2002.
+
+From Jeff: More work on TERMINAL CHARACTER-SET code reorganization, and
+reinstatement of SET TERMINAL CHARACTER-SET in K95G. Also, fix char/CHAR
+warnings in Telnet module. ckuus7.c, ckctel.c, 9 Jan 2002.
+
+Made SET TERM CHARACTER-SET visible for all builds, including K95G, and filled
+in HELP text for it. ckuus[27].c, 9 Jan 2002.
+
+Added help text for new extended options. ckuusy.c, 9 Jan 2002.
+
+Commented out the return(-2) statement at the end of xgnbyte() to make the
+"Statement not reached" errors go away, after checking to make sure that there
+was no path that could fall through to the end. I'm 99.99% sure there isn't,
+but that doesn't mean that some compilers might not still complain. ckcfns.c,
+9 Jan 2002.
+
+From Jeff: fix typo in the K95 extended-option help text; add more
+semaphores to network i/o. ckuusy.c, ckcnet.c, 10 Jan 2002.
+
+Undid ansiisms in set{lcl,rem}charset() declarations. ckuus7.c, 10 Jan 2002.
+
+Removed a duplicated clause from the install target. makefile, 10 Jan 2002.
+
+From Jeff: more semaphores. ckcnet.c, 11 Jan 2002.
+
+Moved references to tmpusrid and tmpstring out of NOSPL #ifdefs -- they can
+be used with NOSPL. setlin(): ckuus7.c, 13 Jan 2002.
+
+Made a dummy dologend() routine outside of #ifndef NOICP, so we don't have
+to enclose every reference to dologend in #ifdefs. (I had added a bunch of
+calls to dologend() throughout the code to handle automatic LOCUS switching.)
+ckuus3.c, 13 Jan 2002.
+
+Moved "extern int nettype" outside of NOICP #ifdefs in ckuus4.c for NOICP
+builds. 13 Jan 2002.
+
+Moved a misplaced #ifdef in the VERSION command. ckuusr.c, 13 Jan 2002.
+
+Did 81 different feature-selection builds on Linux (RH 7.0), all OK after the
+changes listed above for today. 13 Jan 2002.
+
+Added prototypes for set{rem,lcl}charset(). ckcxla.h, 13 Jan 2002.
+
+Added ckcxla.h to dependencies for ckuusy.c. ckvker.com, 13 Jan 2002.
+
+Made a correction to the HELP SET LOCUS text and supplied a missing comma
+for HELP REMOTE. ckuus2.c, 13 Jan 2002.
+
+Built OK on HP-UX 11.11 (K&R and ANSI), Solaris 8 (cc), Solaris 2.5.1 (gcc),
+SunOS 4.1.3 (cc and gcc), VMS 7.1 (DEC C, net and nonet), Unixware 7.1.1,
+Tru64 4.0G, HP-UX 10.20 (K&R), AIX 4.3.3, FreeBSD 2.2.8, Slackware 8.0, IRIX
+6.5.13f, IRIX 5.3 (??? Can't tell -- the computer ran out of swap space -- but
+it was OK a few days ago), VMS 5.5-2 (VAX C, UCX + nonet)... HP-UX 9.05, ...
+
+Some corrections to comments in HP targets from PeterE. makefile, 14 Jan 2002.
+
+Corrections to prototypes for set{rem,lcl}charset() (VOID, not void) from Jeff.
+ckcxla.h, 14 Jan 2002.
+
+Builds, cont'd... SINIX 5.42, Red Hat Linux 5.2 on i386, SuSE 7.0 on S/390,
+Red Hat 7.1 on IA64, QNX 4.25, HP-UX 5.21/WinTCP, ...,
+
+Dell Coleman <dell@aleph.tum.com> noticed that in AIX, the COPY command always
+says "Source and destination are the same file" when the destination file
+doesn't exist. This is because in AIX, realpath() fails with ENOENT (errno
+2). The zfnqfp() code already accounts for this, but evidently not well
+enough. So I did what I should have done long ago. zfnqfp() was originally
+accomplished with do-it-yourself code. Later I added support for realpath(),
+and partitioned the routine into mutually exclusive compile-time sections:
+#ifdef CKREALPATH realpath()... #else do-it-yourself... #endif. But if
+realpath() failed, there was no recourse to the do-it-yourself code. Today I
+replaced the #else with the #endif, so the do-it-yourself part is always
+included and is executed if the realpath() call fails. Built and tested on
+AIX 4.3.3 and Solaris 2.5.1, as well as on Linux with and without the
+realpath() code included. zfnqfp(): ckufio.c, 16 Jan 2002.
+
+Separated K95 and C-Kermit test version numbers, so C-Kermit can be RC.02
+while K95 is Beta.01. ckcmai.c, 16 Jan 2002.
+
+Inhibited 0-length writes by conol() and conoll(), since they cause big
+trouble with the AIX 4.3.3 pty driver, e.g. when you have an SSH connection
+into AIX and run C-Kermit there. ckutio.c, 16 Jan 2002.
+
+Suppressed "Switching LOCUS..." messages from FTP client when it was invoked
+from the command line. ckcfns.c, 17 Jan 2002.
+
+Dave Sneddon noticed that FOPEN /APPEND gets "?Write access denied" in VMS
+if the file exists. This is apparently because VMS zchko() does the wrong
+thing. Commenting out the call zchko() in the VMS case gets past this but
+then the appended part of the file has different attributes than the orignal
+part, e.g.:
+
+ abc <- original line (horizontal, normal)
+ d <- appended line (vertical)
+ e
+ f
+
+VMS fopen() takes an optional 4th argument: a series of RMS keyword=value
+pairs. Kermit doesn't give any. Experimentation shows that appending to
+a Stream_LF works fine. That'll be a restriction for now, until somebody
+sends in code to get the RMS attributes of the original file and feed them
+to fopen(). Also need code to fix VMS zhcko() to say whether it's OK to
+append to a file. ckuus7.c, 17 Jan 2002.
+
+Somebody suggested I could get a working Kermit for Neutrino 2+ by doing the
+QNX6 build on Neutrino itself. I verified that this can't be done -- at least
+not by me -- since Netutrino 2+ doesn't have a compiler, and we already know
+the version cross-built for it on QNX4 doesn't work. 17 Jan 2002.
+
+From Jeff: SET SSH GSSAPI KEY-EXCHANGE { ON, OFF } parsing, SHOW SSH.
+ckuus3.c, 18 Jan 2002.
+
+PeterE suggested that SET ESCAPE allow 8-bit escape characters because of the
+difficulty in entering Ctrl-\ on European keyboards and the hardship (e.g. to
+EMACS and VI users) of sacrificing another C0 control character. Like
+everything these days, this turns out to be rather a bigger deal than it would
+seem. The SET ESCAPE parser calls setcc(), which accepts control characters
+in various formats (literal, ^X notation, or numbers), and gives an error
+return if the value is not 0-31 or 127. This is changed easily enough to also
+allow numbers between 128 and 255. But who else calls setcc()? The commands
+for setting Kermit packet start and end characters. No big deal, this gives
+people a bit more flexibility in case they need it, but it won't be
+documented. setcc(): ckuus7.c, 18 Jan 2002.
+
+Since code to display the escape character is scattered all over the place,
+and some of it indexes into an array based on the character value (which would
+now dump core if the escape character was > 128), I put the code in one place,
+a new shoesc() routine in ckuusx.c (which needs to be outside #ifndef NOICP,
+since the CONNECT modules use it even in command-line only builds). Also
+discovered that this code was indexing into the nm[] array with tt_escape to
+get "enabled" or "disabled", which is no longer appropriate, so fixed this
+too. ckuusr.h, ckuus[5x].c, 18 Jan 2002.
+
+Made SHOW ESCAPE, SHOW TERM, and the various CONNECT modules call shoesc(),
+and updated HELP SET ESC. ckuus[25].c, ckucns.c, ck[cuvd9]con.c, 18 Jan 2002.
+
+After all that, it occurred to me that this is a really bad idea for K95,
+with all the confusion about Console code pages, OEM code pages, Windows
+code pages, and Unicode. But I tried "echo \161" at the K95 prompt and got
+the expected 8-bit character in both the Console version and the GUI, so
+maybe it's OK after all.
+
+Removed the automatic IKSD login code from setlin() since it complicates
+interactive anonymous login. ckuus7.c, 20 Jan 2002.
+
+An #ifdef clause from Matthew Clarke to avoid "redeclaration of free" error
+when building a curses version of C-Kermit for AIX 2.2.1 on RT PC. ckuusx.c,
+22 Jan 2002.
+
+Took care of one detail I omitted when adding the 8-bit escape character:
+not stripping the 8th bit before comparing the keyboard char with the escape
+char. ck[uv]con.c, ckucns.c, 24 Jan 2002.
+
+Started to go through Jeff's changes of the last week but he had run trim -t
+on them, which untabifies, so the diffs were huge. Retabifying Jeff's files
+only makes matters worse. So instead of comparing each old and new source
+file in EMACS windows with M-X Compare-Windows like I usually do (which can't
+be told to ignore whitespace), I had to work from the diff -c -b listings.
+In ascending order of size of diffs:
+
+ckcker.h: Add I_AM_SSHSUB definition.
+ckuusr.h: XXLINK and VN_PERSONAL, etc, definitions.
+ckuusy.c: Support for "I Am SSHSUB" invocation.
+ckuus5.c: Support for new K95 directory structure.
+ckcmai.c: Init endianness earlier (K95 TYPE was broken), "I Am SSHSUB" support.
+ckuus7.c: Security #ifdefs, SSH OPEN /PASSWORD, SSHSUB support
+ckcftp.c: <-- SAVE TIL LAST
+ckuus6.c: Add LINK command for K95 on NT.
+ckuus4.c: Support for new K95 directory structure; SSHSUB support
+ckuus3.c: Support for new K95 directory structure; some SSH changes
+ckuus2.c: Changes to SSH related help text, add HELP LINK text
+ckuusr.c: LINK command, SSH OPEN /PASSWORD: /SUBSYSTEM: switches,
+ Pattern-management fixes.
+ckctel.c, ck_ssl.c, ckuath.c, ckcnet.c:
+ Took Jeff's without looking.
+ckuusx.c, ckucns.c, ckucon.c, ckwart.c:
+ My changes from weeks ago that were never picked up.
+
+Built OK on Solaris with gcc and on SunOS with (K&R non-ANSI) cc.
+31 Jan 2002.
+
+Meanwhile, Jeff had made various changes in response to Jaya Natarajan at IBM,
+whose basic complaint was that numerous failure conditions were not being
+detected if the fullscreen file-transfer display was active. Jeff found that
+this was because big blocks of code were skipped in that case and changed the
+code not to do that, which fixed the reported problems. But later Jaya said
+that "ftp mget file1 file2" acted like "ftp mget *", so it seemed that Jeff's
+fixes broke file selection. After taking Jeff's fixes for ckcftp.c, however,
+I still could not reproduce the problem. ckcftp.c, 31 Jan 2002. <-- Later,
+it turned out the problem was with IBM's custom FTP server.
+
+Fixed updates that I missed yesterday in ckcftp.c, ckuusr.c. Moved misplaced
+#ifdef in ckuusy.c breaking nonet builds. Added #ifdefs to sysinit() for
+nonet builds in ckutio.c. Ran through build-in-many-configurations script
+in Linux, all builds OK. 1 Feb 2002.
+
+Moved shoesc() definition outside of NOXFER to fix NOXFER builds.
+ckuusx.c, 1 Feb 2002.
+
+Added MYCUSTOM definition alongside KERMRC and changed KERMCL to be the
+same as CKMAXPATH, instead of some random hardwired number. ckuusr.h,
+1 Feb 2002.
+
+Changed ckcdeb.h to define DIRSEP and ISDIRSEP(), and put #ifndef
+[IS]DIRSEP..#endif around all [IS]DIRSEP definitions in ck[udso]fio.c, so we
+can finally put away the many repeated #ifdef chains when we get around to it.
+1 Feb 2002.
+
+Make VMS zkermini() return 1 on success, 0 on failure, rather than 0 always.
+ckvfio.c, 1 Feb 2002.
+
+Added code to doinit(), just before it goes to execute the init file. If the
+init file name we are about to open is empty or fails zchki(), substitute the
+customization filename. For now this code is in #ifdef USE_CUSTOM..#endif,
+which is not defined by default. It does the trick in Unix and VMS. Also
+included code from Jeff for K95, but this needs verification and testing.
+Also used DIRSEP and ISDIRSEP() throughout doinit() instead of the long #ifdef
+chains. ckuus5.c, 1 Feb 2002.
+
+Moved shoesc() prototype from ckuusr.h to ckcker.h so modules that need it
+don't have to include ckuusr.h just for this one thing (example: ckvcon.c).
+1 Feb 2002.
+
+Defined USE_CUSTOM by default, except if NOCUSTOM is defined. ckuusr.h,
+1 Feb 2002.
+
+Fixed kermit-sshsub code to really enter server mode, and to print
+"KERMIT READY TO SERVE..." so scripts can wait for it. Also bumped the
+C-Kermit test ID to RC.03 and the K95 one to Beta.02. ckcpro.w, ckcmai.c,
+2 Feb 2002.
+
+I was thinking about adding SET COMMAND BUFFER-SIZE to let people allocate
+as big a buffer as they wanted at runtime, mainly for defining huge macros.
+Moved the SCMD_blah definitions from ckuusr.h to ckuus3.c, since they aren't
+used anywhere else. But stopped there since the rest turns out to be a rather
+big deal. ckuusr.h, ckuus3.c, 2 Feb 2002.
+
+From Jeff, 3 Feb 2002:
+ . Fix an out-of-order modem name in the SET MODEM TYPE table: ckudia.c.
+ . Use SET LOGIN USER and PASSWORD if present. ckcftp.c.
+
+Cody Gould noticed that array declarations had become case sensitive, and
+upper case didn't work. Diagnosis: misplaced case conversion in xarray().
+Fixed in ckuus5.c, 4 Feb 2002.
+
+SHOW VAR dumps core on \v(sexpression) or \v(svalue) -- failure to check for
+NULL pointer. I wonder why this didn't happen before (answer: because I was
+doing it on SunOS; now I'm doing it on Solaris). ckuus4.c, 6 Feb 2002.
+
+I've had several requests for "show var name name name...". I added this to
+doshow(), such that SHOW VAR works exactly as it did before (if you don't give
+it an arg, it lists all variables; if you give it an arg, it appends "*" to it
+and lists all matching variables) but now you can also give more than one arg
+and it works the same way with each one as it did before if you gave it a
+single item (i.e., "*" is appended, so "show var os cmd" shows all variables
+whose names begin with "os" or "cmd". You can also freely use pattern
+notation, including anchors. Hmmm, no, actually it's different in that now
+each includes an implied * before AND after, so "show var version" shows all
+variables whose name contain "version" rather than all variables whose names
+start with it. ckuus5.c, 6 Feb 2002.
+
+Cody Gould reported that WRITE FILE blah blah \fexec(anything) ... got a
+spurious "File or Log not open" error. This turns out to be a rather
+pervasive problem -- whenever you use \fexec() it calls the parser recursively
+and this can run roughshod over global variables, such as our innocent little
+x, y, and s. The fix in this case was to put x and y on the stack. The same
+thing probably needs doing in about 10,000 other places. Too bad C isn't
+Algol. ckuusr.c, 6 Feb 2002.
+
+Minor fix to SHO VAR -- the "^" anchor wasn't working (e.g. "show var ^os").
+ckuus5.c, 6 Feb 2002.
+
+Fixes from Jeff for FTP file-transfer character-set translation in K95 and
+in WIKSD, plus updated K95 SSH help text. ckcftp.c, ckcfns.c, ckuus2.c,
+7 Feb 2002.
+
+Server has its date set in the past. Client says "remote dir". Server sends
+A packet containing old date. If client has FILE COLLISION UPDATE, it
+rejects the directory listing. Changed gattr() to only reject real files
+(introduced by F packet), not X-packet material like directory listings.
+ckcfn3.c, 7 Feb 2002.
+
+Up-down arrow keys for command recall. People have been asking for it for
+years but now it's actually important because of PDAs that don't have Ctrl
+keys. Would have been trivial except that we use getchar() rather than
+coninc() for reading from the keyboard in Unix so conchk() doesn't help. In
+fact there are lots of other places where conchk() is used this way and works
+only by accident. The only reason we never noticed a problem before is that
+characters don't usually arrive from the keyboard that fast. But when an
+arrow key sends "ESC [ A" all once, the stdin buffer gets some extra stuff in
+it, which getchar() will return next time, but which coninc()/conchk() will
+never see. So I added a new cmdconchk() routine which, if the keyboard is
+being read with getchar() rather than coninc(), looks at the stdin buffer.
+Unfortunately, however, there is no API for this, nor is there any standard
+way to access the stdin buffer directly. So first I did it for Solaris. Then
+to make it portable requires a survey of the headers for every platform. I
+found four major variations:
+
+ stdin->_r:
+ {Free,Open,Net}BSD, BSDI
+ stdin->_cnt:
+ SunOS, Solaris, HP-UX 5-6, AIX, VMS, SINIX, IRIX 5.3-6.5, DGUX
+ 4.2BSD, 4.3BSD, OSF/1..Tru64, QNX4, Unixware 1.0-2.1.0
+ stdin->__cnt:
+ HP-UX 7-11, SCO: OSR5.0.6a, Unixware 2.1.3-7.x, OU8, UNIX 3.2v4.x
+ Subtract read from end pointer (_IO_file_flags defined):
+ Linux (tested on RH 5.2 thru 7.1)
+
+The Linux method is new and different to account for multibyte characters.
+All the others assume character == byte.
+
+For docs: ANSI only, 7-bit only; both application and cursor modes are
+accepted. Only up and down arrow are handled; left and right arrows cause
+a beep. ckucmd.c, 8 Feb 2002.
+
+Build-all: Discovered that changing CTTNAM from TT: to SYS$INPUT: in VMS
+(which was done on 3 Jan 2002 to work around problems starting Kermit in
+batch, spawn'd, etc) breaks Kermit on VMS 5.5/VAX (concb() fails with "lacks
+sufficient privilege"; if you enable all privs Kermit starts but then spews
+out a constant stream of BEL characters). If you put dftty back to "TT:",
+everything is fine -- I have no idea why, so I used #ifdef VMSV70 to decide,
+which is totally crude. Next I had to find where the boundary really is: VAX
+vs Alpha? VAX C vs DEC C? Or between VMS releases? Built on:
+ . VMS 6.2 Alpha (DEC C) - OK with TT:
+ . VMS 6.2 Alpha (DEC C) - OK with SYS$INPUT: <-- keep this one
+ . VMS 7.1 VAX (DEC C)
+So the final condition is #ifdef VMSV60. ckvker.com, ckvtio.c, ckuus5.c.
+
+QNX 6 needed some attention too:
+ . Whoever did the makefile target made the default port "/dev/ser1".
+ . Arrow keys...
+But I gave up on getting arrow keys to work -- it should be just like *BSD,
+but for some reason gcc complains that struct FILE has no _r member, even
+though it does (getchar uses it).
+
+Checked stdio.h on Mac OS X and it looks like the *BSDs.
+
+--- C-Kermit 8.0.201 ---
+
+Removed -g from solaris2xg+krb5+krb4+openssl+shadow makefile target -- it
+was producing a 15MB binary! makefile, 14 Feb 2002.
+
+Fixed a couple thinkos in "make install": $(DESTDIR) should not have been
+included in the tests for whether INFODIR or SRCDIR were desired. makefile,
+14 Feb 2002.
+
+(tarball refreshed 16 Feb 2002)
+
+--- C-Kermit 8.0.201 ---
+
+From Jeff: Better seeding of \frandom(): ckcmai.c, ckuus4.c, 18 Feb 2002.
+
+From Jeff: Make arrow keys work in WIKSD, but now also unconditionally
+compile arrow-key code in all versions. ckucmd.c, 18 Feb 2002.
+
+From Jeff: ckuath.c, ck_ssl.c, ckcnet.c (didn't look). 18 Feb 2002.
+
+Added ORIENTATION command, that lists the various important directories, and
+\flongpathname() and \fshortpathname(), which do path format conversions in
+Windows, and are just synonynyms for \fpathname() elsewhere. The new functions
+need building and testing in Windows. ckuusr.h, ckuus[r24].c, 18 Feb 2002.
+
+Changed PWD for Windows only to show both short and long paths (but only if
+they are different; otherwise it behaves as before). ckuusr.c, 18 Feb 2002.
+
+Changed default Windows prompt to show long pathname. ckuus5.c, 18 Feb 2002.
+
+Updated INTRO command to mention FTP, HTTP, and SSH. ckuus2.c, 18 Feb 2002.
+
+From Jeff: fixes for typos in GetLongPathName() code: ckuus[r4].c, 22 Feb 2002.
+
+From Jeff: net/auth updates: ckcnet.c, ckuath.c, 22 Feb 2002.
+
+Added -DUSE_FILE__CNT to NCR MPRAS targets, George Gilmer: makefile,
+24 Feb 2002.
+
+From Jeff: Add support for GetLongPathName() in Win95 and NT: ckcdeb.h,
+ckuus[r4].c, 24 Feb 2002.
+
+From Jeff: More fixes for FTP SIGINT, plus fix [M]PUT /MOVE. ckcftp.c,
+24 Feb 2002.
+
+Fixed an unguarded reference to inserver, gtword(): ckucmd.c, 24 Feb 2002.
+
+Adapted RETRIEVE for use with FTP connections; this one was missed when
+adapting GET, REGET, MOVE, etc. ckuus6.c, ckcftp.c, 24 Feb 2002.
+
+Added special COPYRIGHT command text for the free version of WIKSD.
+ckcmai.c, ckuusr.c, 24 Feb 2002.
+
+C-Kermit, when in CONNECT mode and given the <Esc-Char>U sequence, would
+unconditionally close the connection if it was a network connection. This
+is bad when Telnetting to a modem server. I added to code to prevent this
+in the RFC2117 TELNET COMPORT case but I'm not sure how to exend this to the
+general case (or whether it would be a good idea). ckucns.c, 24 Feb 2002.
+
+During file transfer, chktimo() calls ttgspd() for every packet, which clearly
+doesn't make sense on network connections, especially since on Telnet COMPORT
+connections it results in a network speed query for every packet. Rearranged
+the code so this happens only on true serial-port connections. ckcfn2.c,
+24 Feb 2002.
+
+From Jeff: Fix reversed ANSI/non-ANSI function declarations clauses in
+ckcftp.c, 26 Feb 2002.
+
+Changed Unix CONNECT module to call kstart() only when it has a chance of
+doing anything (i.e. a Kermit packet has been partially detected, or the
+packet start character just came in), rather than unconditionally on every
+incoming character. ckucns.c, 8 Mar 2002.
+
+FTP PUT /SERVER-RENAME:, /RENAME-TO:, /MOVE-TO: were sticky. Patch: In
+ckcftp.c, near the top of doftpput(), add the lines marked with "+":
+
+ makestr(&filefile,NULL); /* No filename list file yet. */
++ makestr(&srv_renam,NULL); /* Clear /SERVER-RENAME: */
++ makestr(&snd_rename,NULL); /* PUT /RENAME */
++ makestr(&snd_move,NULL); /* PUT /MOVE */
+ putpath[0] = NUL; /* Initialize for syncdir(). */
+
+ckcftp.c, 26 Mar 2002.
+
+\fday() and \fnday() were broken for dates prior to 17 Nov 1858. Fixed in
+fneval(): ckuus4.c, 28 Mar 2002.
+
+From Jeff:
+ . New calling convenion for demoscrn(): ckucmd.c, ckuusx.c
+ . Fix for host-initiated 80/132 col screen mode change. ckuus7.c.
+ . New \v(desktop) variable: K95 user desktop directory, ckuusr.h, ckuus4.c
+ . New \v(rfc2717_signature) var: Telnet Com Port, ckuusr.h, ckuus4.c
+ . Uncomment "not-reached" return(-2) in xgnbyte(): ckcfns.c
+ . New dates: ckcmai.c.
+ . Telnet Com Port fixes: ckutio.c
+ . SET PRINTER fixes for K95: ckuus3.c
+ . Session limit adjustments: ckuus3.c
+ . New directory layout for K95 (TAKE, ORIENT): ckuusr.c
+ . Fixes for Telnet Com Port, recycling SSH connections: ckuusr.c
+
+From me, not picked up by Jeff previously:
+ . kstart() speedup: ckucns.c.
+
+1 Apr 2002.
+
+---K95 1.1.21---
+
+From Jeff, 4 Apr 2002:
+ . More fixes for Telnet Com Port: ckuus4.c, ckudia.c, ckutio.c, ckcnet.c:
+ . network connections will check for carrier detect if SET
+ CARRIER-WATCH is ON. This could have a potential conflict if
+ the option is negotiated and the carrier is off, but the site
+ requires login.
+ . modem hangup message generated since the dial module did not
+ believe that network modems could be reset with a DTR drop.
+ . Version number adjustments: 8.0.203, 1.1.99: ckcmai.c.
+ . Security: ck_ssl.[ch], ckuath.c.
+
+---C-Kermit 8.0.203---
+
+From Jeff, 6 Apr 2002:
+ . Fix typo in HELP REMOTE HOST: ckuus2.c.
+ . More Telnet Com Port fixes: ckctel.c, ckcnet.c, ckudia.c, ckutio.c
+
+From Jeff, 9 Apr 2002:
+ . Fix autodownload problem: ckcfn[2s].c.
+
+Chiaki Ishikawa reported that in Linux (two different kinds), if you choose
+hardware parity, CONNECT, then escape back, the speed can change. I tracked
+this down to the following statement in ttvt():
+
+ tttvt.c_cflag &= ~(IGNPAR); /* Don't discard incoming bytes */
+
+Somehow execution of this statement corrupted the speed words of the termios
+struct, which are entirely separate words that are nowhere near the c_cflag
+member. Anyway, the statement is wrong; it should be:
+
+ tttvt.c_cflag |= IGNPAR; /* Don't discard incoming bytes */
+
+Fixing it cured the problem; don't ask me why. ckutio.c, 9 Apr 2002.
+
+From Jeff:
+ fixes the problem reported by robi@hastdeer.com.au. The request to
+ enter server mode was received while we were entering server mode.
+ But the server was waiting for the response to REQ_STOP sent to the
+ client. Therefore, we weren't quite in server mode yet and the
+ request to enter server mode was rejected. A check for the sstate
+ value solves the problem. ckctel.c, 10 Apr 2002.
+
+Chiaki Ishikawa (CI) discovered the real cause for the speed changing problem.
+I was setting the IGNPAR bit in the wrong flag word: it should have been
+c_iflag instead of c_oflag, silly me. Fixed in ttvt() and ttpkt(): ckutio.c.
+I also did a thorough census of all the termio[s] flags to ensure each was
+applied to the right flag word -- they were, IGNPAR in the HWPARITY case was
+the only mistake. CI also discovered that the speed words in the Linux
+termios struct are not used at all -- the speeds are encoded in an
+undocumented field of c_cflag, which explains the problem. 10 Apr 2002.
+
+Any use of \{nnn} character notation in a macro definition, loop, or other
+braced block caused an "unbalanced braces" parse error. The backslash in this
+case is not quoting the open brace; it's introducing a balanced braced
+quantity. Special-cased in getncm(): ckuus5.c, 12 Apr 2002.
+
+The semantics of "if defined \v(xxx)" were changed in 8.0 to avoid obnoxious
+error messages when xxx was not a built-in variable (see notes of 19 Nov
+2000), such that "if defined \v(xxx)" would always succeed if there were such
+a variable, even if it had no value. The behavior that is documented in the
+book (and also in ckermit70.html) and that we had in versions 6 and 7, was
+that IF DEFINED \v(xxx) would fail if \v(xxx) was defined but had an empty
+value OR if it was not defined, and would succeed only if it was defined and
+had a value. Fixed in boolexp(): ckuus6.c, 12 Apr 2002.
+
+What about \function()s? IF DEF \fblah() presently succeeds if the function
+exists; you don't even have to give arguments. I think this behavior is more
+useful than if I required valid arguments and then evaluated the function --
+you can do that anyway with 'if not eq "\fxxx(a,b)" "" ...' Of course this
+argument applies to "if def \v(xxx)" too, except that the current behavior is
+consistent with the 7.0 behavior, so there is no need for a change.
+
+Kent Martin discovered that if a macro contains a LOCAL statement for a
+variable whose name is the same as, or a unique left substring of, the macro's
+name, then undefining the local variable makes the macro disappear:
+
+ define DateDiff {
+ echo {DateDiff(\%1) executing...}
+ }
+ define Kent {
+ do DateDiff {2}
+ local date
+ assign date {}
+ do DateDiff {3} <-- This fails (A)
+ }
+ do DateDiff {1}
+ do Kent
+ do DateDiff {4} <-- So does this (B)
+
+The first part of the problem is that "assign date {}" called delmac with
+exact=0, so delmac evidently deleted first macro whose name started with
+"date" -- and since the only one was DateDiff, that's the one that was
+deleted. Fixing this (change "delmac(vnp,0)" to "delmac(vnp,1)" in dodef())
+got us past A. The second part was making the same fix to the delmac()
+call in popclvl(). ckuus[56].c, 13 Apr 2002.
+
+The INPUT command ignored the parity setting, thus SET PARITY EVEN,
+INPUT 10 "login:" didn't work. Fixed in doinput(): ckuus4.c. Also fixed a
+bogus #ifdef COMMENT section that messed up the block structure of the module
+and therefore EMACS's indenting. 18 Apr 2002.
+
+Added sco32v500net+ssl and Added sco32v505net+ssl targets, from Scott Rochford
+at Dell (not sure yet if they work). Makefile, 19 Apr 2002.
+
+From Jeff, 22 Apr 2002:
+ . Added "darkgray" color and made "dgray" an invisible synonym: ckuus3.c.
+ . Fix carrier sense on Telnet Com Port immediately after dial: ckudia.c.
+ . Change krb5_des_blah() arg list: ckutio.c.
+ . Fix ttgmdm() for Telnet Com Port: ckutio.c.
+ . Fix tthang() return code: ckutio.c.
+ . Add aix43gcc+openssl target: makefile.
+
+From Jeff, 25 Apr 2002:
+ . Fix SET GUI keyword table: ckuus[37].c.
+ . A final fix to Telnet Com Port: ckctel.c, ckcnet.c.
+
+From Jeff, 26 Apr 2002:
+ . Another final fix to Telnet Com Port: ckctel.c, ckudia.c.
+
+From Jeff, 27 Apr 2002:
+ . separate the wait mechanism for TELNET SB COMPORT synchronous messages
+ from the asynchronous TELNET SB COMPORT MODEMSTATUS messages: ckctel.[ch]
+ . fix debug messages in Certificate verify functions: ck_ssl.c, ckcftp.c.a
+
+Frank, 27 Apr 2002:
+ . Fixed VMS zgetfs() to fail when file doesn't exist: ckvfio.c.
+ . Fixed UNIX zgetfs() to check for null or empty arg: ckufio.c.
+ . Added #include <time.h> for time() call: ckcmai.c.
+ . Add casts to args in tn_wait() calls: ckctel.c.
+
+SINIX-P 5.42 (Pyramid architecture) makefile target from Igor Sobrado.
+makefile (no source-code changes), 1 May 2002.
+
+From Jeff, 5 May 2002,
+ . Fix some "unknown host" messages: ckcftp.c.
+ . Add more casts to tnc_wait() calls: ckudia.c.
+ . Improvements to SHOW SSH, SHOW GUI: ckuus3.c.
+ . Fixes to SET COMMAND { WIDTH, HEIGHT }: ckuus3.c.
+ . Updates to ck_ssl.[ch], ckctel.c, ckcnet.c.
+
+Fixed the erroneous setting of ssh_cas during switch parsing rather than
+after cmcfm() in setlin(): ckuus7.c, 5 May 2002.
+
+setlin() decomposition (2300 lines), Part One:
+
+ . Copied a big chunk from the end of setlin(), beginning with net directory
+ lookup, but only the network-specific and common parts, to a new routine,
+ cx_net(), 900 lines.
+
+ . Extracted many repetitious lines of error-message code from cx_net()
+ to a new routine, cx_fail(). Error messages are stored in slmsg, and
+ also printed but only if we were not called from a GUI dialog (and
+ QUIET wasn't set, etc etc). Any adjutments in this policy can now be
+ made in one place.
+
+ . I put a call to cx_net() in setlin() just before all the code it replaced.
+ It works for TELNET and SET HOST /TELNET.
+
+ . Built with mkwatsol-k5k4ssl; after a couple fixes it builds OK and makes
+ Kerberized connections OK.
+
+ . Copied the serial-port and common parts of the setlin() post-cmcfm()
+ code to another new routine, cx_serial(), about 275 lines. Fixed
+ messages not to come out when called from GUI dialog, etc. Inserted
+ a call to cx_serial() at the appropriate spot in setlin(). Tested
+ serial connections on watsun with "set line /dev/ttyh6", works OK.
+
+ . Removed all the code from setlin() that was copied to cx_*(). This slims
+ setlin() down to 1120 lines. Tested regular Telnet, Kerberized Telnet, and
+ serial connections again, all OK. The Unix version of the SSH command is
+ OK too.
+
+setlin() deconstruction, Part Two:
+
+Now that we have the common network and serial connection pieces moved out of
+setlin(), we still need to move out the little code snippets for each network
+type that take place between command confirmation and the common code we just
+replaced. As far as I can tell, this needs doing only for SSH. The code
+labeled "Stash everything" copied to cx_ssh() but I didn't remove the original
+code since I can't test this. I think I'm done -- maybe I'm overlooking
+something but I don't know what... First we need to test the heck out of it
+in all command-line versions (K95 and C-Kermit). Then to use this from
+the GUI, see the calling sequences for cx_serial(), cx_net(), and cx_ssh():
+
+ . For serial or TAPI connections, the GUI should call cx_serial().
+ . For SSH connections, it should call cx_ssh() and then cx_net().
+ . For all other network connections, just calls cx_net().
+
+ckuus7.c, Cinco de Mayo de 2002.
+
+New ckuus7.c from Jeff, 8 May 2002. Merge cx_ssh() into cx_net(). Also: I
+had made line[] an automatic variable, since the global line[] buffer is used
+by almost every parsing routine in C-Kermit to hold string fields between
+parsing and execution but Jeff says he found that some code somewhere depended
+on line[] containing the hostname after setlin() was finished.
+
+From Jeff, 10 May 2002:
+ . Fix SET SSH STRICT-HOST-CHECKING parse: ckuus3.c.
+ . Add prototypes for cx_net() and cx_serial(): ckuusr.h.
+ . Add ANSI versions of cx_net() and cx_serial() declarations and supply a
+ missing parameter in the cx_serial() invocation, change SSHCMD cx_net()
+ invocation to new form.
+
+From Jeff, 16 May 2002:
+ . ANSI strictness changes: ck_ssl.[ch]
+ . New DIALER command: ckuusr.[ch]
+ . Correction to how -0 turns off autodownload: ckuusy.c
+ . Prototypes for GUI menu action functions: ckuusr.h.
+ . Replace setting of GUI-action variables by function calls: ckuus[3457x].c
+ . Fix FTP -z switch parsing: ckcftp.c.
+ . Fix SET HOST testing of setlin() return code: ckuus3.c
+
+From Jeff, 18 May 2002:
+ . Allow half-size GUI fonts: ckuus[35y].c.
+
+Fixed setguifont() to parse fractional font sizes and round to nearest half
+point. ckuus3.c, 18 May 2002.
+
+For GUI, wrote front ends for getyesno(), readtext(), and readpass():
+
+ . uq_ok() prints text and gets Yes/No, OK/Cancel, or just OK response.
+ This replaces getyesno() and can also be used for alert or help boxes.
+
+ . uq_txt() prints text and gets a single text response. Replaces
+ readtext() and readpass().
+
+ . uq_mtxt() is like uq_txt() but allows multiple text fields. Replaces
+ any combination of readtext() and readpass().
+
+Obviously the #ifdef KUI portions of the uq_blah() routines need filling in.
+ckuusr.h, ckuus3.c, 18 May 2002.
+
+Converted selected getyesno() calls to uq_ok(): ckcftp.c, ckuus3.c, ckuus6.c.
+Some were not converted because it was inappropriate, e.g. DELETE /ASK; others
+because they're in Jeff's code. The most interesting conversions are in the
+DIAL command when DIAL CONFIRMATION is ON. Here there is a dialog for each
+phone number asking if it's OK (ug_ok()) and if not, asking for a replacement
+(uq_txt()); seems to work fine in C-Kermit. All the candidates for uq_mtxt()
+are in Jeff's code. 18 May 2002.
+
+From Jeff: Convert remaining getyesno/readtext/readpass calls to uq_blah()
+so they can be GUI dialogs. ckuus[37].c, ckcftp.c, ckuath.c, ck_ssl.c,
+21 May 2002.
+
+Added KCD command = CD to symbolic directory name (EXEDIR, COMMON, APPDATA,
+TMPDIR, etc etc). ckuusr.h, ckuus[r25].c, 21 May 2002.
+
+From Jeff, 28 May 2002:
+ . --title: commandline option: ckuusr.h, ckuusy.c
+ . Fix some #includes, move some declarations: ckcfns.c
+ . Change K95 version from Dev.00 to Beta.01
+ . ASK[Q] /GUI: ckuus6.c.
+ . Various GUI screen updates and #ifdefs: ckuus7.c
+ . Add missing cx_net() calls to new setlin() for file SuperLAT..: ckuus7.c
+ . Updated uq_*() routines for GUI dialogs: ckuus3.c.
+
+Added GETOK switches (/TIMEOUT for all; /POPUP and /GUI for K95G):
+ckuus6.c, 29 May 2002.
+
+Added HELP SET GUI text. ckuus2.c, 29 May 2002.
+
+From Jeff:
+ . Another K95-specific #include for ckcfns.c.
+ . More items for K95G Actions menu.
+ . Change K95G Locus switching to call setlocus() rather than set variable.
+ . Ditto for several other variables now settable from Actions menu.
+ . Fix SET HOST /NET:SSH status code so IF SUCCESS works.
+ . Fix SHOW SSH port-forwarding.
+ckcfns.c, ckuus[r367].c, ckcftp.c, ckcmai.c, 30 May 2002.
+
+Changed SET LOCUS to have a new value, ASK, corresponding to new autolocus
+value of 2, K95G only. Changed setlocus() to not do anything if the new and
+old loci are the same, otherwise to invoke a GUI dialog in K95G if autolocus
+is 2, and also to handle any text messages. Changed SHOW COMMAND to show ASK
+value for SET LOCUS. Rewrote HELP SET LOCUS. ckuusr.[ch], ckuus[23].c,
+ckcftp.c, 30 May 2002.
+
+Add a missing space to Locus popup, and fix Jeff's version of the code to
+compile in C-Kermit. ckuusr.c, 31 May 2002.
+
+From Jeff, for K95 GUI, 6 June 2002:
+ . Force some GUI popups to be in foreground: ckuus3.c.
+ . Fix SHOW TERM font display: ckuus5.c.
+ . Update K95 version numbers and date (4 June 2002): ckcmai.c.
+ . Add note about encrypted private keys vs scripts to HELP SET AUTH: ckuus2.c.
+ . Fix SET HOST for DECnet: ckuus7.c.
+
+--- K95 2.0 ---
+
+From Jeff, 7 June 2002:
+ . Fix some #ifdefs for Unix builds (locus, dial, etc): ckuus7.c
+ . Add gui_resize_scale_font() prototype: ckuus3.c
+ . Add some missing SET GUI commands: ckuus3.c
+ . Update version numbers: ckcmai.c
+
+--- K95 2.0.1 ---
+
+From Jeff, 11 June 2002:
+ . Conditionalize Locus-switching popup text for GUI/Console: ckuusr.c.
+ . Fix the SRP_installed_as_server() function. The new API returns TRUE even
+ if the SRP config and password files cannot be found. Went back to the old
+ API. This bug affects C-Kermit 8 when built with SRP as well as 1.1.21
+ through 2.0.1. Since iksdnt.exe has not been shipped yet I fixed it and
+ uploaded a new non-beta build of it. ckuath.c.
+
+From Jeff, 12 June 2002:
+ . Fix SSH AGENT ADD: ckuusr.c.
+ . Fix --facename: option to not fail if name unknown: ckuusy.c.
+ . Fixes for OpenSSL 0.9.7 and OpenBSD 3.1: ck_ssl.c.
+ . Fix SET AUTH TLS VERIFY NO to prevent a dialog but still a warning if
+ SET AUTH TLS VERBOSE ON is set: ck_ssl.c.
+ . Fix FTP code to verify the hostname as specified by the user and not
+ the hostname discovered by the reverse DNS lookup. For example,
+ FTP OPEN kermit.columbia.edu
+ should produce a dialog because that name is not in the certificate
+ even though ftp.kermit.columbia.edu (the reverse DNS name) is: ckcftp.c.
+
+Add support for Solaris 9 and NetBSD 1.6. makefile, ckuver.h, ckcdeb.h,
+13 Jun 2002.
+
+Discovered that Solaris 9 wants to hide the members of struct FILE, and
+enforces this for 64-bit builds. They offer some functions like __fbufsize()
+to get the info, but not the info we need for reading escape sequences (the
+_cnt member). Let's hear it for political correctness. Created new solaris9g
+(32-bit) and solaris9g64 (64-bit) targets. Sorry, no arrow keys in 64-bit
+mode. Also no more direct access to sys_errlist[]; must use strerror().
+makefile, ckucmd.c, 13 Jun 2002.
+
+Added solaris9g+openssl+zlib+pam+shadow, which in turn required adding
+solaris2xg32+openssl+zlib+pam+shadow, needed for gcc 3.1 in which you have
+to specify 32-bit. Fails for some mysterious reason in link step
+(can't find libssl.so.0.9.6 even though it's there). makefile, 13 Jun 2002.
+
+Solaris 8 empty socket problems again -- tthang() times out, subsequent
+tcsetattr() calls do horrible things. Added a bandaid to ttclos(): don't
+call tcsetattr() any more if hangup timed out. ckutio.c, 14 June 2002.
+
+Gerry B reported the bandaid got us bit farther but Kermit still disappears.
+Added code to reassert the alarm signal handler, since it is likely that
+Solaris has become stricter about this since last time I looked. (Later
+Gerry reported back that this did the trick -- C-Kermit now exits normally
+and releases the lockfile). ttclos(): ckutio.c, 17 Jun 2002.
+
+If you use Kermit to copy a file to a destination file that already exists and
+is longer than the source file, the destination file is not truncated. I had
+mistakenly assumed that setting O_CREAT in the open() call in zcopy() would
+create a new copy of the file. Fixed by also setting O_TRUNC. ckufio.c,
+17 Jun 2002.
+
+Updated HELP INPUT and MINPUT text to explain 0 and -1 timeout values, and
+HELP DIAL to explain about entering CONNECT mode automatically. ckuus2.c,
+17 Jun 2002.
+
+Got rid of client-side "Press the X or E key to cancel" message when giving
+a REMOTE command if QUIET is set or if XFER DISPLAY is NONE. ckuus7.c,
+17 Jun 2002.
+
+From Jeff 25 Jun 2002:
+ . Add SUN terminal type: ckuusr.h, ckuus[57].c.
+ . Add GUI file transfer display: ckcker.h, ckuus[47x].c.
+ . Changes to allow C-Kermit to build with OpenSSL 0.9.7. Current
+ C-Kermit code is designed to compile with 0.9.6 and earlier. To
+ compile with 0.9.7 you must specify -DOPENSSL_097. This avoids
+ missing symbols in the DES library. The functions in OpenSSL were
+ renamed in 0.9.7 to avoid link time conflicts with Kerberos 4.
+ ckufio.c ck_crp.c ckuath.c ck_ssl.h ck_ssl.c, makefile.
+
+From Jeff 26 Jun 2002:
+ . apparently the SSL Passphrase Callback function was not converted
+ from readpass() to uq_txt()
+ . FTP Authentication failure errors were not being reported to the
+ user. So a failure would appear to be a successful completion
+ unless FTP DEBUG was ON. Now the message is reported unless
+ the QUIET flag is set.
+ck_ssl.c, ckcftp.c.
+
+SET TRANSFER MODE MANUAL didn't work for FTP; fixed in putfile() and getfile():
+ckcftp.c, 1 Jul 2002.
+
+Changed debug log for FTP to log "FTP SENT" and "FTP RECD" for protocol
+messages, just like we do for Telnet, to make it easy to grep them out of
+the log. ckcftp.c, 1 Jul 2002.
+
+In FTP MGET /UPDATE, equal times spuriously caused download. doftpget() was
+misinterpreting chkmodtime()'s return code. ckcftp.c, 3 Jul 2002.
+
+In FTP MGET /RECOVER, recovery is skipped if the local file is newer than
+the remote. This would seem to make sense, but when a download is
+interrupted, the partial file never gets the date of the remote file, so
+the partial file is always newer, and recovery never works. Fixed in
+recvrequest() by commenting out the date check. ckcftp.c, 3 Jul 2002.
+
+A better way to fix the previous problem is to always set the file date from
+the server and then only allow /RECOVER to work when the dates are equal.
+But that's not possible because MDTM is not implemented universally, and it
+conflicts with how Kermit currently works, namely that FTP DATES are OFF by
+default. Also, checking dates prevents [M]GET /RECOVER from working with
+files that were incompletely downloaded by some other FTP client.
+
+In FTP MGET /RECOVER <wildcard> <wildcard> ..., the first file in each group
+is always downloaded. Diagnosis: Kermit sends "TYPE A" prior to NLST (as it
+must). Then when it sends its first SIZE command, it's still in ASCII mode,
+so the server sends the "ASCII size" rather than the binary size, which does
+not agree with the size of the local file (which was downloaded in binary
+mode), so recovery is always attempted even when the files are identical. The
+TYPE A command is sent by initconn(). After the remote_files() call, we have
+to change the type back to the prevailing type before sending the first SIZE
+command. Fixed in cmdlinget() and doftpget(): ckcftp.c, 3 Jul 2002.
+
+In FTP MGET /EXCEPT:<pattern> used with SET XFER DISPLAY brief, files that
+are skipped just say ERROR instead of saying why they were skipped. Fixed
+in doftpget(): ckcftp.c, 3 Jul 2002.
+
+Added EXIT to top-level HELP text. ckuus2.c, 13 Jul 2002.
+
+Strip braces in REINPUT n {string}. ckuusr.c, 13 Jul 2002.
+
+Added /QUIET switch to ASK-class commands. This means not to print any error
+messages when an ASK-class command times out waiting for a response. Made
+sure that when a timeout occurs, the command fails. Also made sure the
+c-Kermit prompt doesn't write over the ASK prompt if ASK times out. Also
+fixed ASK, when it times out, not to return -9, which it did in one case,
+which causes a command-stack dump. ckuus[267].c, ckucmd.c, 13 Jul 2002.
+
+Fixed SET FILE INCOMPLETE help text, which said that both KEEP and AUTO were
+the default. ckuus2.c, 13 Jul 2002.
+
+If you SET FTP DEB ON and then turn it OFF, the MGET temp file is still kept.
+Fixed by getting rid of ftp_knf variable and using ftp_deb to control whether
+temp file is deleted (ftp_knf was being set from ftp_deb anyway, but then
+wasn't being reset by SET FTP DEB OFF). ckcftp.c, 13 Jul 2002.
+
+If an FTP transfer was in progress but the FTP connection drops and automatic
+locus switching is enabled, the locus does not change; thus (for example) a
+subsequent DELETE command makes Kermit send a REMOTE DELETE packet on stdout.
+Fixed in lostpeer(): ckcftp.c, 13 Jul 2002.
+
+For docs: FTP CD with no arg might not be accepted by the server; e.g. the
+Kermit FTP server says "501 Invalid number of arguments".
+
+The FTP module never handled SET INCOMPLETE. Fixed in doftprecv2(). ckcftp.c,
+13 Jul 2002.
+
+When FTP DATES is ON, we set an incoming file's date only if the file was
+received successfully. Changed the code to set the file's date even if it was
+received only partially (assuming we can get the date from server). ckcftp.c,
+13 Jul 2002.
+
+Suppose we were doing FTP MGET /UPDATE from a server directory of 100,000
+files. Kermit would send a SIZE command for every file unconditionally. On
+some connections, e.g. to the Red Hat Rawhide server, each one could take up
+to 30 seconds. That would be 3 million seconds = 34 days. Don't send a SIZE
+command during the selection phase unless a /SMALLER or /LARGER selector was
+given. Once the file is selected, send a SIZE command only if one hadn't been
+sent for that file already. ckcftp.c, 13 Jul 2002.
+
+Made [M]GET and [M]PUT /UPDATE switch imply FTP DATES ON, since they didn't
+work unless it was. ckcftp.c, 13 Jul 2002.
+
+Added FTP [M]GET /DATES-DIFFER, which is like /UPDATE except it selects files
+that are newer or older, rather than only newer. This allows updates from
+sources where files might be rolled back to earlier versions. It's a bit
+dangerous if you use it without knowing what it's for, since it allows older
+files to overwrite newer ones. (Code is also in place for [M]PUT
+/DATES-DIFFER, and it works, but I commented it out because it's either
+useless or dangerous since when uploading, you can't set the the file dates
+when they are arrive on the server.) ckcftp.c, 13 Jul 2002.
+
+Changed chkmodtime() to remember if MDTM fails on a particular connection
+because it's an unknown command (500, 502, or 202), and if so, not to ask
+again. ckcftp.c, 13 Jul 2002.
+
+With this last change, I think it's safe to change the default for FTP DATES
+from OFF to ON. ckcftp.c, 13 Jul 2002.
+
+Increased max number of /EXCEPT: patterns from 8 to 64 for file transfer (not
+necessarily for other things). This is now a compile-time symbol NSNDEXCEPT.
+ckcker.h, ckcmai.c, ckclib.c, ckcfns.c, ckcftp.c, ckuus[rx].c. 13 Jul 2002.
+
+Fixed FTP MGET to not send SIZE command when there is a name collision and
+FILE COLLISION is DISCARD, even if /SMALLER or /LARGER were also specified.
+ckcftp.c, 15 Jul 2002.
+
+MGET fails if no files were transferred, even if the reason is that no files
+met the selection critieria: /COLLISION:DISCARD, /UPDATE, /SMALLER, etc.
+Changed MGET to succeed in that case. domget(): ckcftp.c, 16 Jul 2002.
+
+Big problems with canceling MGET; Ctrl-C cancels the current file, but we
+don't break out of the file loop, we just go on to the next file. Worse, if
+we're executing a command file that has a series of MGETs, Ctrl-C doesn't
+break us out of the command file. Fixed by making failftprecv() and
+failftprecv2() "chain" to the main SIGINT handler, trap(). This is fine in
+Unix, but I'd be really surprised if it works in K95 so I put it in #ifndef
+OS2. Ditto for MPUT: Added the same treatment to failftpsend() and
+failftpsend2(). Ditto for cmdcancel(). To adapt to K95, search for "TEST ME
+IN K95" (5 places). ckcftp.c, 16 Jul 2002.
+
+Fixed previous fix to account for the fact that failftpblah() can be called
+not only upon Ctrl-C, but also if transfer interrupted with X or Z.
+ckcftp.c, 16 Jul 2002.
+
+Yesterday's fixes revealed another problem: Interrupt MGET with Ctrl-C, start
+another MGET, and the file list is total garbage. Diagnosis: secure_getc()
+and secure_getbyte() use internal static buffer pointers. The only way they
+ever get reset is when the data connection is closed by the server, so if you
+interrupt a GET, the pointers are not reset and the next network read (e.g. of
+an NLST response) returns whatever junk was lying around in the old buffer.
+ckcftp.c, 17 Jul 2002.
+
+FTP MGET temp file is kept only if FTP DEBUG is ON. Changed FTP module to
+also keep it if the regular debug log is active. ckcftp.c, 17 Jul 2002.
+
+Fixed version test in ckermit.ini: should be 6 digits, not 5. 17 Jul 2002.
+
+Changed C-Kermit version number to 8.0.205 so scripts can test for the
+recent changes. ckcmai.c, 18 Jul 2002.
+
+---8.0.205---
+
+SET FILE COLLISION UPDATE would unset FTP DATES due to a typo in the recent
+changes. ckcftp.c, 21 Jul 2002.
+
+FTP [M]GET /DATES-DIFFER really should have been a collision option. Added
+this option (implemented for FTP only) to both SET FTP COLLISION and the
+FTP [M]GET /COLLISION: table, so this way if you have lots of [M]GETs, you
+don't have to put /DATES-DIFFER on each one. ckcker.h, ckcftp.c, 21 Jul 2002.
+
+"FTP MGET a* b* c*" would fail to get any c*'s if no b*'s existed.
+ckcftp.c, 21 Jul 2002.
+
+From Jeff, 22 Jul 2002:
+ . Beginnings of Ann Arbor Ambassador terminal emulation for K95;
+ ckuus[57].c, ckuusr.h.
+ . Bump K95 version number to 2.0.2: ckcmai.c
+
+Added -DCK_PAM -DCK_SHADOW to all Solaris targets, 2.6 and above. makefile,
+23 Jul 2002.
+
+Discovered that CK_SCRIPTS path search for TAKE files was #ifdef'd out
+except for K95. Fixed in ckuusr.c, 25 Jul 2002.
+
+From Jeff: changes to support K95 italics: ckuus[57].c, 25 Jul 2002.
+
+Fixed path search for TAKE to not search the CK_SCRIPTS path if the filespec
+contains any directory or path parts. Added a new function to check for
+this: int hasnopath(filespec) in ckucmd.c: 26 Jul 2002.
+
+Update HP-UX build instructions from PeterE: makefile, 26 Jul 2002.
+
+Commented out "const" from struct pam_message declarations because it
+causes "initialization type mismatch" warnings. ckufio.c, 26 Jul 2002.
+
+Suppose you have a network directory containing a listing for host "foo":
+
+ foo tcp/ip foo.bar.com
+
+Then in K95 you give a command "set host /network-type:ssh foo". This
+results in the directory lookup replacing the "ssh" network type with TCP/IP,
+and making a Telnet connection. Fix attempted at about line 8625 of ckuus7.c
+in cx_net(); needs testing in K95. 26 Jul 2002.
+
+FTP Password: prompt in Unix was not allowing editing. The code looked right;
+I put in some debugging and suddenly it worked. Took out the debugging and
+it still worked. Maybe I dreamed it. Anyway, I fixed the "FTP SENT" debug
+log entry to not record the password, and removed a redundant section above
+to log the same thing, but prior to any charset conversion. ckcftp.c,
+27 Jul 2002.
+
+From Jeff, 28 Jul 2002:
+ . Fix typo in initxlist(): ckcmai.c.
+ . Fix typo in Friday's set-host fix: ckuus7.c.
+ . Move parsing of --height/width command-line args after prescan(): ckuusy.c.
+
+Added invisible top-level SITE and PASSIVE commands for FTP as a convenience
+for habituated FTP client users. ckuusr.[ch], ckcftp.c, 28 Jul 2002.
+
+A while back a user asked if it was possible to MGET a bunch of files from
+an FTP server and have them all appended to each other upon arrival. The
+obvious way to do this would have been:
+
+ mget /collision:append /as-name:bigfile *.*
+
+But to make this work, I had to get rid of the "as-name must contain
+variables" check in the MGET parser. doftpget(): ckcftp.c, 28 Jul 2002.
+
+Verified that it was possible to do the same thing (GET a bunch of files
+and append them all into one result file) with Kermit protocol. It works
+fine but in this case there is no /COLLISION switch; you have to SET FILE
+COLLISION APPEND first. 30 Jul 2002.
+
+Changed COPY /APPEND to allow wild source to single destination file, e.g.
+"copy /append *.* bigfile". ckuus6.c, 30 Jul 2002.
+
+From Mark Berryman: a replacement for zchkpath(), the VMS routine that checks
+whether a file is in the current directory; the old one (that I wrote) was
+a hack that only worked sometimes. Martin Vorlaender verified Mark's code in
+the situation where mine was breaking (server running in captive account).
+ckvfio.c, 30 Jul 2002.
+
+PeterE reported a problem with SWITCH case labels that start with '#':
+The problem is that the SWITCH variable contents in this case happens to be
+a comment, e.g.:
+
+ CMD(M)[_forward # Stand: 24.07.2002<CR>]
+
+so the GOTO target is null. The solution would be for SWITCH to put the GOTO
+(_FORWARD) target in quotes. But GOTO does not strip quotes or braces from
+around its target. Fixed in ckuusr.c, 30 Jul 2002.
+
+Fixed the SWITCH macro definition to put the _FORWARD target in quotes.
+ckuus5.c, 30 Jul 2002.
+
+PeterE also reported that an empty SWITCH case label did not work. There's no
+particular reason why it should, but after a brief look, it wasn't that hard
+so I did it. It required commenting out the check for empty labels and fixing
+the comparison in dogoto(). Now it's possible to read lines from a file and
+use each line as a SWITCH variable, with patterns as case labels, including an
+empty label to match empty lines, #* labels to match comment lines, etc.
+ckuus[r6].c, 30 Jul 2002.
+
+PeterE also reported the value of \%* acquiring a trailing blank when
+referenced inside a SWITCH statment. This happens because \%* is formed using
+\fjoin() on the \&_[] array based on its dimension, and at some point the
+dimension is spuriously increased by one. As a workaround, I made \fjoin()
+ignore trailing empty \&_[] array elements and oddly enough this also fixed
+the growing dimensions problem. The many script torture tests reveal no ill
+effects, so it seems like a keeper. ckuus4.c, 30 Jul 2002.
+
+Some of Peter's sample scripts made C-Kermit 8.0.201 dump core, but no more.
+
+Fixed "delete xxx" to print an error message and fail if if xxx does not exist.
+Ditto for when xxx is a directory. ckuus6.c, 30 Jul 2002.
+
+Patches to SSL modules from Jeff based on yesterday's advisory. ck_ssl.[ch],
+31 Jul 2002.
+
+Fixed some typos affecting the filename collision action during command-line
+FTP [M]GET. ckcftp.c, 31 Jul 2002.
+
+Fixed SHOW FTP to handle FTP COLLISION DATES-DIFFER. ckcftp.c, 31 Jul 2002.
+
+A while back someone pointed out that SET CONTROL UNPREFIX ALL and SET
+PREFIXING NONE gave different results. Fixed them to use the same code.
+Also made "set prefixing none" visible. ckuus3.c, 4 Aug 2002.
+
+Added SET CD HOME <path>, to let the user specify which directory is intended
+when "CD" or "KCD" is given by itself. This is because in Windows, some
+applications set up their own HOME environment variable that isn't necessarily
+where the user wants "cd" to go, but redefining HOME can interfere with the
+application (example: Windows EMACS). SET CD HOME was done by adding a myhome
+variable, initially a NULL pointer, and then changing homepath() to use it if
+it is set. zhome() is not affected. Also the homepath() prototype had been
+missing from header files. ckcmai.c, ckuusr.h, ckuus[2345].c, 4 Aug 2002.
+
+PeterE got another core dump with his SWITCH statement. Found a place where
+an out-of-bounds array reference could occur if the switch variable was
+empty. ckuus6.c, 5 Aug 2002.
+
+PeterE noticed that if the switch variable contained a comma, spurious matches
+could occur with the label pattern. The real problem turns out to be what
+happens when the SWITCH variable doesn't match any of the case labels and
+there is no DEFAULT label. Fixed by having dogoto() in the SWITCH (_FORWARD)
+case pop the command stack before returning failure, i.e. by moving the
+"if (stopflg) return(0);" statement down a few lines. ckuus6.c, 5 Aug 2002.
+
+PeterE noticed that a SWITCH case label of :* did not match an empty SWITCH
+variable. Fixed in doswitch(): ckuus6.c, 6 Aug 2002.
+
+In testing the previous fix, I found it only worked sometimes. Inspection
+of the debug log showed that a statement like:
+
+ if (y == -3) s = "{}";
+
+was assigning "{" rather than "{}" to s. Replacing the string constant by a
+buffer containing the same string fixed it. The reason (guessed correctly by
+PeterE) was the following sequence:
+
+ y = cmfld("Variable name","",&s,xxstring);
+ if (y == -3) s = "{}";
+ len = ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
+
+brstrip() (by design and as documented) affects the string in place. But in
+this case the string is a constant, not data in a buffer, so all further uses
+of "{}" get the wrong string (at least in optimized builds). The only real
+cure is to change brstrip() to make a copy of its argument if it has to do
+anything to it. This will slow down some scripts, but it's too risky to
+leave it as it was. ckclib.c, 6 Aug 2002.
+
+The previous change required an audit of the C-Kermit code to make sure that
+no references to brstrip() depended the result buffer being persistent, or the
+result pointer indicating a position in the source buffer. Oops, it turns out
+that thousands of places rely on brstrip() working in place. Therefore the
+change had to be undone. There's no good way to write a dummy-proof brstrip();
+programmers either have be sure they're not calling it with a pointer to a
+string constant, or else they have to copy the result back to the right place
+each time. Better to leave it as it was and audit the code to fix any calls
+that refer to string constants (turns out there were only two). Restored the
+original fix to doswitch() (replacing the string constant by a buffer holding
+the same string), plus minor fixes to ckcftp.c, ckuus[r36].c, 6 Aug 2002.
+
+We need file dialogs in several situations in the K95 GUI. I added a "user
+query" routine for this, uq_file(), in ckuus3.c, filling it in only for Unix.
+Then I added code to call it from rcvfil() when (a) it's an autodownload, and
+(b) SET TERM AUTODOWNLOAD is ASK (I just added this option; it needs to be set
+to see it in action -- maybe it should be the default for KUI, in which case
+initialize "int autodl = ?" to TAD_ASK in ckcmai.c). Works fine, except of
+course it interferes with the file-transfer display, but that won't be a
+problem in K95G. ckuusr.h, ckuus[37].c, ckcfns.c, ckucns.c, 6 Aug 2002.
+
+Another place we need a file dialog is when Kermit is a URL interpreter. The
+problem is: how can we let the user decide whether Kermit should ask? There
+really isn't any way. Either it always asks or it never does. In this case I
+think it makes sense to always ask if it's KUI, otherwise never. I added the
+code for ftp: URLs to to doftprecv2(), which I tested successfully in Unix
+before putting it into #ifdef KUI..#endif. Also added code for http[s] to
+ckuusy.c in #ifdef KUI..#endif, not tested.
+
+Still need this added for K95G Actions->Capture. The clearest example is the
+FTP one. Just search for KUI in the FTP module.
+
+Some minor adjustments to yesterday's work, mainly just comments, plus
+generate the full pathname for the default file. ckuus3.c, ckcftp.c,
+7 Aug 2002.
+
+Note: for some reason cmofi() is not supplying the default value if user
+enters an empty name... (but that won't affect the Windows version).
+
+Added /USER: and /PASSWORD: switches to SET TCP { HTTP-PROXY, SOCKS-SERVER }.
+ckuus3.c, 7 Aug 2002.
+
+New 'uninstall' target from PeterE, works by having the 'install' target
+write an UNINSTALL shell script. makefile, 8 Aug 2002.
+
+Added some debugging statements to the VMS communications i/o module to try
+to track down a problem that occurs when the controlling terminal is a LAT
+device. ckvtio.c, 10 Aug 2002.
+
+Fixed the non-K95 uq_file() to respect the given default name, but still show
+the fully qualified absolute pathname for the default in the dialog. The
+reason to not use the fully qualifed name as the default in the cmxxx() calls
+is that this can easily result in a whole directory tree being created due to
+directory aliases, symlinks, etc. So when you get a file by referring to its
+URL (e.g. ftp://kermit.columbia.edu/kermit/READ.ME), uq_file() converts the
+READ.ME part to (e.g.) /home/fdc/tmp/READ.ME but gives just "READ.ME" as the
+default when parsing the name. This way the user knows where it will go and
+gets an opportunity to change it, and if the default is accepted, it goes into
+the current directory. uq_file(): ckuus3.c, 10 Aug 2002.
+
+Found the spot for calling uq_file() for kermit:// URL downloads. Added
+prefatory text to filename prompts for Kermit and FTP downloads. ckcfns.c,
+ckcftp.c, 10 Aug 2002.
+
+Now with kermit:// or ftp:// URL downloads there's no way to disable the
+prompting. I could easily make SET TERMINAL AUTODOWNLOAD ASK cover these
+cases too (even though "terminal" has nothing to do with FTP or URL
+downloads). OK, I did this, but now prompting is disabled by default.
+ckcftp.c, ckcfns.c. 10 Aug 2002.
+
+Enabled file prompting (adl_ask) by default in K95G, disabled it by default
+everywhere else. So now FTP and Kermit URL downloads as well as terminal-mode
+Kermit (but not Zmodem) downloads are prompted for if TERMINAL AUTODOWNLOAD is
+ASK, which is it by default only in K95G. But this will happen only if
+uq_file() is filled in for K95G; otherwise everything should work as before.
+ckcmai.c, 10 Aug 2002.
+
+Notes:
+ . Need a better command to control this.
+ . FTP URL downloads are almost instantaneous, whereas Kermit URL downloads
+ take a really long time to set up (logging in takes at least 10 seconds).
+
+From Jeff, 13 Aug 2002:
+ . Increase K95 version to 2.1.0: ckcmai.c.
+ . SET TCP { HTTP-PROXY, SOCKS-SERVER } /USER: /PASSWORD: actions: ckuus3.c.
+
+From PeterE: a new install target that's only about half as a big as the
+previous one, yet still generates an UNINSTALL script. makefile, 13 Aug 2002.
+
+Vace wanted to be able to give the FTP client an offset for the server time,
+in case the server's time (or timezone) is set incorrectly. I added this by
+building on all the date/time parsing/arithmetic code -- notably delta times
+-- that was done for C-Kermit 8.0. The new command is SET FTP
+SERVER-TIME-OFFSET delta-time; shows up in SHOW FTP and HELP SET FTP.
+ckcftp.c, 13 Aug 2002.
+
+Fixed HELP ASK and HELP GETOK text. ckuus2.c, 14 Aug 2002.
+
+Fixed GETOK to accept /GUI switch even in K95.EXE and C-Kermit, just like ASK
+does (in which case, it just ignores it). ckuus6.c, 14 Aug 2002.
+
+SET XFER CHAR TRANSPARENT no longer disables character-set translation because
+file-scanning turns it back on. The "new way" to disable character-set
+translation is SET XFER TRANSLATION OFF. This needlessly confuses users who
+expect the old way to still work. So I fixed SET XFER CHAR TRANSPARENT to set
+XFER TRANSLATION OFF, and SET XFER CHAR anything-else to set it back ON.
+ckuus3.c, 15 Aug 2002.
+
+Fixed SET TERM AUTODOWNLOAD { ON, OFF } to turn off the ASK flag (adl_ask).
+ckuus7.c, 16 Aug 2002.
+
+Added FEAT query to FTP client from draft-ietf-ftpext-mlst-13.txt. FEAT is
+sent along with REST 0, MODE S, and STRU F if /NOINIT is not included in the
+FTP OPEN command. Parsing the FEAT result is handled by turning the "auth"
+argument to getreply() into a function code: GRF_AUTH to parse AUTH reply;
+GRF_FEAT to parse FEAT reply. For GRF_FEAT, getreply() fills in a flag array,
+sfttab[] (server feature table); sfttab[0] > 0 means server responded to the
+FEAT query, in which case individual elements are set > 0 for each supported
+feature. ckcftp.c, 18 Aug 2002.
+
+If server sends a feature list, display it if FTP DEBUG is on, then set mdtmok
+and sizeok (the flags that say whether it's OK to send MDTM and SIZE commands)
+accordingly. If user gives an [M]PUT /RECOVER command and server has
+announced it doesn't support REST, print a warning but try anyway (maybe
+change this later). Responses about other features that we use such as AUTH
+and PBSZ are ignored for now -- i.e. we try them anyway. And of course
+responses for features we don't care about (LANG, TVFS, PROT) are ignored.
+ckcftp.c, 18 Aug 2002.
+
+If the server says it supports MLST, use MLSD instead of NLST to get the file
+list. This is done in remote_files() with some simple string-twiddling. Then
+replace the relevant (but not all) SIZE commands with code to first check if
+we already got the size from the MLSD response and use that instead rather
+than asking again. Same deal for MDTM. ckcftp.c, 18 Aug 2002.
+
+Checked that this works when giving pathnames in the MGET filespec. Checked
+to make sure everything works as before with servers that don't support FEAT
+or MLSD. Checked to make sure FTP OPEN blah /NOINIT worked with servers that
+do support FEAT and MLSD. Checked that FTP CHECK works. It's all OK.
+
+Tested only with Ipswitch server; need to find and test with others.
+
+The stack of temp files needed for MGET /RECURSIVE is annoying because what
+if we run out of file descriptors... But the spec doesn't provide a way to
+request a recursive listing.
+
+Supplied a missing comma in HELP SET CD text. ckuus2.c, 19 Aug 2002.
+
+Generalized parsing of MLST/MLSD file facts and values. Got file type from
+server and had MGET skip non-regular files. ckcftp.c, 19 Aug 2002.
+
+Kirk Turner-Rustin <ktrustin@owu.edu> reported that if Unix C-Kermit has a SET
+HOST PTY connection (e.g. SSH) open, local window size changes are not
+propogated through the connection to the host. I imagine that must be because
+the SIGWINCH signal is caught by Kermit and its children don't see it; maybe
+if I pass it along to the child fork, all will be OK. Began by exporting
+"slavepid" from the pty module and changing its name to pty_fork_pid. Moved
+the SIGWINCH handler, winchh(), from ckctel.c to ckutio.c. Armed it from Unix
+sysinit() so it's always armed. This way window changes affect Unix C-Kermit
+no matter what mode it's in: tt_rows, tt_cols, cmd_rows, and cmd_cols are all
+kept in sync. Then if we're not in remote mode (i.e. we have a ttyfd), we
+call tn_snaws() and rlog_snaws() (which should be ok since they return right
+away if the appropriate kind of connection is not open) and then if
+(pty_fork_pid > -1), a SIGWINCH signal is sent to it. ckupty.c, ckctel.c,
+ckutio.c, 20 Aug 2002.
+
+All this works fine except the PTY part; in other words, the original problem
+is not fixed. The "kill(pty_fork_pid,SIGWINCH)" call executes without error
+but has no effect because the size of the PTY never changed. To make this
+work I had to add an ioctl() to change the size of the PTY before sending it
+the SIGWINCH. Compiles and works ok on Linux and Solaris; Kirk also confirmed
+it for AIX 4.3.3. ckutio.c, 20 Aug 2002.
+
+Fixed xlookup() to work for uppercase keywords. ckucmd.c, 20 Aug 2002.
+
+Fixed FTP parsefeat() and parsefacts() to use xlookup() instead of lookup(),
+since abbreviated keywords are not allowed. ckcftp.c, 20 Aug 2002.
+
+Adjusted some lines from yesterday's window-size code for platforms I hadn't
+tried yet. ckutio.c, 21 Aug 2002.
+
+EXIT from K95 when it has an FTP connection open and it pops up the
+Locus dialog. Made it not do this if it knows it's in the act of EXITing.
+ckuus[rx].c, 22 Aug 2002.
+
+In K95, FTP GET in ASCII mode results in a file with Unix line terminators
+even though the protocol is correct:
+
+ RETR smjulie.txt
+ 150 Opening ASCII mode data connection for smjulie.txt (1878 bytes).
+
+The source file is a regular Unix text file with LF at the end of each line.
+It's incredible that nobody noticed this before. It only came to light when
+somebody tried to open a downloaded text file with Notepad, which doesn't
+handle Unix-format files (Wordpad and Emacs have no problems with them). The
+problem was in doftprecv2() in the FTT_ASC section. There was no conditional
+code for Unix vs Windows. In all cases, the code discarded incoming CR's in
+ASCII mode. I put the CR-discarding code in #ifdef UNIX..#endif. ckcftp.c,
+22 Aug 2002.
+
+Removed super-verbose debugging from gtword(): ckucmd.c, 23 Aug 2002.
+
+Gregory Bond reported a problem with "if defined \$(BLAH) ..." inside of a
+SWITCH statement. It wasn't really the SWITCH that was doing it, it was the
+fact that he had enclosed the SWITCH case in braces, which made it an
+"immediate macro" (XXMACRO). The XXMACRO code parsed the macro definition
+(the part inside the braces) with cmtxt(...,xxstring), which should have been
+cmtxt(...,NULL) to defer the evaluation of the interior of the macro until it
+was executed. This is better illustrated with the following example:
+
+ { echo START, for \%i 1 3 1 { echo \%i }, echo STOP }
+
+which totally fell on its face prior to the fix. Also fixed ?-help for
+immediate macros, which was broken too. ckuusr.c, 23 Aug 2002.
+
+RFC959 says STOU does not take an argument. But every FTP server I've
+encountered but one accepts the arg and constructs the unique name from it,
+which is better than making up a totally random name for the file, which is
+what RFC959 calls for. Especially because there is no way for the client to
+find out the name chosen by the server (because RFC 959 and 1123 are
+contradictory, plus no servers follow either one of them for this anyway). So
+we try STOU with the argument first, which works with most servers, and if it
+fails, we retry it without the arg, for the benefit of the one picky server
+that is not "liberal in what it accepts" UNLESS the first STOU got a 502 code
+("not implemented") which means STOU is not accepted, period (which happens
+with ProFTPD). ckcftp.c, 25 Aug 2002.
+
+Added SET FTP ANONYMOUS-PASSWORD (plus help text and show value). ckcftp.c,
+25 Aug 2002.
+
+Made FTP command "not available" if NOFTP is defined. ckuusr.c, 25 Aug 2002.
+
+Forced client to send a TYPE command upon initial connection, since given
+the variable quality of FTP servers, it's not safe to assume the server is
+in ASCII or any other particular mode. ckcftp.c, 25 Aug 2002.
+
+SET FTP CHARACTER-SET-TRANSLATION ON is completely broken in K95, although it
+works fine in C-Kermit. Furthermore it is broken in both the GUI and Console
+versions, so it's not a Unicode vs OEM console-character-set issue.
+
+Added Concurrent PowerMAX OS target from Tom Horsley. makefile, ckuver.h,
+27 Aug 2002.
+
+Minor fixes to FTP module from Jeff. ckcftp.c, 27 Aug 2002.
+
+New Makefile target for Mac OS X 10.2, needs -DNDSYSERRLIST added, from
+William Bader. 2 Sep 2002.
+
+SET OPT DIR /DOTFILES didn't work for server listings. A few years ago when
+I front-ended zxpand() with nzxpand(), I missed a couple places where
+traverse() needed to refer to xmatchdot (nzxpand's argument flag) rather than
+global matchdot. Fixed in traverse(): ckufio.c, 2 Sep 2002.
+
+From Jeff, 4 Sep 2002:
+ . setautodl(x) -> setautodl(x,y): ckuusr.h, ckuus[7y].c
+ . Add another parameter to popup_readblah(): ckuus6.c
+ . Sort out some confusion in scanfile() where a parameter was also used as a
+ local flag. ckuusx.c.
+ . Protect restoring of saved terminal idle parameters with a flag that says
+ they were actually saved. ckuusr.c.
+ . Rework uq_text() and uq_mtxt(). ckuus3.c.
+ . Fix FTP charset translation for little-endian hardware: ckcftp.c.
+
+The latter still doesn't work in Linux:
+
+ (/home/fdc/kermit/) C-Kermit>set ftp server-character-set latin1-iso
+ (/home/fdc/kermit/) C-Kermit>set file character-set utf8
+ (/home/fdc/kermit/) C-Kermit>get latin1.txt
+
+Results in "????????: file not found". But it works fine on the Sun.
+
+Jeff's patch removed a little-endian byte-swap (LEBS) from doftpsend2(). But
+the real problem was that LEBS was not being done consistently throughout the
+module. There were similar xgnbyte()/xpnbyte() loops elsewhere in the code,
+and all of them needed to work the same way. Undoing Jeff's fix and then
+adding the LEBS to the loop in getreply() makes downloads work right, but the
+messages are still messed up (they come out in Chinese :-) Begin by moving all
+byte-swapping operations that occur in ckcftp.c itself into a new function,
+bytswap(). It's either right to do it all the time, or to do it never; this
+way we can turn it on and off in one place.
+
+xp/gnbyte() include behavior that depends on what Kermit is doing: W_SEND,
+etc. xpnbyte() tests W_KERMIT, which is a combination of W_SEND, W_RECV, etc.
+Defined a new symbol W_XFER, which is like W_KERMIT but includes W_FTP. These
+are all the "whats" in which character sets might need to be converted.
+Changed the W_KERMIT reference in xpnbyte() to W_XFER. Fixed the inderminate
+"what" state after an FTP command by moving "what = W_COMMAND;" from before
+the main parse loop to inside it (this didn't matter before the addition of
+FTP but now it does). ckcker.h, ckcftp.c, ckuus5.c, 6 Sep 2002.
+
+Finally I changed xlatec() to be consistent with all the other xgnbyte() /
+xpnbyte() usage throughout the FTP module and, poof, everything worked in
+Linux (and still works on the Sun). We still need some work in Windows (where
+the file character-set is not necessarily the console character set for
+messages) but we can tackle that next. ckcftp.c, 6 Sep 2002.
+
+Checking yesterday's work:
+
+Kermit file transfers with charset translation work fine in both directions.
+
+FTP GET with charset translation works fine on both BE and LE
+
+Fixed a typo in yesterday's changes that made FTP PUT with charset translation
+always upload 0-length files. ckcftp.c, 7 Sep 2002.
+
+FTP PUT (after the typo was fixed) with charset translation works fine on BE,
+but on LE the message comes out in Chinese and the resulting file gets ? or
+nothing for all for the accented letters:
+
+ FTP... Kermit
+ Up Dn Up Dn Term
+ BE OK OK OK OK xx
+ LE no OK OK OK xx
+
+xx = C-Kermit CONNECT mode with translation doesn't seem to do anything, not
+only in today's code, but also in the 8.0 release version: "set term char
+latin1 utf8" -- SHOW CHAR shows the right stuff, but no translation is done.
+Ditto for the 7.0 release. That can't be right...
+
+But one problem at a time -- what's wrong with LE FTP uploads? Note that
+XLATE works on the same machine, so it's obviously confusion in xgnbyte()
+about "what". Suppose we make xgnbyte() ALWAYS return bytes in BE order.
+This makes sense because xgnbyte() is almost always used to feed xpnbyte(),
+and xpnbyte() requires its bytes to come in BE order. This means that all
+code that uses xgnbyte()/xpnbyte() loops can be simplifed, which I did for
+the FTP module. ckcfns.c, ckcftp.c, 7 Sep 2002.
+
+Of course Kermit protocol uses xgnbyte() too, but only for filling
+packets, and packets never contain UCS2 and even if they did, it would have
+to be big-endian, so no changes needed for getpkt(). Now we have:
+
+ FTP... Kermit
+ Up Dn Up Dn
+ BE OK OK OK OK
+ LE OK OK OK OK
+
+Now let's look at the remaining xgnbyte() calls in the rest of the code:
+
+ckuus4.c:
+ xlate() uses it of course. I simplified the general-case loop.
+ Works OK on both Sun and Linux.
+
+ckuus6.c:
+ typegetline() uses it. I commented out the byte swap. Seems OK.
+
+Built and tested on Linux, Solaris, and SunOS. I'm sure I must have broken
+something, but the main things are better than they were. Kermit and FTP
+transfers need testing in K95, as well as the TYPE command (there's a bunch of
+special K95 code in there). C-Kermit charset translation during CONNECT is
+still broken, or else I forgot how to use it, but that's a separate issue
+since xgnbyte()/xpnbyte() are not involved. And we still need to do something
+in FTP getreply() for K95 to convert messages to the console character set for
+display, rather than the file character set (should be trivial). Also there's
+still a lot of extra debugging and commented-out junk in ckcftp.c to be
+cleaned up after more testing.
+
+During yesterday's testing, I noticed that REMOTE SET { FILE, XFER }
+CHARACTER-SET didn't work. The server accepted these commands but they didn't
+seem to do anything. In fact, they did work, but they were undone later by
+code in sfile() that restored the global settings in case they had been
+temporarily overridden by autoswitching or whatever. The solution is to
+"unsave" the saved values whenever a global setting is performed explicitly.
+Tested successfully against Sun and Linux servers. Also the server end of
+REMOTE SET needed updating for Unicode. ckcfn[s3].c, ckuus3.c, 8 Sep 2002.
+
+Cleaned commented-out cruft and extra debugging from ckcftp.c. 8 Sep 2002.
+
+Kermit autodownload with ASK file dialog: if user supplied an absolute
+pathname, it was treated like a relative one. Fixed the invocation of
+uq_file() in rcvfil() to temporarily override the RECEIVE PATHNAMES setting.
+ckcfns.c, 10 Sep 2002.
+
+Added SET TERMINAL ROLL KEYSTROKES { SEND, RESTORE-AND-SEND, IGNORE }, parse
+only. Needs implementation (search for tt_rkeys and rollkeytab in ckuus7.c).
+ckuusr.h, ckuus[27].c, 10 Sep 2002.
+
+If FILE INCOMPLETE is DISCARD and a file is being received by IKSD but IKSD
+gets a Telnet LOGOUT command, the partial file is not deleted. In fact this
+happens any time doexit() is called for any reason during file reception,
+e.g. because of SIGHUP. Added code to doclean() to check if a download
+output file was open, and if so, to delete it after closing it if keep==0.
+ckuusx.c, 10 Sep 2002.
+
+Added a brief one-line message after remote-mode file transfer saying
+what (or how many) file(s) were transferred, where they went, and whether
+the transfer was successful -- kind of an automatic WHERE command, useful
+with autodownloads so you know what happened. ckcpro.w, 11 Sep 2002.
+
+The Unix and VMS C-Kermit CONNECT modules have botched remote-charset to
+local-UTF8 translation ever since the Unicode was first added in v7.0. Fixed
+in ckucns.c, ckucon.c, ckvcon.c, 11 Sep 2002.
+
+On to pattern-matching... The matchdot business should apply only for (Unix)
+filename matching, not for general string matching. Fixed in ckmatch():
+ckclib.c, 11 Sep 2002.
+
+A bigger problem occurs in filename matching. Somehow the dirsep == fence
+business interferes with matching {foo,bar,baz} segments. For example, I have
+a filename "foo" and I want to match it with the pattern "{foo,bar}". Somehow
+the segment pattern becomes "*/foo" and doesn't match the string. Where does
+the '/' get tacked on? I don't even know how to explain this, but the short
+story was that ckmatch(), under certain circumstances, would back up to before
+the beginning of the filename string, which just happened to contain a "/"
+(and before that a ".") because of who was calling it. Obviously this is not
+how to write a pattern matching function... Ensuring that it never backs up
+beyond the beginning of a string fixed the immediate problem and does not seem
+to have broken any other matching scenarios (I have 150 of them in my test
+script). ckclib.c, 11 Sep 2002.
+
+There's still a problem though. Suppose the a client sends "dir {{.*,*}}" to
+a server. This asks for a directory listing of all files that begin with
+dot as well as all files. Still doesn't work because we don't normally show
+dot-files, but in this case it SHOULD work because ".*" was explicitly
+requested. Staring at the ckmatch() code revealed how to fix this, and I did,
+but that was only half the problem. The other half was that the list of
+files being fed to ckmatch() did not include the dotfiles in the first place.
+The cure here is to change nzxpand() to prescan the pattern to see if it
+includes a leading dot, and if so to set the "xmatchdot" flag itself, even
+if it wasn't set by the caller. ckclib.c, ckufio.c, 11 Sep 2002.
+
+Now that {foo,bar,...} patterns work better, I added a quick hack to the
+DIRECTORY command to allow multiple filespecs to be given, in which case we
+combine them into a {file1,file2,...} pattern before calling nzxpand(). Works
+fine but it's a hack because you don't get file lists upon "?" in the second
+and subsequent filespec fields, but I doubt anyone will notice. So now,
+finally, people can do "dir .* *" like they do in Unix (except with ls) to get
+a listing of all files in a directory without having to know about or use the
+/DOTFILES switch. This was NOT done for the server end of RDIR because of
+ambiguity of spaces as separators versus filename characters.) domydir():
+ckuus6.c, ckuus[r2].c, 11 Sep 2002.
+
+Added a CONTINUE command. In a script, this does whatever CONTINUE did before
+(e.g. in a FOR or WHILE loop). At the prompt, it calls popclvl(), which gives
+a more natural way to continue a script that has "shelled out" to the prompt.
+ckuusr.[ch], 11 Sep 2002.
+
+Added help text for CONTINUE. ckuus2.c, 12 Sep 2002.
+
+From Jeff, 16 Sep 2002:
+ . SET TERM ROLL KEYSTROKES for K95: ckuusr.h, ckuus7.c
+ . Remove the doexit() call from the Telnet TELOPT_LOGOUT handler: ckctel.c
+
+Fixed an FTP debug message to be consistent with Kermit ones.
+ckcftp.c, 16 Sep 2002.
+
+Added SET/SHOW TRANSFER REPORT to turn the post-transfer report off and on.
+ckuusr.h, ckuus[234].c, 16 Sep 2002.
+
+Fixed Solaris (and maybe some other SVORPOSIX builds) to find out their full
+hostname rather than just short form (e.g. watsol.cc.columbia.edu rather than
+just watsol). ckhost(): ckuusx.c, 16 Sep 2002.
+
+"cat somefile | kermit -Ts -" is supposed to send stdin in text mode, but
+K95's file transfer display reports BINARY. Looked at C-Kermit code; it seems
+fine. Looked at packet and debug logs; C-Kermit was indeed sending in text
+mode and announcing it correctly. K95 gattr() is doing the right thing:
+
+ gattr file type[AMJ]=3
+ gattr attribute A=text=0
+ gattr sets tcharset TC_TRANSP[A]
+
+Same thing happens when C-Kermit is receiving. Yet when I send an actual
+file, rather than stdin, it's received in text mode. The only difference is
+that stdin does not have a Length attribute in its A-packet, so in this case
+the receiver skips any calls to screen() that show the length or percent done.
+Aha, so maybe it's just a display problem -- scrft() is not being called to
+repaint the file type if the size was not known. Fixed in opena() by
+removing the IF clause from "if (fsize > -1L) xxscreen(SCR_FS,0,fsize,"");".
+ckcfn3.c, 18 Sep 2002.
+
+K95 user has a listfile containing some regular filenames and then some
+filenames that include paths and has all kinds of problems with MGET /LISTFILE
+(pieces of different names concatenated to each other, etc). Setting up the
+same scenario here, I don't see the same problems but I do see "Refused: Name"
+when we go to get a path/name file. This happens because (a) we had already
+got a top-level file with a certain name, (b) a file in a subdirectory has the
+same name, (c) we are stripping the path before calling zchki(), and (d)
+FTP COLLISION is set to DISCARD. How do we make FTP not strip the path?
+
+This is an interesting question... The answer depends on where the user
+wants the file to go. Normally if you tell an FTP client to "get foo/bar",
+you want the file "bar" to be downloaded to the current directory.
+
+Anyway, it turns out the FTP module uses paths locally during MGET only if
+/RECURSIVE was specified. So:
+
+ mget /listfile:blah /recursive
+
+should have made this work, but it didn't because in the /LISTFILE case,
+we have effectively turned an MGET into a series of GETs, where the code to
+check whether to strip the path didn't check the recursive flag because how
+could a GET (as opposed to an MGET) be recursive? Adding this exception to
+the if-condition got us a bit farther but now when we try to open the output
+file in doftprecv2(), zopeno() fails because the name contains a dirsep.
+We have to call zmkdir() first but that wasn't happening because some other
+flag wasn't set right in this case. Finally zmkdir was called, but with
+the wrong string. After fixing that, it works. Now we should be able
+to use /RECURSIVE to force the pathname to be used on the local end.
+ckcftp.c, 19 Sep 2002.
+
+Checked FTP filename conversion issues. FTP FILENAMES AUTO is supposed to
+mean LITERAL if "wearealike" OR server is UNIX or Windows, otherwise
+CONVERTED, but there were places where this rule was not applied consistently,
+fixed now. ckcftp.c, 21 Sep 2002.
+
+Added SET FTP DISPLAY, which is like SET TRANSFER DISPLAY but applies only to
+FTP, mainly because I tended to type it all the time. Now if you have dual
+sessions, each session can have its own transfer display style. ckcftp.c,
+ckuusr.h, ckuus[347].c, 21 Sep 2002.
+
+Back to FTP MLSD. We're supposed to match the pattern locally, not rely on
+the server to filter its list according to the client's pattern. Thus we must
+also allow an empty argument to MGET and must not send a filespec with MLSD.
+Actually this is tricky -- how is the client supposed to know whether to send
+a filespec. For example, if the user's command is "mget foo*bar", and the
+server supports MLSD, then what should the client do? The client does not
+know the wildcard syntax on the server, so for all the client knows, this
+might be a valid directory name, in which case it should be sent. On the
+other hand, the user might intend it as a wildcard, in which case it should
+NOT be sent. But the FTP client can't read the user's mind. This is another
+serious flaw in the Elz/Hethmon draft. Anyway, I got the local matching
+business working for MLSD as long as the user's MGET arg is really a pattern
+and not a directory name. To be continued... ckcftp.c, 21 Sep 2002.
+
+Added FTP { ENABLE, DISABLE } { FEAT, MLST }. If we always send FEAT, we
+usually get a complaint. If we send FEAT and MLST is negotiated, there is a
+good chance it is misimplemented or will have undesirable side effects, such
+as sending huge file lists. NOTE: /NOINIT on the FTP OPEN command also
+disables both of these. ckcftp.c, 22 Sep 2002.
+
+Fixed mkstemp() code in FTP remote_files(). mktemp() does not open the file,
+mkstemp() does open it; previously we had been opening it again and never
+closing the first instance so every MGET would create another open file
+descriptor. ckcftp.c, 22 Sep 2002.
+
+Added debug messages for temp-file creation and lines read from the temp file.
+ckcftp.c, 22 Sep 2002.
+
+Eliminated sending of some extraneous TYPE commands, but there's still room
+for improvement. ckcftp.c, 22 Sep 2002.
+
+Moved definition of build date to top of ckcmai.c. 22 Sep 2002.
+
+Added recursion to MGET with MLSD... It's all done in remote_files().
+Temp-file pointers are on a stack (max size 128). When parsing MLSD lines
+from the temp file and we see "type=dir", we create the local directory by
+calling zmkdir(), change to the remote by sending CWD, increment the depth,
+and call ourselves. When reading from a temp file, upon EOF we close and
+dispose of the temp file, return -3 if currently at top level, otherwise we
+free the tmpfile name, decrement the depth, send CDUP to the server, "cd .."
+locally, and go back and read the next line from the previous but now current
+temp file. Conceptually simple but needed hours of debugging -- what must
+be static, what must be on the stack... Seems OK now but still needs some
+heavy testing. ckcftp.c, 22 Sep 2002.
+
+Added FTP { ENABLE, DISABLE } { SIZE, MDTM } and add help text for FTP
+ENABLE and DISABLE. ckcftp.c, 23 Sep 2002.
+
+Don't allow restart if SIZE disabled. ckcftp.c, 23 Sep 2002.
+
+Make sure all implicit SIZE commands are surpressed if SIZE disabled.
+ckcftp.c, 23 Sep 2002.
+
+If an explicit FTP MODTIME command is sent when MDTM is DISABLED, and it
+succeeds, re-ENABLE MDTM. Ditto for SIZE. ckcftp.c, 23 Sep 2002.
+
+If an explicit FTP FEATURES command is sent during an FTP session, redo the
+features database from it. ckcftp.c, 23 Sep 2002.
+
+After further discussion with Robert Elz, I realized I had to expose the
+underlying MGET mechanisms to the user; the draft isn't going to change, and
+the new spec will result in undesirable effects if the client tries to "do the
+right thing" by magic in all situations; thus the user must have some new
+controls:
+
+ MGET [ /MLST, /NLST, /MATCH:xxx ] [ filespec [ filespec [ ... ] ] ]
+
+These switches let the user force the use of MLSD or NLST when there's a
+choice, and to force local use of a pattern rather than sending it to the
+server, and even to send a directory name to the server at the same time as
+specifying a pattern for local matching, and of course by default we try to do
+the right thing in all scenarios. Symbols only; not coded yet. ckuusr.h,
+23 Sep 2002.
+
+Added the three new switches to MGET, plus /MLST is an invisible synonym for
+/MLSD. If /NLST or /MLSD is given it, it forces the corresponding FTP protocol
+directive. ckcftp.c, 25 Sep 2002.
+
+Now for the tricky part: now we have two separate concepts for what to send to
+the server: a filename or wildcard to be interpreted by the server (NLST only)
+or a directory from which to get a list of all the files (NLST or MLSD),
+possibly together with a pattern to be used by the client to match filenames
+returned by the server. This required giving remote_files() an additional
+argument. Now it uses "pattern" (if any) strictly for local pattern matching
+(because now it is the /MATCH: switch argument, not the MGET filespec), and
+"arg" (the MGET filespec) is what it sends to the server, maybe (see the
+comments in the code for the actual details); either or both these can be
+null. ckcftp.c, 25 Sep 2002.
+
+Discovered that "mget foo", where foo is a directory name, never worked.
+Fixed in remote_files(): ckcftp.c, 25 Sep 2002.
+
+Going through every combination of NLST, MLSD, /MATCH:, and MGET arg and
+debugging each case until OK... Then also with the panix.com NetBSD server
+(lukemftpd 1.0) which also supports MLSD.... 11 test cases all debugged and
+tested OK. ckcftp.c, 26 Sep 2002.
+
+Added /NODOTFILES switch to FTP MGET, to control what happens with dot-files
+if the server includes their names in the list (as lukemftpd does). There's
+no point in adding a /DOTFILES switch because what could it possibly do?
+ckcftp.c, 26 Sep 2002.
+
+Changed a bunch of "skipthis++" to "continue" in doftpget(), to avoid
+error messages when skipping files that user said she wanted to skip.
+ckcftp.c, 26 Sep 2002.
+
+Added help text for the new MGET switches. ckcftp.c, 26 Sep 2002.
+
+Don't switch LOCUS when making an FTP connection until logged in.
+ckcftp.c, 26 Sep 2002.
+
+Fixed LDIR to run Kermit's built-in DIRECTORY code rather than the external
+directory program. ckuusr.c, 26 Sep 2002.
+
+Protect iswild() against NULL args. ckufio.c, 26 Sep 2002.
+
+From Jeff: SET GUI WINDOW RUN-MODE { MAXIMIZE, MINIMIZE, RESTORE },
+plus variables for GUI Window X position, GUI Window Y position, GUI
+Window X resolution, GUI Window Y resolution, GUI Window Run mode.
+ckuusr.h, ckuus[24].c, 27 Sep 2002.
+
+From Ronan Flood: updated FreeBSD 1.0 makefile entry, plus an #ifdef to protect
+sysconf() calls. makefile, ckutio.c, 28 Sep 2002.
+
+Change ftp_auth() to return(0) if an AUTH command gets a 500 response, so it
+doesn't keep sending other kinds of AUTH commands. ckcftp.c, 29 Sep 2002.
+
+Changes from Jeff to yesterday's changes. ckcftp.c, 30 Sep 2002.
+
+From Jeff: SSH command-line personality. Uses same command line as the Telnet
+personality. ckcker.h, ckcmai.c, ckuus[4y].c, 3 Oct 2002.
+
+From Jeff, 7 Oct 2002:
+ . SET PRINTER CHARACTER-SET. ckuxla.c, ckuusr.h, ckuus[25].c
+ . Promotion of K95 to Beta.01. ckcmai.c
+ . Promotion of SET GUI { MENUBAR, TOOLBAR } to visible. ckuus3.c
+
+Changed the URL parser as follows: if the username and/or password fields are
+present but empty, as in:
+
+ ftp://@ftp.xyzcorp.com/somepath
+ or: ftp://:@ftp.xyzcorp.com/somepath
+ but not: ftp://:ftp.xyzcorp.com/somepath
+
+the pointer for these items becomes a pointer to an empty string, rather than
+a NULL pointer. Then when we go to open the connection, if the username
+string pointer points to an empty string, we prompt for the username (and/or
+password). ckuusy.c 9 Oct 2002.
+
+Jason Heskett reported an interesting bug involving a core dump when an
+ON_EXIT macro is defined that executes another macro. Sometimes. He was able
+to send a short command file that always crashed. Diagnosis: ON_EXIT, when it
+is called, pokes itself out of the macro table by setting its own entry in the
+macro name list to an empty string. But this interferes with any macro
+lookups that are done while executing ON_EXIT's body and also evidently some
+code is not happy with empty macro names... To fix: replace "on_exit" with
+"on_exxx", so the replacement keyword is (a) nonempty, and (b) doesn't wreck
+the alphabetical sorting of the table. ckuusx.c, 9 Oct 2002.
+
+Added makefile targets for FreeBSD 4.6 and 5.0. Built and tested on 4.6;
+don't know about 5.0. ckuver.h, makefile, 9 Oct 2002.
+
+Added targets for AIX 5.2 and 5.3; totally untested. ckuver.h, makefile,
+9 Oct 2002.
+
+Built current source on Unixware 7.1.3 (make uw7); it's fine. 9 Oct 2002.
+
+Promoted C-Kermit to 8.0.206 Beta.01 in hopes of a simultaneous release
+with K95 2.1. ckcmai.c, 9 Oct 2002.
+
+From Jeff: Change KERMITFONT definitions to use the new (Unicode 3.1) code
+points for the terminal graphics characters (such as VT100 horizontal scan
+lines), rather than private-use codes. ckcuni.c, 10 Oct 2002.
+
+Jason Heskett also complained that REMOTE CD would print the name of the new
+directory returned by the server even if he SET QUIET ON. This is a tricky
+one. Which server replies should the QUIET settings apply to? If I give a
+REMOTE DIRECTORY command, it means I want to see the directory listing,
+period. But if I give a REMOTE CD command, I get an "unsolicited" response
+message that SET QUIET ON should suppress. Adding message suppression to
+rcv_shortreply() is close, but not totally right; for example, it also
+suppresses the response to REMOTE PWD, which is not helpful. The only right
+way to do this is to suppress for REMOTE CD only, which can be done only by
+setting a (new) global flag, rcdactive. ckuus[r57].c, ckcpro.w, 10 Oct 2002.
+
+Ditto for REMOTE LOGIN response message ("Logged in"). ckuus7.c, 11 Oct 2002.
+
+From Jeff: SET GUI WINDOW FONT { NAME, SIZE }. ckuusr.h, ckuus4.c, 11 Oct 2002.
+
+Quick preliminary 8.0.206 build-all:
+
+ OK SunOS 4.1.3
+ OK Solaris 2.5.1
+ OK Solaris 9
+ OK AIX 4.3.3
+ OK HP-UX 10.20
+ OK VMS 7.1 Alpha + TCP/IP
+ OK VMS 7.1 Alpha nonet
+ OK VMS 5.5 VAX + TCP/IP
+ OK VMS 5.5 VAX nonet
+ OK Unixware 7.1.3
+ OK FreeBSD 3.1
+ OK FreeBSD 4.6
+ OK NetBSD 1.5.2 MVME (Gerry B)
+ OK Sinix 5.42
+
+Sinix build got stuck on ckuusr.c even though we're not optimizing on Sinix
+any more. Rebooting the machine fixed it.
+
+Fixed some #ifdefs for VMS in new incomplete-file deletion code in doclean().
+ckuusx.c, 11 Oct 2002.
+
+Moved uq_blah() prototypes from ckuusr.h to ckcker.h because these routines
+are called in modules that don't (and shouldn't have to) include ckuusr.h.
+11 Oct 2002.
+
+Jeff verified secure builds on Linux and Solaris.
+
+Custom-build workout: 80 different feature-selection combinations:
+ . Fixed yesterday's change for NOSPL: ckcfns.c.
+ . Fixed conflict between NORECALL and USE_ARROWKEYS: ckucmd.c.
+ . Moved setseslog() from ckuus5.c to ckuusx.c to avoid link-time foulups.
+ . Fixed an unguarded reference to zmkdir() in ckcftp.c.
+ . Protected rmsg() by #ifndef NOXFER: ckuus7.c.
+ . Protected initxlist() by #ifndef NOXFER: ckcmai.c.
+ . Fixed unguarded references to g_url struct in xx_ftp(): ckuusy.c.
+ . Fixed unguarded references to tt_snaws() in winchh(): ckutio.c.
+
+--- 8.0.206 Beta.01 11 Oct 2002 ---
+
+From Jeff, 16 Oct 2002:
+ . Fix K95 RMDIR: ckcfn3.c.
+ . Makefile targets for Red Hat 7.2, 7.3, 8.0: ckuver.h, makefile.
+ . Added \v(log_xxx) for each kind of log for PeterE: ckuusr.h, ckuus4.c.
+ . Added SET TERM ATTRIBUTE DIM { ON, OFF }: ckuus[27].c.
+ . Change "const" to "CONST" in some PAM declarations. ckufio.c.
+
+Added SET MATCH { DOTFILE, FIFO } { ON, OFF }. A FIFO special file is a named
+pipe, used for interprocess communication. It must be opened at both ends, so
+it's silly to match them by default; opening a FIFO and attempting to read
+will block forever unless somebody is writing into the other end. Made the
+MATCH FIFO default OFF in all cases. The dotfile default is the same as
+always (OFF for UNIX, ON elsewhere); SET MATCH DOTFILE is simply a more
+untuitive and findable command than SET WILD KERMIT /MATCH-DOT-FILES. Note
+that SET MATCH DOTFILE undoes SET OPTIONS DIRECTORY /[NO]DOTFILES, and vice
+versa. ckcmai.c, ckuusr.h, ckuus[23].c, ckufio.c. 17 Oct 2002.
+
+Added client and server end of REMOTE SET MATCH { DOTFILE, FIFO } { ON, OFF }.
+The new protocol codes are 330 and 331, respectively. ckuus[367].c, ckcfns.c,
+17 Oct 2002.
+
+Adjusted the "match dot if pattern starts with dot" heuristic in nzxpand()
+to not kick in if the filespec is "./foo". This probably needs more work.
+ckufio.c, 17 Oct 2002.
+
+Fixed typo in transcribing Jeff's ckcfn3.c code from yesterday. 18 Oct 2002.
+
+Moved some help text out of #ifdef ANYSSH that had nothing to do with SSH.
+(Idea for a new EMACS feature: M-X list-ifdef-environment.)
+ckuus2.c, 18 Oct 2002.
+
+Removed "set file { permission, protection }" keywords, which led nowhere.
+ckuus7.c, 18 Oct 2002.
+
+Added -DSV68 to all SV/68 targets. Make ckgetfqhostname() just return its
+argument in SV/68; it dumps core otherwise. In case this happens anywhere
+else, add -DNOCKGETFQHOST to CFLAGS. makefile, ckcnet.c, 18 Oct 2002.
+
+For PeterE, added SET { SEND, RECEIVE } PERMISSIONS { ON, OFF } so incoming and
+outbound permission attributes can be set separately. ckuus[27].c, 18 Oct 2002.
+
+Changed SHOW ATTRIBUTES to show In and Out permissions separately.
+ckuus5.c, 18 Oct 2002.
+
+Fixed REDO to display the command it's redoing and to add it to the bottom
+of the recall buffer. ckuusr.c, 18 Oct 2002.
+
+Discovered that DATE SATURDAY dumps core... Apparently it always did; this
+case was not included in the date-time torture test script. The crash happens
+because the DATE parsing code doesn't check for a NULL date-converion
+error-message pointer. Fixed in ckuusr.c, 18 Oct 2002.
+
+The reason DATE SATURDAY got a date-conversion error was that this path thru
+the code left a result pointer unset. Fixed in cmcvtdate(): ckucmd.c,
+19 Oct 2002.
+
+DATE SUNDAY +1DAY returned incorrect results (for any day-of-week name, any
+delta time), even though DATE TODAY +1DAY worked fine. Fixed in cmcvtdate():
+ckucmd.c, 19 Oct 2002.
+
+SET TAKE ECHO ON counted each line twice when GOTO was active. Fixed in
+dogoto(): ckuus6.c, 19 Oct 2002.
+
+Jeff noticed:
+"KERMIT READY TO GET...
+ RCVD: (2 files) Last: [/amd/prost/p/kd/jaltman/.src/ckonet.c] (OK)
+the last file attempted may have been ckonet.c but it certainly was
+not the last file received" (similarly for sending). Fixed by having two
+pointers for each name; a preliminary pointer, which is set for each file at
+the beginning of the transfer (when we have all the needed info), and a final
+one that is set from the preliminary one only after the file was transferred
+successfully. This corrects not only the automatic "wheremessage" at the end
+of a remote-mode transfer, but also the WHERE and SHOW FILE command results.
+ckuusx.c, ckcfn[s3].c, ckcpro.w, 19 Oct 2002.
+
+From Jeff: Improve ORIENTATION message for K95 to say which directories are
+for which INI files. ckuusr.c, 23 Oct 2002.
+
+Removed Beta designation from herald. ckcmai.c, 23 Oct 2002.
+
+Put final dates and ID strings in Unix and VMS build procedures.
+Makefile, ckvker.com, 23 Oct 2002.
+
+Build-all... #ifdef adjustments: ckcfns.c... 83 different feature-set
+combinations build OK on Linux. 23 Oct 2002.
+
+From Jeff: SET WIN95 HORIZONTAL-SCAN-LINE-SUBSTITUTIONS. ckuusr.h, ckuus7.c,
+24 Oct 2002.
+
+Fixed Heath-19 graphic character-set table to use new Unicode 3.1 values
+if WIN95 HORIZ OFF. ckcuni.c, 24 Oct 2002.
+
+Changed tx_usub() to return Unicode 3.1 values if WIN95 HORIZ OFF.
+ckcuni.c, 24 Oct 2002. <-- No backed off on this.
+
+Some problems during build-all:
+
+ . VMS 7.1 TGV 4.2: If I make a Telnet connection with it, then try to send
+ a file (itself. wermit.exe) over the connection, the connection drops
+ after about 20%, the thermometer zooms out to 100% and SUCCESS is reported.
+ This doesn't happen with UCX.
+
+ . VMS 7.3 TGV 4.3: ckcmai.c won't compile because of a complaint about the
+ declaration of select() (which ckcmai.c doesn't use) in
+ SYS$COMMON:[SYSLIB]DECC$RTLDEF.TLB. Ditto in VMS 7.2 TGV 4.3.
+ Adding NOSELECT to CFLAGS doesn't help. I don't think the VMS version
+ even uses select(). But the TGV 4.3 builds were OK in 8.0.201, so what
+ changed? I don't see anything in ckcnet.h that would have done it.
+
+It builds OK with VMS 7.1 / TGV 4.2 but sending files on Telnet connections
+fails in a strange way: the connection drops, but the thermomoter goes to 100%
+and success is reported. I don't know why the connection is dropping (s_errno
+is 32 == "broken pipe"), but the spurious success indication is because of
+a double failure in sdata(): (1) The packet-sending loop index could go
+negative without breaking the loop when streaming; (2) if spack() fails,
+sdata() should return -2, not -1 (which means EOF). Also if any ttchk() in
+sdata() returns < 0, sdata should return -2. I fixed this code (which has
+been this way for YEARS) and now VMS C-Kermit properly fails when it gets
+the spack() error, but ttchk() in this case still doesn't think the connection
+is lost, but that must be our new problem with MultiNet. ckcfns.c,
+27 Oct 2002.
+
+The compilation failure in ckcmai.c is a clue... The problem was that I added
+#ifdef VMS / #include <time.h> / #endif to shut up complaints about the time()
+call. Evidently something in VMS <time.h> gives MultiNet a bad case of
+indigestion; removing it fixes the compilation and the result works fine. The
+transmission failures in the other case seem to be a coincidence -- something
+to do with the U of Arizona (probably some obscure VMS quota on my IDs there,
+or some kind of network connection throttling), since it doesn't happen
+anywhere else. ckcmai.c, 27 Oct 2002.
+
+Changed four occurrences of "void" to "VOID" in ckcftp.c, 27 Oct 2002.
+
+Defined NOCKFQHOSTNAME for HPUXPRE65. Might also need this for HP-UX 7
+and maybe 8. ckcnet.c, 27 Oct 2002.
+
+From Jeff: PAM_CONST definition to clear up warnings caused by different
+vendors' definitions of PAM structs. ckufio.c, 28 Oct 2002.
+
+Unixware 2.1.0 build bombs immediately with "UX:make: ERROR: line too long"
+(which line?) Not worth chopping up the makefile to fix but in a pinch it
+could be done. 2.1.3 builds OK.
+
+Did another 20-some platform builds, bringing the total to 83, plus a final
+runthrough of the build-with-84-different-feature-set-combinations script on
+Linux. Should be good to go!
+
+--- 8.0.206 24 Oct 2002 ---
+
+Finally got access to Linux on IA64 again. Builds OK but dumps core
+immediately on startup. Adding -DNOCKGETFQHOST was necessary but not
+sufficient. In this case, the very call to ckgetfqhostname() from ckhost()
+(which is in ckuusx.c) was dumping core; thus I had to move the definition of
+NOCKGETFQHOST from ckcnet.c to ckcdeb.h, add an #ifdef __ia64__ clause to it,
+and protect the call to ckgetfqhostname() with it. Obviously there has to be
+a better fix but this will have to for now. ckcnet.c, ckuusx.c, ckcdeb.h,
+31 Oct 2002.
+
+Link step fails in Mandrake 9.0 with undefined references to res_search,
+dn_expand, and crypt. Turns out the linux makefile target tests for the
+existence of libcrypt.a and libresolv.a, but in Mandrake 9.0 they exist
+only as *.so. Changed linux target to look for both. makefile, 1 Nov 2002.
+
+Vace reported that "ftp mget a b c" would get ALL files from the server's
+directory if c did not exist. Diagnosis: off-by-one error in counting MGET
+args processed. Naturally fixing this bug revealed another (this time
+cosmetic) one, which resulted in spurious error messages when hitting MGET
+args that have no match on the server. Fixed in ckcftp.c, 1 Nov 2002.
+
+Rebuilt about 60 of the most important Unix binaries to pick up the fixes
+31 Oct - 1 Nov 2002, and refreshed the FTP site with new sources, tarballs,
+ZIP files, etc. Sat Nov 2 19:11:30 2002
+
+From Martin Vorlaender and Jeff, SSL/TLS support for VMS:
+ckuusr.h, ckuath.h, ckcnet.c, ckctel.c, ckuath.c, 10 Nov 2002.
+
+Added PASV as invisible synonym for PASSIVE. ckcftp.c, 10 Nov 2002.
+
+--- 8.0.206 24 Oct 2002 #2 ---
+
+More work on SSL in VMS: Jeff + Martin Vorlaender: ck_ssl.c ckcmai.c ckcnet.c
+ckctel.c ckuath.h ckvcon.c ckvtio.c ckvker.com 10-15 Nov 2002.
+
+Discovered that ckvfio.c would not compile on VMS 6.1 with UCX 4.1 because
+<conv$routines.h> was missing, which is explicitly included. Enclosed the
+#include in #ifdef NOCONVROUTINES..#endif and rebuilt with
+@[users.fdc.src]ckvker.com "" "" "NOCONVROUTINES". 16 Nov 2002.
+
+Fixed the "ftp mget */data/*" problem with two small changes to doftpget():
+ckcftp.c, 16 Nov 2002. Placed a copy of ckcftp.patch in ~kermit/f.
+
+From Lucas Hart: Fixes for VAX C 2.x and CMU TCP/IP. "Can't guarantee that
+the revised CKVOLD will work for all combinations of more recent
+VMS/compiler/TCPIP releases, but I've tested it for compatibility on our AXP
+VMS 6.2, UCX 4.0, DECC 5.6, your AXP VMS 7.1, DEC TCPIP 5.1, DECC 6.0 as well
+as with VAX VMS 5.4, VAX C 3.2, CMU12 and VAX VMS 4.7, VAX C 2.4." ckvfio.c,
+ckvtio.c, ckuus5.c, ckvker.com, ckvold.com, 17 Nov 2002.
+
+From Jeff: More work on VMS SSL. Now it actually works, even in CONNECT mode,
+except it hangs when it gets an error alert or the remote closes the
+connection. ckcnet.c. ckvtio.c, 17 Nov 2002.
+
+NOTE: Lucas's changes should go into the 8.0.206 source code but it's too
+late since ckvtio.c (upon which he based his changes) is already full of
+SSL code.
+
+MGET in K95 in totally broken FOR SOME PEOPLE (mainly me) if the TMP (or TEMP)
+value is too long. It works fine if you set these to C:\TMP. Diagnosis: (1)
+we were malloc'ing only 16 bytes for the temp file name (I think this was my
+fault -- I was only looking at the "ckXXXXXX" part and forgetting that this
+was appended to the TMP path); (2) the Windows version of mktemp() wants you
+to use the value pointed to by the pointer it returns, rather than assuming
+the mktemp() arg will be modified in place. The code was changed to malloc a
+longer string and to use the return value from mktemp() (if any) rather than
+assuming that mktemp() modified its argument string (in K95 only -- not sure
+about Unix platforms; the man pages differ on this, but at least this way if
+some mktemp() version does NOT alter its argument, we still have a usable
+filename (like /tmp/ckXXXXXX)). Of course this won't be good for recursive
+MLSD downloads, but we can always issue patches for the Unix version later if
+needed. The Linux and BSD versions use mkstemp() anyway. There is, however,
+a danger of a memory leak in the Unix version if the user has defined a TMPDIR
+or CK_TMP environment variable whose value is longer than 9 bytes. All this
+is highly unlikely so we should be OK. ckcftp.c, 17 Nov 2002.
+
+--- K95 2.1.1 and updated ck[uv]206.{tar,zip} but not C-Kermit binaries ---
+
+From Jeff: Fixes for Telnet Com Port Control, update to a newer release of
+OpenSSL: ck_ssl.c ck_ssl.h ckcdeb.h ckcftp.c ckcmai.c ckcnet.c ckctel.c
+ckuath.c ckuath.h ckucns.c ckuus4.c ckuus5.c ckuusr.c ckuusr.h ckvcon.c
+ckvfio.c ckvker.com ckvtio.c ckvvms.h, 25 Nov 2002.
+
+--- K95 2.1.2 and C-Kermit 8.0 CDROM ---
+
+From Jeff, 28 Nov 2002:
+ . Updated SSL modules: ck_ssl.[ch].
+ . Fixed cipher-list display in SHOW AUTH & FTP ssl_auth(): ckuus7.c. ckcftp.c
+ . Some minor tn_wait() fixes: ckctel.c.
+ . Preliminary SSL support for VMS CONNECT: ckvcon.c.
+
+Bumped C-Kermit edit number to 207.
+
+From Jeff, 29 Nov 2002: "[C-Kermit was dumping core on SCO OSR5 Telnet Com Port
+connections because] the SCO compiler treats all characters as signed. This
+was causing 'sprintf(buf,"02x ",ch);' to produce strings such as "ffffffc2"
+instead of "c2" for values between 128 and 255. This wrote beyond the end of
+a buffer and blew away the stack. Having fixed this I also noticed that
+conect() did not properly check for carrier when TN CPC was negotiated. This
+has now been fixed as well." ckucns.c, ckucon.c, ckctel.c, 29 Nov 2002.
+
+From Jeff, 30 Nov 2002: Fix SSL for VMS and also carry forward the CPC fixes
+to VMS. ckcnet.c, ckvtio.c, ckvcon.c, 30 Nov 2002.
+
+Changed copyright dates that are displayed (but not yet all the internal
+ones) from 2002 to 2003. ckcmai.c, 3 Jan 2003.
+
+Fixed the FTP module's brief-format transaction log, which had the status
+inverted: OK for FAILED and v.v. ckcftp.c 3 Jan 2003.
+
+From Jeff, 4 Jan 2003:
+ . Make /MOVE-TO:xxx convert xxx to full pathname: ckuus[r67].c,
+ . Make SHOW OPTIONS ALL show both kinds of options: ckuus2.c.
+ . More command-line personalities: ckcmai.c, ckuusy.c.
+ . New NOSCROLL command for K95: ckuusr.[ch], ckuus2.c.
+ . New lockdown and other command-line options: ckuusr.h, ckuusy.c.
+ . SSL interface updated to OpenSSL 0.9.7: ck_ssl.c.
+ . SET TERM LINE-SPACING and CURSOR xxx NOBLINK: ckuus[27]c.
+ . Expanded SHOW GUI command: ckuus3.c
+ . New SHOW TABS code: ckuus5.c.
+
+Updated SUPPORT (BUG), NEWS, and INTRO texts. ckuus[26].c. 5 Jan 2003.
+
+Fixed FTP module to suppress "'FEAT': Command not understood" message
+unless FTP DEBUG is ON. ckcftp.c, 6 Jan 2003.
+
+Got a report that C-Kermit dumps core on Solaris when executing a certain
+script. Seems to be related to changing vnambuf[] in zzstring() from an
+automatic array to a malloc'd buffer (see notes from 29 Jun 2000). Changed
+it to an automatic buffer except for K95. ckuus4.c, 6 Jan 2003.
+
+Nope, that's not it. It evidently happens only after FTP PUT has been used.
+Fixed solaris9g makefile target to include -funsigned-char and built a new
+binary. Determined that building with gcc and -funsigned-char makes no
+difference. makefile, 7 Jan 2003.
+
+I did a preliminary audit, looking at the items in the left column: if used in
+a given routine, are there any obvious mistakes:
+
+ 1 2 3 4 5
+ doftpput->putfile->sendrequest->doftpsend2->secure_write
+malloc OK OK OK OK OK
+makestr OK OK OK OK OK
+automatic arrays OK OK OK OK OK
+[ck]str[n]cpy OK OK OK OK OK
+[ck]str[n]cat OK OK OK OK OK
+sprintf OK OK OK OK OK
+nzltor OK OK OK OK OK
+zfnqfp OK OK OK OK OK
+memcpy OK OK OK OK OK
+bcopy OK OK OK OK OK
+
+secure_write sends the data directly on clear-text connections. On secure
+connections, it calls secure_putbuf(), which calls secure_putbyte(), but we
+aren't using those, so secure_write() is the end of the call chain for FTP
+PUT. doftpsend2 has buf[] as an automatic array, which it reads file data
+into using zxin (binary mode only), but this looks OK. Still, I changed it
+read 1 less than the buffer size (fread) just in case. Also there was one
+debug() statement that referred to an automatic array (fullname[]) before it
+was initialized (but not used in this case), which I fixed. ckcftp.c,
+7 Jan 2003.
+
+FTP GET /RECURSIVE somepath/somefile still didn't work, despite what the
+notes of 19 Sep 2001 say. There are so many paths through the code,
+depending on switch values, GET vs MGET, etc, that a crucial spot was missed.
+Fixed in doftpget(): ckcftp.c, 7 Jan 2003.
+
+Back to the core dump... after two days of full-time debugging, I found the
+culprit: the buffer-full test in the zzout() macro should have been ">="
+rather than just ">", thus Kermit wrote 1 byte past the end of the malloc'd
+FTP PUT output buffer, ucbuf. Why did it never happen in K95? Because, since
+it's a secure build, FUDGE_FACTOR is defined there. But it's not defined in
+Solaris or other clear-text builds. Although the crash wouldn't happen in
+secure builds, the 1-byte leak might have caused errors in the data transfer.
+In non-Solaris clear-text builds, like Linux, I suspect that malloc() tends
+add something for safety (especially given the man page statement that it
+allocates "at least" what you asked for). Another reason the problem escaped
+notice is that zzout() is used only for text-mode PUTs (and then only when
+there is no character-set translation), but most transfers these days are
+binary and/or downloads. Anyway, in the course of debugging, a lot of small
+cleanups were done: sizeof(blah) for all arrays was replaced by the same
+symbolic size that was used to allocate the array, numeric array sizes were
+replaced with symbolic ones, etc. The real fix is one character long.
+ckcftp.c, 9 Jan 2003.
+
+Got a report that "mget /recursive */somedir/*" downloaded the files into
+the current directory, rather than re-creating the remote directory structure.
+Fixed in doftpget(): ckcftp.c, 10 Jan 2003.
+
+Unix C-Kermit did not allow file transfer if started under inetd and accessed
+via Internet raw socket (or whatever). Diagnosis: isatty() and friends would
+fail causing ttopen() to fail. Fixed by adding escape clauses for "-l 0"
+situations (i.e. Kermit invoked with an already-open file descriptor) at the
+appropriate places. ckcmai.c, ckutio.c, 14 Jan 2003.
+
+From Jeff for K95 2.1.3
+ . Add test for startflags & 128 to trap() for ignoring BREAK.
+ . Fix for SHOW TRANSMIT.
+
+--- K95 2.1.3 ---
+
+FTP USER, FTP ACCOUNT, plus the various prompts and switches for FTP username,
+password, and account all neglected to strip quotes, and in most cases quotes
+are necessary to specify a username that contains spaces. ckcftp.c,
+15 Jan 2003.
+
+FTP MPUT f1 f2 f3... gets a parse error if any of the fn's do not match an
+existing file. This is bad for scripts. In doftpput(), cmfdb() looks for
+keywords (switches) or CMIFI. When it hits CMIFI, it exits from the initial
+parse loop and then does additional cmifi()s in a loop until done. The most
+obvious fix is to parse each field with cmfdb(CMIFI,CMFLD), i.e. fall back to
+CMFLD if CMIFI doesn't match anything. Then if CMFLD was used, we don't add
+the filespec to the list. This is a rather big change but it seems to work.
+No error messages or failures happen for non-matching fields, but an error
+message is printed (and the MPUT command fails) if none of the fields match
+any files. This fix got in too late for 2.1.3; workaround: use C-Shell
+like wildcard list (ftp mput "{*.abc,foo.*}"). ckcftp.c, 16 Jan 2003.
+
+GREP did not pass its pattern through the expander, thus variables could
+not be used for patterns. This must have been an oversight -- I can't find
+anything in my notes about it. Fixed in dogrep(): ckuus6.c, 24 Jan 2003.
+
+New makefile target for HP-UX 11.xx with OpenSSL from Tapani Tarvainen.
+makefile, 31 Jan 2003.
+
+From Jeff:
+ . Avoid core dump when dereferencing tnc_get_signature(): ckuus4.c.
+ . Bump version numbers to 8.0.208, 2.1.4: ckcmai.c.
+
+Added /NOLOGIN to FTP [OPEN]. ckcftp.c, 10 Feb 2003.
+
+Don't dump core if FTP DEBUG is ON and FTP OPEN does not include a service.
+openftp(): ckcftp.c, 10 Feb 2003.
+
+HELP PATTERN text incorrectly identified commands and functions with
+floating and anchored patterns. The corrected lists are:
+Floating: GREP, TYPE /MATCH:, /EXCEPT: patterns, \farraylook(),
+Anchored: IF MATCH, file-matching wildcards, \fsearch(), \frsearch()
+ckuus2.c, 10 Feb 2003.
+
+INPUT n \fpattern(xxx) did not work for case-independent comparisons.
+Fixed in doinput(): ckuus4.c, 10 Feb 2003.
+
+It seems \fpattern() didn't work with MINPUT at all. There was no code to
+handle \fpattern() in the MINPUT parse loop, so it never worked. The code
+had to be totally rewritten to use cmfld() in a loop, rather than cmtxt()
+and then cksplit(). Furthermore, whenever any of the fields was an
+\fjoin(), this had to be split. ckuusr.c, 10 Feb 2003.
+
+Macro replacement via \m() and \fdefinition() does not work as advertised
+(i.e. case sensitively) for associative array elements; e.g. \m(xxx<abc>) is
+treated the same as \m(xxx<ABC>), contrary to section 7.10.10 of the C-Kermit
+7.0 update notes, and to the fact that the two really do exist separately.
+Fixed by adding a static function isaarray(s) which succeeds if s is an
+associative array reference and fails otherwise, and then having \m()
+and \fdef() call mxxlook() (case-sensitive lookup) if isaarray(), otherwise
+(as before) mxlook()). ckuus4.c, 11 Feb 2003.
+
+Fixed FTP OPEN to allow the /USER switch to override SET FTP AUTOLOGIN OFF,
+just as /NOLOGIN overrides SET FTP AUTOLOGIN ON. ckcftp.c, 11 Feb 2003.
+
+In K95, "set key \1234 \27H" (any SET KEY command in which the first char of
+the definition was backslash, and the ONLY character after the backslash
+quantity was an uppercase letter, that letter would be lowercased). Diagnosis:
+xlookup() poking its argument (see notes from July 2000). Jeff sent a fix.
+ckucmd.c, 15 Feb 2003.
+
+Ran my S-Expression torture test to make sure Sexps still worked. They do,
+except the bitwise & and | operators were broken, e.g. (& 7 2) and (| 1 2 4)
+get "Invalid operand" errors. Jeff's code had added an early failure return
+from the lookup loop when when a single-byte keyword matched a keyword that
+started with the same byte but was more than one byte long. So "&" would hit
+"&&" and fail instead of continuing its search (xlookup tables aren't sorted
+so there can be no early return). Fixed in xlookup(): ckucmd.c, 16 Feb 2003.
+
+Got rid of "krbmit" target from makefile. It's still there, but we don't
+use it any more. All secure targets now use "xermit", and produce a binary
+called wermit, just like the regular ones do (except the old ckucon.c ones).
+Non-secure targets, since they don't define any of the security symbols,
+wind up compiling and linking to (mostly) empty security modules. makefile,
+15 Feb 2003.
+
+Added \fcvtdate(xxx,3) to format its result in MDTM format (yyyymmddhhmmss,
+all numeric, no spaces or punctuation). Of course these numeric strings
+are too big to be 32-bit numbers and are useless for arithmetic, but they're
+useful for lexical comparison, etc. ckuus[24].c, 16 Feb 2003.
+
+The following FTP commands did not set FAILURE when they failed: RMDIR,
+CD, CDUP, Fixed in the corresponding doftpblah() routines. ckcftp.c,
+16 Feb 2003.
+
+RENAME would sometimes not print an error message when it failed, e.g. in K95
+when the destination file already existed. ckuus6.c, 17 Feb 2003.
+
+Fixed COPY error messages, which did not come out in standard format when
+/LIST was not included. ckuus6.c, 17 Feb 2003.
+
+Fixed #ifdefs in ck_crp.c to allow nonsecure builds on old platforms like
+System V/68 R3. 19 Feb 2003.
+
+Similar treatment for ck_ssl.c. 20 Feb 2003.
+
+From Jeff, 21 Feb 2003:
+ . AIX53 and AIX52 symbols for ckcdeb.h, makefile.
+ . New gcc targets for various AIX 4.x/5.x versions: makefile.
+ . Copyright date updates: ck_crp.c, ck_ssl.c.
+ . ENABLE/DISABLE QUERY broken because keyword table out of order: ckuusr.c.
+ . Fixed the use of HTTP proxies for HTTP [RE]OPEN for Unix: ckcnet.c.
+
+Also for K95 only: Allow file transfer when K95 is invoked on the remote end
+of a connection to a Pragma Systems Terminal Server connection; automatically
+SET EXIT HANGUP OFF when invoked with open port handle ("k95 -l nnnn").
+
+"cd a*" failed even when "a*" matched only one directory. Fixed in cmifi():
+ckucmd.c, 21 Feb 2003.
+
+In the Unix version, replace "extern int errno;" with "#include <errno.h>"
+if __GLIBC__ is defined, since glibc now defines a thread-specific errno.
+ckcdeb.h, 26 Feb 2003.
+
+Added #ifdefs to skip compilation of ckuath.c in nonsecure builds. Tested
+by building both secure and regular versions in Linux. ckuath.c, 26 Feb 2003.
+
+Ran the build-in-84-different-configurations script on Linux to make sure it
+still builds with all different combinations of feature selection options.
+All OK. 26 Feb 2003.
+
+Built on VMS. Needed to add a prototype for mxxlook*() to ckuusr.h; built
+OK otherwise. 26 Feb 2003.
+
+From Jeff: More #ifdef shuffling for nonsecure builds: ckuath.c, ck_ssl.c,
+27 Feb 2003.
+
+Added code to ensure \v(download) ends in a directory separator in Unix,
+Windows, and OS/2. ckuus7.c, 27 Feb 2003.
+
+Added code to K95 zfnqfp() to tack on directory separator when returning
+a directory name. ckofio.c, 27 Feb 2003.
+
+Somehow an old copy of ckuath.c popped to replace the new one. Put the new
+one back. 28 Feb 2003.
+
+From Jeff: Fix typo in my K95 zfnqfp() code from yesterday; fixes for handling
+UNCs uniformly, no matter which way their slashes are leaning. ckofio.c,
+28 Feb 2003.
+
+At Jeff Mezei's suggestion, separate text and binary mode open sequences
+for VMS session log. ckvfio.c, 28 Feb 2003.
+
+Added freebsd48 target for FreeBSD 4.8. makefile, 1 Mar 2003.
+
+Changed Mac OS X entries to include -DUSE_STRERROR. makefile, 2 Mar 2003.
+
+Fixed GETOK /GUI to evaluate its text argument. ckuus6.c, 3 Mar 2003.
+
+Jeff fixed the K95 Dialer QUICK dialog to (a) allow templates, and (b) have
+a Save-As option. 3 Mar 2003.
+
+Jeff fixed a problem with the Xmodem-CRC checksum being crunched whenever
+there was a retransmission. 7 Mar 2003.
+
+Added target/banner for Tru64 5.1B. makefile, ckuver.h, 5 Mar 2003.
+
+In Unix, the zcopy() routine (used by the COPY command) reset the user's umask
+to 0 for the remainder of the Kermit process lifetime. The bug was in
+ckufio.c 8.0.194, 24 Oct 2002, and is fixed in ckufio.c 8.0.195, 6 Mar 2003.
+Of course this happened after building 155 C-Kermit 8.0.208 binaries. (But
+before officially releasing 8.0.208.)
+
+In the VMS version, changed:
+
+ while ((n--) && xx_inc(2) > -1) ;
+to:
+ while ((n--) && xx_inc(2) >= 0) ;
+
+to suppress the "...is being compared with a relational operator to a constant
+whose value is not greater than zero" warning. ckvtio.c, 7 Mar 2002.
+
+Added a debug call to dologend in hopes of catching overzealous Locus
+switching, which seems to happen only in K95. ckuus3.c, 7 Mar 2002.
+
+Rebuilt binaries for some of the more current Unix releases: AIX 4.3.3-5.1,
+Solaris 7-9 , Red Hat 7.0-8.0, Slackware 8.1, Freebsd 4.7-4.8, NetBSD 1.6,
+OpenBSD 3.2, Unixware 7.1.3, Open Unix 8, OSR5.0.6a, etc. A Unix binary with
+COPY umask fix shows a 6 Mar 2003 date for "UNIX File support" in SHOW
+VERSIONS; a binary without the fix shows 24 Oct 2002.
+
+C-Kermit 8.0.208 dated 14 March 2003 released on 10 March 2003.
+
+---8.0.208---
+
+From Jeff 13 Mar 2003:
+ . Updated SSL module allows importation of tickets from host.
+ . freebsd50+openssl target: makefile.
+ . FTP PUT /PERMISSIONS error message for K95: ckcftp.c.
+
+Fixed MINPUT to strip quotes or braces from around targets (this was broken
+on Feb 10th). Thanks to Jason Heskett for discovering and reporting this
+(killer) bug. ckuusr.c, 14 Mar 2003.
+
+Changed version number to 209 Dev.00. ckcmai.c, 14 Mar 2003.
+
+While debugging the alphapage script, I found that the command "minput 8 \6\13
+\21\13 \13\27\4\13 \30\13" gets "?Not confirmed" in 8.0.208 and 8.0.209, but
+not in 206 and earlier. This problem too was introduced on Feb 10th by
+changing MINPUT parsing from cmtxt() followed by cksplit() to cmfld() in a
+loop. cmfld() uses setatm() to return its result and of course setatm()
+breaks on \13. Changing setatm() not to do this would break everything else.
+But cmfld() has no arguments that let us tell it to do anything different in
+this case. Changing the API would be a disaster. The only solution is to add
+an "MINPUT ACTIVE" (minputactive) global variable that tells cmfld() to tell
+setatm() not to break on CR. Now MINPUT with braced targets containing CR
+and/or LF works in 209, 206, and 201 (but not 208). ckucmd.c, ckuusr.c,
+ckuus5.c, 15 Mar 2003.
+
+MINPUT n \fjoin(&a) works OK if all the members of \&a[] are text strings, but
+if they are strings of control chars (as above), they don't get separated by
+the spaces. For example in:
+
+ dcl \&a[] = "\4\5" "\6\7" xxx
+ minput 10 \fjoin(&a)
+
+MINPUT gets two targets: "aaa" and "\4\5 \6\7 xxx". The bug was in the
+cksplit() call in the \fjoin() case of MINPUT: it needed to specify an
+include set consisting of all the control characters except NUL. ckuusr.c,
+16 Mar 2003.
+
+But there's still a problem:
+
+ dcl \&a[] = "\4\5\13\10" "\6\7" "xxx"
+
+creates an array whose first member is "^D^E (one doublequote included). But
+if braces are used instead, there's no problem. Same deal as MINPUT: cmfld()
+breaks on CR or LF, thus the end quote is lost. If I set minputactive for
+DECLARE initializers too, that fixes it. Is there any reason not to do this?
+Can't think of any (famous last words)... ckuusr.c, 16 Mar 2003.
+
+Since it has multiple applications, changed the flag's name from minputactive
+to keepallchars. ckucmd.c, ckuus[r5].c, 16 Mar 2003.
+
+\v(exedir) wasn't being set correctly (it included the program name as well
+as the directory). Fixed in getexedir(): ckuus4.c, 16 Mar 2003.
+
+SET CARRIER-WATCH <Esc> "auto matic" (spurious space in supplied keyword).
+Cosmetic only; it still worked. Fixed in setdcd(): ckuus3.c, 16 Mar 2003.
+
+"directory a b c" listed too many files -- all files whose names END WITH a,
+b, or c, rather than the files whose names WERE a, b, or c. Diagnosis: The
+filespec is changed into a pattern: {a,b,c}, which is the correct form. It is
+passed to nzxpand(), which goes through the directory getting filenames and
+sending each one to ckmatch() with the given pattern. ckmatch() receives the
+correct pattern but then prepends a "*" -- that's not right. It's not just
+in filename matching either. The following succeeds when it shouldn't:
+
+ if match xxxxc {{a,b,c}} <command>
+
+Changing ckmatch() to not prepend the "*" to each segment fixes the command
+above but breaks lots of others. Running through the "match" torture-test
+script shows the problem occurs only when the {a,b,c} list is the entire
+pattern, and not embedded within a larger pattern. Testing for this case
+fixed the problem. ckmatch(): ckclib.c, 16 Mar 2003.
+
+Fixed FTP MODTIME to not print anything if QUIET ON. ckcftp.c, 16 Mar 2003.
+
+Picked up a new ckuath.c from Jeff, not sure what the changes are. 16 Mar 2003.
+
+Did a few regular and secure builds to make sure I didn't wreck anything.
+
+Changed version number to 209 (final). ckcmai.c, 16 Mar 2003.
+
+Jason Heskett found another bug: if you define a macro FOO inside the
+definition of another macro BAR, and FOO's definition includes an odd number
+of doublequotes (such as 1), FOO's definition absorbs the rest of BAR's
+definition. Example:
+
+ def TEST {
+ .foo = {X"}
+ sho mac foo
+ }
+ do test
+ sho mac foo
+
+Results in:
+
+ foo = {X"}, sho mac foo
+
+Diagnosis: the TEST definition becomes:
+
+ def TEST .foo = {X"}, sho mac foo
+
+and the macro reader is erroneously treating the doublequote as an open
+quote, and then automatically closes the quote at the end of the definition.
+The error is that a doublequote should be significant only at the beginning of
+a field. But the macro reader isn't a command parser; it doesn't know what
+a field is -- it's just looking for commas and skipping over quoted ones.
+First we have to fix an oversight: SET COMMAND DOUBLEQUOTING OFF should have
+worked here, but it wasn't tested in this case. Fixed in getncm(): ckuus5.c,
+17 Mar 2003.
+
+There are only certain cases where it makes sense to treat doublequotes as
+signicant:
+
+ . An open quote must be at the beginning or preceded by a space.
+ . A close quote is only at the end or else followed by a space.
+
+This too was fixed in getncm(): ckuus5.c, 17 Mar 2003.
+
+A fix from Jeff SSL/TLS FTP data decoding. ckcftp.c, 18 Mar 2003.
+
+Tried building C-Kermit on a Cray Y-MP with UNICOS 9.0. "int suspend",
+declared in ckcmai.c and used in many modules, conflicts with:
+
+ unistd.h:extern int suspend __((int _Category, int _Id));
+
+The "=Dsuspend=xsuspend" trick doesn't work for this; there is no way around
+the conflict other than to rename the variable: ckcmai.c, ckutio.c,
+ckuus[35xy].c. 26 Mar 2003. VMS and K95 not affected.
+
+OK that gets us past ckcmai.c... Then in ckutio.c I had to add a new #ifdef
+around the LFDEVNO setting, because the Cray didn't have mkdev.h. Could not
+find a Cray-specific manifest symbol, so I made a new makefile target (cray9)
+that sets this symbol. Having done this I have no idea what kind of lockfile
+would be created, but I also doubt if anybody dials out from a Cray. The
+binary should run a C90, J90, or Y-MP. makefile, 26 Mar 2003.
+
+Added a target for SCO OSR5.0.7. makefile, ckuver.h, 30 Mar 2003.
+
+Changed since 208:
+makefile ckuver.h ckcmai.c ckclib.c ckcftp.c ckucmd.c ckuus*.c ckutio.c.
+
+---8.0.209---
+
+From Mark Sapiro, a fix for the March 17th doubleqote fix, getncm(): ckuus5.c,
+4 Apr 2003.
+
+From Jeff, 29 Apr 2003:
+ . Corrected target for HP-UX 11.00 + OpenSSL: makefile,
+ . Do not allow WILL AUTH before WONT START_TLS: ckctel.h ckctel.c
+ . Add hooks for SFTP and SET/SHOW SFTP: ckcdeb.h ckuusr.h ckuusr.c ckuus3.c
+ . Add SKERMIT ckuusr.h ckuusr.c
+ . Add ADM-5 terminal emulation: ckuus7.c, ckuus5.c
+ . Uncomment and update HELP SET SSH V2 AUTO-REKEY: ckuus2.c
+ . Enable IF TERMINAL-MACRO and IF STARTED-FROM-DIALER for C-Kermit: ckuus6.c
+ . Fix conflicting NOSCROLL keyword definition: ckuusr.h
+ . Set ttname when I_AM_SSH: ckuusy.c
+ . Add extended arg parsing for SSH, Rlogin, Telnet: ckuusy.c, ckuus4.c
+ . Security updates: ckuath.c, ck_ssl.c
+ . Change K95 version number to 2.2.0: ckcmai.c
+ . Save K95 term i/o state before executing keyboard macro: ckuus4.c
+ . Add tests for SSH Subsystem active during INPUT/OUTPUT/CONNECT: ckuus[45].c
+ . Enable K95 SET SSH V2 AUTO-REKEY: ckuus3.c
+
+SFTP and SET SFTP subcommands are implemented up to the case statements.
+
+Files of mine that Jeff hadn't picked up:
+ ckuver.h ckcftp.c ckutio.c ckuusx.c (just minor changes for last build-all)
+
+On 4 Jan 2003, SET RECEIVE MOVE-TO was changed to convert is argument to an
+absolute path, which made it impossible to specify a relative path, then
+move to different directories and have it apply relatively to each directory.
+Changed this as follows:
+
+ . Parser uses cmtxt() rather than cmdir() so it won't fail at parse time.
+ . If path is absolute, we fail at parse time if directory doesn't exist.
+ . In reof() we run the the path through xxstring (again, in case deferred
+ evaluation of variables is desired) and then, if not null, use it.
+ . If the directory doesn't exist, rename() fails and reof() returns -4,
+ resulting in a protocol error (this is not a change). We do NOT create
+ the directory on the fly.
+
+I also fixed SET SEND/RECEIVE RENAME-TO to parse with cmtxt() rather than
+cmdir(), since it's parsing a text template, not a directory name, e.g.
+"set receive rename-to file-\v(time)-v(date)-\v(pid)". This was totally
+broken, since when I don't know. We don't call xxstring() in this parse, so
+evaluation is always deferred -- I'd better not change this. ckuus7.c,
+ckcfns.c, 1 May 2003.
+
+From Jeff, Sat May 3 14:15:23 2003:
+ . Pick up the right isascii definition for K95: ckctel.c
+ . malloc... ckuath.c (new safe malloc routines for K95)
+ . Add author listing: ckuus5.c
+ . SSH Heartbeat support (K95 only): ckuus[23].c
+ . Prescan --height and --width to avoid window resizing at startup: ckuusy.c
+ . Add checks for fatal() or doexit() called from sysinit(): ckuusx.c
+ . Move some K95-specific definitions to ckoker.h: ckcdeb.h
+ . Add support for ON_CD macro in zchdir(): ckufio.c
+ . Add a command to let FTP client authenticate with SSLv2: ckcftp.c
+ . Fix parsing of FTP file facts like "UNIX.mode": ckcftp.c
+
+ON_CD will need some explaining (to be done). It's implemented for Unix,
+VMS, WIndows, and OS/2.
+
+The FTP file facts fix came from first exposure to the new OpenBSD FTP
+server: ftp://ftp7.usa.openbsd.org/pub/os/OpenBSD/3.3/i386/
+The period in "UNIX.mode" caused an erroneous word break, adding junk to
+the filename.
+
+About the malloc changes, Jeff says "K95 is not behaving well in low memory
+environments. I'm not sure that C-Kermit does much better. The program does
+not crash but it certainly does not behave the way the user expects it to.
+I'm beginning to think that any malloc() error should be treated as fatal."
+
+Not visible in these changes because it's in K95-specific modules: Jeff made
+SET ATTRIBUTES OFF and SET ATTRIBUTES DATE OFF apply to XYZMODEM transfers.
+
+From Jeff, 11 May 2003:
+ . Add support for SSH Keepalive to relevant SET command (K95): ckuus3.c
+ . Reduce max overlapped i/o requests from 30 to 7 (K95): ckuus7.c
+ . Don't call sysinit() in fatal(): ckuusx.c.
+ . Some new conditionalizations for SSL module: ck_ssl.c
+
+The doublequote-parsing fixes from March and April broke the SWITCH statement,
+which is implemented by internally defining, then executing, a macro. If I
+drop back to the old dumb handling of doublequotes, everything is fixed except
+the problem of March 17th. But can we really expect getncm() to pre-guess
+what the parser is going to do? getncm()'s only job is to find command
+boundaries, which are represented by commas. Commas, however, is needed IN
+commands too. We take a comma literally if it is quoted with \, or is inside
+a matched pair of braces, parens, or doublequotes. It is not unreasonable to
+require a doublequote in a macro definition to be prefixed by \ when it is to
+be taken literally. The proper response to Jason Heskett's complaint of March
+17th should have been to leave the code alone and recommand an appropriate
+form of quoting:
+
+ def TEST {
+ .foo = {X\"}
+ sho mac foo
+ }
+
+And this is what I have done. Another reason for sticking with the old method
+is that it's explainable. The "improved" method, even if it worked, would be
+be impossible to explain. Btw, in testing this I noticed that the switch-test
+script made 8.0.201 dump core. Today's version is fine. The problem with
+quoted strings inside of IF {...} clauses and FOR and WHILE loops is fixed
+too. Perhaps "unbroken" would be a better word. ckuus5.c, 11 May 2003.
+
+Vace discovered that FTP MGET /EXCEPT:{... (with an unterminated /EXCEPT list)
+could crash Kermit. Fixed in ckcftp.c, 11 May 2003.
+
+CONTINUE should not affect SUCCESS/FAILURE status. ckuusr.c, 11 May 2003.
+
+Fixed an oversight that goes back 15 years. While \{123} is allowed for
+decimal codes, \x{12} and \o{123} were never handled. ckucmd.c, 11 May 2003.
+
+Added support for Red Hat <baudboy.h> and /usr/sbin/lockdev. Supposedly this
+allows Kermit to be installed without setuid or setgid bits and still be able
+to lock and use the serial device. Compiles and starts, but not tested.
+ckcdeb.h, makefile, ckutio.c, ckuus5.c, 16 May 2003.
+
+From Jeff: FTP ASCII send data to host when FTP /SSL was in use was broken.
+ftp_dpl is set to Clear when FTP /SSL is in use. This was causing the data to
+be written to the socket with send() instead of the OpenSSL routines.
+ckcftp.c, ckuath.c, 21 May 2003.
+
+From Jeff: Stuff for Kerberos 524: ckcdeb.h. Fixes for FTP; "FTP ASCII send
+data did not properly compute the end of line translations. On Unix (and
+similar platforms) the end of line was correct for no character sets but
+incorrect when character sets were specified. On Windows/OS2, the end of line
+was correct when character sets were specified and incorrect when they were
+not. On MAC, both were broken. Also, FTP Send Byte counts were incorrect
+when character sets were specified." ckcftp.c. 17 Jun 2003.
+
+From Jeff: fixes to HTTP /AGENT: and /USER: switch action: ckcnet.c ckuus3.c
+ck_crp.c ckcftp.c ckuus2.c ckuusy.c ckuusr.c ckcnet.h, 21 Jun 2003.
+
+From Jeff: Fix SET DIALER BACKSPACE so it can override a previous SET KEY
+(e.g. from INI file): ckuus7.c. Some SSL/TLS updates: ck_ssl.c. HTTP support
+for VMS and other VMS improvements (e.g. a way to not have to hardwire the
+C-Kerit version number into the build script) from Martin Vorlaender:
+ckcnet.h, ckuus[r3].c, ckcdeb.h, ckvtio.c, ckcnet.c, ckvker.com. Built on
+Solaris (gcc/ansi) and SunOS (cc/k&r). The new VMS script tests the VMS
+version and includes HTTP support only for VMS 6.2 or later. 2 Jul 2003.
+
+Tried to build on our last VMS system but it seems to be dead. Looks like a
+head crash (makes really loud noises, boot says DKA0 not recognized) (fooey, I
+just paid good money to renew the VMS license). Tried building at another
+site with:
+
+ Process Software MultiNet V4.3 Rev A-X,
+ Compaq AlphaServer ES40, OpenVMS AXP V7.3
+ Compaq C V6.4-008 on OpenVMS Alpha V7.3
+
+Had to make a few corrections to ckvker.com. But still, compilation of
+ckcnet.c bombs, indicating that the SELECT definition somehow got lost
+somewhere since the 209 release (i.e. no SELECT type is defined so it falls
+thru to "SELECT is required for this code"). But I don't see anything in
+ckcdeb.h or ckcnet.[ch] that would explain this. Not ckvker.com either
+(putting the old one back gives the same result). OK, I give up, maybe it's
+just that I haven't tried building it on MultiNet recently. What about UCX?
+Aha, builds fine there except for warnings about mlook, dodo, and parser in
+ckvfio.c (because of ON_CD) -- I suppose I have #include <ckucmd.h>... (done)
+Anyhow it builds OK and the HTTP code is active and almost works (HTTP OPEN
+works; HTTP GET seems to succeed but creates an empty file every time). Tried
+building under MultiNet at another installation; same bad result.
+
+OK so why won't it build for MultiNet? Comparing ckcnet.c with the 209
+version, not a single #ifdef or #include is changed. Tried building with
+p3="NOHTTP" -- builds OK, aha. Where's the problem? Not ckcnet.h...
+Not ckcdeb.h... OK I give up, will revisit this next time I get time to
+do anything with the code.
+
+Later Jeff said "Martin did not implement VMS networking for the HTTP code.
+All he did was activate the #define HTTP which happens to work because his
+connections are using SSL/TLS connections. http_inc(), http_tol(), etc have
+no support for VMS networking regardless of whether it is UCX or MULTINET.
+The vast majority of HTTP connections are not secured by SSL/TLS. It makes no
+sense to support HTTP on VMS until someone is willing to either do the work or
+pay have the work done to implement VMS networking in that code base." So the
+fix is to not enable HTTP for VMS after all. Removed the CKHTTP definition
+for VMS from ckcdeb.h, 6 Jul 2003.
+
+Fixed ckvfio.c to #include <ckuusr.h> (instead of <ckucmd.h>) to pick up
+missing prototypes. 6 Jul 2003.
+
+From Arthur Marsh: solaris2xg+openssl+zlib+srp+pam+shadow and the corresponding
+Solaris 7 target. makefile, 6 Jul 2003.
+
+Remove duplicate #includes for <sys/stat.h>, <errno.h>, and <ctype.h> from
+ckcftp.c. 6 Jul 2003.
+
+Add -DUSE_MEMCPY to Motorola SV/68 targets because of shuffled #includes in
+ckcftp.c. 8 Jul 2003.
+
+From Jeff: Fix problems mixing SSL and SRP without Kerberos. Plus a few minor
+#define comment changes and a reshuffling of #defines in ckcdeb.h to allow me
+to build on X86 Windows without Kerberos. ckcdeb.h, ck_crp.c, ckuath.c,
+10 Jul 2003.
+
+From Jeff: updated ckuat2.h and ckuath.c, 29 Jul 2003.
+
+Mats Peterson noticed that a very small Latin-1 file would be incorrectly
+identified as UCS-2 by scanfile(). Fixed in ckuusx.c, 29 Jul 2003.
+
+Fixed ACCESS macro definition to account for the fact that FIND is now a
+built-in command. ckermit.ini, 30 Jul 2003.
+
+From Jeff: Fix for typo in urlparse() (svc/hos): ckuusy.c, 18 Aug 2003.
+
+From Jeff: Redhat9 makefile targets (needed for for OpenSSL 0.9.7):
+makefile, 19 Aug 2003.
+
+GREP /NOLIST and /COUNT did too much magic, with some undesirable fallout:
+"GREP /NOLIST /COUNT:x args" printed "file:count" for each file. "GREP
+/COUNT:x /NOLIST args" did not print "file:count", but neither did it set the
+count variable. Removed the magic. Also one of the GREP switches,
+/LINENUMBERS, was out of order. Fixed in ckuus6.c, 20 Aug 2003.
+
+From Jeff: "Reorganizing code to enable building with different subsets of
+options; a few typos corrected as well." ckcdeb.h, ckuver.h (for RH9),
+ckcnet.c, ckuus7.c, ckuus3.c: 24 Aug 2003.
+
+Scanfile misidentified a big PDF file as text because the first 800K of it
+*was* text (most other PDF files were correctly tagged as binary). Fixed
+by adding a check for the PDF signature at the beginning of the file.
+scanfile(): ckuusx.c, 25 Aug 2003.
+
+Ditto for PostScript files, but conservatively. Signature at beginning of
+file must begin with "%!PS-Ado". If it's just "%!" (or something nonstandard
+like "%%Creator: Windows PSCRIPT") we do a regular scan. Also added "*.ps"
+to all binary filename patterns. ckuusx.c, 4 Sep 2003.
+
+Ditto (but within #ifndef NOPCLSCAN) for PCL (<ESC>E) and PJL (<ESC>%) files,
+but no binpatterns (note: ".PCL" is the extension for TOPS-20 EXEC scripts).
+ckuusx.c, 4 Sep 2003.
+
+Added comments about OpenSSL 0.9.7 to all linux+openssl targets.
+makefile, 4 Sep 2003.
+
+From Jeff: Added - #define ALLOW_KRB_3DES_ENCRYPT. When this symbol is defined
+at compilation Kermit will allow non-DES session keys to be used during Telnet
+Auth. These session keys can then be used for Telnet Encrypt. The reason
+this is not compiled on by default is that the MIT Kerberos Telnet does not
+follow the RFC for constructing keys for ENCRYPT DES when the keys are longer
+than 8 bytes in length. ckuath.c, ckuus5.c, 4 Sep 2003.
+
+"ftp mget a b c" succeeded if one or more of the files did not exist, even
+with "set ftp error-action proceed". This is because the server's NLST file
+list does not include any files that don't exist, so the client never even
+tries to get them. Fortunately, the way the code is structured, this one was
+easy to fix. ckcftp.c, 14 Sep 2003.
+
+From Jeff: Corrected code in ckcnet.c to ensure that Reverse DNS Lookups are
+not performed if tcp_rdns is OFF. Fixed ck_krb5_getrealm() to actually return
+the realm of the credentials cache and not the default realm specified in the
+krb5.conf file. Previously krb5_cc_get_principal() was not being called.
+Fixed ck_krb5_is_tgt_valid() to test the TGT in the current ccache and not the
+TGT constructed from the default realm. ckcnet.c, ckuath.c, 14 Sep 2003.
+
+Marco Bernardi noticed that IF DIRECTORY could produce a false positive if
+the argument directory had previously been referenced but then removed. This
+is because of the clever isdir() cache that was added to speed up recursion
+through big directory trees. Changed IF DIRECTORY to make a second check
+(definitive but more expensive) if isdir() succeeds, and changed the
+directory-deleting routine, ckmkdir(), to flush the directory cache (UNIX
+only -- this also should be done in K95 but it's not critical). This was
+done by adding a routine, clrdircache() to ckufio.c, which sets prevstat
+to -1 and prevpath[0] to NUL. ckcfn3.c, ckuus6.c, ckufio.c, 18 Sep 2003.
+
+Marco reported the second fix still didn't work for him (even though it did
+for me). Rather than try to figure out why, I concluded that the directory
+cache is just not safe: a directory found a second ago might have been deleted
+or renamed not only by Kermit but by some other process. Why did I add this
+in the first place? The log says:
+
+ Some debug logs showed that isdir() is often called twice in a row on the
+ same file. Rather than try to sort out clients, I added a 1-element cache
+ to Unix isdir(). ckufio.c, 24 Apr 2000.
+
+Experimentation with DIR and DIR /RECURSIVE does not show this happening at
+all. So I #ifdef'd out the directory cache (see #ifdef ISDIRCACHE in ckufio.c;
+ISDIRCACHE is not defined) and backed off the previous changes: ckufio.c,
+ckcfn3.c, ckuus6.c, 28 Sep 2003.
+
+From Jeff: Replace the compile time ALLOW_KRB_3DES_ENCRYPT with a run-time
+command SET TELNET BUG AUTH-KRB5-DES which defaults to ON: ckctel.[ch],
+ckuus[234].c, ck_crp.c, ckuath.c. 4 Oct 2003.
+
+Allow DIAL RETRIES to be any positive number, and catch negative ones.
+Also added code to check for atoi() errors (e.g. truncation). At least on
+some platforms (e.g. Solaris) atoi() is supposed to set errno, but it
+doesn't. ckuus3.c, ckucmd.c, 4 Oct 2003.
+
+Added /DEFAULT: to ASK-class commands (ASK, ASKQ, GETOK):
+
+ . For popups: no way to send defaults to popup_readtext() or popup_readpass().
+ . For GUI ASK[Q], pass default to gui_txt_dialog().
+ . For GUI GETOK, convert "yes" "ok" or "no" default to number for uq_ok().
+ . For Text GETOK, add default to cmkey().
+ . For Text ASK[Q], add default to cmtxt().
+ . For GETC, GETKEY, and READ: no changes.
+
+GETOK, ASK, and ASKQ with /TIMEOUT: no longer fail when the timer goes off
+if a /DEFAULT was supplied. The GUI functions (uq_blah) don't seem to
+support timeouts. Only the text version has been tested. ckuus[26].c,
+4 Oct 2003.
+
+From Jeff: add /DEFAULT: for popups. ckuus6.c. 6 Oct 2003.
+
+Change SET DIAL INTERVAL to be like SET DIAL RETRIES. ckuus[34].c, 6 Oct 2003.
+
+Added target for HP-UX 10/11 + OpenSSL built with gcc, from Chris Cheney.
+Makefile, 12 Oct 2003.
+
+From Jeff, 6 Nov 2003:
+ . #ifdef adjustments: ckcftp.c, ckcdeb.h
+ . Fix spurious consumption of first byte(s) on Telnet connection: ckctel.c
+ . Another HP PJL test for scanfile: ckuusx.c.
+ . K95: Recognize DG4xx protected fields in DG2xx emulation: ckuus7.c.
+ . Add SSLeay version display to SHOW AUTH command: ckuus7.c
+ . Improved SET MOUSE CLEAR help text: ckuus2.c.
+ . Improved Kverbs help text: ckuus2.c (+ new IBM-3151 Kverbs).
+ . Some changes to ck_ssl.c, ckuath.c.
+
+From PeterE, 10 Nov 2003:
+ . Improved HP-UX 10/11 makefile targets for OpenSSL.
+ . #ifdef fix for OpenSSL on HP-UX: ck_ssl.c.
+
+Another new makefile from PeterE with improved and integrated HP-UX targets.
+12 Nov 2003.
+
+A couple fixes to the solaris9g+krb5+krb4+openssl+shadow+pam+zlib target
+from Jeff. Added a solaris9g+openssl+shadow+pam+zlib target. makefile,
+21 Nov 2003.
+
+From Jeff, 30 Nov 2003:
+ . Fix SEND /MOVE-TO: ckuusr.c.
+ . Fix K95 SET TITLE to allow quotes/braces around text: ckuus7.c.
+ . Improved "set term autodownload ?" response: ckuus5.c.
+ . Fix SHOW FEATURES to specify the protocol for encryption: ckuus5.c
+ . Make {SEND, RECEIVE} {MOVE-TO, RENAME-TO} work for XYZMODEM (K95 only).
+
+From Jeff: 7 Jan 2004:
+ . At one point Frank started to add a timer parameter to the
+ uq_txt() function but he only did it for the non-ANSI
+ compilers. I added it for the ANSI compilers, fixed the
+ prototypes and provided a default value easily changed
+ DEFAULT_UQ_TIMEOUT: ckcker.h, ckuus[36].c, ck_ssl.c, ckcftp.c, ckuath.c.
+ . Fixed SET TERMINAL DEBUG ON (typo in variable name): ckuus7.c.
+ . Fixed BEEP INFORMATION; previously it made no sound, now uses
+ MB_ICONQUESTION. ckuusx.c.
+
+From Ian Beckwith <ian@nessie.mcc.ac.uk> (Debianization), 7 Jan 2004:
+ . Search dir/ckermit for docs, as well as dir/kermit in cmdini(): ckuus5.c.
+ . New linux+krb5+krb4+openssl+shadow+pam target (kitchen sink minus SRP,
+ which Debian does not distribute): makefile.
+ ? Mangles the DESTDIR support in makefile to install into a staging area:
+ makefile (I didn't take this one yet).
+
+Updated copyright notices for 2004, all modules. 7 Jan 2004.
+
+Added INPUT /NOMATCH, allowing INPUT to be used for a fixed amount of time
+without attempting to match any text or patterns, so it's no longer
+necessary to "input 600 STRING_THAT_WILL_NEVER_COME". If /NOMATCH is
+included, INPUT succeeds if the timeout expires, with \v(instatus) = 1
+(meaning "timed out"); fails upon interruption or i/o error. ckuusr.h,
+ckuus[r24].c, 7 Jan 2004.
+
+Added SET INPUT SCALE-FACTOR <float>. This scales all INPUT timeouts by the
+given factor, allowing time-sensitive scripts to be adjusted to changing
+conditions such as congested networks or different-speed modems without
+having to change each INPUT-class command. This affects only those timeouts
+that are given in seconds, not as wall-clock times. Although the scale
+factor can have a fractional part, the INPUT timeout is still an integer.
+Added this to SHOW INPUT, and added a \v(inscale) variable for it.
+ckuusr.h, ckuus[r257].c, 7 Jan 2004.
+
+undef \%a, \fverify(abc,\%a) returns 0, which makes it look as if \%a is a
+string composed of a's, b's, and/or c's, when in fact it contains nothing.
+Changed \fverify() to return -1 in this case. ckuus4.c, 12 Jan 2004.
+
+\fcode(xxx) returned an empty string if its argument string was empty. This
+makes it unsafe to use in arithmetic or boolean expressions. Changed it to
+return 0 if its argument was missing, null, or empty. ckuus4.c, 12 Jan 2004.
+
+Updated \verify() and \fcode() help text. ckuus2.c, 12 Jan 2004.
+
+While setting up IKSD, Ian Beckwith noticed that including the --initfile:
+option caused Kermit to start parsing its own Copyright string as if it were
+the command line, and eventually crash. I couldn't reproduce on Solaris /
+Sparc but I could in Linux / i386 (what Ian is using) -- a change from Jeff
+on 28 Apr 2003 set the command-line arg pointer to a literal empty string in
+prescan() about line 1740 of of ckuus4.c; the pointer is incremented next
+time thru the loop, resulting in random memory being referenced. Fixed by
+setting the pointer to NULL instead of "". ckuus4.c, 12 Jan 2004.
+
+declare \&a[999999999999999] would dump core on some platforms. atoi()
+or whatever would truncate the dimension to maxint. When we add 1 to the
+result, we get a negative number, which is used as an index, loop test, etc.
+Fixed both dodcl() and dclarray() to check for (n+1 < 0). ckuus[r5].c,
+12 Jan 2004.
+
+Unix zchki() would fail on /dev/tty, which is unreasonable. This prevented
+FOPEN /READ from reading from the terminal. zchki() already allowed for
+/dev/null, so I added /dev/tty to the list of specials. Ditto for FOPEN
+/WRITE and zchko(). ckufio.c 13 Jan 2004.
+
+Added untabify() routine to ckclib.[ch], 13 Jan 2004.
+Added FREAD /TRIM and /UNTABIFY. ckuus[27].c, 13 Jan 2004.
+Added \funtabify(). ckuusr.h, ckuus[24].c, 13 Jan 2004.
+
+Dat Nguyen noticed that (setq u 'p') followed by (u) dumped core. This was
+caused by an over-clever optimization that skipped mallocs for short
+literals, but then went on later to try to free one that hadn't been
+malloc'd. Fixed in dosexp(): ckuus3.c, 14 Jan 2004.
+
+Catch another copyright date. ckuus5.c, 14 Jan 2004.
+
+Fixed SWITCH to work even when SET COMMAND DOUBLEQUOTE OFF (from Mark
+Sapiro). ckuus5.c, 15 Jan 2004.
+
+Changed version to 8.0.211 so scripts can test for recently added features.
+ckcmai.c, 15 Jan 2004.
+
+Fixed a glitch in K95 "help set port". ckuus2.c, 20 Jan 2004.
+
+Fix from Jeff: Connections to a TLS-aware protocol which require a reconnect
+upon certificate verification failure could not reconnect if the connection
+was initiated from the command line or via a URL. ckctel.c ckcmai.c
+ckuusr.c ckuus7.c ckuusy.c, 20 Jan 2004.
+
+From Alex Lewin: makefile target and #ifdef for Mac OS X 10.3 (Panther):
+makefile, ckcnet.c, 7 Feb 2004.
+
+Added KFLAGS to sco32v507 targets to make PTY and SSH commands work. The
+same flags could probably also be added to earlier OSR5 targets but they
+have not been tested there. makefile, 7 Feb 2004.
+
+Checked a complaint that "LOCAL &a" did not make array \&a[] local. Indeed
+it did not, and can not. You have to use the full syntax in the LOCAL
+command, "LOCAL \&a[]", or else it doesn't know it's not a macro named &a.
+7 Feb 2004.
+
+Fixed some confusion in creating IKSD database file and temp-file names.
+I was calling zfnqfp() without remembering that the path member of the
+returned struct included the filename, so to get just the directory name,
+I needed to strip the filename from the right. ckuusy.c, 2 Mar 2004.
+
+New ckuath.c, ck_ssl.c from Jeff. 2 Mar 2004.
+
+Updated Jeff's affiliation in VERSION command text. ckuusr.c, 2 Mar 2004.
+
+Designation changed from Dev.00 to Beta.01. ckcmai.c, 2 Mar 2004.
+
+Fixed zrename() syslogging -- it had success and failure reversed.
+Beta.02: ckufio.c, 4 Mar 2004.
+
+Problem: when accessing IKSD via a kermit:// or iksd:// URL, and a user ID
+is given but no password, doxarg() set the password to "" instead of leaving
+it NULL, but all the tests in dourl() are for NULL. Fixed in doxarg():
+ckuusy.c, 5 Mar 2004.
+
+The logic in dourl() about which macro to construct (login and connect,
+login and get directory listing, or login and fetch a file) was a bit off,
+so all three cases were not handled. ckcmai.c, 5 Mar 2004.
+
+Trial Beta builds:
+ . HP-UX B.11.11 PA-RISC
+ . HP-UX B.11.23 IA64
+ . Tru64 4.0G Alpha
+ . Tru64 5.1B Alpha
+ . Debian 3.0 i386
+ . Red Hat ES 2.1 i386
+ . Slackware 9.1 i386
+ . VMS 7.3-1 Alpha + UCX 5.3
+ . VMS 7.3-1 Alpha no TCP/IP
+ . VMS 7.3 Alpha MultiNet 4.3 A-X
+ . SCO UnixWare 7.1.4 i386
+ . SCO OSR5.0.7 i386
+ . Solaris 9 Sparc
+
+Fixed compiler warning in doxarg() caused by typo (NULL instead of NUL) in
+the 5 March doxarg() edit. ckuusy.c, 9 Mar 2004.
+
+IKSD (kermit://) command-line URLs did not work right if the client had
+already preauthenticated with Kerberos or somesuch because they tried to log
+in again with REMOTE LOGIN. The macros constructed in doxarg() needed to
+check \v(authstate) before attempting REMOTE LOGIN. ckcmai.c, 10 Mar 2004.
+
+Added ckuker.nr to x.sh (ckdaily upload) and updated ckuker.nr with current
+version number and dates. 10 Mar 2004.
+
+Replaced hardwired references to /usr/local in makefile with $(prefix)
+(which defaults to /usr/local, but can be overridden on the command line),
+suggested by Nelson Beebe for use with Configure. 10 Mar 2004.
+
+From Nelson Beebe: In the Kermit makefile in the install target commands,
+line 981 reads:
+
+ cp $(BINARY) $(DESTDIR)$(BINDIR)/kermit || exit 1;\
+
+Could you please add this line before it:
+
+ rm -f $(DESTDIR)$(BINDIR)/kermit;\
+
+Some sites (mine included) keep multiple versions of software around,
+with hard links between $(prefix)/progname and $(prefix)/progname-x.y.z.
+Failure to remove the $(prefix)/progname at "make install" time then
+replaces the old $(prefix)/progname-x.y.z with the new one, destroying
+an old version that the site wanted to be preserved. makefile, 10 Mar 2004.
+
+Minor syntax and typo fixes (mostly prototypes): ckcdeb.h, ckcfns.c,
+ckclib.c, ckufio.c, ckuusr.h, ckuusx.c, 10 Mar 2004. (I still have a few
+more to do.)
+
+Added CC=$(CC) CC2=$(CC2) to many (but not all) makefile targets that
+reference other makefile targets. On some platforms (notably AIX, Solaris,
+SunOS) there are specific targets for different compilers, so I skipped
+those. makefile, 10 Mar 2004.
+
+Added error checking to kermit:// URL macros, so they don't plow ahead
+after the connection is closed. ckcmai.c, 11 Mar 2004.
+
+Added FreeBSD 4.9 and 5.1 targets (only the herald is affected).
+makefile, ckuver.h, 11 Mar 2004.
+
+Added "LIBS=-lcrypt" to bsd44 targets since nowadays crypt is almost always
+unbundled from libc. Also added explanatory notes. makefile, 11 Mar 2004.
+
+Changed MANDIR to default to $(manroot)/man/man1, and manroot to default
+to $(prefix). More adding of CC=$(CC) clauses: {Free,Net,Open}BSD, 4.4BSD.
+makefile, 11 Mar 2004.
+
+Miscellaneous cleanups: ckuusx.c, ckcnet.c, ckufio.c, 11 Mar 2004.
+
+Corrected the check in the linux target to see if /usr/include/crypt.h
+exists, and if so to define HAVE_CRYPT_H, which is used in ckcdeb.h to
+#include <crypt.h> to get the prototype for crypt() and prevent bogus
+conversions on its return type on 64-bit platforms (the previous test wasn't
+quite right and the resulting symbol wasn't spelled right). makefile,
+12 Mar 2004.
+
+From Jeff, 14 Mar 2004:
+ . Initialize localuidbuf[] in tn_snenv(): ckctel.c.
+ . Remove remote-mode checks in hupok() for K95G only (why?): ckuus3.c.
+ . Add help text for new K95-only TYPE /GUI switches: ckuus2.c.
+ . TYPE /GUI parsing, ...: ckuusr.c.
+ . TYPE /GUI action, dotype(): ckuus6.c
+ . Change Jeff's affiliation: most modules.
+
+20 Mar 2004: Looked into adding long file support, i.e. handling files more
+than 2GB (or 4GB) long. Discovered very quickly this would be a major
+project. Each platform has a different API, or environment, or transition
+plan, or whatever -- a nightmare to handle in portable code. At the very
+least we'll need to convert a lot of Kermit variables from long or unsigned
+long to some new Kermit type, which in turn is #defined or typedef'd
+appropriately for each platform (to off_t or size_t or whatever). Then we
+have to worry about the details of open() vs fopen(); printf() formats (%lld
+vs %Ld vs %"PRId64"...), platforms like HP-UX where you might have to use
+different APIs for different file systems on the same computer, etc. We'll
+need to confront this soon, but let's get a good stable 8.0.211 release out
+first! Meanwhile, for future reference, here are a few articles:
+
+General: http://freshmeat.net/articles/view/709/
+Linux: http://www.ece.utexas.edu/~luo/linux_lfs.html
+HP-UX: http://devrsrc1.external.hp.com/STK/partner/lg_files.pdf
+Solaris: http://wwws.sun.com/software/whitepapers/wp-largefiles/largefiles.pdf
+
+Looked into FTP timeouts. It appears I can just call empty() (which is
+nothing more than a front end for select()) with the desired timeout before
+any kind of network read. If it returns <= 0, we have a timeout. This is
+not quite the same as using alarm() / signal() around a recv() (which could
+get stuck) but alarm() / signal() are not not used in the FTP module and are
+not naturally portable to Windows, but select() is already in use in the FTP
+module for both Unix and Windows. This form of timeout could be used
+portably for both command response and data reads. What about writes to the
+command or data socket? They can get stuck for hours and hours without
+returning too, but the select() approach won't help here -- we need the
+actual send() or recv() to time out, or be wrapped in an alarm()/signal()
+kind of mechanism. But if we can do that for sends, we can also do it for
+receives. Better check with Jeff before I start programming anything.
+20 Mar 2004.
+
+Later: Decided to postpone the above two projects (ditto IPv6) until after
+8.0.211 is released because both will have major impacts on portability.
+Grumble: all i/o APIs should have been designed from the beginning with a
+timeout parameter. To this day, hardly any have this feature.
+
+3-4 Apr 2004: More 8.0.211 Beta.02+ test builds:
+
+ . FreeBSD 3.3
+ . FreeBSD 4.4
+ . Linux Debian 2.1
+ . Linux RH 6.1
+ . Linux RH 7.1
+ . Linux RH 7.2
+ . Linux RH 9 (with 84 different combinations of feature selection)
+ . Linux SuSE 6.4
+ . Linux SuSE 7.0
+ . NetBSD 1.4.1
+ . NetBSD 1.5.2
+ . OpenBSD 2.5
+ . OpenBSD 3.0
+ . QNX 4.25
+ . SCO UnixWare 2.1.3
+ . SCO UnixWare 7.1.4
+ . SCO OpenServer 5.0.7
+ . SCO XENIX 2.3.4 (no TCP)
+
+Changes needed: None.
+
+Problem: SCO XENIX 2.3.4 network build failed in the FTP module with
+header-file syntax and conflicting-definitions trouble. I'm not going to
+try to fix it; 8.0.209 built OK with FTP, so we'll just keep that one
+available.
+
+Got access to VMS 8.1 on IA64. Building the nonet version of C-Kermit
+required minor modifications to ckvvms.h, ckv[ft]io.c, and ckvcon.c, to
+account for a third architecture. Also to SHOW FEATURES in ckuus5.c. Once
+that was done, the UCX 5.5 version built OK too. Starts OK, makes Telnet
+connection OK, sends files. Has some obvious glitches though -- "stat"
+after a file transfer reports 0 elapsed time (in fact it was 00:09:48) and
+1219174400 cps (when in fact it was 10364). This doesn't happen on the
+Alpha. Btw, the IA64 binary is twice as big as the Alpha one. Changed
+to Beta.03. 5 Apr 2004.
+
+Fixed the ckdaily script to include the makefile and man page in the Zip
+file (they were not included because the Zip file was intended mainly for
+VMS users, but some Unix users prefer Zip to tar.gz). 6 Apr 2004.
+
+Traced problems in VMS/IA64 statistics report to rftimer()/gftimer() in
+ckvtio.c, which use sys$ and lib$ calls to figure elapsed time. These work
+on VAX and Alpha but not IA64. Sent a report to the chief engineer of the
+IA64 VMS port; he says it's probably a bug in VMS 8.1 (which is not a real
+release); he'll make sure it's fixed in 8.2. As an experiment, tried
+swapping in the Unix versions of these routines (which call gettimeofday()
+etc). They seem work just fine (it hung a couple times but I think that's
+because the underlying system hung too; trying it later on a new connection,
+it was fine; however I noticed a BIG discrepancy in throughput between
+sending and receiving). Moved definitions for VMS64BIT and VMSI64 to
+ckcdeb.h so all modules can use them and added them to the SHOW FEATURES
+display. Added VMSV80 definition to build procedure. Beta.03+. ckcdeb.h,
+ckcuus5.c, ckcvvms.h, ckvtio.c, ckvker.com, 6 Apr 2004.
+
+While doing the build-all, I noticed the VMS version did not build with
+Multinet or older UCX versions, always with the same errors -- undeclared
+variables, undefined symbols, all TCP/IP related. This didn't happen a
+couple weeks ago... Somehow the order of #includes was messed up --
+ckuusr.h depended on symbols that are defined in ckcnet.h, but ckcnet.h
+was being included after ckuusr.h... this was compounded by two missing
+commas in ckvker.com. 11 Apr 2004.
+
+Removed Beta designation, released as 8.0.211, 10 Apr 2004.
+
+---8.0.211---
+
+I had somehow lost the edit to ckutio.c that changed the UUCP lockfile for
+Mac OS X from /var/spool/uucp to /var/spool/lock. So I slipped it in and
+re-uploaded version 8.0.211. You can tell the difference because SHOW
+VERSIONS has a 17 Apr 2004 for the Communications I/O module. Also the 10.3
+executable now has a designer banner: "Mac OS X 10.3". makefile, ckuver.h,
+ckutio.c, ckuus[45].c, 17 Apr 2004.
+
+***********************
diff --git a/ckermit-8.0.211/ckcasc.h b/ckermit-8.0.211/ckcasc.h
new file mode 100644
index 0000000..c7c32cc
--- /dev/null
+++ b/ckermit-8.0.211/ckcasc.h
@@ -0,0 +1,69 @@
+/*
+ File CKCASC.H
+ Mnemonics for ASCII control characters (and Space) for use with C-Kermit.
+*/
+/*
+ Author: Frank da Cruz (fdc@columbia.edu).
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+#ifndef CKCASC_H
+#define CKCASC_H
+
+#define NUL '\0' /* Null Ctrl-@*/
+#define SOH 1 /* Start of header Ctrl-A */
+#define STX 2 /* Ctrl-B */
+#define ETX 3 /* Ctrl-C */
+#define EOT 4 /* Ctrl-D */
+#define ENQ 5 /* ENQ Ctrl-E */
+#define ACK 6 /* Ctrl-F */
+#define BEL 7 /* Bell (Beep) Ctrl-G */
+#define BS 8 /* Backspace Ctrl-H */
+#define HT 9 /* Horizontal Tab Ctrl-I */
+#define LF 10 /* Linefeed Ctrl-J */
+#define VT 11 /* Vertical Tab Ctrl-K */
+#define NL '\n' /* Newline */
+#define FF 12 /* Formfeed Ctrl-L */
+#define CR 13 /* Carriage Return Ctrl-M */
+#define SO 14 /* Shift Out Ctrl-N */
+#define SI 15 /* Shift In Ctrl-O */
+#define DLE 16 /* Datalink Escape Ctrl-P */
+#define XON 17 /* XON Ctrl-Q */
+#define DC1 17
+#define DC2 18 /* Ctrl-R */
+#define XOFF 19 /* XOFF Ctrl-S */
+#define DC3 19
+#define DC4 20 /* Ctrl-T */
+#define NAK 21 /* Ctrl-U */
+#define SYN 22 /* SYN, Ctrl-V */
+#define ETB 23 /* Ctrl-W */
+#define CAN 24 /* CAN, Ctrl-X */
+#define EM 25 /* Ctrl-Y */
+#define SUB 26 /* SUB Ctrl-Z */
+#define ESC 27 /* Escape Ctrl-[ */
+#define XFS 28 /* Field Separator, Ctrl-Backslash */
+#define XGS 29 /* Group Separator, Ctrl-Rightbracket */
+#define XRS 30 /* Record Separator, Ctrl-Circumflex */
+#define US 31 /* Unit Separator, Ctrl-Underscore */
+#define SP 32 /* Space */
+#define DEL 127 /* Delete (Rubout) */
+#define RUB 127 /* Delete (Rubout) */
+
+#ifdef OS2
+/*
+ These are needed in OS/2, so let's not cause any unnecessary conflicts.
+*/
+#define _CSI 0233 /* 8-bit Control Sequence Introducer */
+#define _SS2 0216 /* 8-bit Single Shift 2 */
+#define _SS3 0217 /* 8-bit Single Shift 3 */
+#define _DCS 0220 /* 8-bit Device Control String Introducer */
+#define _ST8 0234 /* 8-bit String Terminator */
+#define _OSC 0235 /* 8-bit Operating System Command */
+#define _PM8 0236 /* 8-bit Privacy Message */
+#define _APC 0237 /* 8-bit Application Program Command */
+#endif /* OS2 */
+#endif /* CKCASC_H */
diff --git a/ckermit-8.0.211/ckcbwr.txt b/ckermit-8.0.211/ckcbwr.txt
new file mode 100644
index 0000000..5e70e65
--- /dev/null
+++ b/ckermit-8.0.211/ckcbwr.txt
@@ -0,0 +1,1467 @@
+
+ C-Kermit 8.0 General Hints and Tips
+
+ Frank da Cruz
+ [1]The Kermit Project, [2]Columbia University
+
+ As of: C-Kermit 8.0.211, 17 March 2003
+ This page last updated: Sat Apr 10 16:37:37 2004 (New York USA Time)
+
+ IF YOU ARE READING A PLAIN-TEXT version of this document, it is a
+ plain-text dump of a Web page. You can visit the original (and
+ possibly more up-to-date) Web page here:
+
+ [3]http://www.columbia.edu/kermit/ckcbwr.html
+
+ This document contains platform-independent C-Kermit hints and tips.
+ Also see the platform-specific C-Kermit hints and tips document for
+ your platform, for example:
+
+ [4]http://www.columbia.edu/kermit/ckubwr.html
+
+ for Unix. This document also applies to [5]Kermit 95 for Windows,
+ which is based on C-Kermit.
+
+ [ [6]C-Kermit ] [ [7]TUTORIAL ]
+ ________________________________________________________________________
+
+ CONTENTS
+
+ 0. [8]PATCHES
+ 1. [9]INCOMPATIBLE CHANGES
+ 2. [10]THE C-KERMIT COMMAND PARSER
+ 3. [11]MULTIPLE SESSIONS
+ 4. [12]NETWORK CONNECTIONS
+ 5. [13]MODEMS AND DIALING
+ 6. [14]DIALING HINTS AND TIPS
+ 7. [15]TERMINAL SERVERS
+ 8. [16]TERMINAL EMULATION
+ 9. [17]KEY MAPPING
+ 10. [18]FILE TRANSFER
+ 11. [19]SCRIPT PROGRAMMING
+ ________________________________________________________________________
+
+ 0. PATCHES
+
+ [ [20]Top ] [ [21]Contents ] [ [22]Next ]
+
+ Source-level patches for C-Kermit 8.0.211:
+
+ (None)
+ ________________________________________________________________________
+
+ 1. INCOMPATIBLE CHANGES
+
+ [ [23]Top ] [ [24]Contents ] [ [25]Next ]
+
+ These are not necessarily exhaustive lists.
+
+ 1.1. C-Kermit 6.0
+
+ C-Kermit 6.0 was released 6 September 1996 and is completely
+ documented in [26]Using C-Kermit, 2nd Edition. The following
+ incompatible changes were made in C-Kermit 6.0:
+
+ * Unless you tell C-Kermit otherwise, if a serial or network
+ connection seems to be open, and you attempt to EXIT or to open a
+ new connection, C-Kermit warns you that an active connection
+ appears to be open and asks you if you really want to close it. If
+ you do not want these warnings, add SET EXIT WARNING OFF to your
+ customization file or script, or give this command at the prompt.
+ * The default for SET { SEND, RECEIVE } PATHNAMES was changed from
+ ON to OFF, to prevent unexpected creation of directories and
+ depositing of incoming files in places you might not know to look.
+ * The default for SET FILE INCOMPLETE was changed from DISCARD to
+ KEEP to allow for file transfer recovery.
+ * The default file-transfer block-check is now 3, rather than 1. If
+ the other Kermit does not support this, the two will drop back to
+ type 1 automatically unless the other Kermit fails to follow the
+ protocol specification.
+ * The default flow-control is now "auto" ("do the right thing for
+ each type of connection"), not Xon/Xoff.
+ * Backslash (\) is no longer a command continuation character. Only
+ - (hyphen, dash) may be used for this in C-Kermit 6.0 and later.
+ * Negative INPUT timeout now results in infinite wait, rather than 1
+ second.
+
+ 1.2. C-Kermit 7.0
+
+ C-Kermit 7.0 was released 1 January 2000. Its new features are
+ documented in the C-Kermit 7.0 Supplement,
+ [27]http://www.columbia.edu/kermit/ckermit2.html. The following
+ incompatible changes were made in C-Kermit 7.0:
+ * The "multiline GET" command is gone. Now use either of the
+ following forms instead:
+ get remote-name local-name
+ get /as-name:local-name remote-name
+ If either name contains spaces, enclose it in braces (or, in
+ C-Kermit 8.0, doublequotes).
+ * To include multiple file specifications in a GET command, you must
+ now use MGET rather than GET:
+ mget file1 file2 file3 ...
+ * C-Kermit 7.0 and later use FAST Kermit protocol settings by
+ default. This includes "unprefixing" of certain control
+ characters. Because of this, file transfers that worked with
+ previous releases might not work in the new release especially
+ against a non-Kermit-Project Kermit protocol implementation (but
+ it is more likely that they will work, and much faster). If a
+ transfer fails, you'll get a context-sensitive hint suggesting
+ possible causes and cures. Usually SET PREFIXING ALL does the
+ trick.
+ * By default C-Kermit 7.0 and later send files in text or binary
+ mode by looking at each file to see which is the appropriate mode.
+ To restore the previous behavior, put SET TRANSFER MODE MANUAL and
+ the desired SET FILE TYPE (TEXT or BINARY) in your C-Kermit
+ initialization file.
+ * The RESEND and REGET commands automatically switch to binary mode;
+ previously if RESEND or REGET were attempted when FILE TYPE was
+ TEXT, these commands would fail immediately, with a message
+ telling you they work only when the FILE TYPE is BINARY. Now they
+ simply do this for you.
+ * SET PREFIXING CAUTIOUS and MINIMAL now both prefix linefeed (10
+ and 138) in case rlogin, ssh, or cu are "in the middle", since
+ otherwise <LF>~ might appear in Kermit packets, and this would
+ cause rlogin, ssh, or cu to disconnect, suspend,escape back, or
+ otherwise wreck the file transfer. Xon and Xoff are now always
+ prefixed too, even when Xon/Xoff flow control is not in effect,
+ since unprefixing them has proven dangerous on TCP/IP connections.
+ * In UNIX, VMS, Windows, and OS/2, the DIRECTORY command is built
+ into C-Kermit itself rather than implemented by running an
+ external command or program. The built-in command might not behave
+ the way the platform-specific external one did, but many options
+ are available for customization. Of course the underlying
+ platform-specific command can still be accessed with "!", "@", or
+ "RUN" wherever the installation does not forbid. In UNIX, the "ls"
+ command can be accessed directly as "ls" in C-Kermit.
+ * SEND ? prints a list of switches rather than a list of filenames.
+ If you want to see a list of filenames, use a (system-dependent)
+ construction such as SEND ./? (for UNIX, Windows, or OS/2), SEND
+ []? (VMS), etc.
+ * In UNIX, OS-9, and Kermit 95, the wildcard characters in previous
+ versions were * and ?. In C-Kermit 7.0 they are *, ?, [, ], {, and
+ }, with dash used inside []'s to denote ranges and comma used
+ inside {} to separate list elements. If you need to include any of
+ these characters literally in a filename, precede each one with
+ backslash (\).
+ * SET QUIET { ON, OFF } is now on the command stack, just like SET
+ INPUT CASE, SET COUNT, SET MACRO ERROR, etc, as described on p.458
+ of [28]Using C-Kermit, 2nd Edition. This allows any macro or
+ command file to SET QUIET ON or OFF without worrying about saving
+ and restoring the global QUIET value. For example, this lets you
+ write a script that tries SET LINE on lots of devices until it
+ finds one free without spewing out loads of error messages, and
+ also without disturbing the global QUIET setting, whatever it was.
+ * Because of the new "." operator (which introduces assignments),
+ macros whose names begin with "." can not be invoked "by name".
+ However, they still can be invoked with DO or \fexecute().
+ * The syntax of the EVALUATE command has changed. To restore the
+ previous syntax, use SET EVALUATE OLD.
+ * The \v(directory) variable now includes the trailing directory
+ separator; in previous releases it did not. This is to allow
+ constructions such as:
+ cd \v(dir)data.tmp
+ to work across platforms that might have different directory
+ notation, such as UNIX, Windows, and VMS.
+ * Prior to C-Kermit 7.0, the FLOW-CONTROL setting was global and
+ sticky. In C-Kermit 7.0, there is an array of default flow-control
+ values for each kind of connection, that are applied automatically
+ at SET LINE/PORT/HOST time. Thus a SET FLOW command given before
+ SET LINE/PORT/HOST is likely to be undone. Therefore SET FLOW can
+ be guaranteed to have the desired effect only if given after the
+ SET LINE/PORT/HOST command.
+ * Character-set translation works differently in the TRANSMIT
+ command when (a) the file character-set is not the same as the
+ local end of the terminal character-set, or (b) when the terminal
+ character-set is TRANSPARENT.
+
+ 1.3. C-Kermit 8.0
+
+ The following incompatible changes were made in C-Kermit 8.0:
+ * C-Kermit now accepts doublequotes in most contexts where you
+ previously had to use braces to group multiple words into a single
+ field, or to force inclusion of leading or trailing blanks. This
+ might cause problems in contexts where you wanted the doublequote
+ characters to be taken literally. Consult [29]Section 5 of the
+ [30]C-Kermit 8.0 Update Notes for further information.
+ * Using the SET HOST command to make HTTP connections is no longer
+ supported. Instead, use the new [31]HTTP OPEN command.
+ ________________________________________________________________________
+
+ 2. THE C-KERMIT COMMAND PARSER
+
+ [ [32]Top ] [ [33]Contents ] [ [34]Next ] [ [35]Previous ]
+
+ Various command-related limits are shown in the following table, in
+ which the sample values are for a "large memory model" build of
+ C-Kermit, typical for modern platforms (Linux, Solaris, AIX, VMS,
+ etc). You can see the values for your version of Kermit by giving the
+ SHOW FEATURES command. The maximum length for a Kermit command (CMDBL)
+ also determines the maximum length for a macro definition, since
+ DEFINE is itself a command. The maximum length for a variable name is
+ between 256 and 4096 characters, depending on the platform; for array
+ declarations and references, that includes the subscript.
+ ______________________________________________________________
+
+ Item Symbol Sample
+ Value Definition
+ Number of characters in a command CMDBL 32763 ckucmd.h
+ Number of chars in a field of a command ATMBL 10238 ckucmd.h
+ Nesting level for command files MAXTAKE 54 ckuusr.h
+ Nesting level for macros MACLEVEL 128 ckuusr.h
+ Nesting level for FOR / WHILE loops FORDEPTH 32 ckuusr.h
+ Number of macros MAC_MAX 16384 ckuusr.h
+ Size of INPUT buffer INPBUFSIZ 4096 ckuusr.h
+ Maximum files to match a wildcard MAXWLD 102400 ckcdeb.h
+ Filespecs in MSEND command MSENDMAX 1024 ckuusr.h
+ Length for GOTO target label LBLSIZ 50 ckuusr.h
+ \fexecute() recursion depth limit CMDDEP 64 ckucmd.h
+ ______________________________________________________________
+
+ If you need to define a macro that is longer than CMDBL, you can break
+ the macro up into sub-macros or rewrite the macro as a command file.
+ In a pinch you can also redefine CMDBL and recompile C-Kermit. All of
+ these numbers represent tradeoffs: the bigger the number, the more
+ "powerful" Kermit in the corresponding area, but also the bigger the
+ program image and possibly disk footprint, and the longer it takes to
+ load and initialize.
+
+ In the interactive command parser:
+
+ * EMACS- or VI-style command line editing is not supported.
+ * Editing keys are hardwired (Ctrl-U, Ctrl-W, etc).
+
+ If you interrupt C-Kermit before it has issued its first prompt, it
+ will exit. This means that you cannot interrupt execution of the
+ initialization file, or of an "application file" (file whose name is
+ given as the first command-line argument), or of an alternative
+ initialization file ("-y filename"), and get to the prompt. There is,
+ however, one exception to this rule: you *can* interrupt commands --
+ including TAKE commands -- given in the '-C "command list"'
+ command-line argument and -- if there were no action commands among
+ the command-line arguments -- you will be returned to the C-Kermit
+ prompt. So, for example, if you want to start C-Kermit in such a way
+ that it executes a command file before issuing its first prompt, and
+ you also want to be able to interrupt the command file and get to the
+ prompt, include a TAKE command for the desired command in the -C
+ argument, for example:
+
+ kermit -C "take dial.scr"
+
+ At the command prompt, if you use the backslash (\) prefix to enter a
+ control character, space, or question mark into a command literally,
+ the backslash disappears and is replaced by the quoted character. If
+ it was a control character, it is shown as a circumflex (^). This
+ allows editing (backspace, delete, Ctrl-W) to work correctly even for
+ control characters.
+
+ Priot to C-Kermit 8.0, the only way to include a comma literally in a
+ macro definition -- as opposed to having it separate commands within
+ the definition -- is to enter its ASCII value (44) in backslash
+ notation, e.g.:
+
+ DEFINE ROWS RUN MODE CO80\{44}\%1
+
+ In C-Kermit 8.0 you can use constructions like this:
+
+ DEFINE ROWS RUN MODE "CO80,\%1"
+
+ If you quote special characters in a filename (e.g. in the SEND
+ command), filename completion may seem to work incorrectly. For
+ example, if you have a file whose name is a*b (the name really
+ contains an asterisk), and you type "send a\\*<ESC>", the "b" does not
+ appear, nor will Ctrl-R redisplay the completed name correctly. But
+ internally the file name is recognized anyway.
+
+ Question-mark help does not work during execution of an ASKQ command.
+ The question marks are simply accepted as text.
+
+ In OUTPUT commands only, \B sends a BREAK signal, \L sends a Long
+ BREAK signal, and \N sends a NUL (ASCII 0). BREAK and Long BREAK are
+ special signals, not characters, and NUL is a character that normally
+ cannot be included in a C string, since it is the C string terminator.
+ If you really want to output a backslash followed by a B, an L, or an
+ N (as is needed to configure certain modems, etc), double the
+ backslash, e.g. "output \\B". In C-Kermit 7.0 or later, you can disarm
+ and re-arm the special OUTPUT-command escapes (\B, \L, and \N) with
+ SET OUTPUT SPECIAL-ESCAPES { OFF, ON }.
+
+ When using the command-line processor ("kermit -l /dev/tty00 -b
+ 19200", etc), note that in some cases the order of the command-line
+ options makes a difference, contrary to the expectation that order of
+ command-line options should not matter. For example, the -b option
+ must be given after the -l option if it is to affect the device
+ specified in the -l option.
+ ________________________________________________________________________
+
+ 3. MULTIPLE SESSIONS
+
+ [ [36]Top ] [ [37]Contents ] [ [38]Next ] [ [39]Previous ]
+
+ C-Kermit 7.0 and earlier do not support multiple sessions. When you
+ SET LINE (or SET PORT, same thing) to a new device, or SET HOST to a
+ new host, the previous SET LINE device or network host connection is
+ closed, resulting in hangup of the modem or termination of the network
+ connection. In windowing environments like HP-VUE, NeXTSTEP, Windows,
+ OS/2, etc, you can run separate copies of Kermit in different windows
+ to achieve multiple sessions.
+
+ To achieve multiple sessions through a single serial port (e.g. when
+ dialing up), you can install SLIP or PPP on your computer and then use
+ C-Kermit's TCP/IP support over the SLIP or PPP connection, assuming
+ you also have TCP/IP networking installed on your computer.
+
+ C-Kermit 8.0 has the same restriction on SET LINE and SET HOST
+ sessions: only one regular session (dialout, Telnet, etc) can be open
+ at a time. However, version 8.0 adds two new kinds of sessions: FTP
+ and HTTP; one or both of these can be open at the same as a regular
+ session.
+ ________________________________________________________________________
+
+ 4. NETWORK CONNECTIONS
+
+ [ [40]Top ] [ [41]Contents ] [ [42]Next ] [ [43]Previous ]
+
+ FTP Client Bugs
+
+ The Unix C-Kermit 8.0.206 FTP client had the following bugs at the
+ time most of the 8.0.206 binaries were built for the C-Kermit 8.0
+ CDROM:
+
+ 1. FTP MGET fails when directory segments contain wildcards, as in
+ "ftp mget */data/*.dat". Work around by doing a separate MGET for
+ each source directory.
+ 2. FTP MGET can fail or produce random side effects if you have a
+ TMPDIR or CK_TMP environment variable definition in effect, or a
+ SET TEMP-DIRECTORY value, longer than 7 characters. Work around by
+ giving a SET TEMP-DIRECTORY command with a short value, such as
+ "/tmp".
+
+ These two bugs are fixed in the source code that is included on the
+ CDROM, and also in Kermit 95 2.1.1. You can tell if a C-Kermit 8.0.206
+ binary has these fixes by typing SHOW VERSION; if it says "FTP Client,
+ 8.0.200, 24 Oct 2002" it has the fixes; if the edit number is less
+ that 200, it doesn't, in which case can build a new binary from the
+ source code (or contact us and we'll try to get get one for you).
+
+ Making TCP/IP Connections Can Take a Long Time
+
+ The most frequently asked question in many newsgroups is "Why does it
+ take such a long time to make a Telnet connection to (or from) my
+ (e.g.) Linux PC?" (this applies to Kermit as well as to regular Telnet
+ clients):
+
+ 1. Most Telnet servers perform reverse DNS lookups on the client for
+ security and/or logging reasons. If the Telnet client's host
+ cannot be found by the server's local DNS server, the DNS request
+ goes out to the Internet at large, and this can take quite some
+ time. The solution to this problem is to make sure that both
+ client and host are registered in DNS.
+ 2. C-Kermit itself performs reverse DNS lookups unless you tell it
+ not to. This is to allow C-Kermit to let you know which host it is
+ actually connected to in case you have made a connection to a
+ "host pool" (multihomed host). You can disable C-Kermit's reverse
+ DNS lookup with SET TCP REVERSE-DNS-LOOKUP OFF.
+ 3. C-Kermit 7.0 and later strictly enforce Telnet protocol rules. One
+ such rule is that certain negotiations must be responded to. If
+ C-Kermit sends a such a negotiation and the host does not respond,
+ C-Kermit waits a long time for the reply (in case the network is
+ congested or the host is slow), but eventually will time out. To
+ eliminate the waits (and therefore risk possible protocol
+ mismatches -- or worse -- between Telnet client and server), tell
+ C-Kermit to SET TELNET WAIT OFF (or include the /NOWAIT switch
+ with the TELNET command).
+
+ The Rlogin Client
+
+ In multiuser operating systems such as UNIX and VMS, TCP/IP Rlogin
+ connections are available only to privileged users, since "login" is a
+ privileged socket. Assuming you are allowed to use it in the first
+ place, it is likely to behave differently depending on what type of
+ host you are rlogging in to, due to technical reasons having to do
+ with conflicting interpretations of RFC793 (Out-Of-Band Data) and
+ Rlogin (RFC1122)... "Specifically, the TCP urgent pointer in BSD
+ points to the byte after the urgent data byte, and an RFC-compliant
+ TCP urgent pointer points to the urgent data byte. As a result, if an
+ application sends urgent data from a BSD-compatible implementation to
+ an [44]RFC-1122 compatible implementation then the receiver will read
+ the wrong urgent data byte (it will read the byte located after the
+ correct byte in the data stream as the urgent data byte)." Rlogin
+ requires the use of OOB data while Telnet does not. Therefore, it is
+ possible for Telnet to work between all systems while BSD and System V
+ TCP/IP implementations are almost always a bad mix.
+
+ The Telnet Client
+
+ On a TCP/IP TELNET connection, you should normally have PARITY set to
+ NONE and (except in VMS C-Kermit) FLOW-CONTROL also set to NONE. If
+ file transfer does not work with these settings (for example, because
+ the remote TELNET server only gives a 7-bit data path), use SET PARITY
+ SPACE. Do not use SET PARITY MARK, EVEN, or ODD on a TELNET connection
+ -- it interferes with TELNET protocol.
+
+ If echoing does not work right after connecting to a network host or
+ after dialing through a TCP/IP modem server, it probably means that
+ the TELNET server on the far end of the connection is executing the
+ TELNET protocol incorrectly. After initially connecting and
+ discovering incorrect echoing (characters are echoed twice, or not at
+ all), escape back, give the appropriate SET DUPLEX command (FULL or
+ HALF), and then CONNECT again. For a consistently misbehaving
+ connection, you can automate this process in a macro or TAKE file.
+
+ TELNET sessions are treated just like serial communications sessions
+ as far as "terminal bytesize" and "command bytesize" are concerned. If
+ you need to view and/or enter 8-bit characters during a TELNET
+ session, you must tell C-Kermit to SET TERMINAL BYTESIZE 8, SET
+ COMMAND BYTESIZE 8, and SET PARITY NONE.
+
+ If you SET TELNET DEBUG ON prior to making a connection, protocol
+ negotiations will be displayed on your screen. You can also capture
+ them in the debug log (along with everything else) and then extract
+ them easily, since all Telnet negotiations lines begin with
+ (uppercase) "TELNET".
+ ________________________________________________________________________
+
+ 5. MODEMS AND DIALING
+
+ [ [45]Top ] [ [46]Contents ] [ [47]Next ] [ [48]Previous ]
+
+ External modems are recommended because:
+
+ * They don't need any special drivers.
+ * They are less likely to interfere with normal operation of your
+ computer.
+ * You can use the lights and speaker to troubleshoot dialing.
+ * You can share them among all types of computers.
+ * You can easily turn them off and on when power-cycling seems
+ warranted.
+ * They are more likely to have manuals.
+
+ Modems can be used by C-Kermit only when they are visible as or
+ through a regular serial port device. Certain modems can not be used
+ in this normal way on many kinds of computers: Winmodems, RPI modems,
+ Controllerless modems, the IBM Mwave, etc; all of these require
+ special drivers that perform some, most, or all of the modem's
+ functions in software. Such drivers are generally NOT available in
+ UNIX or other non-Windows (or non-OS/2, in the case of the Mwave)
+ platforms.
+
+ In order to dial a modem, C-Kermit must know its repertoire of
+ commands and responses. Each modem make and model is likely to have a
+ different repertoire. Since Kermit has no way of knowhing which kind
+ of modem will be dialed, normally you have to tell it with a SET MODEM
+ TYPE command, e.g.:
+
+ set modem type usrobotics
+ set line /dev/cua0
+ set speed 57600
+ dial 7654321
+
+ In the early days, there was a wide variety of modems and command
+ languages. Nowadays, almost every modem uses the Hayes AT command set
+ (but with some differences in the details) and its startup
+ configuration includes error correction, data compression, and
+ hardware (RTS/CTS) flow control. As long as C-Kermit is capable of
+ hardware flow control (as it is on many, but not all, the platforms
+ where it runs, since some operating systems don't support it), the
+ modem can be dailed immediately, without lengthy configuration
+ dialogs, and in fact this is what SET MODEM TYPE GENERIC-HIGH-SPEED
+ does. In C-Kermit 8.0, GENERIC-HIGH-SPEED has become the default modem
+ type, so now it is usually possible to SET LINE, SET SPEED, and DIAL
+ without having to identify your modem. If this doesn't work, of
+ course, then you might have to fall back to the tradiational method:
+ Give a SET MODEM TYPE for a specific modem first, then SET LINE, SET
+ SPEED, and DIAL.
+
+ An important change in C-Kermit 6.0 is that when you give a SET MODEM
+ TYPE command to tell Kermit what kind of modem you have, Kermit also
+ sets a number of other modem-related parameters automatically from its
+ internal modem database. Thus, the order in which you give
+ modem-related commands is significant, whereas in prior releases they
+ could be given in any order.
+
+ In particular, MODEM SPEED-MATCHING is set according to whether the
+ modem is known to be capable of speed buffering. SET MODEM TYPE
+ HAYES-2400 automatically turns SPEED-MATCHING ON, because when the
+ Hayes 2400 reports a particular speed in its CONNECT message, that
+ means its interface speed has changed to that speed, and C-Kermit's
+ must change accordingly if it is to continue communicating. This might
+ cause some confusion if you use "set modem type hayes" for dialing a
+ more advanced type of modem.
+
+ The new default for flow control is "auto", meaning "do the right
+ thing for each type of connection". So (for example) if your version
+ of C-Kermit supports SET FLOW RTS/CTS and your modem also supports
+ RTS/CTS, then Kermit automatically sets its flow control to RTS/CTS
+ and set modem's flow control to RTS/CTS too before attempting to use
+ the modem.
+
+ For these reasons, don't assume that "set modem type hayes" should be
+ used for all modems that uses the Hayes AT command set. "set modem
+ type hayes" really does mean Hayes 1200 or 2400, which in turn means
+ no hardware flow control, and no speed buffering. This choice will
+ rarely work with a modern high-speed modem.
+ ________________________________________________________________________
+
+ 6. DIALING HINTS AND TIPS
+
+ [ [49]Top ] [ [50]Contents ] [ [51]Next ] [ [52]Previous ]
+
+ If you have a high-speed, error-correcting, data-compressing,
+ speed-buffering modem, you should fix the modem's interface speed as
+ high as possible, preferably (at least) four times higher than its
+ maximum connection (modulation) speed to allow compression to work at
+ full advantage. In this type of setup, you must also have an effective
+ means of flow control enabled between C-Kermit and the modem,
+ preferably hardware (RTS/CTS) flow control. On platforms that do not
+ support hardware flow control, it is usually possible to select
+ software flow control (Xon/Xoff), and C-Kermit will do its best to set
+ the modem for local Xon/Xoff flow control too (but then, of course,
+ Ctrl-S and Ctrl-Q characters can not be transmitted on the
+ connection).
+
+ If you are having trouble dialing your modem, SET DIAL DISPLAY ON to
+ watch the dialing interactions between C-Kermit and your modem.
+ Consult Chapters 3-4 of [53]Using C-Kermit (2nd Ed) for modem-dialing
+ troubleshooting instructions. The following sections offer some
+ addtional hints and tips.
+
+ 6.1. Syntax
+
+ If you want to dial a number that starts with #, you'll need to quote
+ the "#" character (as \# or \{35}), since it is also a comment
+ introducer:
+
+ C-Kermit>dial #98765421-1-212-5551212 ; Looks like a comment
+ ?You must specify a number to dial
+ C-Kermit>dial \#98765421-1-212-5551212 ; Works OK
+ C-Kermit>dial =#98765421-1-212-5551212 ; This works too
+
+ When using a dialing directory, remember what happens if a name is not
+ found:
+
+ C-Kermit>dial xyzcorp
+ Lookup: "xyzcorp" - not found - dialing as given
+
+ This normally does no harm, but some modems might behave strangely
+ when given dial strings that contain certain letters. For example, a
+ certain German modem treats any dial string that contains the letter
+ "s" as a command to fetch a number from its internal list, and replies
+ OK to the ATD command, which is normally not a valid response except
+ for partial dialing. To avoid this situation, use:
+
+ lookup xyzcorp
+ if success dial
+
+ 6.2. The Carrier Signal
+
+ Remember: In many C-Kermit implementations (depending on the
+ underlying operating system -- mostly Windows, OS/2, and
+ System-V-based UNIX versions, and in C-Kermit 7.0, also VMS), you
+ can't CONNECT to a modem and type the modem's dialing command (like
+ "ATDT7654321") manually, unless you first tell C-Kermit to:
+
+ SET CARRIER-WATCH OFF
+
+ This is because (in these implementations), the CONNECT command
+ requires the modem's Carrier Detect (CD) signal to be on, but the CD
+ signal doesn't come on until after dialing is complete. This
+ requirement is what allows C-Kermit to pop back to its prompt
+ automatically when the connection is hung up. See the description of
+ SET CARRIER-WATCH in "Using C-Kermit".
+
+ Similarly, if your dialed connection drops when CARRIER-WATCH is set
+ to AUTO or ON, you can't CONNECT back to the (now disconnected) screen
+ to see what might have happened unless you first SET CARRIER-WATCH
+ OFF. But sometimes not even SET CARRIER-WATCH OFF will help in this
+ situation: certain platforms (for example Unixware 2.1), once carrier
+ drops, won't let the application do i/o with the device any more. In
+ that case, if you want to use the device again, you have to CLOSE it
+ and OPEN it again. Or you can have Kermit do this for you
+ automatically by telling it to SET CLOSE-ON-DISCONNECT ON.
+
+ 6.3. Dialing and Flow Control
+
+ Don't SET FLOW RTS/CTS if your modem is turned off, or if it is not
+ presenting the CTS signal. Otherwise, the serial device driver can get
+ stuck waiting for this signal to appear.
+
+ Most modern modems support RTS/CTS (if they support any hardware flow
+ control at all), but some computers use different RS-232 circuits for
+ the same purposes, e.g. DTR and CD, or DTR and CTS. In such cases, you
+ might be able to make your computer work with your modem by
+ appropriately cross-wiring the circuits in the cable connector, for
+ example the computer's DTR to the modem's RTS, and modem's CD to the
+ computer's CTS. HOWEVER, C-Kermit does not know you have done this. So
+ if you have (say) SET FLOW DTR/CD, C-Kermit will make no attempt to
+ tell the modem to use RTS/CTS. You probably did this yourself when you
+ configured the modem.
+
+ 6.4. The Dial Timeout
+
+ If it takes your call longer to be completed than the timeout interval
+ that C-Kermit calculates, you can use the SET DIAL TIMEOUT command to
+ override C-Kermit's value. But beware: the modem has its own timeout
+ for completing the call. If it is a Hayes-like modem, C-Kermit adjusts
+ the modem's value too by setting register S7. But the maximum value
+ for S7 might be smaller than the time you need! In that case, C-Kermit
+ sets S7 to 0, 255, or other (modem-specific) value to signify "no
+ timeout". If Kermit attempts to set register S7 to a value higher than
+ your modem's maximum, the modem will say "ERROR" and you will get a
+ "Failure to initialize modem" error. In that case, use SET DIAL
+ TIMEOUT to override C-Kermit's calculation of the timeout value with
+ the highest value that is legal for your modem, e.g. 60.
+
+ 6.5. Escape Sequence Guard Time
+
+ A "TIES" (Time-Independent Escape Sequence) modem does not require any
+ guard time around its escape sequence. The following text:
+
+ +++ATH0
+
+ if sent through a TIES modem, for example because you were uploading
+ this file through it, could pop the modem back into command mode and
+ make it hang up the connection. Later versions of the Telebit T1600
+ and T3000 (version LA3.01E firmware and later), and all WorldBlazers,
+ use TIES.
+
+ Although the probability of "+++" appearing in a Kermit packet is
+ markedly lower than with most other protocols (see the [54]File
+ Transfer section below), it can still happen under certain
+ circumstances. It can also happen when using C-Kermit's TRANSMIT
+ command. If you are using a Telebit TIES modem, you can change the
+ modem's escape sequence to an otherwise little-used control character
+ such as Ctrl-_ (Control-Underscore):
+
+ AT S2=31
+
+ A sequence of three consecutive Ctrl-_ characters will not appear in a
+ Kermit packet unless you go to extraordinary lengths to defeat more
+ than a few of Kermit's built-in safety mechanisms. And if you do this,
+ then you should also turn off the modem's escape-sequence recognition
+ altogether:
+
+ AT S48=0 S2=255
+
+ But when escape sequence recognition is turned off, "modem hangup"
+ (<pause>+++<pause>ATH0<CR>) will not work, so you should also SET
+ MODEM HANGUP RS232-SIGNAL (rather then MODEM-COMMAND).
+
+ 6.6. Adaptive Dialing
+
+ Some modems have a feature called adaptive dialing. When they are told
+ to dial a number using Tone dialing, they check to make sure that
+ dialtone has gone away after dialing the first digit. If it has not,
+ the modem assumes the phone line does not accept Tone dialing and so
+ switches to Pulse. When dialing out from a PBX, there is almost always
+ a secondary dialtone. Typically you take the phone off-hook, get the
+ PBX dialtone, dial "9" to get an outside line, and then get the phone
+ company's dialtone. In a situation like this, you need to tell the
+ modem to expect the secondary dialtone. On Hayes and compatible
+ modems, this is done by putting a "W" in the dial string at the
+ appropriate place. For example, to dial 9 for an outside line, and
+ then 7654321, use ATDT9W7654321:
+
+ SET PBX-OUTSIDE-PREFIX 9W
+
+ (replace "9" with whatever your PBX's outside-line prefix is).
+
+ 6.7. The Busy Signal
+
+ Some phone companies are eliminating the busy signal. Instead, they
+ issue a voice message such as "press 1 to automatically redial until
+ the number answers, or...". Obviously this is a disaster for modem
+ calls. If your service has this feature, there's nothing Kermit can do
+ about it. Your modem will respond with NO CARRIER (after a long time)
+ rather than BUSY (immediately), and Kermit will declare the call a
+ failure, rather than trying to redial the same number.
+
+ 6.8. Hanging Up
+
+ There are two ways to hang up a modem: by turning off the serial
+ port's DTR signal (SET MODEM HANGUP-METHOD RS232-SIGNAL) or sending
+ the modem its escape sequence followed by its hangup command (SET
+ MODEM HANGUP-METHOD MODEM-COMMAND). If one doesn't work, try the
+ other. If the automatic hangup performed at the beginning of a DIAL
+ command causes trouble, then SET DIAL HANGUP OFF.
+
+ The HANGUP command has no effect when C-Kermit is in remote mode. This
+ is on purpose. If C-Kermit could hang up its own controlling terminal,
+ this would (a) most likely leave behind zombie processes, and (b) pose
+ a security risk.
+
+ If you DIAL a modem, disconnect, then SET HOST or TELNET, and then
+ HANGUP, Kermit sends the modem's hangup command, such as "+++ATHO".
+ There is no good way to avoid this, because this case can't reliably
+ be distinguished from the case in which the user does SET HOST
+ terminal-server, SET MODEM TYPE name, DIAL. In both cases we have a
+ valid modem type selected and we have a network connection. If you
+ want to DIAL and then later make a regular network connection, you
+ will have to SET MODEM TYPE NONE or SET DIAL HANGUP OFF to avoid this
+ phenomenon.
+ ________________________________________________________________________
+
+ 7. TERMINAL SERVERS
+
+ [ [55]Top ] [ [56]Contents ] [ [57]Next ] [ [58]Previous ]
+
+ Watch out for terminal server's escape character -- usually a control
+ character such as Ctrl-Circumflex (Ctrl-^). Don't unprefix it in
+ Kermit!
+
+ Ciscos -- must often be told to "terminal download"... Cisco ASM
+ models don't have hardware flow control in both directions.
+
+ Many terminal servers only give you a 7-bit connection, so if you
+ can't make it 8-bit, tell Kermit to "set parity space".
+
+ The following story, regarding trouble transferring 8-bit files
+ through a reverse terminal server, was contributed by an Annex
+ terminal server user:
+
+ Using C-Kermit on an HP 9000 712/80 running the HP-UX 10.0
+ operating system. The HP was connected to a Xylogics Annex
+ MICRO-ELS-UX R7.1 8 port terminal server via ethernet. On the
+ second port of the terminal server is an AT&T Paradyne 3810 modem,
+ which is connected to a telephone line. There is a program which
+ runs on the HP to establish a Telnet connection between a serial
+ line on the Annex and a character special file on the HP (/dev
+ file). This is an Annex specific program called rtelnet (reverse
+ telnet) and is provided with the terminal server software. The
+ rtelnet utility runs on top of the pseudo-terminal facility
+ provided by UNIX. It creates host-originiated connections to
+ devices attached ot Annex serial ports. There are several command
+ line arguments to be specified with this program: the IP address of
+ the terminal server, the number of the port to attach to, and the
+ name of the pseudo-device to create. In addition to these there are
+ options to tell rtelnet how to operate on the connect: -b requests
+ negotiation for Telnet binary mode, -d turns on socket-leve
+ debugging, -f enables "connect on the fly" mode, -r removes the
+ device-name if it already exists, etc. The most important of these
+ to be specified when using 8 data bits and no parity, as we found
+ out, was the -t option. This creates a transparent TCP connection
+ to the terminal server. Again, what we assumed to be happening was
+ that the rtelnet program encountered a character sequence special
+ to itself and then "eating" those kermit packets. I think this is
+ all of the information I can give you on the configuration, short
+ of the values associated with the port on the terminal server.
+
+ How to DIAL from a TCP/IP reverse terminal server (modem server):
+
+ 1. (only if necessary) SET TELNET ECHO REMOTE
+ 2. SET HOST terminal-server-ip-name-or-address [ port ]
+ 3. SET MODEM TYPE modem-type
+ 4. (only if necessary) SET DIAL HANGUP OFF
+ 5. (for troubleshooting) SET DIAL DISPLAY ON
+ 6. DIAL phone-number
+
+ The order is important: SET HOST before SET MODEM TYPE. Since this is
+ a Telnet connection, serial-port related commands such as SET SPEED,
+ SET STOP-BITS, HANGUP (when MODEM HANGUP-METHOD is RS232), etc, have
+ no effect. However, in C-Kermit 8.0, if the modem server supports
+ [59]RFC-2217 Telnet Com-Port Control protocol, these commands do
+ indeed take effect at the server's serial port.
+ ________________________________________________________________________
+
+ 8. TERMINAL EMULATION
+
+ [ [60]Top ] [ [61]Contents ] [ [62]Next ] [ [63]Previous ]
+
+ Except for the Windows, OS/2, and Macintosh versions, C-Kermit does
+ not emulate any kind of terminal. Rather, it acts as a
+ "semitransparent pipe", passing the characters you type during a
+ CONNECT session to the remote host, and sending the characters
+ received from the remote host to your screen. Whatever is controlling
+ your keyboard and screen provides the specific terminal emulation: a
+ real terminal, a PC running a terminal emulator, etc, or (in the case
+ of a self-contained workstation) your console driver, a terminal
+ window, xterm, etc.
+
+ Kermit is semitrantsparent rather than fully transparent in the
+ following ways:
+
+ * During a TELNET ("set host") session, C-Kermit itself executes the
+ TELNET protocol and performs TELNET negotiations. (But it does not
+ perform TN3270 protocol or any other type of 3270 terminal
+ emulation.)
+ * If you have changed your keyboard mapping using SET KEY, C-Kermit
+ replaces the characters you type with the characters or strings
+ they are mapped to.
+ * If you SET your TERMINAL CHARACTER-SET to anything but
+ TRANSPARENT, C-Kermit translates your keystrokes (after applying
+ any SET KEY definitions) before transmitting them, and translates
+ received characters before showing them on your screen.
+ * If your remote and/or local TERMINAL CHARACTER-SET is an ISO 646
+ 7-bit national character set, such as German, French, Italian,
+ Swedish, etc, or Short KOI used for Cyrillic, C-Kermit's CONNECT
+ command automatically skips over ANSI escape sequences to avoid
+ translating their characters. Only ANSI/ISO standard
+ (VT100/200/300-like) 7-bit escape sequence formats are supported
+ for this purpose, no proprietary schemes like H-P, Televideo,
+ Tektronix, etc.
+ * If your version of C-Kermit includes SET TERMINAL APC command,
+ then C-Kermit's CONNECT command will handle APC escape sequences
+ if TERMINAL APC is not set to OFF (which is the default).
+
+ You can make C-Kermit fully transparent by starting it with the -0
+ (dash zero) command-line option.
+
+ If you are running C-Kermit under a console driver, or in a terminal
+ window, that emulates the VT100, and use C-Kermit to log in to a VMS
+ system, the console driver or terminal window (not Kermit) is supposed
+ to reply to the "what are you?" query (ESC Z) from the VAX. If it
+ doesn't, and you can't make it do so, then you can (a) live with the
+ "unknown terminal" problem; (b) tell VMS to SET TERMINAL/DEVICE=VT100;
+ (c) program a key using SET KEY to send the appropriate sequence and
+ then punch the key at the right time; or (d) use the VMSLOGIN macro
+ that is defined in CKERMIT.INI to do this for you automatically.
+
+ SET SESSION-LOG { TEXT, BINARY }, which is effective in UNIX and
+ AOS/VS but not other C-Kermit versions, removes CR, DEL, NUL, XON, and
+ XOFF characters (Using C-Kermit neglects to mention that XON and XOFF
+ are removed). The TEXT-mode setting is ineffective during SCRIPT
+ command execution, as well as on X.25 connections.
+ ________________________________________________________________________
+
+ 9. KEY MAPPING
+
+ [ [64]Top ] [ [65]Contents ] [ [66]Next ] [ [67]Previous ]
+
+ Except in the terminal-emulating versions, C-Kermit's key mapping
+ facilities are limited to normal "ASCII" keys, and cannot be used with
+ function keys, arrow keys, arcane key combinations, etc. Since
+ C-Kermit runs on such a wide variety of hardware platforms (including,
+ for example, more than 360 different UNIX platforms), it is not
+ possible for C-Kermit to support every conceivable keyboard under
+ every release of every UNIX (or VMS, or ...) product on every
+ different kind of computer possibly under all manner of different
+ console drivers, even if it had the means to do so.
+
+ In technical terms, C-Kermit uses the read() function to read
+ keystrokes, and read() returns a single byte (value 0 through 255).
+ C-Kermit's SET KEY function applies to these single-byte codes.
+ "Extended function" keys, such as F-keys, arrow keys, etc, usually
+ return either a 2-byte "scan code" or else a character string (such as
+ an escape sequence like "<ESC> O p"). In both cases, C-Kermit has no
+ way to tell the difference between such multibyte key values, and the
+ corresponding series of single-byte key values. This could only be
+ done by accessing the keyboard at a much lower level in a highly
+ platform-dependent manner, probably requiring tens of thousands of
+ lines of code to support even a sampling of the most popular
+ workstation / OS combinations.
+
+ However, most workstation console drivers (terminal emulation windows,
+ etc) include their own key-mapping facility. For example in AIX, the
+ AIXterm program (in whose window you would run C-Kermit) allows
+ rebinding of the F1-F12 keys to arbitrary strings. The same is true of
+ Xterm and DECterm windows, etc. Consult the technical documentation
+ for your workstation or emulator. See sample Xterm (Xmodmap) mappings
+ in the [68]Unix C-Kermit Hints and Tips document.
+
+ The SET KEY command (except in Kermit 95) does not allow a key
+ definition to be (or contain) the NUL (\0) character.
+ ________________________________________________________________________
+
+ 10. FILE TRANSFER
+
+ [ [69]Top ] [ [70]Contents ] [ [71]Next ] [ [72]Previous ]
+
+ C-Kermit 7.0 is the first release of C-Kermit to use fast (rather than
+ robust and therefore slow) protocol defaults: long packets, sliding
+ windows, control-character unprefixing, and streaming where possible.
+ This makes most transfers (partner willing) dramatically faster "out
+ of the box" but might break some combinations that worked before. If
+ transfers with C-Kermit 7.0 or later fail where transfers worked with
+ earlier C-Kermit versions, try the following (one at a time, in this
+ order):
+
+ 1. SET PREFIXING ALL: Disables control-character unprefixing.
+ 2. SET STREAMING OFF: Disables streaming.
+ 3. CAUTIOUS: Selects medium but cautious protocol settings.
+ 4. ROBUST: this command reverts to the most conservative protocol
+ settings.
+
+ Execution of multiple file transfers by C-Kermit from a command file
+ when in remote mode might exhibit long delays between each transfer.
+ To avoid this, just include the command "SET DELAY 0" in your command
+ file before any of the file-transfer commands.
+
+ File transfer failures can occur for all sorts of reasons, most of
+ them listed in Chapter 10 of [73]Using C-Kermit. The following
+ sections touch on some that aren't.
+
+ The [74]C-Kermit 7.0 Release Notes document SEND /COMMAND as taking an
+ argument, but it doesn't. Instead of SEND /COMMAND:{some command},
+ use:
+
+SEND /COMMAND [ other switches such as /AS-NAME: ] command [ arguments... ]
+
+ 10.1. Laptops
+
+ Watch out for laptops and their assorted power-saver features; for
+ example, a built-in modem's "auto timeout delay" hanging up the
+ connection in the middle of a file transfer. Most modems, even if they
+ have this feature, do not have it enabled by default. But if you
+ experience otherwise inexplicable disconnections in the midst of your
+ Kermit sessions, check the modem manual for such things as "idle
+ timeout", "auto timeout", etc, and add the command to disable this
+ feature to Kermit's init string for this modem.
+
+ 10.2. NFS
+
+ If uploading a large file to an NFS-mounted disk fails (or is
+ painfully slow), try uploading it to a local disk (e.g. /tmp on Unix)
+ and then copying to the NFS disk later.
+
+ 10.3. Modems
+
+ If you are dialing out and find that downloads work but uploads don't,
+ try again with a lower serial-port speed. Case in point: dialing out
+ on a certain PC from Linux at 115200 bps using a USR Courier 56K
+ "V.Everything" external modem and RTS/CTS flow control. Downloads
+ worked flawlessly, uploads stopped dead after the first few packets
+ were sent. The modem lights showed constant retraining (ARQ light
+ blinks slowly), and the CTS light was off 95% of the time, allowing
+ nothing to get through. Reducing the serial port speed to 57600 bps
+ made the problems go away. Evidently the PC in question has a very
+ fast serial port, since dialing the same modem with a different PC at
+ 115200 bps works without incident.
+
+ 10.4. TCP/IP Connections
+
+ If you have trouble transferring files over a TCP/IP connection, tell
+ Kermit to SET PARITY SPACE and try again. If that doesn't work, also
+ try a shorter packet length or smaller window size (to compensate for
+ certain well-known broken Telnet servers), and/or SET RELIABLE OFF.
+
+ 10.5. Multihop Connections
+
+ If you have a multihop connection, with the interior nodes in CONNECT
+ mode (Kermit, Telnet, Rlogin, or any other), you can expect (a) file
+ transfer to be slower, and (b) the connection to be less transparent
+ (to control characters, perhaps to the 8th bit) than a more direct
+ connection. C-Kermit 7.0 and later have a "-0" (dash-zero)
+ command-line option to make it 100% transparent in cases where it is
+ to be used in the middle.
+
+ 10.6. Recovery
+
+ The recovery feature (RESEND command) that was added in version
+ 5A(190) works only for binary-mode transfers. In order for this
+ feature to be useful at all, the default for SET FILE INCOMPLETE was
+ changed from DISCARD to KEEP. Otherwise an interrupted transfer would
+ leave no partial file behind unless you had remembered to change the
+ default. But now you have to pay closer attention to Kermit's messages
+ to know whether a transfer succeeded or failed -- previously, if it
+ failed, the file would not show up on the receiving end at all; in
+ 5A(190) and later, you'll get a partial file which could easily be
+ mistaken for the complete file unless you change the default back to
+ DISCARD or read the screen messages, or keep a transaction log.
+
+ 10.7. Filename Collisions
+
+ SET FILE COLLISION BACKUP is the default. This means:
+
+ * If you send the same file lots of times, there will be many backup
+ files. There is no automatic mechanism within Kermit to delete
+ them, no notion of a "version retention count", etc, but you can
+ use the PURGE command to clean them up.
+ * If a file arrives that has the same name as a directory, the file
+ transfer fails because Kermit will not rename a directory. Send
+ the file with another name, or use SET FILE COLLISION RENAME.
+ * If the directory lacks write permission, the file transfer fails
+ even if you have write access to the file that is being backed up;
+ in that case, switch to SET FILE COLLISION OVERWRITE or APPEND, or
+ send to a different directory.
+
+ SET FILE COLLISION UPDATE depends on the date/time stamp in the
+ attribute packet. However, this is recorded in local time, not
+ Universal Time (GMT), and there is no indication of time zone. The
+ time is expressed to the precision of 1 second, but some file systems
+ do not record with this precision -- for example, MS-DOS records the
+ file date/time only to the nearest 2 seconds. This might cause update
+ operations to send more files than necessary.
+
+ (This paragraph does NOT apply to UNIX, where, as of C-Kermit 7.0,
+ C-Kermit pipes incoming mail and print material directly the mail or
+ print program): When C-Kermit is receiving files from another Kermit
+ program that has been given the MAIL or REMOTE PRINT command, C-Kermit
+ follows the current filename collision action. This can be
+ disconcerting if the action was (for example) BACKUP, because the
+ existing file will be renamed, and the new file will be mailed (or
+ printed) and then deleted. Kermit cannot temporarily change to RENAME
+ because the file collision action occurs when the filename packet is
+ received, and the PRINT or MAIL disposition only comes later, in the
+ Attribute packet.
+
+ Watch out for SET FILE COLLISION RENAME, especially when used in
+ conjunction with recovery. Recall that this option (which is NOT the
+ default) renames the incoming file if a file already exists with the
+ same name (the default is to rename the previously existing file, and
+ store the incoming file with its own name). It is strongly recommended
+ that you do not use SET FILE COLLISION RENAME if you ever intend to
+ use the recovery feature:
+
+ * When the file is first received by C-Kermit, its name is changed
+ if another file already has the same name. When you RESEND the
+ same file after a failure, C-Kermit will probably try to append
+ the re-sent portion to the wrong file.
+ * Assuming that you get RESEND to work with FILE COLLISION RENAME,
+ C-Kermit, when receiving the remainder of the file during a RESEND
+ operation, will report back the wrong name. Nothing can be done
+ about this because the name is reported back before the receiving
+ Kermit program finds out that it is a recovery operation.
+
+ Also watch out for DISABLE DELETE, since this implicitly sets FILE
+ COLLISION to RENAME. And note tht DELETE is DISABLEd automatically any
+ time you Kermit is in local mode (i.e. it makes a connection). Also
+ note that for purposes of DISABLE and ENABLE, "set host *" connections
+ do not count as local mode even though, strictly speaking, they are.
+
+ 10.8. DOS Pathnames
+
+ When referring to foreign MS-DOS, Windows, Atari ST, OS/2, or other
+ file specifications that contain backslash characters in a C-Kermit
+ command, you might have to double each backslash, for example:
+
+ C-Kermit>get c:\\directory\\foo.txt
+
+ This is because backslash is used in C-Kermit commands for introducing
+ special character codes, variables, functions, etc.
+
+ 10.9. Cancellation
+
+ If attempting to cancel local-mode file reception at a very early
+ stage (i.e. before data packets are exchanged) with X or Z does not
+ work, use E or Ctrl-C instead, or wait until the first data packets
+ are sent.
+
+ If you cancel a transfer that is underway using X or Z, and a lot of
+ window slots are in use, it might take a while for the cancellation to
+ take effect, especially if you do this on the receiving end; that's
+ because a lot of packets might already be on their way to you. In that
+ case, just be patient and let Kermit "drain" them.
+
+ If C-Kermit is sending a file, remote-mode packet-mode breakout (three
+ consecutive Ctrl-C's by default) is not effective until after C-Kermit
+ sends its first packet. If C-Kermit is receiving a file or is in
+ server mode, it is effective right away. In the former case, the SET
+ DELAY value determines the earliest time at which you can break out of
+ packet mode.
+
+ 10.10. Partner Peculiarities
+
+ When one or both partners is on an SCO operating system such as OSR5,
+ you might issue the command:
+
+mapchan -n
+
+ to disable character-set conversion by the terminal driver. Similarly
+ for AIX:
+
+setmaps -t NOMAP
+
+ When using C-Kermit to transfer files with the HP48SX calculator, you
+ must SET FLOW NONE. The HP48SX does not support flow control, and
+ evidently also becomes confused if you attempt to use it. You might
+ also need to use SET SEND PAUSE 100 (or other number). For greater
+ detail about transferring files the the HP-48, see:
+
+ [75]http://www.columbia.edu/kermit/hp48.html
+
+ Some communication programs have errors in their implementation of
+ Kermit attribute packets. If you get an error message from your
+ communication program like "Attribute error", tell C-Kermit to SET
+ ATTRIBUTES OFF. Better yet, switch to a real Kermit program.
+
+ Some communication software claims to implement Kermit sliding
+ windows, but does so incorrectly. If sliding window transfers fail,
+ set C-Kermit's window size to the smallest one that works, for
+ example, SET WINDOW 1.
+
+ For lots more detail about how to cope with defective Kermit partners,
+ see:
+
+ * [76]Coping with Faulty Kermit Implementations (C-Kermit 7.0 and
+ later).
+ * [77]Coping with Broken Kermit Partners (C-Kermit 8.0 and later).
+
+ The UNIX version of C-Kermit discards carriage returns when receiving
+ files in text mode. Thus, "bare" carriage returns (sometimes used to
+ achieve overstriking) are lost.
+ ________________________________________________________________________
+
+ 11. SCRIPT PROGRAMMING
+
+ [ [78]Top ] [ [79]Contents ] [ [80]Previous ]
+
+ 11.1. Comments Versus the SCRIPT Command
+
+ Remember that ";" and "#" introduce comments when (a) they are the
+ first character on the line, or (b) they are preceded by at least one
+ blank or tab within a line. Thus constructions like:
+
+ INPUT 5 ;
+ SCRIPT ~0 #--#--#
+
+ must be coded using backslash notation to keep the data from being
+ ignored:
+
+ INPUT 5 \59 ; 59 is the decimal ASCII code for ";"
+ SCRIPT ~0 \35--#--# ; 43 is the decimal ASCII code for "#"
+
+ or, more simply:
+
+ INPUT 5 \; ; Just quote the semicolon
+ SCRIPT ~0 \#--#--# ; Just quote the "#"
+ ________________________________________________________________________
+
+ 11.2. Alphabetic Case and the INPUT Command
+
+ INPUT and MINPUT caseless string comparisons do not work for non-ASCII
+ (international) characters. Workaround: SET INPUT CASE OBSERVE. Even
+ then, the "lexically less than" and "lexically greater than"
+ operations (IF LLT, IF LGT) probably won't work as expected. The same
+ is true for the case-conversion functions \Flower() and \Fupper().
+ C-Kermit does not know the collating sequence for different character
+ sets and languages. (On the other hand, it might work depending on
+ such items as how Kermit was linked, whether your operating supports
+ "locales", etc)
+ ________________________________________________________________________
+
+ 11.3. NUL (0) Characters in C-Kermit Commands
+
+ You can't include a NUL character (\0) in C-Kermit command text
+ without terminating the character string in which it appears. For
+ example:
+
+ echo In these brackets [\0] is a NUL
+
+ will echo "In these brackets [". This applies to ECHO, INPUT, OUTPUT,
+ and all other commands (but you can represent NUL by "\N" in an OUTPUT
+ string). This is because C-language strings are terminated internally
+ by the NUL character, and it allows all of C-Kermit's string
+ comparison and manipulation functions to work in the normal "C" way.
+
+ To illustrate:
+
+ INPUT 5 \0
+
+ is equivalent to:
+
+ INPUT 5
+
+ and:
+
+ INPUT 5 ABC\0DEF
+
+ is equivalent to:
+
+ INPUT 5 ABC
+
+ INPUT operations discard and ignore NUL characters that arrive from
+ the communication device, meaning that they do not figure into
+ matching operations (e.g. A<NUL>B matches AB); they are not deposited
+ in the INPUT buffer (\v(input)); and they are not counted in
+ \v(incount), with two exceptions:
+
+ 1. An arriving NUL character restarts the INPUT SILENCE timer.
+ 2. An arriving NUL character terminates the INPUT command with the
+ SUCCESS condition if the INPUT command was given an empty search
+ string. In this case \v(incount) is set to 1.
+
+ Also, the \v(inchar) variable is null (completely empty) if the last
+ INPUT character was NUL. That is, there is no way to tell only by
+ looking at \v(inchar) the difference between a NUL that was INPUT and
+ no INPUT at all. If the INPUT command succeeded but \v(inchar) is
+ empty, then a NUL character was input. Also, \v(incount) will be set
+ to 1.
+
+ Here's a sample script fragment to read characters, possibly including
+ NUL, from the communication connection and write them to a file:
+
+ while true {
+ input 1 ; read one byte
+ if fail break ; timed out or connection closed
+ fwrite /char \%c \v(inchar) ; record the byte
+ }
+
+ This works because when \v(inchar) is NUL, that's equivalent to FWRITE
+ /CHAR having no text argument at all, in which case it writes a NUL
+ character.
+
+ \v(incount) and \v(inchar) are NOT affected by the CLEAR command.
+ ________________________________________________________________________
+
+ 11.4. \ffiles() and \fnextfile() Peculiarities
+
+ The following script program:
+
+ for \%i 1 \ffiles(oofa.*) 1 {
+ send \fnextfile()
+ }
+
+ did not work as expected in C-Kermit 6.0 and earlier but does work in
+ C-Kermit 7.0 and later.
+ ________________________________________________________________________
+
+ 11.5. Commands That Have Only Local Effect
+
+ Certain settings are local to each command level, meaning that
+ subordinate command levels (macros or command files) can change them
+ without affecting their values at higher command levels. When a new
+ command level is invoked, the value is inherited from the previous
+ level. These settings are:
+
+ CASE
+ COUNT and \v(count)
+ INPUT CASE
+ INPUT TIMEOUT
+ MACRO ERROR
+ QUIET
+ TAKE ERROR
+
+ This arrangement allows CASE, TIMEOUT, and ERROR settings, which are
+ used to control automatic exit from a command file or macro upon
+ error, to be automatically restored when the command file or macro
+ exits.
+
+ The COUNT variable follows this rule too, which permits nested SET
+ COUNT / IF COUNT loops, as in this example in which the inner loop
+ counts down from the current COUNT value of the outer loop (try it):
+
+ DEFINE INNER WHILE COUNT { WRITE SCREEN { Inner:}, SHOW COUNT }
+ SET COUNT 5
+ WHILE COUNT { WRITE SCREEN Outer:, SHOW COUNT, DO INNER }
+
+ Keep in mind that an inferior command level cannot manipulate the
+ COUNT value held by a higher level. For example:
+
+ DEFINE OOFA SHOW COUNT, IF COUNT GOTO LOOP
+ SET COUNT 5
+ :LOOP
+ OOFA
+ ECHO Done
+
+ results in an infinite loop; the COUNT value remains at 5 because it
+ is never decremented at the same level at which it was set.
+ ________________________________________________________________________
+
+ 11.6. Literal Braces in Function Calls
+
+ Since braces are used in function calls to indicate grouping, there is
+ no way to pass literal braces to the function itself. Solution: Define
+ a variable containing the string that has braces. Example:
+
+ define \%a ab{cd
+ echo \fsubstring(\%a)
+ ab{cd
+
+ If the string is to start with a leading brace and end with a closing
+ brace, then double braces must appear around the string (which itself
+ is enclosed in braces):
+
+ define \%a {{{foo}}}
+ echo \fsubstring(\%a)
+ {foo}
+
+ This also works for any other kind of string:
+
+ define \%a {{ab{cd}}
+ echo \fsubstring(\%a)
+ ab{cd
+ ________________________________________________________________________
+
+ 11.7. Defining Variables on the C-Kermit Command Line
+
+ To define variables on the C-Kermit command line, use the -C
+ command-line option with one or more DEFINE or ASSIGN commands. Note
+ that the C-Kermit command line must cope with the quoting rules of
+ your shell. Examples:
+
+ kermit -C "define \\%a foo, define phonenumber 7654321"
+
+ In this case we follow UNIX quoting rules by doubling the backslash.
+ Once C-Kermit starts, the \%a and \m(phonenumber) variables are
+ defined as indicated and can be used in the normal way.
+
+ In DOS or Windows or OS/2 the command would be:
+
+ kermit -C "define \%%a foo, define phonenumber 7654321"
+
+ Here we need to double the percent sign rather than the backslash
+ because of DOS shell quoting rules.
+ ________________________________________________________________________
+
+ 11.8. Per-Character Echo Check with the OUTPUT Command
+
+ Sometimes the OUTPUT command must be used to send commands or data to
+ a device in "echoplex" mode, meaning that characters must be sent one
+ at a time, and the next character can not be sent until the echo from
+ the previous one has been received. For example, a certain PBX might
+ have this characteristic. Let's say a Kermit script is used to program
+ the PBX. If characters are sent too fast, they can be lost. It would
+ seem that the command:
+
+ SET OUTPUT PACING milliseconds
+
+ could be used to take care of this, but the pacing interval is
+ constant and must be set large enough to allow even the slowest echo
+ to finish. If the script is large (an actual example is 14,000 lines
+ long), this can cause it to take hours longer than it needs to.
+
+ Here is a macro you can use to OUTPUT a string in an Echoplex
+ environment:
+
+ define XOUTPUT {
+ local \%c \%i
+ set output pacing 0
+ for \%i 1 \flen(\%*) 1 {
+ asg \%c \fsubstr(\%*,\%i,1)
+ output \%c
+ input 2 \%c
+ }
+ }
+
+ C-Kermit 7.0 or later is required.
+
+ It sends one character at a time and then waits up to 2 seconds for
+ the character to be echoed back, but continues to the next character
+ as soon as the echo appears, so no time is wasted. You can add an IF
+ FAIL clause after the INPUT in case you want to do something special
+ about failure to detect an echo within the timeout period. Obviously
+ you can also change the 2-second limit, and adjust the script in any
+ other desired way.
+ ________________________________________________________________________
+
+ 11.9. Scripted File Transfer
+
+ Sometimes a user complains that when she makes a connection by hand,
+ logs in, and transfers a file, there are no problems, but when she
+ scripts the the exact same sequence, the file transfer always fails
+ after a few packets. Here's a scenario where this can happen:
+
+ 1. Upon logging in to the remote computer, it sends a "What Are You?"
+ escape sequence.
+ 2. When you log in interactively, your terminal emulator sends the
+ response. This is invisible to you; you don't know it's happening.
+ 3. When you script the login, and begin a file transfer immediately
+ upon logging in, the host still sends the "What Are You?"
+ sequence. Kermit's INPUT ECHO setting is ON by default, so the
+ escape sequence passes through to the terminal, and the terminal
+ sends its response. But by this time Kermit has already started
+ the file transfer.
+ 4. By default, the local Kermit program examines the keyboard for
+ interruption characters between every packet. The "What Are You"
+ response is sitting in the keyboard buffer. Eventually Kermit will
+ read a character such as "c" that is a valid interruption
+ character, and the file transfer stops with "User cancelled".
+
+ The right way to handle this situation is to have your look for the
+ "What Are You?" sequence and send the response itself, as described in
+ Using C-Kermit, pp.429-431. Or you can work around it by telling the
+ local Kermit to "set input echo off" and/or "set transfer interruption
+ off".
+ ________________________________________________________________________
+
+ 11.10. Other...
+
+ Escape sequences (or any strings that contain control characters)
+ can't be used as labels, GOTO targets, or SWITCH cases.
+
+ [ [81]Top ] [ [82]Contents ] [ [83]C-Kermit Home ] [ [84]C-Kermit 8.0
+ Overview ] [ [85]Kermit Home ]
+ _________________________________________________________________
+
+ C-Kermit 8.0 Unix Hints and Tips / [86]The Kermit Project /
+ [87]Columbia University / [88]kermit@columbia.edu / 10 April 2004
+
+References
+
+ 1. http://www.columbia.edu/kermit/
+ 2. http://www.columbia.edu/
+ 3. http://www.columbia.edu/kermit/ckcbwr.html
+ 4. http://www.columbia.edu/kermit/ckubwr.html
+ 5. http://www.columbia.edu/kermit/k95.html
+ 6. http://www.columbia.edu/kermit/ckermit.html
+ 7. http://www.columbia.edu/kermit/ckututor.html
+ 8. http://www.columbia.edu/kermit/ckcbwr.html#x0
+ 9. http://www.columbia.edu/kermit/ckcbwr.html#x1
+ 10. http://www.columbia.edu/kermit/ckcbwr.html#x2
+ 11. http://www.columbia.edu/kermit/ckcbwr.html#x3
+ 12. http://www.columbia.edu/kermit/ckcbwr.html#x4
+ 13. http://www.columbia.edu/kermit/ckcbwr.html#x5
+ 14. http://www.columbia.edu/kermit/ckcbwr.html#x6
+ 15. http://www.columbia.edu/kermit/ckcbwr.html#x7
+ 16. http://www.columbia.edu/kermit/ckcbwr.html#x8
+ 17. http://www.columbia.edu/kermit/ckcbwr.html#x9
+ 18. http://www.columbia.edu/kermit/ckcbwr.html#x10
+ 19. http://www.columbia.edu/kermit/ckcbwr.html#x11
+ 20. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 21. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 22. http://www.columbia.edu/kermit/ckcbwr.html#x2
+ 23. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 24. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 25. http://www.columbia.edu/kermit/ckcbwr.html#x2
+ 26. http://www.columbia.edu/kermit/ck60manual.html
+ 27. http://www.columbia.edu/kermit/ckermit2.html
+ 28. http://www.columbia.edu/kermit/ck60manual.html
+ 29. http://www.columbia.edu/kermit/ckermit80.html#x5
+ 30. http://www.columbia.edu/kermit/ckermit80.html
+ 31. http://www.columbia.edu/kermit/ckermit80.html#x2.2
+ 32. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 33. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 34. http://www.columbia.edu/kermit/ckcbwr.html#x3
+ 35. http://www.columbia.edu/kermit/ckcbwr.html#x1
+ 36. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 37. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 38. http://www.columbia.edu/kermit/ckcbwr.html#x4
+ 39. http://www.columbia.edu/kermit/ckcbwr.html#x2
+ 40. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 41. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 42. http://www.columbia.edu/kermit/ckcbwr.html#x5
+ 43. http://www.columbia.edu/kermit/ckcbwr.html#x3
+ 44. ftp://ftp.isi.edu/in-notes/rfc1122.txt
+ 45. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 46. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 47. http://www.columbia.edu/kermit/ckcbwr.html#x6
+ 48. http://www.columbia.edu/kermit/ckcbwr.html#x4
+ 49. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 50. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 51. http://www.columbia.edu/kermit/ckcbwr.html#x7
+ 52. http://www.columbia.edu/kermit/ckcbwr.html#x5
+ 53. http://www.columbia.edu/kermit/ck60manual.html
+ 54. http://www.columbia.edu/kermit/ckcbwr.html#x10
+ 55. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 56. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 57. http://www.columbia.edu/kermit/ckcbwr.html#x8
+ 58. http://www.columbia.edu/kermit/ckcbwr.html#x6
+ 59. ftp://ftp.isi.edu/in-notes/rfc2217.txt
+ 60. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 61. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 62. http://www.columbia.edu/kermit/ckcbwr.html#x9
+ 63. http://www.columbia.edu/kermit/ckcbwr.html#x7
+ 64. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 65. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 66. http://www.columbia.edu/kermit/ckcbwr.html#x10
+ 67. http://www.columbia.edu/kermit/ckcbwr.html#x8
+ 68. http://www.columbia.edu/kermit/ckubwr.html
+ 69. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 70. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 71. http://www.columbia.edu/kermit/ckcbwr.html#x11
+ 72. http://www.columbia.edu/kermit/ckcbwr.html#x9
+ 73. http://www.columbia.edu/kermit/ck60manual.html
+ 74. http://www.columbia.edu/kermit/ckermi70.htm
+ 75. http://www.columbia.edu/kermit/hp48.html
+ 76. http://www.columbia.edu/kermit/ckermit70.html#x4.22
+ 77. http://www.columbia.edu/kermit/ckermit80.html#x15
+ 78. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 79. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 80. http://www.columbia.edu/kermit/ckcbwr.html#x10
+ 81. http://www.columbia.edu/kermit/ckcbwr.html#top
+ 82. http://www.columbia.edu/kermit/ckcbwr.html#contents
+ 83. http://www.columbia.edu/kermit/ckermit.html
+ 84. http://www.columbia.edu/kermit/ck80.html
+ 85. http://www.columbia.edu/kermit/index.html
+ 86. http://www.columbia.edu/kermit/index.html
+ 87. http://www.columbia.edu/
+ 88. mailto:kermit@columbia.edu
diff --git a/ckermit-8.0.211/ckccfg.txt b/ckermit-8.0.211/ckccfg.txt
new file mode 100644
index 0000000..5870974
--- /dev/null
+++ b/ckermit-8.0.211/ckccfg.txt
@@ -0,0 +1,1766 @@
+
+ C-Kermit Configuration Options
+
+ Frank da Cruz
+ [1]The Kermit Project
+ [2]Columbia University
+
+ As of: C-Kermit 8.0.211, 10 April 2004
+ This page last updated: Sun Apr 11 16:45:55 2004 (New York USA Time)
+
+ IF YOU ARE READING A PLAIN-TEXT version of this document, note that
+ this file is a plain-text dump of a Web page. You can visit the
+ original (and possibly more up-to-date) Web page here:
+
+ [3]http://www.columbia.edu/kermit/ckccfg.html
+
+ [ [4]C-Kermit Home ] [ [5]Kermit Home ]
+ ________________________________________________________________________
+
+ CONTENTS
+
+ 1. [6]FILE TRANSFER
+ 2. [7]SERIAL COMMUNICATION SPEEDS
+ 3. [8]FULLSCREEN FILE TRANSFER DISPLAY
+ 4. [9]CHARACTER SETS
+ 5. [10]APC EXECUTION
+ 6. [11]PROGRAM SIZE
+ 7. [12]MODEM DIALING
+ 8. [13]NETWORK SUPPORT
+ 9. [14]EXCEPTION HANDLING
+ 10. [15]SECURITY FEATURES
+ 11. [16]ENABLING SELECT()
+ 12. [17]I/O REDIRECTION
+ 13. [18]FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC
+ 14. [19]SPECIAL CONFIGURATIONS
+ I. [20]SUMMARY OF COMPILE-TIME OPTIONS
+ ________________________________________________________________________
+
+ OVERVIEW
+
+ This document describes configuration options for C-Kermit (5A and
+ later). The major topics covered include program size (and how to
+ reduce it), how to include or exclude particular features, notes on
+ serial-port, modem, and network support, and a list of C-Kermit's
+ compile-time options.
+
+ For details about your particular operating system, also see the
+ system-specific installation instructions file, such as the
+ [21]C-Kermit Installation Instructions for Unix.
+
+ [ [22]C-Kermit Home ] [ [23]Kermit Home ]
+ ________________________________________________________________________
+
+ 1. FILE TRANSFER
+
+ [ [24]Top ] [ [25]Contents ] [ [26]Next ] [ [27]Previous ]
+
+ Prior to version 7.0, C-Kermit was always built with the most
+ conservative Kermit file-transfer protocol defaults on every platform:
+ no control-character prefixing, 94-byte packets, and a window size of
+ 1.
+
+ Starting in version 7.0, fast settings are the default. To override
+ these at compile time, include:
+
+ -DNOFAST
+
+ in the C compiler CFLAGS. Even with the fast defaults, C-Kermit
+ automatically drops down to whatever window and packet sizes requested
+ by the other Kermit, if these are smaller, when sending files (except
+ for control-character unprefixing, which is not negotiated, and which
+ is now set to CAUTIOUS rather than NONE at startup). C-Kermit's
+ settings prevail when it is receiving.
+
+ [ [28]C-Kermit Home ] [ [29]Kermit Home ]
+ ________________________________________________________________________
+
+ 2. SERIAL COMMUNICATION SPEEDS
+
+ [ [30]Top ] [ [31]Contents ] [ [32]Next ] [ [33]Previous ]
+
+ As of 6 September 1997, a new simplified mechanism for obtaining the
+ list of legal serial interface speeds is in place:
+
+ * If the symbol TTSPDLIST is defined, the system-dependent routine
+ ttspdlist() is called at program initialization to obtain the
+ list.
+ * This symbol should be defined only for C-Kermit implementations
+ that have implemented the ttspdlist() function, typically in the
+ ck?tio.c module. See [34]ckutio.c for an example.
+ * TTSPDLIST is automatically defined in [35]ckcdeb.h for UNIX. Add
+ the appropriate #ifdefs for other platforms when the corresponding
+ ttspdlist() functions are filled in.
+ * If TTSPDLIST is (or normally would be) defined, the old code
+ (described below) can still be selected by defining NOTTSPDLIST.
+
+ The ttspdlist() function can obtain the speeds in any way that works.
+ For example, based simply on #ifdef Bnnnn..#endif (in UNIX). Although
+ it might be better to actually check each speed against the currently
+ selected hardware interface before allowing it in the array, there is
+ usually no passive and/or reliable and safe way to do this, and so
+ it's better to let some speeds into the array that might not work,
+ than it is to erroneously exclude others. Speeds that don't work are
+ caught when the SET SPEED command is actually given.
+
+ Note that this scheme does not necessarily rule out split speed
+ operation, but effectively it does in C-Kermit as presently
+ constituted since there are no commands to set input and output speed
+ separately (except the special case "set speed 75/1200").
+
+ Note that some platforms, notably AIX 4.2 and 4.3, implement high
+ serial speeds transparently to the application, e.g. by mapping 50 bps
+ to 57600 bps, and so on.
+
+ That's the whole deal. When TTSPDLIST is not defined, the following
+ applies:
+
+ Speeds are defined in two places: the SET SPEED keyword list in the
+ command parser (as of this writing, in the [36]ckuus3.c source file),
+ and in the system- dependent communications i/o module, ck?tio.c,
+ functions ttsspd() (set speed) and ttgspd() (get speed). The following
+ speeds are assumed to be available in all versions:
+
+ 0, 110, 300, 600, 1200, 2400, 4800, 9600
+
+ If one or more of these speeds is not supported by your system, you'll
+ need to change the source code (this has never happened so far). Other
+ speeds that are not common to all systems have Kermit-specific
+ symbols:
+
+ Symbol Symbol
+ Speed (bps) to enable to disable
+ 50 BPS_50 NOB_50
+ 75 BPS_75 NOB_75
+ 75/1200 BPS_7512 NOB_7512
+ 134.5 BPS_134 NOB_134
+ 150 BPS_150 NOB_150
+ 200 BPS_200 NOB_200
+ 1800 BPS_1800 NOB_1800
+ 3600 BPS_3600 NOB_3600
+ 7200 BPS_7200 NOB_7200
+ 14400 BPS_14K NOB_14K
+ 19200 BPS_19K NOB_19K
+ 28800 BPS_28K NOB_28K
+ 38400 BPS_38K NOB_38K
+ 57600 BPS_57K NOB_57K
+ 76800 BPS_76K NOB_76K
+ 115200 BPS_115K NOB_155K
+ 230400 BPS_230K NOB_230K
+ 460800 BPS_460K NOB_460K
+ 921600 BPS_921K NOB_921K
+
+ The [37]ckcdeb.h header file contains default speed configurations for
+ the many systems that C-Kermit supports. You can override these
+ defaults by (a) editing ckcdeb.h, or (b) defining the appropriate
+ enabling and/or disabling symbols on the CC command line, for example:
+
+ -DBPS_14400 -DNOB_115200
+
+ or the "make" command line, e.g.:
+
+ make blah "KFLAGS=-DBPS_14400 -DNOB_115200"
+
+ Note: some speeds have no symbols defined for them, because they have
+ never been needed: 12.5bps, 45.5bps, 20000bps, etc. These can easily
+ be added if required (but they will work only if the OS supports
+ them).
+
+ IMPORTANT: Adding one of these flags at compile time does not
+ necessarily mean that you will be able to use that speed. A particular
+ speed is usable only if your underlying operating system supports it.
+ In particular, it needs to be defined in the appropriate system header
+ file (e.g. in UNIX, cd to /usr/include and grep for B9600 in *.h and
+ sys/*.h to find the header file that contains the definitions for the
+ supported speeds), and supported by the serial device driver, and of
+ course by the physical device itself.
+
+ ALSO IMPORTANT: The list of available speeds is independent of how
+ they are set. The many UNIXes, for example, offer a wide variety of
+ APIs that are BSD-based, SYSV-based, POSIX-based, and purely made up.
+ See the ttsspd(), ttgspd(), and ttspdlist() routines in [38]ckutio.c
+ for illustrations.
+
+ The latest entries in this horserace are the tcgetspeed() and
+ ttsetspeed() routines found in UnixWare 7. Unlike other methods, they
+ accept the entire range of integers (longs really) as speed values,
+ rather than certain codes, and return an error if the number is not,
+ in fact, a legal speed for the device/driver in question. In this
+ case, there is no way to build a list of legal speeds at compile time,
+ since no Bnnnn symbols are defined (except for "depracated, legacy"
+ interfaces like ioctl()) and so the legal speed list must be
+ enumerated in the code -- see ttspdlist() in [39]ckutio.c.
+
+ [ [40]C-Kermit Home ] [ [41]Kermit Home ]
+ ________________________________________________________________________
+
+ 3. FULLSCREEN FILE TRANSFER DISPLAY
+
+ [ [42]Top ] [ [43]Contents ] [ [44]Next ] [ [45]Previous ]
+
+ New to edit 180 is support for an MS-DOS-Kermit-like local-mode full
+ screen file transfer display, accomplished using the curses library,
+ or something equivalent (for example, the Screen Manager on DEC VMS).
+ To enable this feature, include the following in your CFLAGS:
+
+ -DCK_CURSES
+
+ and then change your build procedure (if necessary) to include the
+ necessary libraries. For example, in Unix these are usually "curses"
+ or "ncurses" (and more recenlty, "ncursesw" and "slang"), perhaps also
+ "termcap", "termlib", or "tinfo":
+
+ "LIBS= -lcurses -ltermcap"
+ "LIBS= -lcurses -ltermlib"
+ "LIBS= -lncurses"
+ "LIBS= -ltermlib"
+ "LIBS= -ltinfo"
+
+ "man curses" for further information, and search through the Unix
+ [46]makefile for "CK_CURSES" to see many examples, and also see the
+ relevant sections of the [47]Unix C-Kermit Installation Instructions,
+ particularly Sections [48]4 and [49]9.2.
+
+ There might still be a complication. Some implementations of curses
+ reserve the right to alter the buffering on the output file without
+ restoring it afterwards, which can leave Kermit's command processing
+ in a mess when the prompt comes back after a fullscreen file transfer
+ display. The typical symptom is that characters you type at the prompt
+ after a local-mode file transfer (i.e. after seeing the curses
+ file-transfer display) do not echo until you press the Return (Enter)
+ key. If this happens to you, try adding
+
+ -DCK_NEWTERM
+
+ to your makefile target (see comments in screenc() in [50]ckuusx.c for
+ an explanation).
+
+ If that doesn't fix the problem, then use a bigger hammer and replace
+ -DCK_NEWTERM with:
+
+ -DNONOSETBUF
+
+ which tells Kermit to force stdout to be unbuffered so CBREAK mode can
+ work.
+
+ In SCO Xenix and SCO UNIX, there are two separate curses libraries,
+ one based on termcap and the other based on terminfo. The default
+ library, usually terminfo, is established when the development system
+ is installed. To manually select terminfo (at compile time):
+
+ compile -DM_TERMINFO and link -ltinfo
+
+ and to manually select termcap:
+
+ compile -DM_TERMCAP and link -ltcap -ltermlib
+
+ <curses.h> looks at M_TERMINFO and M_TERMCAP to decide which header
+ files to use. /usr/lib/libcurses.a is a link to either libtinfo.a or
+ libtcap.a. The C-Kermit compilation options must agree with the
+ version of the curses library that is actually installed.
+
+ NOTE: If you are doing an ANSI-C compilation and you get compile time
+ warnings like the following:
+
+ Warning: function not declared in ckuusx.c: wmove, printw, wclrtoeol,
+ wclear, wrefresh, endwin, etc...
+
+ it means that your <curses.h> file does not contain prototypes for
+ these functions. The warnings should be harmless.
+
+ New to edit 190 is the ability to refresh a messed-up full-screen
+ display, e.g. after receiving a broadcast message. This depends on the
+ curses package including the wrefresh() and clearok() functions and
+ the curscr variable. If your version has these, or has code to
+ simulate them, then add:
+
+ -DCK_WREFRESH
+
+ The curses and termcap libraries add considerable size to the program
+ image (e.g. about 20K on a SUN-4, 40K on a 386). On some small
+ systems, such as the AT&T 6300 PLUS, curses can push Kermit over the
+ edge... even though it compiles, loads, and runs correctly, its
+ increased size apparently makes it swap constantly, slowing it down to
+ a crawl, even when the curses display is not in use. Some new makefile
+ targets have been added to take care of this (e.g. sys3upcshcc), but
+ similar tricks might be necessary in other cases too.
+
+ On the curses file-transfer display, just below the "thermometer", is
+ a running display of the transfer rate, as a flat quotient of file
+ characters per elapsed seconds so far. You can change this to an
+ average that gives greater weight to recent history (0.25 *
+ instantaneous cps + 0.75 * historical cps) by adding -DCPS_WEIGHTED to
+ your CFLAGS (sorry folks, this one is not worth a SET command). You
+ can choose a second type of weighted average in which the weighting
+ smooths out progressively as the transfer progresses by adding
+ -DCPS_VINCE to -DCPS_WEIGHTED.
+
+ An alternative to curses is also available at compile time, but should
+ be selected if your version of Kermit is to be run in local mode only
+ in an ANSI terminal environment, for example on a desktop workstation
+ that has an ANSI console driver. To select this option in place of
+ curses, define the symbol MYCURSES:
+
+ -DMYCURSES
+
+ instead of CK_CURSES. The MYCURSES option uses built-in ANSI (VT100)
+ escape sequences, and depends upon your terminal or console driver to
+ interpret them correctly.
+
+ In some C-Kermit builds, we replace printf() via #define printf...
+ However, this can cause conflicts with the [n]curses header files.
+ Various hacks are required to get around this -- see [51]ckutio.c,
+ [52]ckufio.c, [53]ckuusx.c, [54]ckucmd.c, etc.
+
+ [ [55]C-Kermit Home ] [ [56]Kermit Home ]
+ ________________________________________________________________________
+
+ 4. CHARACTER SETS
+
+ [ [57]Top ] [ [58]Contents ] [ [59]Next ] [ [60]Previous ]
+
+ Since version 5A, C-Kermit has included support for conversion of
+ character sets for Western European languages (i.e. languages that
+ originated in Western Europe, but are now also spoken in the Western
+ Hemisphere and other parts of the world), via ISO 8859-1 Latin
+ Alphabet 1, for Eastern European languages (ISO Latin-2), Hebrew (and
+ Yiddish), Greek, and Cyrillic-alphabet languages (ISO Latin/Cyrillic).
+ Many file (local) character sets are supported: ISO 646 7-bit national
+ sets, IBM code pages, Apple, DEC, DG, NeXT, etc.
+
+ To build Kermit with no character-set translation at all, include
+ -DNOCSETS in the CFLAGS. To build with no Latin-2, add -DNOLATIN2. To
+ build with no Cyrillic, add -DNOCYRIL. To omit Hebrew, add -DNOHEBREW.
+ If -DNOCSETS is *not* included, you'll always get LATIN1. To build
+ with no KANJI include -DNOKANJI. There is presently no way to include
+ Latin-2, Cyrillic, Hebrew, or Kanji without also including Latin-1.
+
+ [61]Unicode support was added in C-Kermit 7.0, and it adds a fair
+ amount of tables and code (and this is only a "Level 1" implementation
+ -- a higher level would also require building in the entire Unicode
+ database). On a PC with RH 5.2 Linux, building C-Kermit 7.0, we get
+ the following sizes:
+
+ NOCSETS NOUNICODE NOKANJI Before After
+ [ ] [ ] [ ] 1329014 (Full)
+ [ ] [ ] [ X ] 1325686 (Unicode but no Kanji)
+ [ ] [ X ] [ ] 1158837 (All charsets except Unicode)
+ [ X ] [ x ] [ x ] 1090845 (NOCSETS implies the other two)
+
+ Note, by the way, that NOKANJI without NOUNICODE only removes the
+ non-Unicode Kanji sets (Shift-JIS, EUC-JP, JIS-7, etc). Kanji is still
+ representable in UCS-2 and UTF-8.
+
+ [ [62]C-Kermit Home ] [ [63]Kermit Home ]
+ ________________________________________________________________________
+
+ 5. APC EXECUTION
+
+ [ [64]Top ] [ [65]Contents ] [ [66]Next ] [ [67]Previous ]
+
+ The Kermit CONNECT and INPUT commands are coded to execute Application
+ Program Command escape sequences from the host:
+
+ <ESC>_<text><ESC>\
+
+ where <text> is a C-Kermit command, or a list of C-Kermit commands
+ separated by commas, up to about 1K in length.
+
+ To date, this feature has been included in the OS/2, Windows, VMS,
+ OS-9, and Unix versions, for which the symbol:
+
+ CK_APC
+
+ is defined automatically in [68]ckuusr.h. For OS/2, APC is enabled at
+ runtime by default, for UNIX it is disabled. It is controlled by the
+ SET TERMINAL APC command. Configuring APC capability into a version
+ that gets it by default (because CK_APC is defined in [69]ckuusr.h)
+ can be overridden by including:
+
+ -DNOAPC
+
+ on the CC command line.
+
+ C-Kermit's autodownload feature depends on the APC feature, so
+ deconfiguring APC also disables autodownload (it doesn't use APC
+ escape sequences, but uses the APC switching mechanism internally).
+
+ [ [70]C-Kermit Home ] [ [71]Kermit Home ]
+ ________________________________________________________________________
+
+ 6. PROGRAM SIZE
+
+ [ [72]Top ] [ [73]Contents ] [ [74]Next ] [ [75]Previous ]
+
+ SECTION CONTENTS
+
+ 6.1. [76]Feature Selection
+ 6.2. [77]Changing Buffer Sizes
+ 6.3. [78]Other Size-Related Items
+ 6.4. [79]Space/Time Tradeoffs
+
+ (Also see [80]Section 4)
+
+ Each release of C-Kermit is larger than the last. On some computers
+ (usually old ones) the size of the program prevents it from being
+ successfully linked and loaded. On some others (also usually old
+ ones), it occupies so much memory that it is constantly swapping or
+ paging. In such cases, you can reduce C-Kermit's size in various ways,
+ outlined in this section. The following options can cut down on the
+ program's size at compile time by removing features or changing the
+ size of storage areas.
+
+ If you are reading this section because all you want is a small, fast,
+ quick-to-load Kermit file-transfer application for the remote end of
+ your connection, and the remote end is Unix based, take a look at
+ G-Kermit:
+
+ [81]http://www.columbia.edu/kermit/gkermit.html
+
+ 6.1. Feature Selection
+
+ Features can be added or removed by defining symbols on the CC (C
+ compiler) command line. "-D" is the normal CC directive to define a
+ symbol so, for example, "-DNODEBUG" defines the symbol NODEBUG. Some C
+ compilers might use different syntax, e.g. "-d NODEBUG" or
+ "/DEFINE=NODEBUG". For C compilers that do not accept command-line
+ definitions, you can put the corresponding #define statements in the
+ file ckcsym.h, for example:
+
+ #define NODEBUG
+
+ The following table shows the savings achieved when building C-Kermit
+ 8.0 (Beta.04) with selected feature-deselection switches on an
+ Intel-based PC with Red Hat Linux 7.0 and gcc 2.96. The sizes are for
+ non-security builds. The fully configured non-security build is
+ 2127408 bytes.
+
+ Option Size Savings Effect
+ NOICP 545330 74.4% No Interactive Command Parser (command-line only)
+ NOLOCAL 1539994 27.6% No making connections.
+ NOXFER 1551108 27.1% No file transfer.
+ IKSDONLY 1566608 26.4% Internet Kermit Server only.
+ NOCSETS 1750097 17.7% No character-set conversion.
+ NOSPL 1800293 15.4% No Script Programming Language.
+ NONET 1808575 15.0% No making network connections.
+ NOUNICODE 1834426 13.8% No Unicode character-set conversion.
+ NOHELP 1837877 13.6% No built-in help text.
+ NODEBUG 1891669 11.1% No debug log.
+ NOFRILLS 1918966 9.8% No "frills".
+ NOFTP 1972496 7.3% No FTP client.
+ NODIAL 1984488 6.7% No automatic modem dialing.
+ NOPUSH 2070184 2.7% No shell access, running external programs, etc.
+ NOIKSD 2074129 2.5% No Internet Kermit Server capability.
+ NOHTTP 2082610 2.1% No HTTP client.
+ NOFLOAT 2091332 1.7% No floating-point arithmetic.
+ NOCHANNELIO 2095978 1.5% No FOPEN/FREAD/FWRITE/FCLOSE, etc.
+ MINIDIAL 2098035 1.4% No built-in support for many kinds of modems.
+ NOSERVER 2098987 1.3% No server mode.
+ NOSEXP 2105898 1.0% No S-Expressions.
+ NOPTY 2117743 0.5% No pseudoterminal support.
+ NORLOGIN 2121089 0.3% No RLOGIN connections.
+ NOOLDMODEMS 2124038 0.2% No built-in support for old kinds of modems.
+ NOSSH 2125696 0.1% No SSH command.
+
+ And here are a few combinations
+
+ Options Size Savings Effect
+ NODEBUG NOICP NOCSETS NOLOCAL 281641 86.7% No debug log, parser,
+ character sets, or making connections.
+ NOICP NOCSETS NOLOCAL 376468 82.3% No parser, character sets, or
+ making connections.
+ NOICP NOCSETS NONET 427510 79.9% No parser, character sets, or network
+ connections.
+ NOSPL NOCSETS 1423784 33.1% No script language, or character sets.
+
+ -DNOFRILLS removes various command synonyms; the following top-level
+ commands: CLEAR, DELETE, DISABLE, ENABLE, GETOK, MAIL, RENAME, TYPE,
+ WHO; and the following REMOTE commands: KERMIT, LOGIN, LOGOUT, PRINT,
+ TYPE, WHO.
+
+ 6.2. Changing Buffer Sizes
+
+ Most modern computers have so much memory that (a) there is no need to
+ scrimp and save, and (b) C-Kermit, even when fully configured, is
+ relatively small by today's standards.
+
+ Two major factors affect Kermit's size: feature selection and buffer
+ sizes. Buffer sizes affect such things as the maximum length for a
+ Kermit packet, the maximum length for a command, for a macro, for the
+ name of a macro, etc. Big buffer sizes are used when the following
+ symbol is defined:
+
+ BIGBUFOK
+
+ as it is by default for most modern platforms (Linux, AIX 4 and 5,
+ HP-UX 10 and 11, Solaris, etc) in [82]ckuusr.h. If your build does not
+ get big buffers automatically (SHOW FEATURES tells you), you can
+ include them by rebuilding with BIGBUFOK defined; e.g. in Unix:
+
+ make xxxx KFLAGS=-DBIGBUFOK
+
+ where xxxx is the makefile target. On the other hand, if you want to
+ build without big buffers when they normally would be selected, use:
+
+ make xxxx KFLAGS=-DNOBIGBUF
+
+ There are options to control Kermit's packet buffer allocations. The
+ following symbols are defined in [83]ckcker.h in such a way that you
+ can override them by redefining them in CFLAGS:
+
+ -DMAXSP=xxxx - Maximum send-packet length.
+ -DMAXRP=xxxx - Maximum receive-packet length.
+ -DSBSIZ=xxxx - Total allocation for send-packet buffers.
+ -DRBSIZ=xxxx - Total allocation for receive-packet buffers.
+
+ The defaults depend on the platform.
+
+ Using dynamic allocation (-DDYNAMIC) reduces storage requirements for
+ the executable program on disk, and allows more and bigger packets at
+ runtime. This has proven safe over the years, and now most builds
+ (e.g. all Unix, VMS, Windows, and OS/2 ones) use dynamic memory
+ allocation by default. If it causes trouble, however, then omit the
+ -DDYNAMIC option from CFLAGS, or add -DNODYNAMIC.
+
+ 6.3. Other Size-Related Items
+
+ To make Kermit compile and load successfully, you might have to change
+ your build procedure to:
+
+ a. Request a larger ("large" or "huge") compilation / code-generation
+ model. This is needed for 16-bit PC-based UNIX versions (most or
+ all of which fail to build C-Kermit 7.0 and later anyway). This is
+ typically done with a -M and/or -F switch (see your cc manual or
+ man page for details).
+ b. Some development systems support overlays. If the program is too
+ big to be built as is, check your loader manual ("man ld") to see
+ if an overlay feature is available. See the 2.10/2.11 BSD example
+ in the UNIX makefile. (Actually, as of version 7.0, C-Kermit is
+ too big to build, period, even with overlays, on 2.xx BSD).
+ c. Similarly, some small and/or segment-based architectures support
+ "code mapping", which is similar to overlays (PDP11-based VENIX
+ 1.0, circa 1984, was an example). See the linker documentation on
+ the affected platform.
+
+ It is also possible to reduce the size of the executable program file
+ in several other ways:
+
+ a. Include the -O (optimize) compiler switch if it isn't already
+ included in your "make" entry (and if it works!). If your compiler
+ supports higher levels of optimization (e.g. -O2 or higher number,
+ -Onolimit (HP-UX), etc), try them; the greater the level of
+ optimization, the longer the compilation and more likely the
+ compiler will run out of memory. The the latter eventuality, some
+ compilers also provide command-line options to allocate more
+ memory for the optimizer, like "-Olimit number" in Ultrix.
+ b. If your platofrm supports shared libraries, change the make entry
+ to take advantage of this feature. The way to do this is, of
+ course, platform dependent; see the NeXT makefile target for an
+ example. some platforms (like Solaris) do it automatically and
+ give you no choice. But watch out: executables linked with shared
+ libraries are less portable than statically linked executables.
+ c. Strip the program image after building ("man strip" for further
+ info), or add -s to the LNKFLAGS (UNIX only). This strips the
+ program of its symbol table and relocation information.
+ d. Move character strings into a separate file. See the 2.11 BSD
+ target for an example.
+
+ 6.4. Space/Time Tradeoffs
+
+ There are more than 6000 debug() statements in the program. If you
+ want to save both space (program size) and time (program execution
+ time), include -DNODEBUG in the compilation. If you want to include
+ debugging for tracking down problems, omit -DNODEBUG from the make
+ entry. But when you include debugging, you have two choices for how
+ it's done. One definition defines debug() to be a function call; this
+ is cheap in space but expensive in execution. The other defines debug
+ as "if (deblog)" and then the function call, to omit the function call
+ overhead when the debug log is not active. But this adds a lot of
+ space to the program. Both methods work, take your choice; IFDEBUG is
+ preferred if memory is not a constraint but the computer is likely to
+ be slow. The first method is the default, i.e. if nothing is done to
+ the CFLAGS or in [84]ckcdeb.h (but in some cases, e.g. VMS, it is). To
+ select the second method, include -DIFDEBUG in the compilation (and
+ don't include -DNODEBUG).
+
+ [ [85]C-Kermit Home ] [ [86]Kermit Home ]
+ ________________________________________________________________________
+
+ 7. MODEM DIALING
+
+ [ [87]Top ] [ [88]Contents ] [ [89]Next ] [ [90]Previous ]
+
+ -DNODIAL removes automatic modem dialing completely, including the
+ entire [91]ckudia.c module, plus all commands that refer to dialing in
+ the various ckuus*.c modules.
+
+ -DMINIDIAL leaves the DIAL and related commands (SET/SHOW MODEM,
+ SET/SHOW DIAL) intact, but removes support for all types of modems
+ except CCITT, Hayes, Unknown, User-defined, Generic-high-speed, and
+ None (= Direct). The MINIDIAL option cuts the size of the dial module
+ approximately in half. Use this option if you have only Hayes or CCITT
+ modems and don't want to carry the baggage for the other types.
+
+ A compromise between full dialer support and MINIDIAL is obtained by
+ removing support for "old" modems -- all the strange non-Hayes
+ compatible 1200 and 2400 bps modems that C-Kermit has been carrying
+ around since 1985 or so. To remove support for these modems, add
+ -DNOOLDMODEMS to CFLAGS at compilation time.
+
+ Finally, if you keep support for old modems, you will notice that
+ their names appear on the "set modem ?" menu. That's because their
+ names are, by default, "visible". But the list is confusing to the
+ younger generation, who have only heard of modems from the
+ V.32bis-and-later era. If you want to be able to use old modems, but
+ don't want their names cluttering up menus, add this to CFLAGS:
+
+ -DM_OLD=1
+
+ [ [92]C-Kermit Home ] [ [93]Kermit Home ]
+ ________________________________________________________________________
+
+ 8. NETWORK SUPPORT
+
+ [ [94]Top ] [ [95]Contents ] [ [96]Next ] [ [97]Previous ]
+
+ SECTION CONTENTS
+
+ 8.1. [98]TCP/IP
+ 8.2. [99]X.25
+ 8.3. [100]Other Networks
+
+ C-Kermit supports not only serial-port and modem connections, but also
+ TCP/IP and X.25 network connections. Some versions support other
+ network types too like DECnet, LAT, NETBIOS, etc. If you define the
+ following symbol:
+
+ NONET
+
+ then all network support is compiled away.
+
+ 8.1. TCP/IP
+
+ SUBSECTION CONTENTS
+
+ 8.1.1. [101]Firewalls
+ 8.1.2. [102]Compilation and Linking Problems
+ 8.1.3. [103]Enabling Host Address Lists
+ 8.1.4. [104]Enabling Telnet NAWS
+ 8.1.5. [105]Enabling Incoming TCP/IP Connections
+ 8.1.6. [106]Disabling SET TCP Options
+
+ C-Kermit's TCP/IP features require the Berkeley sockets library or
+ equivalent, generally available on any Unix system, as well as in
+ Windows 9x/NT, OS/2, VMS, AOS/VS, VOS, etc. The TCP/IP support
+ includes built-in TELNET, FTP, and HTTP protocol. To select TCP/IP
+ support, include -DTCPSOCKET in your makefile target's CFLAGS, or (in
+ VMS) the appropriate variant (e.g. -DWOLLONGONG, -DMULTINET,
+ -DEXCELAN, -DWINTCP, etc).
+
+ The VMS and/or early Unix third-party TCP/IP products are often
+ incompatible with each other, and sometimes with different versions of
+ themselves. For example, Wollongong reportedly put header files in
+ different directories for different UNIX versions:
+
+ * in.h can be in either /usr/include/sys or /user/include/netinet.
+ * telnet.h can be in either /usr/include/arpa or
+ /user/include/netinet.
+ * inet.h can be in either /usr/include/arpa or /user/include/sys.
+
+ In cases like this, use the -I cc command-line option when possible;
+ otherwise it's better to make links in the file system than it is to
+ hack up the C-Kermit source code. Suppose, for example, Kermit is
+ looking for telnet.h in /usr/include/arpa, but on your computer it is
+ in /usr/include/netinet. Do this (as root, or get the system
+ administrator to do it):
+
+ cd /usr/include/arpa
+ ln /usr/include/netinet/telnet.h telnet.h
+
+ ("man ln" for details about links.)
+
+ The network support for TCP/IP and X.25 is in the source files
+ [107]ckcnet.h, [108]ckctel.c, [109]ckctel.c, [110]ckctel.h,
+ [111]ckcftp.c, with miscellaneous SHOW commands, etc, in the various
+ ckuus*.c modules, plus code in the ck*con.c or ckucns.c (CONNECT
+ command) and several other modules to detect TELNET negotiations, etc.
+
+ Within the TCPSOCKET code, some socket-level controls are included if
+ TCPSOCKET is defined in the C-Kermit CFLAGS and SOL_SOCKET is defined
+ in in the system's TCP-related header files, such as <sys/socket.h>.
+ These are:
+
+ SET TCP KEEPALIVE
+ SET TCP LINGER
+ SET TCP RECVBUF
+ SET TCP SENDBUF
+
+ In addition, if TCP_NODELAY is defined, the following command is also
+ enabled:
+
+ SET TCP NODELAY (Nagle algorithm)
+
+ See the [112]C-Kermit user documentation for descriptions of these
+ commands.
+
+ 8.1.1. Firewalls
+
+ There exist various types of firewalls, set up to separate users of an
+ internal TCP/IP network ("Intranet") from the great wide Internet, but
+ then to let selected users or services get through after all.
+
+ One firewall method is called SOCKS, in which a proxy server allows
+ users inside a firewall to access the outside world, based on a
+ permission list generally stored in a file. SOCKS is enabled in one of
+ two ways. First, the standard sockets library is modified to handle
+ the firewall, and then all the client applications are relinked (if
+ necessary, i.e. if the libraries are not dynamically loaded) with the
+ modified sockets library. The APIs are all the same, so the
+ applications do not need to be recoded or recompiled.
+
+ In the other method, the applications must be modified to call
+ replacement routines, such as Raccept() instead of accept(), Rbind()
+ instead of bind(), etc, and then linked with a separate SOCKS library.
+ This second method is accomplished (for SOCKS4) in C-Kermit by
+ including -DCK_SOCKS in your CFLAGS, and also adding:
+
+ -lsocks
+
+ to LIBS, or replacing -lsockets with -lsocks (depending on whether the
+ socks library also includes all the sockets entry points).
+
+ For SOCKS5, use -DCK_SOCKS5.
+
+ Explicit firewall support can, in general, not be a standard feature
+ or a feature that is selected at runtime, because the SOCKS library
+ tends to be different at each site -- local modifications abound.
+
+ The ideal situation occurs when firewalls are supported by the first
+ method, using dynamically linked sockets-replacement libraries; in
+ this case, all your TCP/IP client applications negotiate the firewall
+ transparently.
+
+ 8.1.2. Compilation and Linking Problems
+
+ If you get a compilation error in [113]ckcnet.c, with a complaint like
+ "incompatible types in assignment", it probably has something to do
+ with the data type your system uses for the inet_addr() function,
+ which is declared (usually) in <arpa/inet.h>. Kermit uses "unsigned
+ long" unless the symbol INADDRX is defined, in which case "struct
+ inaddr" is used instead. Try adding -DINADDRX to CFLAGS in your make
+ entry, and if that fixes the problem, please send a report to
+ kermit@columbia.edu.
+
+ Compilation errors might also have to do with the data type used for
+ getsockopt() and setsockopt() option-length field. This is normally an
+ int, but sometimes it's a short, a long, or an unsigned any of those,
+ or a size_t. To fix the compilation problem, add -DSOCKOPT_T=xxx to
+ the CFLAGS in your makefile target, where xxx is the appropriate type
+ (use "man getsockopt" or grep through your system/network header files
+ to find the needed type).
+
+ 8.1.3. Enabling Host Address Lists
+
+ When you give Kermit an IP host name, it calls the socket routine
+ gethostbyname() to resolve it. gethostbyname() returns a hostent
+ struct, which might or might not not include a list of addresses; if
+ it does, then if the first one fails, Kermit can try the second one,
+ and so on. However, this will only work if the symbol "h_addr" is a
+ macro defined as "h_addr_list[0]", usually in netdb.h. If it is, then
+ you can activate this feature by defining the following symbol in
+ CFLAGS:
+
+ HADDRLIST
+
+ 8.1.4. Enabling Telnet NAWS
+
+ The Telnet Negotiation About Window Size (NAWS) option requires the
+ ability to find out the terminal screen's dimensions. E.g. in Unix, we
+ need something like ioctl(0, TIOCGWINSZ, ...). If your version of
+ Kermit was built with NAWS capability, SHOW VERSIONS includes CK_NAWS
+ among the compiler options. If it doesn't, you can add it by defining
+ CK_NAWS at compile time. Then, if the compiler or linker complain
+ about undefined or missing symbols, or there is no complaint but SHOW
+ TERMINAL fails to show reasonable "Rows =, Columns =" values, then
+ take a look at (or write) the appropriate ttgwsiz() routine. On the
+ other hand, if CK_NAWS is defined by default for your system (in
+ [114]ckcnet.h), but causes trouble, you can override this definition
+ by including the -DNONAWS switch on your CC command line, thus
+ disabling the NAWS feature.
+
+ This appears to be needed at least on the AT&T 3B2, where in
+ [115]ckutio.c, the routine ttgwsiz() finds that the TIOCGWINSZ symbol
+ is defined but lacks definitions for the corresponding winsize struct
+ and its members ws_col and ws_row.
+
+ The UNIX version of C-Kermit also traps SIGWINCH, so it can send a
+ NAWS to the Telnet server any time the local console terminal window
+ size changes, e.g. when you stretch it with a mouse. The
+ SIGWINCH-trapping code is enabled if SIGWINCH is defined (i.e. in
+ signal.h). If this code should cause problems, you can disable it
+ without disabling the NAWS feature altogether, by defining NOSIGWINCH
+ at compile time.
+
+ 8.1.5. Enabling Incoming TCP/IP Connections
+
+ This feature lets you "set host * port" and wait for an incoming
+ connection on the given port. This feature is enabled automatically at
+ compile if TCPSOCKET is defined and SELECT is also defined. But watch
+ out, simply defining SELECT on the cc command line does not guarantee
+ successful compilation or linking (see [116]Section 11).
+
+ If you want to disable incoming TCP/IP connections, then build
+ C-Kermit with:
+
+ -DNOLISTEN
+
+ 8.1.6. Disabling SET TCP Options
+
+ The main reason for this is because of header file / prototype
+ conflicts at compile time regardting get- / setsockopt(). If you can't
+ fix them (without breaking other builds), add the following in CFLAGS:
+
+ -DNOTCPOPTS
+
+ 8.2. X.25
+
+ X.25 support requires (a) a Sun, (b) the SunLink product (libraries
+ and header files), and (c) an X.25 connection into your Sun. Similarly
+ (in C-Kermit 7.0 or later) Stratus VOS and IBM AIX.
+
+ In UNIX, special makefile targets sunos4x25 and sunos41x25 (for SUNOS
+ 4.0 and 4.1, respectively), or aix41x25, are provided to build in this
+ feature, but they only work if conditions (a)-(c) are met. To request
+ this feature, include -DSUNX25 (or -DIBMX25) in CFLAGS.
+
+ SUNX25 (or -DIBMX25) and TCPSOCKET can be freely mixed and matched,
+ and selected by the user at runtime with the SET NETWORK TYPE command
+ or SET HOST switches.
+
+ 8.3. Other Networks
+
+ Support for other networking methods -- NETBIOS, LAT, Named Pipes, etc
+ -- is included in ck*net.h and ck*net.c for implementations (such as
+ Windows or OS/2) where these methods are supported.
+
+ Provision is made in the organization of the modules, header files,
+ commands, etc, for addition of new network types such as DECnet, X.25
+ for other systems (HP-UX, VMS, etc), and so on. Send email to
+ [117]kermit@columbia.edu if you are willing and able to work on such a
+ project.
+
+ [ [118]C-Kermit Home ] [ [119]Kermit Home ]
+ ________________________________________________________________________
+
+ 9. EXCEPTION HANDLING
+
+ [ [120]Top ] [ [121]Contents ] [ [122]Next ] [ [123]Previous ]
+
+ The C language setjmp/longjmp mechanism is used for handling
+ exceptions. The jump buffer is of type jmp_buf, which almost
+ everywhere is typedef'd as an array, in which case you should have no
+ trouble compiling the exception-handling code. However, if you are
+ building C-Kermit in/for an environment where jmp_buf is something
+ other than an array (e.g. a struct), then you'll have to define the
+ following symbol:
+
+ JBNOTARRAY
+
+ [ [124]C-Kermit Home ] [ [125]Kermit Home ]
+ ________________________________________________________________________
+
+ 10. SECURITY FEATURES
+
+ [ [126]Top ] [ [127]Contents ] [ [128]Next ] [ [129]Previous ]
+
+ Security, in the sense of secure authentication and strong encryption,
+ can be built into versionf of C-Kermit for which the appropriate
+ libraries and header files are available (Kerberos IV, Kerberos V,
+ OpenSSL, SRP), as explained in great detail in the Kermit Security
+ Reference
+ . The following symbols govern C-Kermit's security features at build
+ time:
+
+ NO_AUTHENTICATION
+ Means do not configure any TELNET AUTHENTICATION support. It
+ implies NO_ENCRYPTION and undefines any of the auth and encrypt
+ types. It does not undefine CK_SSL even though builds with
+ CK_SSL cannot succeed without CK_AUTHENTICATION. (This will be
+ supported in a future release. It will be needed to allow
+ C-Kermit to be built only as an FTP client.)
+
+ NO_KERBEROS
+ Means do not compile in any KERBEROS support when
+ CK_AUTHENTICATION has been defined.
+
+ NO_SRP
+ Do not compile in any SRP support when CK_AUTHENTICATION has
+ been defined.
+
+ NO_SSL
+ Do not compile in any SSL/TLS support
+
+ NO_ENCRYPTION
+ Do not compile in any Telnet encryption support. It does not
+ affect the use of SSL/TLS
+
+ NOSSH
+ Do not compile in any SSH support whether internal or external
+
+ CK_AUTHENTICATION
+ Telnet AUTHENTICATION support. (Also, required if SSL/TLS
+ support is desired.) On most platforms this does not autodefine
+ any authentication mechanisms such as Kerberos V, Kerberos IV,
+ SRP, ... Those need to be defined separately.
+
+ CK_KERBEROS
+ Defined automatically when KRB4, KRB5, or KRB524 are defined.
+ Implies that some version of Kerberos is in use.
+
+ KRB4
+ Should be defined when Kerberos IV support is desired.
+
+ KRB5
+ Should be defined when Kerberos V support is desired.
+
+ KRB524
+ Should be defined if both Kerberos V and Kerberos IV are used
+ and the Kerberos IV support is provided by the MIT Kerberos IV
+ compatibility library in the current Kerberos 5 distribution.
+
+ KRB5_U2U
+ Should be defined if KRB5 is defined and Kerberos 5 User to
+ User mode is desired.
+
+ HEIMDAL
+ Should be defined if Kerberos V support is provided by HEIMDAL.
+ Support for this option is not complete in C-Kermit 8.0. Anyone
+ interested in working on this should contact kermit-support.
+
+ CK_SRP
+ Should be defined if SRP support is desired.
+
+ CK_ENCRYPTION
+ Should be defined if TELNET ENCRYPTION option support is
+ desired. This option does not define any particular encryption
+ types. That should be done by defining CK_DES or CK_CAST.
+
+ CK_DES
+ Should be defined if either DES or 3DES Telnet Encryption
+ option support is desired.
+
+ LIBDES
+ If CK_DES is defined and DES support is being provided by
+ either Eric Young's libdes.a or OpenSSL 0.9.6x or earlier, this
+ option must be defined. If it is not defined, it will be
+ assumed that DES support is provided by the MIT Kerberos IV
+ libraries.
+
+ CK_CAST
+ Should be defined if CAST Telnet Encryption option support is
+ desired
+
+ CK_SSL
+ Should be defined if SSL/TLS support (OpenSSL) is desired.
+
+ SSL_KRB5
+ If KRB5 is defined, and OpenSSL is built to support the
+ Kerberos 5 ciphers, then you should define SSL_KRB5
+
+ NOSSLKRB5
+ If you are using OpenSSL 0.9.7 or higher and do not wish to
+ build with support for Kerberos 5 TLS ciphers, this option must
+ be defined.
+
+ ZLIB
+ If you are using OpenSSL 0.9.6 or higher and it has been
+ compiled with support for ZLIB compression, this option should
+ be defined to enable Kermit to properly enable the use of
+ compression.
+
+ SSHCMD
+ Defined for C-Kermit to enable the use of external SSH clients
+ from the Kermit command language
+
+ SSHBUILTIN
+ Defined for Kermit implementations that have integrated SSH
+ support. Currently only Windows.
+
+ ANYSSH
+ Defined if either SSHCMD or SSHBUILTIN are defined.
+
+ CK_SNDLOC
+ Telnet Send Location support.
+
+ NOSNDLOC
+ Do not include Telnet Send Location support.
+
+ CK_XDISPLOC
+ Telnet X-Display Location support. Determines if the X-Display
+ location information is sent to the Telnet server either via
+ Telnet XDISPLOC or NEW-ENV options.
+
+ NOXDISPLOC
+ Do not include Telnet X-Display Location support.
+
+ CK_FORWARD_X
+ Telnet Forward X Windows Session Data option. Used to protect
+ the privacy and integrity of X Windows Sessions when secure
+ telnet sessions are in use.
+
+ NOFORWARDX
+ Do not include Telnet Forward X Windows Session Data option.
+
+ Besides the strong forms of security listed above, C-Kermit also
+ embodies various internal security features, including:
+
+ NOPUSH
+ Compiling with the NOPUSH symbol defined removes all the "shell
+ escape" features from the program, including the PUSH, RUN, and
+ SPAWN commands, the "!" and "@" command prefixes, OPEN !READ,
+ OPEN !WRITE, job control (including the SUSPEND command), the
+ REDIRECT command, shell/DCL escape from CONNECT mode, as well
+ as the server's execution of REMOTE HOST commands (and, of
+ course, the ENABLE HOST command). Add NODISPO to also prevent
+ acceptance of incoming MAIL or REMOTE PRINT files. For UNIX,
+ also be sure to read [130]Section 11 of the [131]Unix C-Kermit
+ Installation Instructions. about set[ug]id configuration.
+ Additional restrictions can be enforced when in server mode;
+ read about the DISABLE command in the user manual.
+
+ NOCCTRAP
+ Compiling with NOCCTRAP prevents the trapping of SIGINT by
+ Kermit. Thus if the user generates a SIGINT signal (e.g. by
+ typing the system's interrupt character), Kermit will exit
+ immediately, rather than returning to its prompt.
+
+ NOPUSH and NOCCTRAP together allow Kermit to be run from restricted
+ shells, preventing access to system functions.
+
+ [ [132]C-Kermit Home ] [ [133]Kermit Home ]
+ ________________________________________________________________________
+
+ 11. ENABLING SELECT()
+
+ [ [134]Top ] [ [135]Contents ] [ [136]Next ] [ [137]Previous ]
+
+ Kermit works best if it can do nonblocking reads, nondestructive input
+ buffer checking, and millisecond sleeps. All of these functions can be
+ accomplished by the select() function, which, unfortunately, is not
+ universally available. Furthermore, select() is required if incoming
+ TCP/IP connections are to be supported.
+
+ select() was introduced with Berkeley UNIX, rejected by AT&T for
+ System V, but is gradually creeping in to all UNIX versions (and other
+ operating systems too) by virtue of its presence in the sockets
+ library, which is needed for TCP/IP. AT&T SVID for System V R4
+ includes select(), but that does not mean that all SVR4
+ implementations have it.
+
+ Furthermore, even when select() is available, it might work only on
+ socket file descriptors, but not on others like serial ports, pipes,
+ etc. For example, in AOS/VS and BeOS, it works only with file
+ descriptors that were created by socket() and opened by connect() or
+ accept().
+
+ Other alternatives include poll() and rdchk(). Only one of these three
+ functions should be included. The following symbols govern this:
+
+ SELECT Use select() (BSD, or systems with sockets libraries)
+ CK_POLL Use poll() (System V)
+ RDCHK Use rdchk() (SCO XENIX and UNIX)
+
+ If your system supports the select() function, but your version of
+ C-Kermit does not, try adding:
+
+ -DSELECT
+
+ to the CFLAGS, and removing -DRDCHK or -DCK_POLL if it is there. If
+ you get compilation errors, some adjustments to ck*tio.c and/or
+ ck*net.c might be needed; search for SELECT (uppercase) in these files
+ (note that there are several variations on the calling conventions for
+ select()).
+
+ Various macros and data types need to be defined in order to use
+ select(). Usually these are picked up from <types.h> or <sys/types.h>.
+ But on some systems, they are in <sys/select.h>. In that case, add the
+ following:
+
+ -DSELECT_H
+
+ to the CFLAGS to tell C-Kermit to #include <sys/select.h>. A good
+ indication that you need to do this would be if you get compile-time
+ complaints about "fd_set" or "FD_SET" not being declared or defined.
+
+ In UNIX, the use of select() vs fork() in the CONNECT command is
+ independent of the above considerations, and is governed by choosing a
+ particular makefile target.
+
+ As of C-Kermit 7.0, select() is also the preferred control mechanism
+ for the CONNECT command. Unfortunately, the structures used by the
+ original UNIX CONNECT command, based on fork(), and those used by
+ select(), are so different, it was not practical to implement them
+ both in one module. So the select()-based CONNECT command module for
+ UNIX is [138]ckucns.c, and the fork-based one remains [139]ckucon.c.
+ To choose the fork-based one, which is more portable (but slower and
+ more fragile), use "wermit" as the make target. To choose the
+ select-based one, use "xermit". Only do this if you can verify that
+ the CONNECT command works on serial connections and PIPE connections
+ as well as TCP connections.
+
+ The select()-based Unix CONNECT module, ckucns.c, must be used if
+ encryption is to be done, since the fork() version (ckucon.c) loses
+ its ability to share vital state information between the two forks.
+ Also note that the select() version is superior in many other ways
+ too. For example, it recovers better from exterior killing, forced
+ disconnections, etc, plus it goes faster.
+
+ SHOW VERSIONS tells whether the CONNECT module uses fork() or
+ select().
+
+ C-Kermit 8.0 adds learned script capability, which depends on
+ select(). All the "wermit" based targets (as opposed to "xermit") had
+ NOLEARN added to them. Whenever changing a target over from wermit to
+ xermit, also remember to remove NOLEARN.
+
+ [ [140]C-Kermit Home ] [ [141]Kermit Home ]
+ ________________________________________________________________________
+
+ 12. I/O REDIRECTION
+
+ [ [142]Top ] [ [143]Contents ] [ [144]Next ] [ [145]Previous ]
+
+ The REDIRECT command allows a local program to be run with its i/o
+ redirected over the communications connection. Your version of
+ C-Kermit has a REDIRECT command if it was built with the following
+ CFLAG:
+
+ -DCK_REDIR
+
+ This, in turn, is possible only if the underlying API is there. In the
+ case of UNIX this is just the wait() system call, so all UNIX versions
+ get this feature as of 6.0.192 (earlier versions needed a <sys/wait.h>
+ header file defining the symbols WIFEXITED and WEXITSTATUS).
+
+ As of version 7.0, file transfer can be done using pipes and filters.
+ To enable this feature, #define PIPESEND (and fill in the code). To
+ disable on systems where it is normally enabled, define NOPIPESEND.
+ This feature is, of course, also disabled by building with NOPUSH (or
+ giving the "nopush" command at runtime).
+
+ C-Kermit 7.0 also adds the PIPE and SET HOST /COMMAND commands, which
+ provide another form of redirection. This feature is selected with
+ -DNETCMD. CK_RDIR must also be defined, since the same mechanisms are
+ used internally.
+
+ [ [146]C-Kermit Home ] [ [147]Kermit Home ]
+ ________________________________________________________________________
+
+ 13. FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC
+
+ [ [148]Top ] [ [149]Contents ] [ [150]Next ] [ [151]Previous ]
+
+ Floating-point support was added in C-Kermit 7.0.
+
+ Floating-point numbers are enabled internally, at least for use in
+ high-precision file-transfer timers and statistics, unless the
+ following symbol is defined at compile time:
+
+ -DNOFLOAT
+
+ This might be necessary on old PCs that do not have built-in
+ floating-point hardware.
+
+ When NOFLOAT is not defined, the following symbol tells which
+ floating-point type to use:
+
+ -DCKFLOAT=xxxx
+
+ The value is either "double" (normal for 32- and 16-bit architectures)
+ or "float" (normal for 64-bit architectures).
+
+ C-Kermit can be configured to use high-precision file-transfer timers
+ for more accurate statistics. This feature is enabled with:
+
+ -DGFTIMER
+
+ and disabled with:
+
+ -DNOGFTIMER
+
+ If you try to build with -DGFTIMER but you get compilation errors,
+ either fix them (and send email to kermit@columbia.edu telling what
+ you did), or else give up and use -DNOGFTIMER (or -DNOFLOAT) instead.
+ Hint: depending on your machine architecture, you might have better
+ luck using double than float as the data type for floating-point
+ numbers, or vice versa. Look in [152]ckcdeb.h for the CKFLOAT
+ definition.
+
+ Floating-point arithmetic is also supported in the script programming
+ language. First via the \fpp...() functions, such as \fppadd(), which
+ adds two floating-point numbers, second in S-Expressions. Addition,
+ subtraction, multiplication, and division are always available. But
+ other functions such as logs, raising to powers, sines and cosines,
+ etc, require the C Math library. To include user-level floating-point
+ math you must put:
+
+ -DFNFLOAT
+
+ and in Unix you must link with the Math library:
+
+ LIBS=".... -lm"
+
+ In K95 and VMS, FNFLOAT is defined automatically if CKFLOAT is
+ defined. In Unix, however, FNFLOAT must be added to each makefile
+ target individually, because of the special linking instructions that
+ must also be added to each target.
+
+ Note: S-Expressions require FNFLOAT.
+
+ [ [153]C-Kermit Home ] [ [154]Kermit Home ]
+ ________________________________________________________________________
+
+ 14. SPECIAL CONFIGURATIONS
+
+ [ [155]Top ] [ [156]Contents ] [ [157]Previous ]
+
+ As of C-Kermit 7.0, if you build C-Kermit normally, but with -DNOICP
+ (No Interactive Command Parser), you get a program capable of making
+ serial connections (but not dialing) and network connections (if
+ TCPSOCKET or other network option included), and can also transfer
+ files using Kermit protocol, but only via autodownload/upload.
+ Furthermore, if you call the executable "telnet", it will act like
+ Telnet -- using the command-line options. However, in this case there
+ is nothing to escape back to, so if you type Ctrl-\c, it just prints a
+ message to this effect.
+
+ You can also build C-Kermit with -DNOXFER, meaning omit all the
+ file-transfer features. This leaves you with a scriptable
+ communications program that is considerably smaller than the full
+ C-Kermit.
+
+ [ [158]C-Kermit Home ] [ [159]Kermit Home ]
+ ________________________________________________________________________
+
+ APPENDIX I: SUMMARY OF COMPILE-TIME OPTIONS
+
+ [ [160]Top ] [ [161]Contents ]
+
+ These are the symbols that can be specified on the cc command line,
+ listed alphabetically. Others are used internally, including those
+ taken from header files, those defined by the compiler itself, and
+ those inferred from the ones given below. Kermit's SHOW VERSIONS
+ command attempts to display most of these. See [162]ckcdeb.h and
+ [163]ckcnet.h for inference rules. For example SVR3 implies ATTSV,
+ MULTINET implies TCPSOCKET, and so on.
+
+ Here is the complete list of the Kermit-specific compile-time
+ switches:
+
+ ACUCNTRL Select BSD 4.3-style acucntrl() bidirectional tty control.
+ aegis Build for Apollo Aegis (predefined on Apollo systems).
+ AIX370 Build for IBM AIX/370 for IBM mainframes.
+ AIXESA Build for IBM AIX/ESA for IBM mainframes.
+ AIXPS2 Build for IBM AIX 3.0 for PS/2 series (never formally
+ released).
+ AIXRS Build for IBM AIX 3.x on RS/6000.
+ AIX41 Build for IBM AIX 4.x on RS/6000.
+ AMIGA Build for Commodore Amiga with Intuition OS.
+ ATT6300 Build for AT&T 6300 PLUS.
+ ATT7300 Build for AT&T 7300 UNIX PC (3B1).
+ ATTSV Build for AT&T System III or V UNIX.
+ AUX Build for Apple A/UX for the Macintosh.
+ BIGBUFOK OK to use big buffers - "memory is not a problem"
+ BPS_xxxx Enable SET SPEED xxxx
+ BSD29 Build for BSD 2.9 or 2.10.
+ BSD4 Build for BSD 4.2.
+ BSD41 Build for BSD 4.1.
+ BSD43 Build for BSD 4.3.
+ BSD44 Build for BSD 4.4.
+ C70 Build for BBN C/70.
+ CIE Build for CIE Systems 680/20.
+ CKCONINTB4CB Work around prompt-disappears after escape back from
+ CONNECT.
+ CKLEARN Build with support for learned scripts.
+ CKLOGDIAL Enable connection log.
+ CKMAXPATH Maximum length for a fully qualified filename.
+ CKREGEX (misnomer) Include [...] or {xxx,xxx,xxx} matching in
+ ckmatch().
+ CKSYSLOG Enable syslogging.
+ CK_ANSIC Enable ANSI C constructs - prototypes, etc.
+ CK_ANSILIBS Use header files for ANSI C libraries.
+ CK_APC Enable APC execution by CONNECT module.
+ CK_CURSES Enable fullscreen file transfer display.
+ CK_DSYSINI Use system-wide init file, with name supplied by Kermit.
+ CK_DTRCD DTR/CD flow control is available.
+ CK_FAST Build with fast Kermit protocol defaults.
+ CK_FORK_SIG UNIX only: signal() number for CONNECT module forks.
+ CK_IFRO IF REMOTE command is available (and can run in remote mode).
+ CK_INI_A System-wide init file takes precedence over user's.
+ CK_INI_B User's init file takes precedence over the system-wide one.
+ CK_LABELED Include support for SET FILE TYPE LABELED.
+ CK_LBRK This version can send Long BREAK.
+ CK_LINGER Add code to turn of TCP socket "linger" parameter.
+ CK_MKDIR This version has a zmkdir() command to create directories.
+ CK_NAWS Include TELNET Negotiate About Window Size support.
+ CK_NEWTERM Use newterm() rather than initscr() to initialize curses.
+ CK_PAM Include PAM authentication (might also require -lpam).
+ CK_PCT_BAR Fullscreen file transfer display should include
+ "thermometer".
+ CK_POLL System-V or POSIX based UNIX has poll() function.
+ CK_POSIX_SIG Use POSIX signal handing: sigjmp_buf, sigsetjmp,
+ siglongjmp.
+ CK_READ0 read(fd,&x,0) can be used to test TCP/IP connections.
+ CK_REDIR Enable the REDIRECT command.
+ CK_RESEND Include the RESEND command (needs zfseek() + append).
+ CK_RTSCTS RTS/CTS flow control is available.
+ CK_SHADOW Include support for shadow passwords (e.g. for IKSD
+ authentication).
+ CK_SOCKBUF Enable TCP socket-buffer-size-increasing code.
+ CK_SOCKS UNIX only: Build with socks library rather than regular
+ sockets
+ CK_SOCKS5 UNIX only: Build with socks 5 lib rather than regular
+ sockets
+ CK_SPEED Enable control-character unprefixing.
+ CK_SYSINI="xxxxx" Quoted string to be used as system-wide init file
+ name.
+ CK_TIMERS Build with support for dynamically calculated packet
+ timeouts.
+ CK_TMPDIR This version of Kermit has an isdir() function.
+ CK_TTYFD Defined on systems where the communications connection file
+ descriptor (ttyfd) can be passed to other processes as a command-line
+ argument via \v(ttyfd).
+ CK_URL Parse URLs as well as hostnames, etc.
+ CK_XONXOFF Xon/Xoff flow control available.
+ CK_XYZ Include support for XYZMODEM protocols.
+ CK_WREFRESH Curses package includes wrefresh(),clearok() for screen
+ refresh.
+ CKFLOAT=type Floating-point data type, "double" or "float".
+ CKTYP_H=xxx Force include of xxx as <types.h> file.
+ CLSOPN When hanging up a tty device, also close and reopen it.
+ CMDDEP Maximum recursion depth for self-referential user-defined fn's.
+ COHERENT Build for Mark Williams Coherent UNIX
+ CONGSPD Define if this version has congspd() routine in ck?tio.c
+ datageneral Build for Data General AOS/VS or AOS/VS II
+ DCLPOPEN popen() is available but needs to be declared
+ DEC_TCPIP Build with support for DEC TCP/IP (UCX) for (Open)VMS
+ DGUX430 Build for DG/UX 4.30
+ DGUX540 Build for DG/UX 5.40
+ DEFPAR=x Default parity, 0, 'e', 'o', 'm', or 's'.
+ DFTTY=xxx Default communications device name.
+ DIRENT UNIX directory structure to be taken from <dirent.h>.
+ DIRPWDRP Prompt for password in REMOTE CWD command.
+ DTILDE Include UNIX ~ notation for username/home-directory
+ DYNAMIC Allocate file transfer packet buffers dynamically with malloc.
+ ENCORE Build for Encore Multimax computers.
+ EXCELAN Build with excelan TCP/IP.
+ FNFLOAT Include floating-point math functions (logs, sin, cos, exp,
+ etc)
+ FT18 Build for Fortune For:Pro 1.8.
+ FT21 Build for Fortune For:Pro 2.1.
+ GEMDOS Build for Atari ST GEMDOS.
+ GFTIMER Use high-precision floating-point file-transfer timers.
+ GID_T=xxx Group IDs are of type xxx (usually int, short, or gid_t).
+ HADDRLIST If gethostbyname() hostent struct contains a list of
+ addresses.
+ HDBUUCP Build with support for Honey DanBer UUCP.
+ HPUX Build for Hewlett Packard HP-UX.
+ HPUX9 Build for Hewlett Packard HP-UX 9.x.
+ HPUX10 Build for Hewlett Packard HP-UX 10.x.
+ HWPARITY Define if this version can SET PARITY HARDWARE { EVEN,
+ ODD...}
+ I386IX Build for Interactive System V R3.
+ IFDEBUG Add IF stmts "if (deblog)" before "debug()" calls.
+ INADDRX TCP/IP inet_addr() type is struct inaddr, not unsigned long.
+ INTERLAN Build with support for Racal/Interlan TCP/IP.
+ ISDIRBUG System defs of S_ISDIR and S_ISREG have bug, define
+ ourselves.
+ ISIII Build for Interactive System III.
+ IX370 Build for IBM IX/370.
+ KANJI Build with Kanji character-set translation support.
+ LCKDIR UUCP lock directory is /usr/spool/uucp/LCK/.
+ LFDEVNO UUCP lockfile name uses device numbers, as in SVR4.
+ LINUXFSSTND For Linux, use FSSTND UUCP lockfile conventions (default).
+ LOCK_DIR=xxx UUCP lock directory is xxx (quoted string).
+ LOCKF Use lockf() (in addition to lockfiles) on serial lines
+ LONGFN BSD long filenames supported using <dir.h> and opendir().
+ LYNXOS Build for Lynx OS 2.2 or later (POSIX-based).
+ MAC Build for Apple Macintosh with Mac OS.
+ MATCHDOT Make wildcards match filenames that start with period (.)
+ MAXRP=number Maximum receive-packet length.
+ MAXSP=number Maximum send-packet length.
+ MDEBUG Malloc-debugging requested.
+ MINIDIAL Minimum modem dialer support: CCITT, Hayes, Unkown, and None.
+ MINIX Build for MINIX.
+ MIPS Build for MIPS workstation.
+ MULTINET Build with support for TGV MultiNet TCP/IP (VAX/VMS).
+ M_UNIX Defined by SCO.
+ NAP The nap() is available (conflicts with SELECT and USLEEP)
+ NAPHACK The nap() call is available but only as syscall(3112,...)
+ NDIR BSD long filenames supported using <ndir.h> and opendir().
+ NDGPWNAM Don't declare getpwnam().
+ NDSYSERRLIST Don't declare sys_errlist[].
+ NEEDSELECTDEFS select() is avaible but we need to define FD_blah
+ ourselves.
+ NETCMD Build with support for SET HOST /COMMAND and PIPE commands.
+ NEXT Build for NeXT Mach 1.x or 2.x or 3.0, 3.1, or 3.2.
+ NEXT33 Build for NeXT Mach 3.3.
+ NOANSI Disable ANSI C function prototyping.
+ NOAPC Do not include CK_APC code.
+ NOARROWKEYS Exclude code to parse ANSI arrow-key sequences.
+ NOB_xxxx Disable SET SPEED xxxx
+ NOBIGBUF Override BIGBUFOK when it is the default
+ NOBRKC Don't try to refer to t_brkc or t_eof tchars structure members.
+ NOCKFQHOSTNAME Exclude code to get fully qualified hostname in case it
+ causes core dumps.
+ NOCCTRAP Disable Control-C (SIGINT) trapping.
+ NOCKSPEED Disable control-prefix removal feature (SET CONTROL).
+ NOCKTIMERS Build without support for dynamic timers.
+ NOCKXYZ Overrides CK_XYZ.
+ NOCKREGEX Do not include [...] or {xxx,xxx,xxx} matching in ckmatch().
+ NOCMDL Build with no command-line option processing.
+ NOCOTFMC No Close(Open()) To Force Mode Change (UNIX version).
+ NOCSETS Build with no support for character set translation.
+ NOCYRIL Build with no support for Cyrillic character set translation.
+ NOCYRILLIC Ditto.
+ NODEBUG Build with no debug logging capability.
+ NODIAL Build with no DIAL or SET DIAL commands.
+ NODISPO Build to always refuse incoming MAIL or REMOTE PRINT files.
+ DNODISPLAY Build with no file-transfer display.
+ NOESCSEQ Build with no support for ANSI escape sequence recognition.
+ NOFAST Do not make FAST Kermit protocol settings the default.
+ NOFDZERO Do not use file descriptor 0 for remote-mode file transfer.
+ NOFILEH Do not #include <sys/file.h>.
+ NOFLOAT Don't include any floating-point data types or operations.
+ NOFRILLS Build with "no frills" (this should be phased out...)
+ NOFTRUNCATE Include this on UNIXes that don't have ftruncate().
+ NOGETUSERSHELL Include this on UNIXes that don't have getusershell().
+ NOGFTIMER Don't use high-precision floating-point file-transfer
+ timers.
+ NOHEBREW Build with no support for Hebrew character sets.
+ NOHELP Build with no built-in help.
+ NOIKSD Build with IKSD support excluded.
+ NOINITGROUPS Include this on UNIXes that don't have initgroups().
+ NOICP Build with no interactive command parser.
+ NOJC Build with no support for job control (suspend).
+ NOKANJI Build with no support for Japanese Kanji character sets.
+ NOKVERBS Build with no support for keyboard verbs (\Kverbs).
+ NOLATIN2 Build with no ISO Latin-2 character-set translation support.
+ NOLEARN Build with no support for learned scripts.
+ NOLINKBITS Use of S_ISLNK and _IFLNK untrustworthy; use readlink()
+ instead.
+ NOLOCAL Build without any local-mode features: No Making Connections.
+ NOLOGDIAL Disable connection log.
+ NOLOGIN Build without IKSD (network login) support.
+ NOLSTAT Not OK to use lstat().
+ NOMDMHUP Build without "modem-specific hangup" (e.g. ATH0) feature.
+ NOMHHOST Exclude the multihomed-host TCP/IP code (if compilcation
+ errors)
+ NOMINPUT Build without MINPUT command.
+ NOMSEND Build with no MSEND command.
+ NONAWS Do not include TELNET Negotiate About Window Size support.
+ NONET Do not include any network support.
+ NONOSETBUF (See NOSETBUF)
+ NOPARSEN Build without automatic parity detection.
+ NOPIPESEND Disable file transfer using pipes and filters.
+ NOPOLL Override CK_POLL definition.
+ NOPOPEN The popen() library call is not available.
+ NOPURGE Build with no PURGE command.
+ NOPUSH Build with no escapes to operating system.
+ NOREALPATH In UNIX, realpath() function is not available.
+ NORECALL Disable the command-recall feature.
+ NOREDIRECT Disable REDIRECT command.
+ NORENAME Don't use rename() system call, use link()/unlink() (UNIX).
+ NORESEND Build with no RESEND command.
+ NORETRY Build with no command-retry feature.
+ NOSCRIPT Build with no SCRIPT command.
+ NOSELECT Don't try to use select().
+ NOSERVER Build with no SERVER mode and no server-related commands.
+ NOSETBUF Don't make console writes unbuffered.
+ NONOSETBUF DO make console writes unbuffered.
+ NOSETREU setreuid() and/or setregid() not available.
+ NOSHOW Build with no SHOW command (not recommended!).
+ NOSIGWINCH Disable SIGWINCH signal trapping.
+ NOSPL Build with no script programming language.
+ NOSTAT Don't call stat() from mainline code.
+ NOSYMLINK Include this for UNIXes that don't have readlink().
+ NOSYSIOCTLH Do not #include <sys/ioctl.h>.
+ NOSYSTIMEH Co not include <sys/time.h>.
+ NOSYSLOG Disable syslogging code.
+ NOTCPOPTS Build with no SET TCP options or underlying support.
+ NOTLOG Build with no support for transaction logging.
+ NOTM_ISDST Struct tm has no tm_isdst member.
+ NOUNICODE Build with no support for Unicode character-set translation.
+ NOURL Don't parse URLs
+ NOUUCP Build with no UUCP lockfile support (dangerous!).
+ NOWARN Make EXIT WARNING be OFF by default (otherwise it's ON).
+ NOWREFRESH Override built-in definition of CK_WREFRESH (q.v.).
+ NOXFER Build with no Kermit or other file-transfer protocols.
+ NOXMIT Build with no TRANSMIT command.
+ NOXPRINT Disables transparent print code.
+ OLDMSG Use old "entering server mode" message (see [164]ckcmai.c).
+ OLINUXHISPEED Build in old Linux hi-serial-speed code (for Linux <=
+ 1.0).
+ OPENBSD Build for OpenBSD.
+ OS2 Build for OS/2.
+ OSF Build for OSF/1.
+ OSFPC Build for OSF/1 on a PC.
+ OSF32 Digital UNIX 3.2 or later.
+ OSF40 Build for Digital UNIX 4.0.
+ OSF50 Build for Digital UNIX 5.0.
+ OSK Build for OS-9.
+ OXOS Build for Olivetti X/OS 2.3.
+ PCIX Build for PC/IX
+ PID_T=xxx Type for pids is xxx (normally int or pid_t).
+ POSIX Build for POSIX: use POSIX header files, functions, etc.
+ _POSIX_SOURCE Disable non-POSIX features.
+ PROVX1 Build for Venix 1.0 on DEC Professional 3xx.
+ PTX Build for Dynix/PTX
+ PWID_T=xxx getpwid() type is xxx.
+ RBSIZ=xxx Define overall size of receive-packet buffer (with DYNAMIC).
+ RDCHK rdchk() system call is available.
+ RENAME rename() system call is available (UNIX).
+ RTAIX Build for AIX 2.2.1 on IBM RT PC.
+ RTU Build for Masscomp / Concurrent RTU.
+ SAVEDUID BSD or other non-AT&T UNIX has saved-setuid feature.
+ SBSIZ=xxx Define overall size of send-packet buffer (use with
+ DYNAMIC).
+ SDIRENT Directory structure specified in <sys/dirent.h>.
+ SELECT select() function available (conflicts with RDCHK and CK_POLL)
+ SELECT_H Include <sys/select.h> for select()-releated definitions.
+ SETEUID BSD 4.4-style seteXid() functions available.
+ SIG_V Type for signal() is void. Used to override normal assumption.
+ SIG_I Type for signal() is int. Used to override normal assumption.
+ SOCKOPT_T Override default data type for get/setsockopt() option
+ length.
+ SOLARIS Build for Solaris.
+ SOLARIS25 Build for Solaris 2.5 or later.
+ SONYNEWS Build for Sony NEWS-OS.
+ STERMIOX <sys/termiox.h> is available.
+ STRATUS Build for Stratus VOS.
+ STRATUSX25 Include Stratus VOS X.25 support.
+ SUN4S5 Build for SUNOS 4.x in the System V R3 environment.
+ SUNOS4 Build for SUNOS 4.0 in the BSD environment.
+ SUNOS41 Build for SUNOS 4.1 in the BSD environment.
+ SUNX25 Build with support for SunLink X.25.
+ SVR3 Build for AT&T System V Release 3.
+ SVR3JC Allow job control support on System V Release 3 UNIX versions.
+ SVR4 Build for AT&T System V Release 4.
+ SW_ACC_ID UNIX only -- swap real & effective ids around access()
+ calls.
+ sxaE50 Build for PFU Compact A Series SX/A TISP.
+ SYSLOGLEVEL=n Force syslogging at given level.
+ SYSTIMEH Include <sys/time.h>.
+ SYSUTIMEH Include <sys/utime.h> for setting file dates (88OPEN)
+ TCPSOCKET Build with support for TCP/IP via Berkeley sockets library.
+ TERMIOX <termiox.h> header file is available (mostly SVR4).
+ TNCODE Include TELNET-specific code.
+ TOWER1 Build for NCR Tower 1632 with OS 1.02.
+ TRS16 Build for Tandy 16/6000.
+ UID_T=xxx Type for uids is xxx (normally int or uid_t).
+ UNIX Must be defined for all UNIX versions.
+ UNIX351M AT&T UNIX 3.51m on the AT&T 7300 UNIX PC.
+ USE_ARROWKEYS Include code to parse ANSI arrow-key sequences.
+ USE_LSTAT OK to use lstat().
+ USE_MEMCPY Define this if memcpy()/memset()/memmove() available.
+ USE_STRERROR Define this if strerror() is available.
+ USLEEP usleep() system call available (conflicts with NAP & SELECT).
+ UTEK Build for Tektronix workstations with UTEK OS.
+ UTIMEH Include <utime.h> for setting file dates (SVR4, POSIX)
+ UTS24 Build for Amdahl UTS 2.4.
+ V7 Build for Version 7 UNIX.
+ VMS Build for VAX/VMS.
+ VOID=xxx VOID type for functions (int or void).
+ VXVE Build for CDC VX/VE 5.2.1.
+ WAIT_T=xxx Type of argument passed to wait().
+ WINTCP Build with Wollongong VAX/VMS TCP/IP (implies TCPSOCKET)
+ WOLLONGONG Build with Wollongong UNIX TCP/IP (implies TCPSOCKET)
+ XENIX Build for Xenix (SCO, Tandy, others).
+ XNDIR Support for BSD long filenames via <sys/ndir.h>.
+ XYZ_INTERNAL Support for XYZMODEM protocols is internal, not external.
+ ZFCDAT Define this if zfcdat() function is available in Kermit.
+ ZILOG Build for Zilog ZEUS.
+ ZJDATE Has zjdate() function that converts date to Julian format.
+ XPRINT Transparent print code included in CONNECT module.
+
+ [ [165]Top ] [ [166]Contents ] [ [167]C-Kermit Home ] [ [168]Kermit
+ Home ]
+ _________________________________________________________________
+
+
+ C-Kermit Configuration Options / [169]The Kermit Project /
+ [170]Columbia University / [171]kermit@columbia.edu / 14 March 2003
+
+References
+
+ 1. http://www.columbia.edu/kermit/
+ 2. http://www.columbia.edu/
+ 3. http://www.columbia.edu/kermit/ckccfg.html
+ 4. http://www.columbia.edu/kermit/ckermit.html
+ 5. http://www.columbia.edu/kermit/index.html
+ 6. http://www.columbia.edu/kermit/ckccfg.html#x1
+ 7. http://www.columbia.edu/kermit/ckccfg.html#x2
+ 8. http://www.columbia.edu/kermit/ckccfg.html#x3
+ 9. http://www.columbia.edu/kermit/ckccfg.html#x4
+ 10. http://www.columbia.edu/kermit/ckccfg.html#x5
+ 11. http://www.columbia.edu/kermit/ckccfg.html#x6
+ 12. http://www.columbia.edu/kermit/ckccfg.html#x7
+ 13. http://www.columbia.edu/kermit/ckccfg.html#x8
+ 14. http://www.columbia.edu/kermit/ckccfg.html#x9
+ 15. http://www.columbia.edu/kermit/ckccfg.html#x10
+ 16. http://www.columbia.edu/kermit/ckccfg.html#x11
+ 17. http://www.columbia.edu/kermit/ckccfg.html#x12
+ 18. http://www.columbia.edu/kermit/ckccfg.html#x13
+ 19. http://www.columbia.edu/kermit/ckccfg.html#x14
+ 20. http://www.columbia.edu/kermit/ckccfg.html#xa1
+ 21. http://www.columbia.edu/kermit/ckuins.html
+ 22. http://www.columbia.edu/kermit/ckermit.html
+ 23. http://www.columbia.edu/kermit/index.html
+ 24. http://www.columbia.edu/kermit/ckccfg.html#top
+ 25. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 26. http://www.columbia.edu/kermit/ckccfg.html#x2
+ 27. http://www.columbia.edu/kermit/ckccfg.html#x0
+ 28. http://www.columbia.edu/kermit/ckermit.html
+ 29. http://www.columbia.edu/kermit/index.html
+ 30. http://www.columbia.edu/kermit/ckccfg.html#top
+ 31. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 32. http://www.columbia.edu/kermit/ckccfg.html#x3
+ 33. http://www.columbia.edu/kermit/ckccfg.html#x1
+ 34. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
+ 35. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
+ 36. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c
+ 37. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
+ 38. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
+ 39. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
+ 40. http://www.columbia.edu/kermit/ckermit.html
+ 41. http://www.columbia.edu/kermit/index.html
+ 42. http://www.columbia.edu/kermit/ckccfg.html#top
+ 43. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 44. http://www.columbia.edu/kermit/ckccfg.html#x4
+ 45. http://www.columbia.edu/kermit/ckccfg.html#x2
+ 46. ftp://kermit.columbia.edu/kermit/c-kermit/makefile
+ 47. http://www.columbia.edu/kermit/ckuins.html
+ 48. http://www.columbia.edu/kermit/ckuins.html#x4
+ 49. http://www.columbia.edu/kermit/ckuins.html#x9.2
+ 50. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c
+ 51. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
+ 52. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c
+ 53. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c
+ 54. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c
+ 55. http://www.columbia.edu/kermit/ckermit.html
+ 56. http://www.columbia.edu/kermit/index.html
+ 57. http://www.columbia.edu/kermit/ckccfg.html#top
+ 58. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 59. http://www.columbia.edu/kermit/ckccfg.html#x5
+ 60. http://www.columbia.edu/kermit/ckccfg.html#x3
+ 61. http://www.columbia.edu/kermit/unicode.html
+ 62. http://www.columbia.edu/kermit/ckermit.html
+ 63. http://www.columbia.edu/kermit/index.html
+ 64. http://www.columbia.edu/kermit/ckccfg.html#top
+ 65. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 66. http://www.columbia.edu/kermit/ckccfg.html#x6
+ 67. http://www.columbia.edu/kermit/ckccfg.html#x4
+ 68. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h
+ 69. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h
+ 70. http://www.columbia.edu/kermit/ckermit.html
+ 71. http://www.columbia.edu/kermit/index.html
+ 72. http://www.columbia.edu/kermit/ckccfg.html#top
+ 73. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 74. http://www.columbia.edu/kermit/ckccfg.html#x7
+ 75. http://www.columbia.edu/kermit/ckccfg.html#x5
+ 76. http://www.columbia.edu/kermit/ckccfg.html#x6.1
+ 77. http://www.columbia.edu/kermit/ckccfg.html#x6.2
+ 78. http://www.columbia.edu/kermit/ckccfg.html#x6.3
+ 79. http://www.columbia.edu/kermit/ckccfg.html#x6.4
+ 80. http://www.columbia.edu/kermit/ckccfg.html#x4
+ 81. http://www.columbia.edu/kermit/gkermit.html
+ 82. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h
+ 83. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h
+ 84. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
+ 85. http://www.columbia.edu/kermit/ckermit.html
+ 86. http://www.columbia.edu/kermit/index.html
+ 87. http://www.columbia.edu/kermit/ckccfg.html#top
+ 88. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 89. http://www.columbia.edu/kermit/ckccfg.html#x8
+ 90. http://www.columbia.edu/kermit/ckccfg.html#x6
+ 91. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c
+ 92. http://www.columbia.edu/kermit/ckermit.html
+ 93. http://www.columbia.edu/kermit/index.html
+ 94. http://www.columbia.edu/kermit/ckccfg.html#top
+ 95. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 96. http://www.columbia.edu/kermit/ckccfg.html#x9
+ 97. http://www.columbia.edu/kermit/ckccfg.html#x7
+ 98. http://www.columbia.edu/kermit/ckccfg.html#x8.1
+ 99. http://www.columbia.edu/kermit/ckccfg.html#x8.2
+ 100. http://www.columbia.edu/kermit/ckccfg.html#x8.3
+ 101. http://www.columbia.edu/kermit/ckccfg.html#x8.1.1
+ 102. http://www.columbia.edu/kermit/ckccfg.html#x8.1.2
+ 103. http://www.columbia.edu/kermit/ckccfg.html#x8.1.3
+ 104. http://www.columbia.edu/kermit/ckccfg.html#x8.1.4
+ 105. http://www.columbia.edu/kermit/ckccfg.html#x8.1.5
+ 106. http://www.columbia.edu/kermit/ckccfg.html#x8.1.6
+ 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h
+ 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c
+ 109. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c
+ 110. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h
+ 111. ftp://kermit.columbia.edu/kermit/c-kermit/ckcftp.c
+ 112. http://www.columbia.edu/kermit/ckermit.html
+ 113. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c
+ 114. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h
+ 115. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c
+ 116. http://www.columbia.edu/kermit/ckccfg.html#x11
+ 117. mailto:kermit@columbia.edu
+ 118. http://www.columbia.edu/kermit/ckermit.html
+ 119. http://www.columbia.edu/kermit/index.html
+ 120. http://www.columbia.edu/kermit/ckccfg.html#top
+ 121. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 122. http://www.columbia.edu/kermit/ckccfg.html#x10
+ 123. http://www.columbia.edu/kermit/ckccfg.html#x8
+ 124. http://www.columbia.edu/kermit/ckermit.html
+ 125. http://www.columbia.edu/kermit/index.html
+ 126. http://www.columbia.edu/kermit/ckccfg.html#top
+ 127. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 128. http://www.columbia.edu/kermit/ckccfg.html#x11
+ 129. http://www.columbia.edu/kermit/ckccfg.html#x9
+ 130. http://www.columbia.edu/kermit/ckuins.html#x11
+ 131. http://www.columbia.edu/kermit/ckuins.html
+ 132. http://www.columbia.edu/kermit/ckermit.html
+ 133. http://www.columbia.edu/kermit/index.html
+ 134. http://www.columbia.edu/kermit/ckccfg.html#top
+ 135. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 136. http://www.columbia.edu/kermit/ckccfg.html#x12
+ 137. http://www.columbia.edu/kermit/ckccfg.html#x10
+ 138. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c
+ 139. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c
+ 140. http://www.columbia.edu/kermit/ckermit.html
+ 141. http://www.columbia.edu/kermit/index.html
+ 142. http://www.columbia.edu/kermit/ckccfg.html#top
+ 143. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 144. http://www.columbia.edu/kermit/ckccfg.html#x13
+ 145. http://www.columbia.edu/kermit/ckccfg.html#x11
+ 146. http://www.columbia.edu/kermit/ckermit.html
+ 147. http://www.columbia.edu/kermit/index.html
+ 148. http://www.columbia.edu/kermit/ckccfg.html#top
+ 149. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 150. http://www.columbia.edu/kermit/ckccfg.html#x14
+ 151. http://www.columbia.edu/kermit/ckccfg.html#x12
+ 152. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
+ 153. http://www.columbia.edu/kermit/ckermit.html
+ 154. http://www.columbia.edu/kermit/index.html
+ 155. http://www.columbia.edu/kermit/ckccfg.html#top
+ 156. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 157. http://www.columbia.edu/kermit/ckccfg.html#x13
+ 158. http://www.columbia.edu/kermit/ckermit.html
+ 159. http://www.columbia.edu/kermit/index.html
+ 160. http://www.columbia.edu/kermit/ckccfg.html#top
+ 161. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 162. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h
+ 163. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h
+ 164. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c
+ 165. http://www.columbia.edu/kermit/ckccfg.html#top
+ 166. http://www.columbia.edu/kermit/ckccfg.html#contents
+ 167. http://www.columbia.edu/kermit/ckermit.html
+ 168. http://www.columbia.edu/kermit/index.html
+ 169. http://www.columbia.edu/kermit/index.html
+ 170. http://www.columbia.edu/
+ 171. mailto:kermit@columbia.edu
diff --git a/ckermit-8.0.211/ckcdeb.h b/ckermit-8.0.211/ckcdeb.h
new file mode 100644
index 0000000..31f9c55
--- /dev/null
+++ b/ckermit-8.0.211/ckcdeb.h
@@ -0,0 +1,6369 @@
+/* C K C D E B . H */
+
+/*
+ Tue Apr 6 14:00:16 2004
+
+ NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
+ compatible with C preprocessors that support only #ifdef, #else, #endif,
+ #define, and #undef. Please do not use #if, logical operators, or other
+ later-model preprocessor features in any of the portable C-Kermit modules.
+ You can, of course, use these constructions in platform-specific modules
+ when you know they are supported.
+*/
+
+/*
+ This file is included by all C-Kermit modules, including the modules
+ that aren't specific to Kermit (like the command parser and the ck?tio and
+ ck?fio modules). It should be included BEFORE any other C-Kermit header
+ files. It specifies format codes for debug(), tlog(), and similar
+ functions, and includes any necessary definitions to be used by all C-Kermit
+ modules, and also includes some feature selection compile-time switches, and
+ also system- or compiler-dependent definitions, plus #includes and prototypes
+ required by all C-Kermit modules.
+*/
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+/*
+ Etymology: The name of this file means "C-Kermit Common-C-Language Debugging
+ Header", because originally it contained only the formats (F000-F111) for
+ the debug() and tlog() functions. Since then it has grown to inlcude all
+ material required by all other C-Kermit modules, including the non-Kermit
+ specific ones.
+*/
+
+#ifndef CKCDEB_H /* Don't include me more than once. */
+#define CKCDEB_H
+
+#ifdef OS2
+#include "ckoker.h"
+#else /* OS2 */
+/* Unsigned numbers */
+
+#ifndef USHORT
+#define USHORT unsigned short
+#endif /* USHORT */
+
+#ifndef UINT
+#define UINT unsigned int
+#endif /* UINT */
+
+#ifndef ULONG
+#define ULONG unsigned long
+#endif /* ULONG */
+#endif /* OS2 */
+
+/* Structure definitions for Kermit file attributes */
+/* All strings come as pointer and length combinations */
+/* Empty string (or for numeric variables, -1) = unused attribute. */
+
+struct zstr { /* string format */
+ int len; /* length */
+ char *val; /* value */
+};
+struct zattr { /* Kermit File Attribute structure */
+ long lengthk; /* (!) file length in K */
+ struct zstr type; /* (") file type (text or binary) */
+ struct zstr date; /* (#) file creation date yyyymmdd[ hh:mm[:ss]] */
+ struct zstr creator; /* ($) file creator id */
+ struct zstr account; /* (%) file account */
+ struct zstr area; /* (&) area (e.g. directory) for file */
+ struct zstr password; /* (') password for area */
+ long blksize; /* (() file blocksize */
+ struct zstr xaccess; /* ()) file access: new, supersede, append, warn */
+ struct zstr encoding; /* (*) encoding (transfer syntax) */
+ struct zstr disp; /* (+) disposition (mail, message, print, etc) */
+ struct zstr lprotect; /* (,) protection (local syntax) */
+ struct zstr gprotect; /* (-) protection (generic syntax) */
+ struct zstr systemid; /* (.) ID for system of origin */
+ struct zstr recfm; /* (/) record format */
+ struct zstr sysparam; /* (0) system-dependent parameter string */
+ long length; /* (1) exact length on system of origin */
+ struct zstr charset; /* (2) transfer syntax character set */
+#ifdef OS2
+ struct zstr longname; /* OS/2 longname if applicable */
+#endif /* OS2 */
+ struct zstr reply; /* This goes last, used for attribute reply */
+};
+
+/* Kermit file information structure */
+
+struct filinfo {
+ int bs; /* Blocksize */
+ int cs; /* Character set */
+ long rl; /* Record length */
+ int org; /* Organization */
+ int fmt; /* Record format */
+ int cc; /* Carriage control */
+ int typ; /* Type (text/binary) */
+ int dsp; /* Disposition */
+ char *os_specific; /* OS-specific attributes */
+#ifdef OS2
+ unsigned long int lblopts; /* LABELED FILE options bitmask */
+#else
+ int lblopts;
+#endif /* OS2 */
+};
+
+#ifdef MACOSX10 /* Mac OS X 1.0 */
+#ifndef MACOSX /* implies Mac OS X */
+#define MACOSX
+#endif /* MACOSX */
+#endif /* MACOSX10 */
+
+#ifdef MACOSX /* Mac OS X */
+#ifndef BSD44 /* implies 4.4 BSD */
+#define BSD44
+#endif /* BSD44 */
+#endif /* MACOSX */
+
+#ifdef SCO_OSR505 /* SCO 3.2v5.0.5 */
+#ifndef SCO_OSR504 /* implies SCO 3.2v5.0.4 */
+#define SCO_OSR504
+#endif /* SCO_OSR504 */
+#endif /* SCO_OSR505 */
+
+#ifdef SCO_OSR504 /* SCO 3.2v5.0.4 */
+#ifndef CK_SCOV5 /* implies SCO 3.2v5.0 */
+#define CK_SCOV5
+#endif /* CK_SCOV5 */
+#include <sys/types.h> /* To sidestep header-file mess */
+#endif /* SCO_OSR504 */
+
+#ifdef CK_SCOV5
+#ifndef ANYSCO
+#define ANYSCO
+#endif /* ANYSCO */
+#endif /* CK_SCOV5 */
+
+#ifdef UNIXWARE
+#ifndef ANYSCO
+#define ANYSCO
+#endif /* ANYSCO */
+#endif /* UNIXWARE */
+
+#ifdef CK_SCO32V4 /* SCO 3.2v4 */
+#ifndef ANYSCO
+#define ANYSCO
+#endif /* ANYSCO */
+#ifndef XENIX
+#define XENIX
+#endif /* XENIX */
+#ifndef SVR3
+#define SVR3
+#endif /* SVR3 */
+#ifndef DIRENT
+#define DIRENT
+#endif /* DIRENT */
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#ifndef SVR3JC
+#define SVR3JC
+#endif /* SVR3JC */
+#ifndef CK_RTSCTS
+#define CK_RTSCTS
+#endif /* CK_RTSCTS */
+#ifndef PID_T
+#define PID_T pid_t
+#endif /* PID_T */
+#ifndef PWID_T
+#define PWID_T int
+#endif /* PWID_T */
+#endif /* CK_SCO32V4 */
+
+#ifdef NOICP /* If no command parser */
+#ifndef NOSPL /* Then no script language either */
+#define NOSPL
+#endif /* NOSPL */
+#ifndef NOCSETS /* Or characer sets */
+#define NOCSETS
+#endif /* NOCSETS */
+#ifndef NOFTP /* Or FTP client */
+#define NOFTP
+#endif /* NOFTP */
+#endif /* NOICP */
+
+/* Built-in makefile entries */
+
+#ifdef SOLARIS9 /* Solaris 9 implies 8 */
+#ifndef SOLARIS8
+#define SOLARIS8
+#endif /* SOLARIS8 */
+#endif /* SOLARIS9 */
+
+#ifdef SOLARIS8 /* Solaris 8 implies 7 */
+#ifndef SOLARIS7
+#define SOLARIS7
+#endif /* SOLARIS7 */
+#endif /* SOLARIS8 */
+
+#ifdef SOLARIS7 /* Solaris 7 implies 2.6 */
+#ifndef SOLARIS26
+#define SOLARIS26
+#endif /* SOLARIS26 */
+#endif /* SOLARIS7 */
+
+#ifdef SOLARIS26 /* Solaris 2.6 implies 2.5 */
+#ifndef SOLARIS25
+#define SOLARIS25
+#endif /* SOLARIS25 */
+#endif /* SOLARIS26 */
+
+#ifdef SOLARIS25 /* Solaris 2.5 implies Solaris */
+#ifndef SOLARIS
+#define SOLARIS
+#endif /* SOLARIS */
+#ifndef POSIX /* And POSIX */
+#define POSIX
+#endif /* POSIX */
+#ifndef CK_WREFRESH /* And this (curses) */
+#define CK_WREFRESH
+#endif /* CK_WREFRESH */
+#endif /* SOLARIS25 */
+
+#ifdef SOLARIS24 /* Solaris 2.4 implies Solaris */
+#ifndef SOLARIS
+#define SOLARIS
+#endif /* SOLARIS */
+#endif /* SOLARIS24 */
+
+#ifdef SOLARIS /* Solaris gets "POSIX" RTS/CTS API */
+#ifdef POSIX
+#ifndef POSIX_CRTSCTS
+#define POSIX_CRTSCTS
+#endif /* POSIX_CRTSCTS */
+#endif /* POSIX */
+#endif /* SOLARIS */
+
+#ifdef SUN4S5 /* Sun-4 System V environment */
+#ifndef SVR3 /* implies System V R3 or later */
+#define SVR3
+#endif /* SVR3 */
+#endif /* SUN4S5 */
+#ifdef SUNOS41 /* SUNOS41 implies SUNOS4 */
+#ifndef SUNOS4
+#define SUNOS4
+#endif /* SUNOS4 */
+#endif /* SUNOS41 */
+
+#ifdef SUN4S5 /* Sun-4 System V environment */
+#ifndef SVR3 /* implies System V R3 or later */
+#define SVR3
+#endif /* SVR3 */
+#endif /* SUN4S5 */
+
+#ifdef SUNOS41 /* SUNOS41 implies SUNOS4 */
+#ifndef SUNOS4
+#define SUNOS4
+#endif /* SUNOS4 */
+#endif /* SUNOS41 */
+
+#ifdef SUNOS4 /* Built-in SUNOS4 makefile entry */
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#ifndef BSD4
+#define BSD4
+#endif /* BSD4 */
+#ifndef NOSETBUF
+#define NOSETBUF
+#endif /* NOSETBUF */
+#ifndef DIRENT
+#define DIRENT
+#endif /* DIRENT */
+#ifndef NONET
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#endif /* NONET */
+#ifndef SAVEDUID
+#define SAVEDUID
+#endif /* SAVEDUID */
+#ifndef DYNAMIC
+#define DYNAMIC
+#endif /* DYNAMIC */
+#endif /* SUNOS4 */
+
+#ifdef SOLARIS /* Built in makefile entry */
+#ifndef NOSETBUF /* for Solaris 2.x */
+#define NOSETBUF
+#endif /* NOSETBUF */
+#ifndef NOCURSES
+#ifndef CK_CURSES
+#define CK_CURSES
+#endif /* CK_CURSES */
+#endif /* NOCURSES */
+#ifndef CK_NEWTERM
+#define CK_NEWTERM
+#endif /* CK_NEWTERM */
+#ifndef DIRENT
+#define DIRENT
+#endif /* DIRENT */
+#ifndef NONET
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#endif /* NONET */
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#ifndef SVR4
+#define SVR4
+#endif /* SVR4 */
+#ifndef HADDRLIST
+#define HADDRLIST
+#endif /* HADDRLIST */
+#ifndef STERMIOX
+#define STERMIOX
+#endif /* STERMIOX */
+#ifndef SELECT
+#define SELECT
+#endif /* SELECT */
+#ifndef DYNAMIC
+#define DYNAMIC
+#endif /* DYNAMIC */
+#ifndef NOUUCP
+#ifndef HDBUUCP
+#define HDBUUCP
+#endif /* HDBUUCP */
+#endif /* NOUUCP */
+#endif /* SOLARIS */
+
+/* Features that can be eliminated from a no-file-transfer version */
+
+#ifdef NOXFER
+#ifndef NOFTP
+#define NOFTP
+#endif /* NOFTP */
+#ifndef OS2
+#ifndef NOCURSES /* Fullscreen file-transfer display */
+#define NOCURSES
+#endif /* NOCURSES */
+#endif /* OS2 */
+#ifndef NOCKXYZ /* XYZMODEM support */
+#define NOCKXYZ
+#endif /* NOCKXYZ */
+#ifndef NOCKSPEED /* Ctrl-char unprefixing */
+#define NOCKSPEED
+#endif /* NOCKSPEED */
+#ifndef NOSERVER /* Server mode */
+#define NOSERVER
+#endif /* NOSERVER */
+#ifndef NOCKTIMERS /* Dynamic packet timers */
+#define NOCKTIMERS
+#endif /* NOCKTIMERS */
+#ifndef NOPATTERNS /* File-type patterns */
+#define NOPATTERNS
+#endif /* NOPATTERNS */
+#ifndef NOSTREAMING /* Streaming */
+#define NOSTREAMING
+#endif /* NOSTREAMING */
+#ifndef NOIKSD /* Internet Kermit Service */
+#define NOIKSD
+#endif /* NOIKSD */
+#ifndef NOPIPESEND /* Sending from pipes */
+#define NOPIPESEND
+#endif /* NOPIPESEND */
+#ifndef NOAUTODL /* Autodownload */
+#define NOAUTODL
+#endif /* NOAUTODL */
+#ifndef NOMSEND /* MSEND */
+#define NOMSEND
+#endif /* NOMSEND */
+#ifndef NOTLOG /* Transaction logging */
+#define NOTLOG
+#endif /* NOTLOG */
+#ifndef NOCKXXCHAR /* Packet character doubling */
+#define NOCKXXCHAR
+#endif /* NOCKXXCHAR */
+#endif /* NOXFER */
+
+#ifdef NOICP /* No Interactive Command Parser */
+#ifndef NODIAL /* Implies No DIAL command */
+#define NODIAL
+#endif /* NODIAL */
+#ifndef NOCKXYZ /* and no external protocols */
+#define NOCKXYZ
+#endif /* NOCKXYZ */
+#endif /* NOICP */
+
+#ifndef NOIKSD
+#ifdef IKSDONLY
+#ifndef IKSD
+#define IKSD
+#endif /* IKSD */
+#ifndef NOLOCAL
+#define NOLOCAL
+#endif /* NOLOCAL */
+#ifndef NOPUSH
+#define NOPUSH
+#endif /* NOPUSH */
+#ifndef TNCODE
+#define TNCODE
+#endif /* TNCODE */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef NETCONN
+#define NETCONN
+#endif /* NETCONN */
+#ifdef SUNX25
+#undef SUNX25
+#endif /* SUNX25 */
+#ifdef IBMX25
+#undef IBMX25
+#endif /* IBMX25 */
+#ifdef STRATUSX25
+#undef STRATUSX25
+#endif /* STRATUSX25 */
+#ifdef CK_NETBIOS
+#undef CK_NETBIOS
+#endif /* CK_NETBIOS */
+#ifdef SUPERLAT
+#undef SUPERLAT
+#endif /* SUPERLAT */
+#ifdef NPIPE
+#undef NPIPE
+#endif /* NPIPE */
+#ifdef NETFILE
+#undef NETFILE
+#endif /* NETFILE */
+#ifdef NETCMD
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#ifdef RLOGCODE
+#undef RLOGCODE
+#endif /* RLOGCODE */
+#ifdef NETDLL
+#undef NETDLL
+#endif /* NETDLL */
+#ifndef NOSSH
+#undef NOSSH
+#endif /* NOSSH */
+#ifndef NOFORWARDX
+#define NOFORWARDX
+#endif /* NOFORWARDX */
+#ifndef NOBROWSER
+#define NOBROWSER
+#endif /* NOBROWSER */
+#ifndef NOHTTP
+#define NOHTTP
+#endif /* NOHTTP */
+#ifndef NOFTP
+#define NOFTP
+#endif /* NOFTP */
+#ifndef NO_COMPORT
+#define NO_COMPORT
+#endif /* NO_COMPORT */
+#endif /* IKSDONLY */
+#endif /* NOIKSD */
+
+/* Features that can be eliminated from a remote-only version */
+
+#ifdef NOLOCAL
+#ifndef NOFTP
+#define NOFTP
+#endif /* NOFTP */
+#ifndef NOHTTP
+#define NOHTTP
+#endif /* NOHTTP */
+#ifndef NOSSH
+#define NOSSH
+#endif /* NOSSH */
+#ifndef NOTERM
+#define NOTERM
+#endif /* NOTERM */
+#ifndef NOCURSES /* Fullscreen file-transfer display */
+#define NOCURSES
+#endif /* NOCURSES */
+#ifndef NODIAL
+#define NODIAL
+#endif /* NODIAL */
+#ifndef NOSCRIPT
+#define NOSCRIPT
+#endif /* NOSCRIPT */
+#ifndef NOSETKEY
+#define NOSETKEY
+#endif /* NOSETKEY */
+#ifndef NOKVERBS
+#define NOKVERBS
+#endif /* NOKVERBS */
+#ifndef NOXMIT
+#define NOXMIT
+#endif /* NOXMIT */
+#ifdef CK_CURSES
+#undef CK_CURSES
+#endif /* CK_CURSES */
+#ifndef IKSDONLY
+#ifndef NOAPC
+#define NOAPC
+#endif /* NOAPC */
+#ifndef NONET
+#define NONET
+#endif /* NONET */
+#endif /* IKSDONLY */
+#endif /* NOLOCAL */
+
+#ifdef NONET
+#ifdef NETCONN
+#undef NETCONN
+#endif /* NETCONN */
+#ifdef TCPSOCKET
+#undef TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef NOTCPOPTS
+#define NOTCPOPTS
+#endif /* NOTCPOPTS */
+#ifdef SUNX25
+#undef SUNX25
+#endif /* SUNX25 */
+#ifdef IBMX25
+#undef IBMX25
+#endif /* IBMX25 */
+#ifdef STRATUSX25
+#undef STRATUSX25
+#endif /* STRATUSX25 */
+#ifdef CK_NETBIOS
+#undef CK_NETBIOS
+#endif /* CK_NETBIOS */
+#ifdef SUPERLAT
+#undef SUPERLAT
+#endif /* SUPERLAT */
+#ifdef NPIPE
+#undef NPIPE
+#endif /* NPIPE */
+#ifdef NETFILE
+#undef NETFILE
+#endif /* NETFILE */
+#ifdef NETCMD
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#ifdef RLOGCODE
+#undef RLOGCODE
+#endif /* RLOGCODE */
+#ifdef NETDLL
+#undef NETDLL
+#endif /* NETDLL */
+#ifndef NOSSH
+#define NOSSH
+#endif /* NOSSH */
+#ifndef NOFTP
+#define NOFTP
+#endif /* NOFTP */
+#ifndef NOHTTP
+#define NOHTTP
+#endif /* NOHTTP */
+#ifndef NOBROWSER
+#define NOBROWSER
+#endif /* NOBROWSER */
+#ifndef NOFORWARDX
+#define NOFORWARDX
+#endif /* NOFORWARDX */
+#endif /* NONET */
+
+#ifdef IKSDONLY
+#ifdef SUNX25
+#undef SUNX25
+#endif /* SUNX25 */
+#ifdef IBMX25
+#undef IBMX25
+#endif /* IBMX25 */
+#ifdef STRATUSX25
+#undef STRATUSX25
+#endif /* STRATUSX25 */
+#ifdef CK_NETBIOS
+#undef CK_NETBIOS
+#endif /* CK_NETBIOS */
+#ifdef SUPERLAT
+#undef SUPERLAT
+#endif /* SUPERLAT */
+#ifdef NPIPE
+#undef NPIPE
+#endif /* NPIPE */
+#ifdef NETFILE
+#undef NETFILE
+#endif /* NETFILE */
+#ifdef NETCMD
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#ifdef RLOGCODE
+#undef RLOGCODE
+#endif /* RLOGCODE */
+#ifdef NETDLL
+#undef NETDLL
+#endif /* NETDLL */
+#ifndef NOSSH
+#define NOSSH
+#endif /* NOSSH */
+#ifndef NOHTTP
+#define NOHTTP
+#endif /* NOHTTP */
+#ifndef NOBROWSER
+#define NOBROWSER
+#endif /* NOBROWSER */
+#endif /* IKSDONLY */
+/*
+ Note that none of the above precludes TNCODE, which can be defined in
+ the absence of TCPSOCKET, etc, to enable server-side Telnet negotation.
+*/
+#ifndef TNCODE /* This is for the benefit of */
+#ifdef TCPSOCKET /* modules that might need TNCODE */
+#define TNCODE /* not all of ckcnet.h... */
+#endif /* TCPSOCKET */
+#endif /* TNCODE */
+
+#ifndef NETCONN
+#ifdef TCPSOCKET
+#define NETCONN
+#endif /* TCPSOCKET */
+#endif /* NETCONN */
+
+#ifndef DEFPAR /* Default parity */
+#define DEFPAR 0 /* Must be here because it is used */
+#endif /* DEFPAR */ /* by all classes of modules */
+
+#ifdef NT
+#ifndef OS2ORWIN32
+#define OS2ORWIN32
+#endif /* OS2ORWIN32 */
+#ifndef OS2
+#define WIN32ONLY
+#endif /* OS2 */
+#endif /* NT */
+
+#ifdef OS2 /* For OS/2 debugging */
+#ifndef OS2ORWIN32
+#define OS2ORWIN32
+#endif /* OS2ORWIN32 */
+#ifdef NT
+#define NOCRYPT
+#include <windows.h>
+#define NTSIG
+#else /* NT */
+#define OS2ONLY
+#include <os2def.h>
+#endif /* NT */
+#ifndef OS2ORUNIX
+#define OS2ORUNIX
+#endif /* OS2ORUNIX */
+#ifndef OS2ORVMS
+#define OS2ORVMS
+#endif /* OS2ORVMS */
+#endif /* OS2 */
+
+#include <stdio.h> /* Begin by including this. */
+#include <ctype.h> /* and this. */
+
+/* System-type compilation switches */
+
+#ifdef FT21 /* Fortune For:Pro 2.1 implies 1.8 */
+#ifndef FT18
+#define FT18
+#endif /* FT18 */
+#endif /* FT21 */
+
+#ifdef __bsdi__
+#ifndef BSDI
+#define BSDI
+#endif /* BSDI */
+#endif /* __bsdi__ */
+
+#ifdef AIXPS2 /* AIXPS2 implies AIX370 */
+#ifndef AIX370
+#define AIX370
+#endif /* AIX370 */
+#endif /* AIXPS2 */
+
+#ifdef AIX370 /* AIX PS/2 or 370 implies BSD4 */
+#ifndef BSD4
+#define BSD4
+#endif /* BSD4 */
+#endif /* AIX370 */
+
+#ifdef AIXESA /* AIX/ESA implies BSD4.4 */
+#ifndef BSD44
+#define BSD44
+#endif /* BSD44 */
+#endif /* AIXESA */
+
+#ifdef AIX53 /* AIX53 implies AIX52 */
+#ifndef AIX52
+#define AIX52
+#endif /* AIX52 */
+#endif /* AIX53 */
+
+#ifdef AIX52 /* AIX52 implies AIX51 */
+#ifndef AIX51
+#define AIX51
+#endif /* AIX51 */
+#endif /* AIX52 */
+
+#ifdef AIX51 /* AIX51 implies AIX50 */
+#ifndef AIX50
+#define AIX50
+#endif /* AIX50 */
+#endif /* AIX51 */
+
+#ifdef AIX50 /* AIX50 implies AIX45 */
+#ifndef AIX45
+#define AIX45
+#endif /* AIX45 */
+#endif /* AIX50 */
+
+#ifdef AIX45 /* AIX45 implies AIX44 */
+#ifndef AIX44
+#define AIX44
+#endif /* AIX44 */
+#endif /* AIX45 */
+
+#ifdef AIX44 /* AIX44 implies AIX43 */
+#ifndef AIX43
+#define AIX43
+#endif /* AIX43 */
+#endif /* AIX44 */
+
+#ifdef AIX43 /* AIX43 implies AIX42 */
+#ifndef AIX42
+#define AIX42
+#endif /* AIX42 */
+#endif /* AIX43 */
+
+#ifdef AIX42 /* AIX42 implies AIX41 */
+#ifndef AIX41
+#define AIX41
+#endif /* AIX41 */
+#endif /* AIX42 */
+
+#ifdef SV68R3V6 /* System V/68 R32V6 implies SVR3 */
+#ifndef SVR3
+#define SVR3
+#endif /* SVR3 */
+#endif /* SV68R3V6 */
+
+#ifdef SV88R32 /* System V/88 R32 implies SVR3 */
+#ifndef SVR3
+#define SVR3
+#endif /* SVR3 */
+#endif /* SV88R32 */
+
+#ifdef DGUX540 /* DG UX 5.40 implies Sys V R 4 */
+#ifndef SVR4
+#define SVR4
+#endif /* SVR4 */
+#endif /* DGUX540 */
+
+#ifndef DGUX
+#ifdef DGUX540 /* DG/UX 5.40 implies DGUX */
+#define DGUX
+#else
+#ifdef DGUX430 /* So does DG/UX 4.30 */
+#define DGUX
+#endif /* DGUX430 */
+#endif /* DGUX540 */
+#endif /* DGUX */
+
+#ifdef IRIX65 /* IRIX 6.5 implies IRIX 6.4 */
+#ifndef IRIX64
+#define IRIX64
+#endif /* IRIX64 */
+#endif /* IRIX65 */
+
+#ifdef IRIX64 /* IRIX 6.4 implies IRIX 6.2 */
+#ifndef BSD44ORPOSIX
+#define BSD44ORPOSIX /* for ckutio's benefit */
+#endif /* BSD44ORPOSIX */
+#ifndef IRIX62
+#define IRIX62
+#endif /* IRIX62 */
+#endif /* IRIX64 */
+
+#ifdef IRIX62 /* IRIX 6.2 implies IRIX 6.0 */
+#ifndef IRIX60
+#define IRIX60
+#endif /* IRIX60 */
+#endif /* IRIX62 */
+
+#ifdef IRIX60 /* IRIX 6.0 implies IRIX 5.1 */
+#ifndef IRIX51
+#define IRIX51
+#endif /* IRIX51 */
+#ifndef IRIX52 /* And IRIX 5.2 (for hwfc) */
+#define IRIX52
+#endif /* IRIX52 */
+#endif /* IRIX60 */
+
+#ifndef IRIX /* IRIX 4.0 or greater implies IRIX */
+#ifdef IRIX64
+#define IRIX
+#else
+#ifdef IRIX62
+#define IRIX
+#else
+#ifdef IRIX60
+#define IRIX
+#else
+#ifdef IRIX51
+#define IRIX
+#else
+#ifdef IRIX40
+#define IRIX
+#endif /* IRIX40 */
+#endif /* IRIX51 */
+#endif /* IRIX60 */
+#endif /* IRIX62 */
+#endif /* IRIX64 */
+#endif /* IRIX */
+
+#ifdef MIPS /* MIPS System V environment */
+#ifndef SVR3 /* implies System V R3 or later */
+#define SVR3
+#endif /* SVR3 */
+#endif /* MIPS */
+
+#ifdef HPUX9 /* HP-UX 9.x */
+#ifndef SVR3
+#define SVR3
+#endif /* SVR3 */
+#ifndef HPUX
+#define HPUX
+#endif /* HPUX */
+#ifndef HPUX9PLUS
+#define HPUX9PLUS
+#endif /* HPUX9PLUS */
+#endif /* HPUX9 */
+
+#ifdef HPUX10 /* HP-UX 10.x */
+#ifndef HPUX1010 /* If anything higher is defined */
+#ifdef HPUX1020 /* define HPUX1010 too. */
+#define HPUX1010
+#endif /* HPUX1020 */
+#ifdef HPUX1030
+#define HPUX1010
+#endif /* HPUX1030 */
+#endif /* HPUX1010 */
+
+#ifdef HPUX1100 /* HP-UX 11.00 implies 10.10 */
+#ifndef HPUX1010
+#define HPUX1010
+#endif /* HPUX1010 */
+#endif /* HPUX1100 */
+
+#ifndef SVR4
+#define SVR4
+#endif /* SVR4 */
+#ifndef HPUX
+#define HPUX
+#endif /* HPUX */
+#ifndef HPUX9PLUS
+#define HPUX9PLUS
+#endif /* HPUX9PLUS */
+#endif /* HPUX10 */
+
+#ifdef QNX /* QNX Software Systems Inc */
+#ifndef POSIX /* QNX 4.0 or later is POSIX */
+#define POSIX
+#endif /* POSIX */
+#ifndef __386__ /* Comes in 16-bit and 32-bit */
+#define __16BIT__
+#define CK_QNX16
+#else
+#define __32BIT__
+#define CK_QNX32
+#endif /* __386__ */
+#endif /* QNX */
+
+/*
+ 4.4BSD is a mixture of System V R4, POSIX, and 4.3BSD.
+*/
+#ifdef BSD44 /* 4.4 BSD */
+#ifndef SVR4 /* BSD44 implies SVR4 */
+#define SVR4
+#endif /* SVR4 */
+#ifndef NOSETBUF /* NOSETBUF is safe */
+#define NOSETBUF
+#endif /* NOSETBUF */
+#ifndef DIRENT /* Uses <dirent.h> */
+#define DIRENT
+#endif /* DIRENT */
+#endif /* BSD44 */
+
+#ifdef OPENBSD /* OpenBSD might or might not */
+#ifndef __OpenBSD__ /* have this defined... */
+#define __OpenBSD__
+#endif /* __OpenBSD__ */
+#endif /* OPENBSD */
+
+#ifdef SVR3 /* SVR3 implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* SVR3 */
+
+#ifdef SVR4 /* SVR4 implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#ifndef SVR3 /* ...as well as SVR3 */
+#define SVR3
+#endif /* SVR3 */
+#endif /* SVR4 */
+
+#ifdef OXOS
+#ifndef ATTSV
+#define ATTSV /* OXOS implies ATTSV */
+#endif /* ! ATTSV */
+#define SW_ACC_ID /* access() wants privs on */
+#define kill priv_kill /* kill() wants privs on */
+#ifndef NOSETBUF
+#define NOSETBUF /* NOSETBUF is safe */
+#endif /* ! NOSETBUF */
+#endif /* OXOS */
+
+#ifdef UTSV /* UTSV implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* UTSV */
+
+#ifdef XENIX /* XENIX implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* XENIX */
+
+#ifdef AUX /* AUX implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* AUX */
+
+#ifdef ATT7300 /* ATT7300 implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* ATT7300 */
+
+#ifdef ATT6300 /* ATT6300 implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* ATT6300 */
+
+#ifdef HPUX /* HPUX implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* HPUX */
+
+#ifdef ISIII /* ISIII implies ATTSV */
+#ifndef ATTSV
+#define ATTSV
+#endif /* ATTSV */
+#endif /* ISIII */
+
+#ifdef NEXT33 /* NEXT33 implies NEXT */
+#ifndef NEXT
+#define NEXT
+#endif /* NEXT */
+#endif /* NEXT33 */
+
+#ifdef NEXT /* NEXT implies BSD4 */
+#ifndef BSD4
+#define BSD4
+#endif /* BSD4 */
+#endif /* NEXT */
+
+#ifdef BSD41 /* BSD41 implies BSD4 */
+#ifndef BSD4
+#define BSD4
+#endif /* BSD4 */
+#endif /* BSD41 */
+
+#ifdef BSD43 /* BSD43 implies BSD4 */
+#ifndef BSD4
+#define BSD4
+#endif /* BSD4 */
+#endif /* BSD43 */
+
+#ifdef BSD4 /* BSD4 implies ANYBSD */
+#ifndef ANYBSD
+#define ANYBSD
+#endif /* ANYBSD */
+#endif /* BSD4 */
+
+#ifdef BSD29 /* BSD29 implies ANYBSD */
+#ifndef ANYBSD
+#define ANYBSD
+#endif /* ANYBSD */
+#endif /* BSD29 */
+
+#ifdef ATTSV /* ATTSV implies UNIX */
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#endif /* ATTSV */
+
+#ifdef ANYBSD /* ANYBSD implies UNIX */
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#endif /* ANYBSD */
+
+#ifdef POSIX /* POSIX implies UNIX */
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#ifndef DIRENT /* and DIRENT, i.e. <dirent.h> */
+#ifndef SDIRENT
+#define DIRENT
+#endif /* SDIRENT */
+#endif /* DIRENT */
+#ifndef NOFILEH /* POSIX doesn't use <sys/file.h> */
+#define NOFILEH
+#endif /* NOFILEH */
+#endif /* POSIX */
+
+#ifdef V7
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#endif /* V7 */
+
+#ifdef COHERENT
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#ifdef COMMENT
+#ifndef NOCURSES
+#define NOCURSES
+#endif /* NOCURSES */
+#endif /* COMMENT */
+#endif /* COHERENT */
+
+#ifdef MINIX
+#ifndef UNIX
+#define UNIX
+#endif /* UNIX */
+#endif /* MINIX */
+/*
+ The symbol SVORPOSIX is defined for both AT&T and POSIX compilations
+ to make it easier to select items that System V and POSIX have in common,
+ but which BSD, V7, etc, do not have.
+*/
+#ifdef ATTSV
+#ifndef SVORPOSIX
+#define SVORPOSIX
+#endif /* SVORPOSIX */
+#endif /* ATTSV */
+
+#ifdef POSIX
+#ifndef SVORPOSIX
+#define SVORPOSIX
+#endif /* SVORPOSIX */
+#endif /* POSIX */
+
+/*
+ The symbol SVR4ORPOSIX is defined for both AT&T System V R4 and POSIX
+ compilations to make it easier to select items that System V R4 and POSIX
+ have in common, but which BSD, V7, and System V R3 and earlier, etc, do
+ not have.
+*/
+#ifdef POSIX
+#ifndef SVR4ORPOSIX
+#define SVR4ORPOSIX
+#endif /* SVR4ORPOSIX */
+#endif /* POSIX */
+#ifdef SVR4
+#ifndef SVR4ORPOSIX
+#define SVR4ORPOSIX
+#endif /* SVR4ORPOSIX */
+#endif /* SVR4 */
+
+/*
+ The symbol BSD44ORPOSIX is defined for both 4.4BSD and POSIX compilations
+ to make it easier to select items that 4.4BSD and POSIX have in common,
+ but which System V, BSD, V7, etc, do not have.
+*/
+#ifdef BSD44
+#ifndef BSD44ORPOSIX
+#define BSD44ORPOSIX
+#endif /* BSD44ORPOSIX */
+#endif /* BSD44 */
+
+#ifdef POSIX
+#ifndef BSD44ORPOSIX
+#define BSD44ORPOSIX
+#endif /* BSD44ORPOSIX */
+#endif /* POSIX */
+
+#ifdef UNIX /* For items common to OS/2 and UNIX */
+#ifndef OS2ORUNIX
+#define OS2ORUNIX
+#endif /* OS2ORUNIX */
+#endif /* UNIX */
+
+#ifdef UNIX /* For items common to VMS and UNIX */
+#define VMSORUNIX
+#else
+#ifdef VMS
+#define VMSORUNIX
+#ifndef OS2ORVMS
+#define OS2ORVMS
+#endif /* OS2ORVMS */
+#endif /* VMS */
+#endif /* UNIX */
+
+#ifndef UNIXOROSK /* UNIX or OS-9 (or OS-9000) */
+#ifdef UNIX
+#define UNIXOROSK
+#else
+#ifdef OSK
+#define UNIXOROSK
+#endif /* OSK */
+#endif /* UNIX */
+#endif /* UNIXOROSK */
+
+#ifndef OSKORUNIX
+#ifdef UNIXOROSK
+#define OSKORUNIX
+#endif /* UNIXOROSK */
+#endif /* OSKORUNIX */
+
+#ifdef OS2
+#define CK_ANSIC /* OS/2 supports ANSIC and more extensions */
+#endif /* OS2 */
+
+#ifdef OSF50 /* Newer OSF/1 versions imply older ones */
+#ifndef OSF40
+#define OSF40
+#endif /* OSF40 */
+#endif /* OSF50 */
+
+#ifdef OSF40
+#ifndef OSF32
+#define OSF32
+#endif /* OSF32 */
+#endif /* OSF40 */
+
+#ifdef OSF32
+#ifndef OSF30
+#define OSF30
+#endif /* OSF30 */
+#endif /* OSF32 */
+
+#ifdef OSF30
+#ifndef OSF20
+#define OSF20
+#endif /* OSF20 */
+#endif /* OSF30 */
+
+#ifdef OSF20
+#ifndef OSF10
+#define OSF10
+#endif /* OSF10 */
+#endif /* OSF20 */
+
+#ifdef __DECC /* For DEC Alpha VMS or OSF/1 */
+#ifndef CK_ANSIC
+#define CK_ANSIC /* Even with /stand=vaxc, need ansi */
+#endif /* CKANSIC */
+#ifndef SIG_V
+#define SIG_V /* and signal type is VOID */
+#endif /* SIG_V */
+#ifndef CK_ANSILIBS
+#define CK_ANSILIBS /* (Martin Zinser, Feb 1995) */
+#endif /* CK_ANSILIBS */
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 1
+#endif /* _POSIX_C_SOURCE */
+#endif /* __DECC */
+
+#ifdef VMS
+#ifdef __ia64 /* VMS on Itanium */
+#ifndef VMSI64
+#define VMSI64
+#endif /* VMSI64 */
+#endif /* __ia64 */
+#ifndef VMS64BIT /* 64-bit VMS on Itanium or Alpha */
+#ifdef __ia64
+#define VMS64BIT
+#else
+#ifdef __ALPHA
+#define VMS64BIT
+#endif /* __ia64 */
+#endif /* __ALPHA */
+#endif /* VMS64BIT */
+#endif /* VMS */
+
+#ifdef apollo /* May be ANSI-C, check further */
+#ifdef __STDCPP__
+#define CK_ANSIC /* Yes, this is real ANSI-C */
+#define SIG_V
+#else
+#define NOANSI /* Nope, not ANSI */
+#undef __STDC__ /* Even though it say it is! */
+#define SIG_I
+#endif /* __STDCPP__ */
+#endif /* apollo */
+
+#ifdef POSIX /* -DPOSIX on cc command line */
+#ifndef _POSIX_SOURCE /* Implies _POSIX_SOURCE */
+#define _POSIX_SOURCE
+#endif /* _POSIX_SOURCE */
+#endif /* POSIX */
+
+/*
+ ANSI C? That is, do we have function prototypes, new-style
+ function declarations, and parameter type checking and coercion?
+*/
+#ifdef MAC /* MPW C is ANSI */
+#ifndef NOANSI
+#ifndef CK_ANSIC
+#define CK_ANSIC
+#endif /* CK_ANSIC */
+#endif /* NOANSI */
+#endif /* MAC */
+
+#ifdef STRATUS /* Stratus VOS */
+#ifndef CK_ANSIC
+#define CK_ANSIC
+#endif /* CK_ANSIC */
+#ifndef NOSTAT
+#define NOSTAT
+#endif /* NOSTAT */
+#endif /* STRATUS */
+
+#ifndef NOANSI
+#ifdef __STDC__ /* __STDC__ means ANSI C */
+#ifndef CK_ANSIC
+#define CK_ANSIC
+#endif /* CK_ANSIC */
+#endif /* __STDC__ */
+#endif /* NOANSI */
+/*
+ _PROTOTYP() is used for forward declarations of functions so we can have
+ parameter and return value type checking if the compiler offers it.
+ __STDC__ should be defined by the compiler only if function prototypes are
+ allowed. Otherwise, we get old-style forward declarations. Our own private
+ CK_ANSIC symbol tells whether we use ANSI C prototypes. To force use of
+ ANSI prototypes, include -DCK_ANSIC on the cc command line. To disable the
+ use of ANSI prototypes, include -DNOANSI.
+*/
+#ifdef CK_ANSIC
+#define _PROTOTYP( func, parms ) func parms
+#else /* Not ANSI C */
+#define _PROTOTYP( func, parms ) func()
+#endif /* CK_ANSIC */
+
+#ifndef OS2
+#ifdef NOLOGIN /* NOLOGIN implies NOIKSD */
+#ifndef NOIKSD
+#define NOIKSD
+#endif /* NOIKSD */
+#endif /* NOLOGIN */
+#endif /* OS2 */
+
+#ifdef NOIKSD /* Internet Kermit Service Daemon */
+#ifndef OS2
+#ifndef NOPRINTFSUBST
+#define NOPRINTFSUBST
+#endif /* NOPRINTFSUBST */
+#endif /* OS2 */
+#ifndef NOLOGIN
+#define NOLOGIN
+#endif /* NOLOGIN */
+#ifndef NOSYSLOG
+#define NOSYSLOG
+#endif /* NOSYSLOG */
+#ifndef NOWTMP
+#define NOWTMP
+#endif /* NOWTMP */
+#else
+#ifndef IKSD
+#ifdef OS2ORUNIX /* Platforms where IKSD is supported */
+#define IKSD
+#endif /* OS2ORUNIX */
+#endif /* IKSD */
+#endif /* NOIKSD */
+
+#ifdef IKSD /* IKSD options... */
+#ifndef IKSDCONF /* IKSD configuration file */
+#ifdef UNIX
+#define IKSDCONF "/etc/iksd.conf"
+#else
+#ifdef OS2
+#define IKSDCONF "iksd.ksc"
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* IKSDCONF */
+#ifndef NOIKSDB
+#ifndef IKSDB /* IKSD database */
+#ifdef UNIX
+#define IKSDB
+#define IK_LCKTRIES 16 /* How many times to try to get lock */
+#define IK_LCKSLEEP 1 /* How long to sleep between tries */
+#define IK_LOCKFILE "iksd.lck" /* Database lockfilename */
+#define IK_DBASEDIR "/var/log/" /* Database directory */
+#define IK_DBASEFIL "iksd.db" /* Database filename */
+#else /* UNIX */
+#ifdef OS2
+#define IKSDB
+#ifndef NOFTRUNCATE /* ftruncate() not available */
+#define NOFTRUNCATE
+#endif /* NOFTRUNCATE */
+#define IK_LCKTRIES 16 /* How many times to try to get lock */
+#define IK_LCKSLEEP 1 /* How long to sleep between tries */
+#define IK_LOCKFILE "iksd.lck" /* DB lockfilename (in systemroot) */
+#define IK_DBASEFIL "iksd.db" /* Database filename */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* IKSDB */
+#endif /* NOIKSDB */
+#endif /* IKSD */
+/*
+ Substitutes for printf() and friends used in IKS to compensate for
+ lack of a terminal driver, mainly to supply CR after LF.
+*/
+#ifndef NOPRINTFSUBST
+#ifdef MAC
+/*
+ * The MAC doesn't use standard stdio routines.
+ */
+#undef getchar
+#define getchar() mac_getchar()
+#undef putchar
+#define putchar(c) mac_putchar(c)
+#define printf mac_printf
+#define perror mac_perror
+#define puts mac_puts
+extern int mac_putchar (int c);
+extern int mac_puts (const char *string);
+extern int mac_printf(const char *, ...);
+extern int mac_getchar (void);
+#endif /* MAC */
+
+#ifdef OS2
+#define printf Vscrnprintf
+#define fprintf Vscrnfprintf
+extern int Vscrnprintf(const char *, ...);
+extern int Vscrnprintw(const char *, ...);
+extern int Vscrnfprintf(FILE *, const char *, ...);
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(x) Vscrnprintf("%c",x)
+#define perror(x) Vscrnperror(x)
+#endif /* OS2 */
+
+#ifndef CKWART_C
+#ifdef UNIX
+#ifndef pdp11
+#ifndef CKXPRINTF
+#define CKXPRINTF
+#endif /* CKXPRINTF */
+#endif /* pdp11 */
+#endif /* UNIX */
+#endif /* CKWART_C */
+#endif /* NOPRINTFSUBST */
+
+#ifdef CKXPRINTF
+#define printf ckxprintf
+#define fprintf ckxfprintf
+#ifdef CK_ANSIC
+_PROTOTYP(int ckxprintf,(const char *, ...));
+#ifdef NEXT
+_PROTOTYP(void ckxperror,(const char *));
+#else
+#ifdef CK_SCOV5
+_PROTOTYP(void ckxperror,(const char *));
+#else
+_PROTOTYP(int ckxperror,(const char *));
+#endif /* CK_SCOV5 */
+#endif /* NEXT */
+_PROTOTYP(int ckxfprintf,(FILE *, const char *, ...));
+#endif /* CK_ANSIC */
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(x) ckxprintf("%c",x)
+#ifdef putc
+#undef putc
+#endif /* putc */
+#define putc(a,b) ckxfprintf(b,"%c",a)
+#define perror(x) ckxperror(x)
+#endif /* CKXPRINTF */
+
+/*
+ Altos-specific items: 486, 586, 986 models...
+*/
+#ifdef A986
+#define M_VOID
+#define void int
+#define CHAR char
+#define SIG_I
+#endif /* A986 */
+
+/* Signal handling */
+
+#ifdef QNX
+#ifndef CK_POSIX_SIG
+#define CK_POSIX_SIG
+#endif /* CK_POSIX_SIG */
+#endif /* QNX */
+
+/* Void type */
+
+#ifndef VOID /* Used throughout all C-Kermit */
+#ifdef CK_ANSIC /* modules... */
+#define VOID void
+#else
+#define VOID int
+#endif /* CK_ANSIC */
+#endif /* VOID */
+
+/* Const type */
+
+#ifndef CONST
+#ifdef OSK
+#ifdef _UCC
+#define CONST const
+#else
+#define CONST
+#endif /* _UCC */
+#else /* !OSK */
+#ifdef CK_SCO32V4
+#define CONST
+#else
+#ifdef CK_ANSIC
+#define CONST const
+#else
+#define CONST
+#endif /* CK_ANSIC */
+#endif /* CK_SCO32V4 */
+#endif /* OSK */
+#endif /* CONST */
+
+/* Signal type */
+
+#ifndef SIG_V /* signal() type, if not def'd yet */
+#ifndef SIG_I
+#ifdef OS2
+#define SIG_V
+#else
+#ifdef POSIX
+#define SIG_V
+#else
+#ifdef SVR3 /* System V R3 and later */
+#define SIG_V
+#else
+#ifdef SUNOS4 /* SUNOS V 4.0 and later */
+#ifndef sun386
+#define SIG_V
+#else
+#define SIG_I
+#endif /* sun386 */
+#else
+#ifdef NEXT /* NeXT */
+#define SIG_V
+#else
+#ifdef AIX370
+#include <signal.h>
+#define SIG_V
+#define SIGTYP __SIGVOID /* AIX370 */
+#else
+#ifdef STRATUS /* Stratus VOS */
+#define SIG_V
+#else
+#ifdef MAC
+#define SIGTYP long
+#define SIG_I
+#ifndef MPW33
+#define SIG_IGN 0
+#endif /* MPW33 */
+#define SIGALRM 1
+#ifndef MPW33
+#define SIGINT 2
+#endif /* MPW33 */
+#else /* Everything else */
+#define SIG_I
+#endif /* MAC */
+#endif /* STRATUS */
+#endif /* AIX370 */
+#endif /* NEXT */
+#endif /* SUNOS4 */
+#endif /* SVR3 */
+#endif /* POSIX */
+#endif /* OS2 */
+#endif /* SIG_I */
+#endif /* SIG_V */
+
+#ifdef SIG_I
+#define SIGRETURN return(0)
+#ifndef SIGTYP
+#define SIGTYP int
+#endif /* SIGTYP */
+#endif /* SIG_I */
+
+#ifdef SIG_V
+#define SIGRETURN return
+#ifndef SIGTYP
+#define SIGTYP void
+#endif /* SIGTYP */
+#endif /* SIG_V */
+
+#ifdef NT
+#ifndef SIGTYP
+#define SIGTYP void
+#endif /* SIGTYP */
+#endif /* NT */
+
+#ifndef SIGTYP
+#define SIGTYP int
+#endif /* SIGTYP */
+
+#ifndef SIGRETURN
+#define SIGRETURN return(0)
+#endif /* SIGRETURN */
+
+#ifdef CKNTSIG
+/* This does not work, so don't use it. */
+#define signal ckntsignal
+SIGTYP (*ckntsignal(int type, SIGTYP (*)(int)))(int);
+#endif /* CKNTSIG */
+
+/* We want all characters to be unsigned if the compiler supports it */
+
+#ifdef KUI
+#ifdef CHAR
+#undef CHAR
+#endif /* CHAR */
+#define CHAR unsigned char
+#else
+#ifdef PROVX1
+typedef char CHAR;
+/* typedef long LONG; */
+typedef int void;
+#else
+#ifdef MINIX
+typedef unsigned char CHAR;
+#else
+#ifdef V7
+typedef char CHAR;
+#else
+#ifdef C70
+typedef char CHAR;
+/* typedef long LONG; */
+#else
+#ifdef BSD29
+typedef char CHAR;
+/* typedef long LONG; */
+#else
+#ifdef datageneral
+#define CHAR unsigned char /* 3.22 compiler */
+#else
+#ifdef HPUX
+#define CHAR unsigned char
+#else
+#ifdef OS2
+#ifdef NT
+#define CHAR unsigned char
+#else /* NT */
+#ifdef CHAR
+#undef CHAR
+#endif /* CHAR */
+typedef unsigned char CHAR;
+#endif /* NT */
+#else /* OS2 */
+#ifdef VMS
+typedef unsigned char CHAR;
+#else
+#ifdef CHAR
+#undef CHAR
+#endif /* CHAR */
+typedef unsigned char CHAR;
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* HPUX */
+#endif /* datageneral */
+#endif /* BSD29 */
+#endif /* C70 */
+#endif /* V7 */
+#endif /* MINIX */
+#endif /* PROVX1 */
+#endif /* KUI */
+
+union ck_short { /* Mainly for Unicode */
+ USHORT x_short;
+ CHAR x_char[2];
+};
+
+#ifdef MAC /* Macintosh file routines */
+#ifndef CKWART_C /* But not in "wart"... */
+#ifdef feof
+#undef feof
+#endif /* feof */
+#define feof mac_feof
+#define rewind mac_rewind
+#define fgets mac_fgets
+#define fopen mac_fopen
+#define fclose mac_fclose
+int mac_feof();
+void mac_rewind();
+char *mac_fgets();
+FILE *mac_fopen();
+int mac_fclose();
+#endif /* CKCPRO_W */
+#endif /* MAC */
+/*
+ Systems whose mainline modules have access to the communication-line
+ file descriptor, ttyfd.
+*/
+#ifndef CK_TTYFD
+#ifdef UNIX
+#define CK_TTYFD
+#else
+#ifdef OS2
+#define CK_TTYFD
+#else
+#ifdef VMS
+#define CK_TTYFD
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* CK_TTYFD */
+
+/* Systems where we can get our own process ID */
+
+#ifndef CK_PID
+#ifdef UNIX
+#define CK_PID
+#endif /* UNIX */
+#ifdef OS2
+#define CK_PID
+#endif /* OS2 */
+#ifdef VMS
+#define CK_PID
+#endif /* VMS */
+#endif /* CK_PID */
+
+/* Systems that support the Microsoft Telephony API (TAPI) */
+
+#ifndef NODIAL
+#ifndef CK_TAPI
+#ifdef NT
+#define CK_TAPI
+#endif /* NT */
+#endif /* CK_TAPI */
+#endif /* NODIAL */
+
+#ifndef NONZXPAND
+#ifndef NZXPAND
+#ifdef OS2ORUNIX
+#define NZXPAND
+#else
+#ifdef VMS
+#define NZXPAND
+#else
+#ifdef datageneral
+#define NZXPAND
+#else
+#ifdef OSK
+#define NZXPAND
+#endif /* OSK */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* OS2ORUNIX */
+#endif /* NZXPAND */
+#else
+#ifdef NZXPAND
+#undef NZXPAND
+#endif /* NZXPAND */
+#endif /* NONZXPAND */
+
+/* nzxpand() option flags */
+
+#define ZX_FILONLY 1 /* Match only regular files */
+#define ZX_DIRONLY 2 /* Match only directories */
+#define ZX_RECURSE 4 /* Descend through directory tree */
+#define ZX_MATCHDOT 8 /* Match "dot files" */
+#define ZX_NOBACKUP 16 /* Don't match "backup files" */
+#define ZX_NOLINKS 32 /* Don't follow symlinks */
+
+#ifndef NZXPAND
+#define nzxpand(a,b) zxpand(a)
+#endif /* NZXPAND */
+
+#ifndef NOZXREWIND
+#ifndef ZXREWIND /* Platforms that have zxrewind() */
+#ifdef OS2ORUNIX
+#define ZXREWIND
+#else
+#ifdef VMS
+#define ZXREWIND
+#else
+#ifdef datageneral
+#define ZXREWIND
+#else
+#ifdef OSK
+#define ZXREWIND
+#else
+#ifdef STRATUS
+#define ZXREWIND
+#endif /* STRATUS */
+#endif /* OSK */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* OS2ORUNIX */
+#endif /* ZXREWIND */
+#else
+#ifdef ZXREWIND
+#undef ZXREWIND
+#endif /* ZXREWIND */
+#endif /* NOZXREWIND */
+
+/* Temporary-directory-for-RECEIVE feature ... */
+/* This says whether we have the isdir() function defined. */
+
+#ifdef UNIX /* UNIX has it */
+#ifndef CK_TMPDIR
+#ifndef pdp11
+#define CK_TMPDIR
+#define TMPDIRLEN 256
+#endif /* pdp11 */
+#endif /* CK_TMPDIR */
+#endif /* UNIX */
+
+#ifdef VMS /* VMS too */
+#ifndef CK_TMPDIR
+#define CK_TMPDIR
+#define TMPDIRLEN 256
+#endif /* CK_TMPDIR */
+#endif /* VMS */
+
+#ifdef OS2 /* OS two too */
+#ifndef CK_TMPDIR
+#define CK_TMPDIR
+#define TMPDIRLEN 129
+#endif /* CK_TMPDIR */
+#endif /* OS2 */
+
+#ifdef STRATUS /* Stratus VOS too. */
+#ifndef CK_TMPDIR
+#define CK_TMPDIR
+#define TMPDIRLEN 256
+#endif /* CK_TMPDIR */
+#endif /* STRATUS */
+
+#ifdef OSK /* OS-9 too */
+#ifndef CK_TMPDIR
+#define CK_TMPDIR
+#define TMPDIRLEN 256
+#endif /* CK_TMPDIR */
+#endif /* OSK */
+
+#ifdef datageneral /* AOS/VS too */
+#ifndef CK_TMPDIR
+#define CK_TMPDIR
+#define TMPDIRLEN 256
+#endif /* CK_TMPDIR */
+#endif /* datageneral */
+
+#ifdef CK_TMPDIR /* Needs command parser */
+#ifdef NOICP
+#undef CK_TMPDIR
+#endif /* NOICP */
+#endif /* CK_TMPDIR */
+
+/* Whether to include <sys/time.h> */
+
+#ifndef NOTIMEH /* <time.h> */
+#ifndef TIMEH
+#define TIMEH
+#endif /* TIMEH */
+#endif /* NOTIMEH */
+
+#ifndef NOSYSTIMEH /* <sys/time.h> */
+#ifndef SYSTIMEH
+#ifdef UNIX /* UNIX */
+#ifdef SVORPOSIX /* System V or POSIX... */
+#ifdef M_UNIX
+#define SYSTIMEH
+#else
+#ifdef SCO_32V4
+#define SYSTIMEH
+#else
+#ifdef OXOS
+#define SYSTIMEH
+#else
+#ifdef BSD44
+#define SYSTIMEH
+#else
+#ifdef __linux__
+#define SYSTIMEH
+#else
+#ifdef AIXRS
+#ifndef AIX41
+#define SYSTIMEH
+#endif /* AIX41 */
+#else
+#ifdef IRIX60
+#define SYSTIMEH
+#else
+#ifdef I386IX
+#define SYSTIMEH
+#else
+#ifdef SV68R3V6
+#define SYSTIMEH
+#endif /* SV68R3V6 */
+#endif /* I386IX */
+#endif /* IRIX60 */
+#endif /* AIXRS */
+#endif /* __linux__ */
+#endif /* BSD44 */
+#endif /* OXOS */
+#endif /* SCO_32V4 */
+#endif /* M_UNIX */
+
+#else /* Not SVORPOSIX */
+
+#ifndef BELLV10 /* All but these... */
+#ifndef PROVX1
+#ifndef V7
+#ifndef BSD41
+#ifndef COHERENT
+#define SYSTIMEH
+#endif /* COHERENT */
+#endif /* BSD41 */
+#endif /* V7 */
+#endif /* PROVX1 */
+#endif /* BELLV10 */
+#endif /* SVORPOSIX */
+#endif /* UNIX */
+#endif /* SYSTIMEH */
+#endif /* NOSYSTIMEH */
+
+#ifndef NOSYSTIMEBH /* <sys/timeb.h> */
+#ifndef SYSTIMEBH
+#ifdef OSF
+#define SYSTIMEBH
+#else
+#ifdef COHERENT
+#define SYSTIMEBH
+#else
+#ifdef BSD41
+#define SYSTIMEBH
+#else
+#ifdef BSD29
+#define SYSTIMEBH
+#else
+#ifdef TOWER1
+#define SYSTIMEBH
+#else
+#ifdef FT21
+#define SYSTIMEBH
+#else
+#ifdef BELLV10
+#define SYSTIMEBH
+#endif /* BELLV10 */
+#endif /* FT21 */
+#endif /* TOWER1 */
+#endif /* BSD29 */
+#endif /* BSD41 */
+#endif /* COHERENT */
+#endif /* OSF */
+#endif /* SYSTIMEBH */
+#endif /* NOSYSTIMEBH */
+
+/*
+ Debug and transaction logging is included automatically unless you define
+ NODEBUG or NOTLOG. Do this if you want to save the space and overhead.
+ (Note, in version 4F these definitions changed from "{}" to the null string
+ to avoid problems with semicolons after braces, as in: "if (x) tlog(this);
+ else tlog(that);"
+*/
+#ifndef NODEBUG
+#ifndef DEBUG
+#define DEBUG
+#endif /* DEBUG */
+#else
+#ifdef DEBUG
+#undef DEBUG
+#endif /* DEBUG */
+#endif /* NODEBUG */
+
+#ifdef NOTLOG
+#ifdef TLOG
+#undef TLOG
+#endif /* TLOG */
+#else /* NOTLOG */
+#ifndef TLOG
+#define TLOG
+#endif /* TLOG */
+#endif /* NOTLOG */
+
+/* debug() macro style selection. */
+
+#ifdef VMS
+#ifndef IFDEBUG
+#define IFDEBUG
+#endif /* IFDEBUG */
+#endif /* VMS */
+
+#ifdef MAC
+#ifndef IFDEBUG
+#define IFDEBUG
+#endif /* IFDEBUG */
+#endif /* MAC */
+
+#ifdef OS2
+#ifndef IFDEBUG
+#define IFDEBUG
+#endif /* IFDEBUG */
+#endif /* OS2 */
+
+#ifdef OXOS /* tst is faster than jsr */
+#ifndef IFDEBUG
+#define IFDEBUG
+#endif /* IFDEBUG */
+#endif /* OXOS */
+
+#ifndef CKCMAI
+extern int deblog;
+extern int debok;
+extern int debxlen;
+extern int matchdot;
+extern int tt_bell;
+#endif /* CKCMAI */
+
+#ifdef OS2
+_PROTOTYP( void bleep, (short) );
+#else /* OS2 */
+#define bleep(x) if(tt_bell)putchar('\07')
+#endif /* OS2 */
+
+#ifndef BEOSORBEBOX
+#ifdef BEBOX /* This was used only for DR7 */
+#define BEOSORBEBOX
+#else
+#ifdef BEOS /* This is used for BeOS 4.x */
+#define BEOSORBEBOX
+#endif /* BEOS */
+#endif /* BEBOX */
+#endif /* BEOSORBEBOX */
+
+#ifdef NOICP
+#ifdef TLOG
+#undef TLOG
+#endif /* TLOG */
+#endif /* NOICP */
+
+#ifndef TLOG
+#define tlog(a,b,c,d)
+#else
+#ifndef CKCMAI
+/* Debugging included. Declare debug log flag in main program only. */
+extern int tralog, tlogfmt;
+#endif /* CKCMAI */
+_PROTOTYP(VOID dotlog,(int, char *, char *, long));
+#define tlog(a,b,c,d) if (tralog && tlogfmt) dotlog(a,b,c,d)
+_PROTOTYP(VOID doxlog,(int, char *, long, int, int, char *));
+#endif /* TLOG */
+
+/* Formats for debug() and tlog() */
+
+#define F000 0
+#define F001 1
+#define F010 2
+#define F011 3
+#define F100 4
+#define F101 5
+#define F110 6
+#define F111 7
+
+#ifdef __linux__
+#ifndef LINUX
+#define LINUX
+#endif /* LINUX */
+#endif /* __linux__ */
+
+/* Platforms where small size is needed */
+
+#ifdef pdp11
+#define CK_SMALL
+#endif /* pdp11 */
+
+/* Can we use realpath()? */
+
+#ifndef NOREALPATH
+#ifdef pdp11
+#define NOREALPATH
+#endif /* pdp11 */
+#endif /* NOREALPATH */
+
+#ifndef NOREALPATH
+#ifdef UNIX
+#ifdef HPUX5
+#define NOREALPATH
+#else
+#ifdef HPUX6
+#define NOREALPATH
+#else
+#ifdef HPUX7
+#define NOREALPATH
+#else
+#ifdef HPUX8
+#define NOREALPATH
+#else
+#ifdef SV68R3V6
+#define NOREALPATH
+#else
+#ifdef XENIX
+#define NOREALPATH
+#else
+#ifdef CK_SCO32V4
+#define NOREALPATH
+#else
+#ifdef CK_SCOV5
+#define NOREALPATH
+#else
+#ifdef OSF32
+#define NOREALPATH
+#else
+#ifdef OSF30
+#define NOREALPATH
+#else
+#ifdef ultrix
+#define NOREALPATH
+#else
+#ifdef COHERENT
+#define NOREALPATH
+#endif /* COHERENT */
+#endif /* ultrix */
+#endif /* OSF30 */
+#endif /* OSF32 */
+#endif /* CK_SCOV5 */
+#endif /* CK_SCO32V4 */
+#endif /* XENIX */
+#endif /* SV68R3V6 */
+#endif /* HPUX8 */
+#endif /* HPUX7 */
+#endif /* HPUX6 */
+#endif /* HPUX5 */
+#endif /* NOREALPATH */
+
+#ifndef NOREALPATH
+#ifndef CKREALPATH
+#define CKREALPATH
+#endif /* NOREALPATH */
+#endif /* CKREALPATH */
+#endif /* UNIX */
+
+#ifdef CKREALPATH
+#ifdef OS2ORUNIX
+#ifndef CKROOT
+#define CKROOT
+#endif /* CKROOT */
+#endif /* OS2ORUNIX */
+#endif /* CKREALPATH */
+
+/* CKSYMLINK should be set only if we can use readlink() */
+
+#ifdef UNIX
+#ifndef NOSYMLINK
+#ifndef CKSYMLINK
+#define CKSYMLINK
+#endif /* NOSYMLINK */
+#endif /* CKSYMLINK */
+#endif /* UNIX */
+
+/* Platforms where we can use lstat() instead of stat() (for symlinks) */
+/* This should be set only if both lstat() and readlink() are available */
+
+#ifndef NOLSTAT
+#ifndef NOSYMLINK
+#ifndef USE_LSTAT
+#ifdef UNIX
+#ifdef CKSYMLINK
+#ifdef SVR4 /* SVR4 has lstat() */
+#define USE_LSTAT
+#else
+#ifdef BSD42 /* 4.2BSD and 4.3BSD have it */
+#define USE_LSTAT /* This should include old HPUXs */
+#else
+#ifdef BSD44 /* 4.4BSD has it */
+#define USE_LSTAT
+#else
+#ifdef LINUX /* LINUX has it */
+#define USE_LSTAT
+#else
+#ifdef SUNOS4 /* SunOS has it */
+#define USE_LSTAT
+#endif /* SUNOS4 */
+#endif /* LINUX */
+#endif /* BSD44 */
+#endif /* BSD42 */
+#endif /* SVR4 */
+#endif /* CKSYMLINK */
+#endif /* UNIX */
+#endif /* USE_LSTAT */
+#endif /* NOSYMLINK */
+#endif /* NOLSTAT */
+
+#ifdef NOLSTAT
+#ifdef USE_LSTAT
+#undef USE_LSTAT
+#endif /* USE_LSTAT */
+#endif /* NOLSTAT */
+
+#ifndef NOTTYLOCK /* UNIX systems that have ttylock() */
+#ifndef USETTYLOCK
+#ifdef AIXRS /* AIX 3.1 and later */
+#define USETTYLOCK
+#else
+#ifdef USE_UU_LOCK /* FreeBSD or other with uu_lock() */
+#define USETTYLOCK
+#else
+#ifdef HAVE_BAUDBOY /* Red Hat Linux >= 7.2 */
+#define USETTYLOCK
+#endif /* HAVE_BAUDBOY */
+#endif /* USE_UU_LOCK */
+#endif /* AIXRS */
+#endif /* USETTYLOCK */
+#endif /* NOTTYLOCK */
+
+/* Kermit feature selection */
+
+#ifndef NOSPL
+#ifndef NOCHANNELIO /* Channel-based file i/o package */
+#ifndef CKCHANNELIO
+#ifdef UNIX
+#define CKCHANNELIO
+#else
+#ifdef OS2
+#define CKCHANNELIO
+#else
+#ifdef VMS
+#define CKCHANNELIO
+#else
+#ifdef STRATUS
+#define CKCHANNELIO
+#endif /* STRATUS */
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* CKCHANNELIO */
+#endif /* NOCHANNELIO */
+#endif /* NOSPL */
+
+#ifndef NOCKEXEC /* EXEC command */
+#ifndef NOPUSH
+#ifndef CKEXEC
+#ifdef UNIX /* UNIX can do it */
+#define CKEXEC
+#endif /* UNIX */
+#endif /* CKEXEC */
+#endif /* NOPUSH */
+#endif /* NOCKEXEC */
+
+#ifndef NOFAST /* Fast Kermit protocol by default */
+#ifndef CK_FAST
+#ifdef UNIX
+#define CK_FAST
+#else
+#ifdef VMS
+#define CK_FAST
+#else
+#ifdef OS2
+#define CK_FAST
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* CK_FAST */
+#endif /* NOFAST */
+
+#ifdef UNIX /* Transparent print */
+#ifndef NOXPRINT
+#ifndef XPRINT
+#define XPRINT
+#endif /* XPRINT */
+#endif /* NOXPRINT */
+#endif /* UNIX */
+
+#ifndef NOHWPARITY /* Hardware parity */
+#ifndef HWPARITY
+#ifdef SVORPOSIX /* System V or POSIX can have it */
+#define HWPARITY
+#else
+#ifdef SUNOS41 /* SunOS 4.1 can have it */
+#define HWPARITY
+#else
+#ifdef OS2 /* K95 can have it */
+#define HWPARITY
+#endif /* OS2 */
+#endif /* SUNOS41 */
+#endif /* SVORPOSIX */
+#endif /* HWPARITY */
+#endif /* NOHWPARITY */
+
+#ifndef NOSTOPBITS /* Stop-bit selection */
+#ifndef STOPBITS
+#ifdef OS2ORUNIX
+/* In Unix really this should only be if CSTOPB is defined. */
+/* But we don't know that yet. */
+#define STOPBITS
+#else
+#ifdef TN_COMPORT
+#define STOPBITS
+#endif /* TN_COMPORT */
+#endif /* OS2ORUNIX */
+#endif /* STOPBITS */
+#endif /* NOSTOPBITS */
+
+#ifdef UNIX
+#ifndef NETCMD /* Can SET NETWORK TYPE COMMAND */
+#define NETCMD
+#endif /* NETCMD */
+#endif /* UNIX */
+
+/* Pty support, nonportable, available on a case-by-case basis */
+
+#ifndef NOPTY
+#ifdef NEXT /* NeXTSTEP (tested on 3.1)*/
+#define NETPTY
+#else
+#ifdef CK_SCOV5 /* SCO OSR5 (tested on 5.0.5)*/
+#define NETPTY
+#else
+#ifdef QNX /* QNX (tested on 4.25) */
+#define NETPTY
+#else
+#ifdef SINIX /* Sinix (tested on 5.42) */
+#define NETPTY
+#else
+#ifdef DGUX540 /* DG/UX 5.4++ (tested on 5.4R4.11) */
+#define NETPTY
+#else
+#ifdef OSF32 /* Digital Unix 3.2 */
+#define NETPTY
+#else
+#ifdef OSF40 /* Digital Unix 4.0 / Tru64 */
+#define NETPTY
+#else
+#ifdef IRIX60 /* IRIX 6.0 (not earlier) */
+#define NETPTY
+#else
+#ifdef HPUX10 /* HPUX 10.00 or later */
+#define NETPTY
+#ifndef HAVE_PTYTRAP
+#define HAVE_PTYTRAP
+#endif /* HAVE_PTYTRAP */
+#else
+#ifdef HPUX9 /* HPUX 9.00 (not earlier) */
+#define NETPTY
+#ifndef HAVE_PTYTRAP
+#define HAVE_PTYTRAP
+#endif /* HAVE_PTYTRAP */
+#else
+#ifdef BSD44 /* BSD44, {Net,Free,Open}BSD */
+#define NETPTY
+#else
+#ifdef BSDI /* BSDI/OS (tested in 4) */
+#define NETPTY
+#else
+#ifdef SOLARIS /* Solaris (tested in 2.5) */
+#define NETPTY
+#else
+#ifdef UW7 /* Unixware 7 */
+#define NETPTY
+#else
+#ifdef SUNOS41 /* SunOS (tested in 4.1.3) */
+#define NETPTY
+#else
+#ifdef AIX41 /* AIX 4.1 and later */
+#define NETPTY
+#else
+#ifdef LINUX /* Linux */
+#define NETPTY
+#endif /* LINUX */
+#endif /* AIX41 */
+#endif /* SUNOS41 */
+#endif /* UW7 */
+#endif /* SOLARIS */
+#endif /* BSDI */
+#endif /* BSD44 */
+#endif /* HPUX9 */
+#endif /* HPUX10 */
+#endif /* IRIX60 */
+#endif /* OSF40 */
+#endif /* OSF32 */
+#endif /* DGUX540 */
+#endif /* SINIX */
+#endif /* QNX */
+#endif /* CK_SCOV5 */
+#endif /* NEXT */
+
+#else /* NOPTY */
+
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#endif /* NOPTY */
+
+#ifdef NETPTY /* NETCMD required for NETPTY */
+#ifndef NETCMD
+#define NETCMD
+#endif /* NETCMD */
+#endif /* NETPTY */
+
+#ifndef CK_UTSNAME /* Can we call uname()? */
+#ifdef VMS
+#define CK_UTSNAME
+#else
+#ifdef OS2
+#define CK_UTSNAME
+#else
+#ifdef POSIX /* It's in POSIX.1 */
+#define CK_UTSNAME
+#else
+#ifdef SUNOS41 /* It's in SunOS 4.1 */
+#define CK_UTSNAME
+#else
+#ifdef AIXRS /* It's in AIX */
+#define CK_UTSNAME
+#else
+#ifdef SVR4 /* It's in SVR4 (but not SVR3) */
+#define CK_UTSNAME
+#else
+#ifdef HPUX /* It's in HP-UX 5.00 and later */
+#define CK_UTSNAME
+#else
+#ifdef OSF /* It's in OSF/1 / Digital UNIX */
+#define CK_UTSNAME
+#else
+#ifdef CK_SCOV5
+#define CK_UTSNAME
+#endif /* CK_SCOV5 */
+#endif /* OSF */
+#endif /* HPUX */
+#endif /* SVR4 */
+#endif /* AIXRS */
+#endif /* SUNOS41 */
+#endif /* POSIX */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* CK_UTSNAME */
+
+/* This section for anything that might use floating-point */
+
+/* If the following causes trouble use -DFLOAT=float on the command line */
+
+#ifdef NOSPL
+#ifdef FNFLOAT
+#undef FNFLOAT
+#endif /* FNFLOAT */
+#ifdef CKFLOAT
+#undef CKFLOAT
+#endif /* CKFLOAT */
+#endif /* NOSPL */
+
+#ifndef NOFLOAT
+
+#ifndef CKFLOAT
+#ifdef __alpha
+/* Don't use double on 64-bit platforms -- bad things happen */
+#define CKFLOAT float
+#define CKFLOAT_S "float"
+#else
+#define CKFLOAT double
+#define CKFLOAT_S "double"
+#endif /* __alpha */
+#endif /* CKFLOAT */
+
+#ifndef NOGFTIMER /* Floating-point timers */
+#ifndef GFTIMER
+#ifdef UNIX /* For UNIX */
+#define GFTIMER
+#endif /* UNIX */
+#ifdef VMS /* VMS */
+#ifndef OLD_VMS /* 5.0 and later */
+#define GFTIMER
+#endif /* OLD_VMS */
+#endif /* VMS */
+#ifdef OS2 /* And K95 */
+#define GFTIMER
+#endif /* OS2 */
+#ifdef STRATUS /* And Stratus VOS */
+#define GFTIMER
+#endif /* STRATUS */
+#endif /* GFTIMER */
+#endif /* NOGFTIMER */
+
+#ifndef NOSPL
+#ifndef FNFLOAT /* Floating-point math functions */
+#ifdef VMS /* defined by default in VMS */
+#define FNFLOAT
+#else
+#ifdef OS2 /* and K95 */
+#define FNFLOAT
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* FNFLOAT */
+#endif /* NOSPL */
+
+#else /* NOFLOAT is defined */
+
+#ifdef CKFLOAT
+#undef CKFLOAT
+#endif /* CKFLOAT */
+
+#ifdef GFTIMER
+#undef GFTIMER
+#endif /* GFTIMER */
+
+#ifdef FNFLOAT
+#undef FNFLOAT
+#endif /* FNFLOAT */
+
+#endif /* NOFLOAT */
+
+#ifdef GFTIMER /* Fraction of second to use when */
+#ifndef GFMINTIME /* elapsed time is <= 0 */
+#define GFMINTIME 0.005
+#endif /* GFMINTIME */
+#endif /* GFTIMER */
+
+#ifndef CKCMAI
+extern long ztmsec, ztusec; /* Fraction of sec of current time */
+#endif /* CKCMAI */
+
+#ifndef NOUNPREFIXZERO /* Allow unprefixing of NUL (0) */
+#ifndef UNPREFIXZERO /* in file-transfer packets */
+#define UNPREFIXZERO
+#endif /* UNPREFIXZERO */
+#endif /* NOUNPREFIXZERO */
+
+#ifdef CK_SMALL
+#define NOCAL /* Calibrate */
+#endif /* CK_SMALL */
+
+#ifndef NOPATTERNS /* Filetype matching patterns */
+#ifndef PATTERNS
+#ifndef VMS
+#ifndef CK_SMALL
+#define PATTERNS
+#endif /* CK_SMALL */
+#endif /* VMS */
+#endif /* PATTERNS */
+#endif /* NOPATTERNS */
+
+#ifndef NOCAL
+#ifndef CALIBRATE
+#define CALIBRATE
+#endif /* CALIBRATE */
+#else
+#ifdef CALIBRATE
+#undef CALIBRATE
+#endif /* CALIBRATE */
+#endif /* NOCAL */
+
+#ifndef NORECURSE /* Recursive directory traversal */
+#ifndef RECURSIVE
+#ifdef VMS
+#define RECURSIVE
+#else
+#ifdef OS2ORUNIX
+#ifndef CK_SMALL
+#define RECURSIVE
+#endif /* CK_SMALL */
+#else
+#ifdef STRATUS
+#define RECURSIVE
+#else
+#ifdef OSK
+#define RECURSIVE
+#endif /* OSK */
+#endif /* STRATUS */
+#endif /* OS2ORUNIX */
+#endif /* VMS */
+#endif /* RECURSIVE */
+#endif /* NORECURSE */
+
+#ifndef CK_SMALL /* Enable file-transfer tuning code */
+#ifndef CKTUNING /* in which more code is added */
+#ifndef NOTUNING /* to avoid function calls, etc */
+#define CKTUNING
+#endif /* NOTUNING */
+#endif /* CKTUNING */
+#endif /* CK_SMALL */
+
+#ifndef NOURL /* Parse URLs in SET HOST, etc */
+#define CK_URL
+#define NO_FTP_AUTH /* No auth "ftp" / "anonymous" */
+#endif /* NOURL */
+
+#ifndef NOTRIGGER
+#ifndef CK_TRIGGER /* Trigger string to exit CONNECT */
+#ifdef OS2ORUNIX /* OK for UNIX and K95 */
+#define CK_TRIGGER
+#else
+#ifdef VMS /* and VMS */
+#define CK_TRIGGER
+#else
+#ifdef datageneral /* and AOS/VS */
+#define CK_TRIGGER
+#endif /* datageneral */
+#endif /* OS2ORUNIX */
+#endif /* VMS */
+#endif /* CK_TRIGGER */
+#endif /* NOTRIGGER */
+
+#ifdef CK_TRIGGER
+#define TRIGGERS 8 /* How many triggers allowed */
+#endif /* CK_TRIGGER */
+
+#ifndef XLIMITS /* CONNECT limits */
+#ifdef OS2
+#define XLIMITS
+#endif /* OS2 */
+#endif /* XLIMITS */
+
+#ifdef NOFRILLS
+#ifndef NOBROWSER
+#define NOBROWSER
+#endif /* NOBROWSER */
+#ifndef NOFTP
+#define NOFTP
+#endif /* NOFTP */
+#endif /* NOFRILLS */
+
+#ifndef NOHTTP /* HTTP features need... */
+#ifdef NOICP /* an interactive command parser */
+#define NOHTTP
+#endif /* NOICP */
+#ifndef VMS
+#ifndef OS2ORUNIX /* K95 or UNIX (because of */
+#define NOHTTP /* time functions, time_t, etc) */
+#endif /* OS2ORUNIX */
+#endif /* VMS */
+#endif /* NOHTTP */
+
+
+#ifndef NONET
+#ifdef TCPSOCKET
+
+/* The HTTP code is not very portable, so it must be asked for with -DCKHTTP */
+
+#ifndef NOHTTP
+#ifndef CKHTTP
+#ifdef SUNOS4 /* We can use it in SunOS */
+#define CKHTTP
+#endif /* SUNOS4 */
+#ifdef SOLARIS /* And in Solaris */
+#define CKHTTP
+#endif /* SOLARIS */
+#ifdef LINUX /* And Linux */
+#define CKHTTP
+#endif /* LINUX */
+#ifdef HPUX10 /* And HP-UX 10 and above */
+#define CKHTTP
+#endif /* HPUX10 */
+#ifdef OS2 /* And in K-95 */
+#define CKHTTP
+#endif /* OS2 */
+#ifdef AIX41 /* In AIX 4.1 and higher */
+#define CKHTTP
+#endif /* AIX41 */
+#ifdef UNIXWARE /* In Unixware 2.1 and higher */
+#define CKHTTP /* and probably also in 1.x and 2.0 */
+#endif /* UNIXWARE */
+#ifdef CK_SCOV5
+#define CKHTTP
+#endif /* CK_SCOV5 */
+#ifdef OSF /* And in OSF Digital UNIX/True 64 */
+#define CKHTTP
+#endif /* OSF */
+#ifdef ultrix /* And in Ultrix Mips */
+#ifdef mips
+#define CKHTTP
+#endif /* mips */
+#endif /* ultrix */
+/* Add more here... */
+#endif /* CKHTTP */
+#ifndef CKHTTP /* If CKHTTP not defined yet */
+#define NOHTTP /* then define HOHTTP */
+#endif /* CKHTTP */
+#endif /* NOHTTP */
+
+#ifdef NETCONN /* Special "network" types... */
+#ifndef NOLOCAL
+#ifdef OS2
+#ifndef NETFILE
+#define NETFILE
+#endif /* NETFILE */
+#ifndef NOPUSH
+#ifndef NETCMD
+#define NETCMD
+#endif /* NETCMD */
+#endif /* NOPUSH */
+#ifdef NT
+#ifndef NETDLL
+#define NETDLL
+#endif /* NETDLL */
+#endif /* NT */
+#endif /* OS2 */
+#endif /* NOLOCAL */
+#endif /* NETCONN */
+
+#ifndef NOFTP
+#ifndef SYSFTP
+#ifndef NEWFTP
+#ifdef OS2ORUNIX
+#define NEWFTP
+#endif /* OS2ORUNIX */
+#endif /* NEWFTP */
+#endif /* SYSFTP */
+#endif /* NOFTP */
+
+#ifndef NOFTP
+#ifdef NEWFTP
+#ifdef SYSFTP
+#undef SYSFTP
+#endif /* SYSFTP */
+#else /* NEWFTP */
+#ifndef SYSFTP
+#define SYSFTP
+#endif /* SYSFTP */
+#endif /* NEWFTP */
+#else /* NOFTP */
+#ifdef NEWFTP
+#undef NEWFTP
+#endif /* NEWFTP */
+#ifdef SYSFTP
+#undef SYSFTP
+#endif /* SYSFTP */
+#endif /* NOFTP */
+
+#ifndef NOBROWSER
+#ifdef UNIX
+#ifndef BROWSER
+#ifndef NOPUSH
+#define BROWSER
+#endif /* NOPUSH */
+#endif /* BROWSER */
+#endif /* UNIX */
+#ifdef OS2
+#ifndef BROWSER
+#ifndef NOPUSH
+#define BROWSER
+#endif /* NOPUSH */
+#endif /* BROWSER */
+#endif /* OS2 */
+#else
+#ifdef BROWSER
+#undef BROWSER
+#endif /* BROWSER */
+#endif /* NOBROWSER */
+
+#else /* TCPSOCKET */
+#ifndef NOHTTP /* HTTP requires TCPSOCKET */
+#define NOHTTP
+#endif /* NOHTTP */
+#endif /* TCPSOCKET */
+#endif /* NONET */
+
+#ifdef TCPSOCKET
+#ifndef NOCKGETFQHOST
+#ifdef __ia64__
+#define NOCKGETFQHOST
+#else /* __ia64__ */
+#ifdef SV68
+#define NOCKGETFQHOST
+#else
+#ifdef HPUXPRE65
+#define NOCKGETFQHOST
+#endif /* HPUXPRE65 */
+#endif /* SV68 */
+#endif /* __ia64 */
+#endif /* NOCKGETFQHOST */
+/*
+ Regarding System V/68 (SV68) (from Gerry Belanger, Oct 2002):
+
+ 1) The gethostbyname() appears to return the actual host IP
+ address in the hostent struct, instead of the expected pointer
+ to the address. Hence the bogus address in the bcopy/memcopy.
+ This is despite the header agreeing with our expectations.
+
+ 2) the expected argument swap between bcopy and memcopy
+ did not happen. What grief this might cause, I know not.
+*/
+#endif /* TCPSOCKET */
+
+#ifdef TCPSOCKET
+#ifdef OS2ONLY
+#ifndef NOSOCKS
+#define NOSOCKS
+#endif /* NOSOCKS */
+#endif /* OS2ONLY */
+#ifdef NOSOCKS
+#ifdef CK_SOCKS
+#undef CK_SOCKS
+#endif /* CK_SOCKS */
+#ifdef CK_SOCKS5
+#undef CK_SOCKS5
+#endif /* CK_SOCKS5 */
+#else /* NOSOCKS */
+#ifdef NT
+#ifndef CK_SOCKS
+#define CK_SOCKS
+#endif /* CK_SOCKS */
+#endif /* NT */
+#ifdef CK_SOCKS5 /* CK_SOCKS5 implies CK_SOCKS */
+#ifndef CK_SOCKS
+#define CK_SOCKS
+#endif /* CK_SOCKS */
+#endif /* CK_SOCKS5 */
+#endif /* NOSOCKS */
+#endif /* TCPSOCKET */
+
+#ifdef TNCODE
+#ifndef CK_AUTHENTICATION
+#ifdef OS2
+#ifdef _M_PPC
+#define NO_KERBEROS
+#define NO_SRP
+#else /* _M_PPC */
+#ifndef NO_SSL
+#define CK_SSL
+#define SSLDLL
+#endif /* NO_SSL */
+#endif /* _M_PPC */
+#ifndef NO_KERBEROS
+#define CK_KERBEROS
+#define KRB4
+#define KRB5
+#define KRB524
+#define KRB524_CONV
+#ifdef NT
+#ifndef _M_PPC
+#ifndef _M_ALPHA
+#ifndef NO_SSL_KRB5
+#define SSL_KRB5
+#endif /* NO_SSL_KRB5 */
+#endif /* _M_ALPHA */
+#endif /* _M_PPC */
+#endif /* NT */
+#endif /* NO_KERBEROS */
+#ifndef NO_SRP
+#define CK_SRP
+#endif /* NO_SRP */
+#define CK_AUTHENTICATION
+#endif /* OS2 */
+#endif /* CK_AUTHENTICATION */
+
+#ifdef CK_AUTHENTICATION /* Encryption must have Auth */
+#ifndef CK_ENCRYPTION
+#ifndef NO_ENCRYPTION
+#ifdef OS2
+#define CK_ENCRYPTION
+#define CK_DES
+#define CK_CAST
+#endif /* OS2 */
+#endif /* NO_ENCRYPTION */
+#endif /* CK_ENCRYPTION */
+#endif /* CK_AUTHENTICATION */
+
+#ifdef NO_AUTHENTICATION /* Allow authentication to be */
+#ifdef CK_AUTHENTICATION /* disabled in NT and OS/2 */
+#undef CK_AUTHENTICATION
+#endif /* CK_AUTHENTICATION */
+#ifdef CK_KERBEROS
+#undef CK_KERBEROS
+#endif /* CK_KERBEROS */
+#ifdef CK_SRP
+#undef CK_SRP
+#endif /* CK_SRP */
+#ifdef CK_ENCRYPTION
+#undef CK_ENCRYPTION
+#endif /* CK_ENCRYPTION */
+#endif /* NO_AUTHENTICATION */
+
+#ifdef NO_ENCRYPTION /* Allow encryption to be */
+#ifdef CK_ENCRYPTION /* disabled in NT and OS/2 */
+#undef CK_ENCRYPTION
+#endif /* CK_ENCRYPTION */
+#endif /* NO_ENCRYPTION */
+
+#ifdef CK_KERBEROS /* Disable funcs not yet supported with Heimdal */
+#ifdef KRB5
+#ifndef HEIMDAL
+#define KRB5_U2U
+#endif /* HEIMDAL */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+/*
+ SSH section. NOSSH disables any form of SSH support.
+ If NOSSH is not defined (or implied by NONET, NOLOCAL, etc)
+ then SSHBUILTIN is defined for K95 and SSHCMD is defined for UNIX.
+ Then, if either SSHBUILTIN or SSHCMD is defined, ANYSSH is also defined.
+*/
+
+#ifndef NOSSH
+#ifndef NO_SSL
+#ifdef OS2ONLY
+#define NOSSH
+#endif /* OS2ONLY */
+#ifdef NT
+#ifndef CK_SSL
+#define NOSSH
+#endif /* CK_SSL */
+#endif /* NT */
+#else /* NO_SSL */
+#define NOSSH
+#endif /* NO_SSL */
+#endif /* NOSSH */
+
+#ifdef NOSSH /* NOSSH */
+#ifdef SSHBUILTIN /* undefines any SSH selctors */
+#undef SSHBUILTIN
+#endif /* SSHBUILTIN */
+#ifdef SFTP_BUILTIN
+#undef SFTP_BUILTIN
+#endif /* SFTP_BUILTIN */
+#ifdef SSHCMD
+#undef SSHCMD
+#endif /* SSHCMD */
+#ifdef ANYSSH
+#undef ANYSSH
+#endif /* ANYSSH */
+#else /* Not NOSSH */
+#ifndef NOLOCAL
+#ifdef OS2
+#ifndef SSHBUILTIN
+#define SSHBUILTIN
+#endif /* SSHBUILTIN */
+#else /* Not OS2 */
+#ifdef UNIX
+#ifndef SSHCMD
+#ifdef NETPTY
+#ifndef NOPUSH
+#define SSHCMD
+#endif /* NOPUSH */
+#endif /* NETPTY */
+#endif /* SSHCMD */
+#endif /* UNIX */
+#endif /* OS2 */
+#ifndef ANYSSH
+#ifdef SSHBUILTIN
+#define ANYSSH
+#ifdef SSHCMD
+#undef SSHCMD
+#endif /* SSHCMD */
+#else /* SSHBUILTIN */
+#ifdef SSHCMD
+#define ANYSSH
+#endif /* SSHCMD */
+#endif /* SSHBUILTIN */
+#endif /* ANYSSH */
+#endif /* NOLOCAL */
+#endif /* NOSSH */
+
+/* This is in case #ifdef SSH is used anywhere in the K95 modules */
+
+#ifdef OS2
+#ifdef SSHBUILTIN
+#ifndef SSH
+#define SSH
+#endif /* SSH */
+#endif /* SSHBUILTIN */
+#endif /* OS2 */
+
+#ifdef CK_AUTHENTICATION
+#define CK_SECURITY
+#else
+#ifdef CK_SSL
+#define CK_AUTHENTICATION
+#define CK_SECURITY
+#endif /* CK_SSL */
+#endif /* CK_AUTHENTICATION */
+
+/* Environment stuff */
+
+#ifndef OS2ORUNIX
+#ifndef NOPUTENV
+#define NOPUTENV
+#endif /* NOPUTENV */
+#endif /* OS2ORUNIX */
+
+#ifndef CK_ENVIRONMENT
+#ifdef OS2
+#define CK_ENVIRONMENT
+#else
+#ifdef UNIX
+#define CK_ENVIRONMENT
+#else
+#ifdef STRATUS
+#define CK_ENVIRONMENT
+#else
+#ifdef VMS
+#define CK_ENVIRONMENT
+#endif /* VMS */
+#endif /* STRATUS */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* CK_ENVIRONMENT */
+#ifndef NOSNDLOC /* RFC 779 SEND LOCATION */
+#ifndef CK_SNDLOC
+#define CK_SNDLOC
+#endif /* CK_SNDLOC */
+#endif /* NOSNDLOC */
+#ifndef NOXDISPLOC /* RFC 1096 XDISPLOC */
+#ifndef CK_XDISPLOC
+#define CK_XDISPLOC
+#endif /* CK_XDISPLOC */
+#endif /* NOXDISPLOC */
+#ifndef NOFORWARDX
+#ifndef NOPUTENV
+#ifndef NOSELECT
+#ifndef CK_FORWARD_X
+#ifdef CK_AUTHENTICATION
+#ifndef OS2ONLY
+#define CK_FORWARD_X
+#endif /* OS2ONLY */
+#endif /* CK_AUTHENTICATION */
+#endif /* CK_FORWARD_X */
+#endif /* NOSELECT */
+#endif /* NOPUTENV */
+#endif /* NOFORWARDX */
+#ifndef NO_COMPORT
+#ifdef TCPSOCKET
+#ifndef TN_COMPORT
+#define TN_COMPORT
+#endif /* TN_COMPORT */
+#endif /* TCPSOCKET */
+#endif /* NO_COMPORT */
+#endif /* TNCODE */
+
+#ifndef NOXFER
+#ifndef NOCTRLZ /* Allow SET FILE EOF CTRL-Z */
+#ifndef CK_CTRLZ
+#ifdef OS2ORUNIX
+#define CK_CTRLZ
+#endif /* OS2ORUNIX */
+#endif /* CK_CTRLZ */
+#endif /* NOCTRLZ */
+#endif /* NOXFER */
+
+#ifndef NOPERMS /* File permissions in A packets */
+#ifndef CK_PERMS
+#ifdef UNIX
+#define CK_PERMS
+#else
+#ifdef VMS
+#define CK_PERMS
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* CK_PERMS */
+#endif /* NOPERMS */
+#ifdef CK_PERMS
+#define CK_PERMLEN 24 /* Max length of sys-dependent perms */
+#endif /* CK_PERMS */
+
+#ifdef UNIX /* NOSETBUF for everybody */
+#ifndef NOSETBUF
+#ifndef USE_SETBUF /* This is the escape clause */
+#define NOSETBUF
+#endif /* USE_SETBUF */
+#endif /* NOSETBUF */
+#endif /* UNIX */
+
+#ifndef USE_STRERROR /* Whether to use strerror() */
+#ifdef pdp11
+#define USE_STRERROR
+#endif /* pdp11 */
+#endif /* USE_STRERROR */
+
+#ifdef VMS /* Features for all VMS builds */
+#ifndef NOJC
+#define NOJC
+#endif /* NOJC */
+#ifndef NOSETBUF
+#define NOSETBUF
+#endif /* NOSETBUF */
+#ifndef DYNAMIC
+#define DYNAMIC
+#endif /* DYNAMIC */
+#ifndef NOCURSES
+#ifndef CK_CURSES
+#define CK_CURSES
+#endif /* CK_CURSES */
+#endif /* NOCURSES */
+#endif /* VMS */
+
+#ifndef NOCKTIMERS /* Dynamic timeouts */
+#ifndef CK_TIMERS
+#define CK_TIMERS
+#endif /* CK_TIMERS */
+#endif /* NOCKTIMERS */
+
+#define CK_SPEED /* Control-prefix removal */
+#ifdef NOCKSPEED
+#undef CK_SPEED
+#endif /* NOCKSPEED */
+
+#ifndef NOCKXXCHAR
+#ifndef CKXXCHAR
+#ifdef UNIX
+#define CKXXCHAR
+#else
+#ifdef OS2
+#define CKXXCHAR
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* CKXXCHAR */
+#endif /* NOCKXXCHAR */
+
+#ifdef MAC /* For Macintosh, no escape */
+#define NOPUSH /* to operating system */
+#endif /* MAC */
+
+/* Systems where we can call zmkdir() to create directories. */
+
+#ifndef CK_MKDIR
+#ifndef NOMKDIR
+
+#ifdef UNIX
+#ifndef pdp11
+#define CK_MKDIR
+#endif /* pdp11 */
+#endif /* UNIX */
+
+#ifdef OS2
+#define CK_MKDIR
+#endif /* OS2 */
+
+#ifdef VMS
+#define CK_MKDIR
+#endif /* VMS */
+
+#ifdef STRATUS
+#define CK_MKDIR
+#endif /* STRATUS */
+
+#ifdef OSK
+#define CK_MKDIR
+#endif /* OSK */
+
+#ifdef datageneral
+#define CK_MKDIR
+#endif /* datageneral */
+
+#endif /* CK_MKDIR */
+#endif /* NOMKDIR */
+
+#ifdef NOMKDIR /* Allow for command-line override */
+#ifdef CK_MKDIR
+#undef CK_MKDIR
+#endif /* CK_MKDIR */
+#endif /* NOMKDIR */
+
+/* Systems for which we can enable the REDIRECT command automatically */
+/* As of 6.0.193, it should work for all UNIX... */
+
+#ifndef NOREDIRECT
+#ifndef CK_REDIR
+#ifdef UNIX
+#define CK_REDIR
+#endif /* UNIX */
+#ifdef OS2 /* As well as OS/2 and friends... */
+#define CK_REDIR
+#endif /* OS2 */
+#endif /* CK_REDIR */
+#endif /* NOREDIRECT */
+
+#ifdef NOPUSH /* But... REDIRECT command is not */
+#ifdef CK_REDIR /* allowed if NOPUSH is defined. */
+#undef CK_REDIR
+#endif /* CK_REDIR */
+#ifdef NETCMD /* Nor is SET NET COMMAND */
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#endif /* NOPUSH */
+
+#ifndef PEXITSTAT /* \v(pexitstat) variable defined */
+#ifdef OS2ORUNIX
+#define PEXITSTAT
+#else
+#ifdef VMS
+#define PEXITSTAT
+#endif /* VMS */
+#endif /* OS2ORUNIX */
+#endif /* PEXITSTAT */
+
+/* The following allows automatic enabling of REDIRECT to be overridden... */
+
+#ifdef NOREDIRECT
+#ifdef NETCMD
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#ifdef CK_REDIR
+#undef CK_REDIR
+#endif /* CK_REDIR */
+#endif /* NOREDIRECT */
+
+#ifdef NONETCMD
+#ifdef NETCMD
+#undef NETCMD
+#endif /* NETCMD */
+#ifdef NETPTY
+#undef NETPTY
+#endif /* NETPTY */
+#endif /* NONETCMD */
+
+#ifdef CK_REDIR
+_PROTOTYP( int ttruncmd, (char *) );
+#endif /* CK_REDIR */
+
+/* Use built-in DIRECTORY command */
+
+#ifndef NOMYDIR
+#ifndef DOMYDIR
+#ifdef UNIXOROSK
+#define DOMYDIR
+#else
+#ifdef OS2
+#define DOMYDIR
+#else
+#ifdef VMS
+#define DOMYDIR
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* UNIXOROSK */
+#endif /* DOMYDIR */
+#endif /* NOMYDIR */
+
+/* Sending from and receiving to commands/pipes */
+
+#ifndef PIPESEND
+#ifdef UNIX
+#define PIPESEND
+#endif /* UNIX */
+#ifdef OS2
+#define PIPESEND
+#endif /* OS2 */
+#endif /* PIPESEND */
+
+#ifdef PIPESEND
+#ifdef NOPIPESEND
+#undef PIPESEND
+#endif /* NOPIPESEND */
+#ifdef NOPUSH
+#undef PIPESEND
+#endif /* NOPUSH */
+#endif /* PIPESEND */
+
+#ifdef NOPUSH
+#ifdef BROWSER
+#undef BROWSER
+#endif /* BROWSER */
+#endif /* NOPUSH */
+
+/* Versions where we support the RESEND command */
+
+#ifndef NOXFER
+#ifndef NORESEND
+#ifndef CK_RESEND
+#ifdef UNIX
+#ifndef pdp11
+#define CK_RESEND
+#endif /* pdp11 */
+#endif /* UNIX */
+
+#ifdef VMS
+#define CK_RESEND
+#endif /* VMS */
+
+#ifdef OS2
+#define CK_RESEND
+#endif /* OS2 */
+
+#ifdef AMIGA
+#define CK_RESEND
+#endif /* AMIGA */
+
+#ifdef datageneral
+#define CK_RESEND
+#endif /* datageneral */
+
+#ifdef STRATUS
+#define CK_RESEND
+#endif /* STRATUS */
+
+#ifdef OSK
+#define CK_RESEND
+#endif /* OSK */
+
+#endif /* CK_RESEND */
+#endif /* NORESEND */
+#endif /* NOXFER */
+
+/* Systems implementing "Doomsday Kermit" protocol ... */
+
+#ifndef DOOMSDAY
+#ifdef UNIX
+#define DOOMSDAY
+#else
+#ifdef VMS
+#define DOOMSDAY
+#else
+#ifdef OS2
+#define DOOMSDAY
+#else
+#ifdef STRATUS
+#define DOOMSDAY
+#endif /* STRATUS */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* DOOMSDAY */
+
+/* Systems where we want the Thermometer to be used for fullscreen */
+
+#ifdef OS2
+#ifndef CK_PCT_BAR
+#define CK_PCT_BAR
+#endif /* CK_PCT_BAR */
+#endif /* OS2 */
+
+/* Systems where we have a REXX command */
+
+#ifdef OS2
+#ifdef __32BIT__
+#ifndef NOREXX
+#define CK_REXX
+#endif /* NOREXX */
+#endif /* __32BIT__ */
+#endif /* OS2 */
+
+/* Platforms that have a ZCHKPID function */
+
+#ifdef OS2ORUNIX
+#define ZCHKPID
+#endif /* OS2ORUNIX */
+
+#ifndef ZCHKPID
+/* If we can't check pids then we have treat all pids as active & valid. */
+#define zchkpid(x) 1
+#endif /* ZCHKPID */
+
+/* Systems that have a ZRENAME function */
+
+#define ZRENAME /* They all do */
+
+/* Systems that have a ZCOPY function */
+
+#ifndef ZCOPY
+#ifdef VMS
+#define ZCOPY
+#else
+#ifdef OS2
+#define ZCOPY
+#else
+#ifdef UNIX
+#define ZCOPY
+#else
+#ifdef STRATUS
+#define ZCOPY
+#endif /* STRATUS */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* ZCOPY */
+
+/* Systems that have ttgwsiz() (they all should but they don't) */
+
+#ifndef NOTTGWSIZ
+#ifndef CK_TTGWSIZ
+#ifdef UNIX
+#define CK_TTGWSIZ
+#else
+#ifdef VMS
+#define CK_TTGWSIZ
+#else
+#ifdef OS2
+#define CK_TTGWSIZ
+#else
+#ifdef OSK
+#define CK_TTGWSIZ
+#endif /* OSK */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* CK_TTGWSIZ */
+#endif /* NOTTGWSIZ */
+
+#ifdef NOTTGWSIZ
+#ifdef CK_TTGWSIZ
+#undef CK_TTGWSIZ
+#endif /* CK_TTGWSIZ */
+#endif /* NOTTGWSIZ */
+
+/* OS/2 C-Kermit features not available in 16-bit version... */
+
+#ifdef OS2ONLY
+#ifndef __32BIT__
+#ifndef NOLOCAL
+#ifdef PCFONTS /* PC Font support */
+#undef PCFONTS
+#endif /* PCFONTS */
+#ifdef NPIPE /* Named Pipes communication */
+#undef NPIPE
+#endif /* NPIPE */
+#ifdef CK_NETBIOS /* NETBIOS communication */
+#undef CK_NETBIOS
+#endif /* CK_NETBIOS */
+#ifdef OS2MOUSE /* Mouse */
+#undef OS2MOUSE
+#endif /* OS2MOUSE */
+#ifdef OS2PM /* Presentation Manager */
+#undef OS2PM
+#endif /* OS2PM */
+#endif /* NOLOCAL */
+#ifdef CK_REXX /* Rexx */
+#undef CK_REXX
+#endif /* CK_REXX */
+#endif /* __32BIT__ */
+#endif /* OS2ONLY */
+
+/* OS/2 C-Kermit features not available in Windows NT version... */
+
+#ifdef OS2
+#ifdef NT
+#ifdef PCFONTS /* PC Font support */
+#undef PCFONTS
+#endif /* PCFONTS */
+#ifdef NPIPE /* Named Pipes communication */
+#undef NPIPE
+#endif /* NPIPE */
+#ifdef OS2PM /* Presentation Manager */
+#undef OS2PM
+#endif /* OS2PM */
+#ifdef CK_REXX /* Rexx */
+#undef CK_REXX
+#endif /* CK_REXX */
+#endif /* NT */
+#endif /* OS2 */
+
+/*
+ Systems that have select().
+ This is used for both msleep() and for read-buffer checking in in_chk().
+*/
+#define CK_SLEEPINT 250 /* milliseconds - set this to something that
+ divides evenly into 1000 */
+#ifndef SELECT
+#ifndef NOSELECT
+#ifdef __linux__
+#define SELECT
+#else
+#ifdef SUNOS4
+#define SELECT
+#else
+#ifdef NEXT
+#define SELECT
+#else
+#ifdef RTAIX
+#define SELECT
+#else
+#ifdef HPUX
+/*
+ Not really. I think it's only in HP-UX 7.0 and later, except it's also
+ in earlier versions that have TCP/IP installed. Override this default
+ in particular HP-UX makefile entries by adding -DNOSELECT, as in (e.g.)
+ the HP-UX 6.5 ones.
+*/
+#define SELECT
+#else
+#ifdef AIXRS
+#define SELECT
+#else
+#ifdef BSD44
+#define SELECT
+#else
+#ifdef BSD4
+#define SELECT
+#else
+#ifdef OXOS
+#define SELECT
+#else
+#ifdef OS2
+#define SELECT
+#else
+#ifdef BEBOX
+#define SELECT
+#endif /* BEBOX */
+#endif /* OS2 */
+#endif /* OXOS */
+#endif /* BSD4 */
+#endif /* BSD44 */
+#endif /* AIXRS */
+#endif /* HPUX */
+#endif /* RTAIX */
+#endif /* NEXT */
+#endif /* __linux__ */
+#endif /* SUNOS4 */
+#endif /* NOSELECT */
+#endif /* SELECT */
+
+/*
+ The following section moved here from ckcnet.h in 6.1 because select()
+ is now used for non-networking purposes.
+*/
+
+/* On HP-9000/500 HP-UX 5.21 this stuff is not defined in any header file */
+
+#ifdef hp9000s500
+#ifndef NEEDSELECTDEFS
+#define NEEDSELECTDEFS
+#endif /* NEEDSELECTDEFS */
+#endif /* hp9000s500 */
+
+#ifdef NEEDSELECTDEFS
+typedef long fd_mask;
+#ifndef NBBY
+#define NBBY 8
+#endif /* NBBY */
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 32
+#endif /* FD_SETSIZE */
+#ifndef NFDBITS
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+#endif /* NFDBITS */
+#ifndef howmany
+#define howmany(x,y) (((x)+((y)-1))/(y))
+#endif /* howmany */
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+#ifndef FD_SET
+#define FD_SET(n,p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#endif /* FD_SET */
+#ifndef FD_CLR
+#define FD_CLR(n,p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#endif /* FD_CLR */
+#ifndef FD_ISSET
+#define FD_ISSET(n,p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#endif /* FD_ISSET */
+#ifndef FD_COPY
+#define FD_COPY(f,t) (bcopy(f,t,sizeof(*(f)))
+#endif /* FD_COPY */
+#ifndef FD_ZERO
+#define FD_ZERO(p) bzero((char *)(p),sizeof(*(p)))
+#endif /* FD_ZERO */
+#endif /* NEEDSELECTDEFS */
+
+/*
+ CK_NEED_SIG is defined if the system cannot check the console to
+ to see if characters are waiting. This is used during local-mode file
+ transfer to interrupt the transfer, refresh the screen display, etc.
+ If CK_NEED_SIG is defined, then file-transfer interruption characters
+ have to be preceded a special character, e.g. the SIGQUIT character.
+ CK_NEED_SIG should be defined if the conchk() function is not operational.
+*/
+#ifdef NOPOLL /* For overriding CK_POLL definition */
+#ifdef CK_POLL
+#undef CK_POLL
+#endif /* CK_POLL */
+#endif /* NOPOLL */
+
+#ifndef CK_POLL /* If we don't have poll() */
+#ifndef RDCHK /* And we don't have rdchk() */
+#ifndef SELECT /* And we don't have select() */
+#ifdef ATTSV
+#ifndef aegis
+#ifndef datageneral
+#ifndef OXOS
+#define CK_NEED_SIG
+#endif /* OXOS */
+#endif /* datageneral */
+#endif /* aegis */
+#endif /* ATTSV */
+#ifdef POSIX
+#ifndef CK_NEED_SIG
+#define CK_NEED_SIG
+#endif /* CK_NEED_SIG */
+#endif /* POSIX */
+#endif /* SELECT */
+#endif /* RDCHK */
+#endif /* CK_POLL */
+
+#ifdef HPUX /* HP-UX has select() */
+#ifdef CK_NEED_SIG
+#undef CK_NEED_SIG
+#endif /* CK_NEED_SIG */
+#endif /* HPUX */
+
+#ifdef AIXRS /* AIX has select() */
+#ifdef CK_NEED_SIG
+#undef CK_NEED_SIG
+#endif /* CK_NEED_SIG */
+#endif /* AIXRS */
+
+#ifdef BSD44 /* 4.4BSD has FIONREAD */
+#ifdef CK_NEED_SIG
+#undef CK_NEED_SIG
+#endif /* CK_NEED_SIG */
+#endif /* BSD44 */
+
+#ifdef QNX /* QNX has FIONREAD and select() */
+#ifdef CK_NEED_SIG
+#undef CK_NEED_SIG
+#endif /* CK_NEED_SIG */
+#endif /* QNX */
+
+#ifdef COHERENT
+#ifndef NOTIMEZONE
+#define NOTIMEZONE
+#endif /* NOTIMEZONE */
+#endif /* COHERENT */
+
+#ifdef UNIX
+#ifndef HAVE_TZ /* Can we use struct timezone? */
+#ifndef NOTIMEZONE
+#ifdef PTX
+#define NOTIMEZONE
+#else
+#ifndef SELECT
+#ifdef COHERENT
+#define NOTIMEZONE
+#else
+#ifdef BELLV10
+#define NOTIMEZONE
+#endif /* BELLV10 */
+#endif /* COHERENT */
+#endif /* SELECT */
+#endif /* PTX */
+#endif /* NOTIMEZONE */
+#endif /* HAVE_TZ */
+#ifndef NOTIMEVAL /* Can we use struct timeval? */
+#ifndef HAVE_TV
+#define HAVE_TV
+#endif /* HAVE_TV */
+#endif /* NOTIMEVAL */
+#ifndef NOTIMEZONE
+#ifndef HAVE_TZ
+#define HAVE_TZ
+#endif /* HAVE_TZ */
+#endif /* NOTIMEZONE */
+#endif /* UNIX */
+
+#ifdef SCO32
+#ifdef HAVE_TV
+#undef HAVE_TV
+#endif /* HAVE_TV */
+#ifdef HAVE_TZ
+#undef HAVE_TZ
+#endif /* HAVE_TZ */
+#ifndef NOTIMEVAL
+#define NOTIMEVAL
+#endif /* NOTIMEVAL */
+#ifndef NOTIMEZONE
+#define NOTIMEZONE
+#endif /* NOTIMEZONE */
+#endif /* SCO32 */
+
+#ifdef ATT7300
+#ifdef HAVE_TV
+#undef HAVE_TV
+#endif /* HAVE_TV */
+#ifdef HAVE_TZ
+#undef HAVE_TZ
+#endif /* HAVE_TZ */
+#ifndef NOTIMEVAL
+#define NOTIMEVAL
+#endif /* NOTIMEVAL */
+#ifndef NOTIMEZONE
+#define NOTIMEZONE
+#endif /* NOTIMEZONE */
+#endif /* ATT7300 */
+
+/*
+ Automatic parity detection.
+ This actually implies a lot more now: length-driven packet reading,
+ "Doomsday Kermit" IBM Mainframe file transfer through 3270 data streams, etc.
+*/
+#ifdef UNIX /* For Unix */
+#ifndef NOPARSEN
+#define PARSENSE
+#endif /* NOPARSEN */
+#endif /* UNIX */
+
+#ifdef VMS /* ... and VMS */
+#ifndef NOPARSEN
+#define PARSENSE
+#endif /* NOPARSEN */
+#ifdef __GNUC__
+#define VMSGCC
+#endif /* __GNUC__ */
+#endif /* VMS */
+
+#ifdef MAC /* and Macintosh */
+#ifndef NOPARSEN
+#define PARSENSE
+#endif /* NOPARSEN */
+#endif /* MAC */
+
+#ifdef STRATUS /* and Stratus VOS */
+#ifndef NOPARSEN
+#define PARSENSE
+#endif /* NOPARSEN */
+#endif /* STRATUS */
+
+#ifdef OS2 /* and OS/2, finally */
+#ifndef NOPARSEN
+#define PARSENSE
+#endif /* NOPARSEN */
+#endif /* OS2 */
+
+#ifndef NODYNAMIC /* DYNAMIC is default for UNIX */
+#ifndef DYNAMIC /* as of C-Kermit 7.0 */
+#ifdef UNIX
+#define DYNAMIC
+#endif /* UNIX */
+#endif /* DYNAMIC */
+#endif /* NODYNAMIC */
+
+#ifdef DYNAMIC /* If DYNAMIC is defined */
+#define DCMDBUF /* then also define this. */
+#endif /* DYNAMIC */
+
+#ifndef CK_LBRK /* Can send Long BREAK */
+
+#ifdef UNIX /* (everybody but OS-9) */
+#define CK_LBRK
+#endif /* UNIX */
+#ifdef VMS
+#define CK_LBRK
+#endif /* VMS */
+#ifdef datageneral
+#define CK_LBRK
+#endif /* datageneral */
+#ifdef GEMDOS
+#define CK_LBRK
+#endif /* GEMDOS */
+#ifdef OS2
+#define CK_LBRK
+#endif /* OS2 */
+#ifdef AMIGA
+#define CK_LBRK
+#endif /* AMIGA */
+#ifdef STRATUS
+#define CK_LBRK
+#endif /* STRATUS */
+
+#endif /* CK_LBRK */
+
+/* Carrier treatment */
+/* These are defined here because they are shared by the system dependent */
+/* and the system independent modules. */
+
+#define CAR_OFF 0 /* Off: ignore carrier always. */
+#define CAR_ON 1 /* On: heed carrier always, except during DIAL. */
+#define CAR_AUT 2 /* Auto: heed carrier, but only if line is declared */
+ /* to be a modem line, and only during CONNECT. */
+
+/* And more generically (for use with any ON/OFF/AUTO feature) */
+#define CK_OFF 0
+#define CK_ON 1
+#define CK_AUTO 2
+
+#ifndef NOLOCAL
+/*
+ Serial interface speeds available.
+
+ As of C-Kermit 6.1 there is a new method to get the supported
+ speeds, which obviates the need for all the craziness below. At runtime,
+ just call the new ttspdlist() routine to get a list of supported speeds.
+ Then the user interface module can build a keyword table or menu from it.
+*/
+#ifndef TTSPDLIST
+#ifdef UNIX /* For now, only for UNIX */
+#ifndef OLINUXHISPEED /* But not systems with hacks for */
+#ifndef MINIX /* high speeds, like 110 = 115200 */
+#define TTSPDLIST
+#endif /* MINIX */
+#endif /* OLINUXHISPEED */
+#else
+#ifdef VMS
+#define TTSPDLIST /* VMS gets it too */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* TTSPDLIST */
+
+#ifndef NODIAL /* Hangup by modem command */
+#ifndef NOMDMHUP
+#ifndef MDMHUP
+#define MDMHUP
+#endif /* MDMHUP */
+#endif /* NOMDMHUP */
+#endif /* NODIAL */
+
+#ifdef NOSPL
+#ifndef NOLOGDIAL /* Connection log needs mjd(), etc. */
+#define NOLOGDIAL
+#endif /* NOLOGDIAL */
+#endif /* NOSPL */
+
+#ifdef pdp11
+#define NOLOGDIAL
+#endif /* pdp11 */
+
+#ifndef NOLOGDIAL /* Connection log */
+#ifndef CXLOGFILE
+#define CXLOGFILE "CX.LOG" /* Default connection log file name */
+#endif /* CXLOGFILE */
+#ifndef CKLOGDIAL
+#ifndef CK_SMALL
+#define CKLOGDIAL
+#define CXLOGBUFL 1024 /* Connection log record buffer size */
+#endif /* CK_SMALL */
+#endif /* NOLOGDIAL */
+#endif /* CKLOGDIAL */
+
+#endif /* NOLOCAL */
+
+#ifdef NOTTSPDLIST /* Except if NOTTSPDLIST is defined */
+#ifdef TTSPDLIST
+#undef TTSPDLIST
+#endif /* TTSPDLIST */
+#endif /* NOTTSPDLIST */
+
+#ifdef TTSPDLIST
+
+_PROTOTYP( long * ttspdlist, (void) );
+
+#else /* TTSPDLIST not defined */
+/*
+ We must use a long and convoluted series of #ifdefs that have to be kept in
+ sync with the code in the ck?tio.c module.
+
+ We assume that everybody supports: 0, 110, 300, 600, 1200, 2400, 4800, and
+ 9600 bps. Symbols for other speeds are defined here. You can also add
+ definitions on the CC command lines. These definitions affect the SET SPEED
+ keyword table, and are not necessarily usable in the system-dependent
+ speed-setting code in the ck?tio.c modules, which depends on system-specific
+ symbols like (in UNIX) B19200. In other words, just defining it doesn't
+ mean it'll work -- you also have to supply the supporting code in ttsspd()
+ and ttgspd() in ck?tio.c.
+
+ The symbols have the form BPS_xxxx, where xxxx is the speed in bits per
+ second, or (for bps values larger than 9999) thousands of bps followed by K.
+ The total symbol length should be 8 characters or less. Some values are
+ enabled automatically below. You can disable a particular value by defining
+ NOB_xxxx on the CC command line.
+
+*/
+
+#ifndef NOB_50
+#define BPS_50 /* 50 bps */
+#endif
+
+#ifndef NOB_75
+#define BPS_75 /* 75 bps */
+#endif
+
+#ifndef NOB7512
+#ifdef ANYBSD
+#define BPS_7512 /* 75/1200 Split Speed */
+#endif /* ANYBSD */
+#endif /* NOB7512 */
+
+#ifndef NOB134
+#ifdef SOLARIS25
+#define BPS_134
+#else
+#undef BPS_134 /* 134.5 bps (IBM 2741) */
+#endif /* BPS_134 */
+#endif /* NOB134 */
+
+#ifndef NOB_150
+#define BPS_150 /* 150 bps */
+#endif
+
+#ifndef NOB_200
+#define BPS_200 /* 200 bps */
+#endif
+
+#ifndef NOB_1800
+#ifdef MAC
+#define BPS_1800 /* 1800 bps */
+#else
+#ifdef SOLARIS25
+#define BPS_1800
+#endif
+#endif
+#endif
+
+#ifndef NOB_3600
+#ifndef SOLARIS25
+#define BPS_3600 /* 3600 bps */
+#endif
+#endif
+
+#ifndef NOB_7200
+#ifndef SOLARIS25
+#define BPS_7200 /* 7200 bps */
+#endif /* SOLARIS25 */
+#endif
+
+#ifndef NOB_14K
+#ifdef BSD44
+#define BPS_14K /* 14400 bps */
+#else
+#ifdef OS2
+#define BPS_14K
+#else
+#ifdef NEXT
+#define BPS_14K
+#else
+#ifdef MAC
+#define BPS_14K
+#else
+#ifdef AMIGA
+#define BPS_14K
+#endif /* AMIGA */
+#endif /* MAC */
+#endif /* NEXT */
+#endif /* OS2 */
+#endif /* BSD44 */
+#endif /* NOB_14K */
+
+#ifndef NOB_19K
+#define BPS_19K /* 19200 bps */
+#endif
+
+#ifndef NOB_28K
+#ifdef BSD44
+#define BPS_28K
+#else
+#ifdef OS2
+#define BPS_28K
+#else
+#ifdef NEXT
+#define BPS_28K /* 28800 bps */
+#else
+#ifdef MAC
+#define BPS_28K /* 28800 bps */
+#endif /* MAC */
+#endif /* NEXT */
+#endif /* OS2 */
+#endif /* BSD44 */
+#endif /* NOB_28K */
+
+#ifndef NOB_38K
+#define BPS_38K /* 38400 bps */
+#endif
+
+#ifndef NOB_57K
+#ifdef Plan9
+#define BPS_57K
+#else
+#ifdef SOLARIS25
+#define BPS_57K
+#else
+#ifdef VMS
+#define BPS_57K /* 57600 bps */
+#else
+#ifdef OS2
+#define BPS_57K
+#else
+#ifdef __linux__
+#define BPS_57K
+#else
+#ifdef HPUX
+#define BPS_57K
+#else
+#ifdef NEXT
+#define BPS_57K
+#else
+#ifdef __386BSD__
+#define BPS_57K
+#else
+#ifdef __FreeBSD__
+#define BPS_57K
+#else
+#ifdef __NetBSD__
+#define BPS_57K
+#else
+#ifdef MAC
+#define BPS_57K
+#else
+#ifdef QNX
+#define BPS_57K
+#else
+#ifdef BEOSORBEBOX
+#define BPS_57K
+#else
+#ifdef IRIX62
+#define BPS_57K
+#else
+#ifdef SCO_OSR504
+#define BPS_57K
+#else
+#ifdef BSDI2
+#define BPS_57K
+#endif /* BSDI2 */
+#endif /* SCO_OSR504 */
+#endif /* IRIX62 */
+#endif /* BEOSORBEBOX */
+#endif /* QNX */
+#endif /* MAC */
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+#endif /* __386BSD__ */
+#endif /* NEXT */
+#endif /* HPUX */
+#endif /* __linux__ */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* SOLARIS25 */
+#endif /* Plan9 */
+#endif /* NOB_57K */
+
+#ifndef NOB_76K
+#ifdef BSDI2
+#define BPS_76K
+#endif /* BSDI2 */
+#ifdef Plan9
+#define BPS_76K
+#endif /* Plan9 */
+#ifdef SOLARIS25
+#define BPS_76K
+#endif /* SOLARIS25 */
+#ifdef VMS
+#define BPS_76K /* 76800 bps */
+#endif /* VMS */
+#ifdef OS2
+#ifdef __32BIT__
+#define BPS_76K
+#endif /* __32BIT__ */
+#endif /* OS2 */
+#ifdef QNX
+#define BPS_76K
+#endif /* QNX */
+#ifdef IRIX62
+#define BPS_76K
+#endif /* IRIX62 */
+#ifdef SCO_OSR504
+#define BPS_76K
+#endif /* SCO_OSR504 */
+#endif /* NOB_76K */
+
+#ifndef NOB_115K
+#ifdef BSDI2
+#define BPS_115K
+#endif /* BSDI2 */
+#ifdef Plan9
+#define BPS_115K
+#endif /* Plan9 */
+#ifdef SOLARIS25
+#define BPS_115K
+#endif /* SOLARIS25 */
+#ifdef VMS
+#define BPS_115K /* 115200 bps */
+#else
+#ifdef QNX
+#define BPS_115K
+#else
+#ifdef HPUX
+#define BPS_115K
+#else
+#ifdef __linux__
+#define BPS_115K
+#else
+#ifdef __386BSD__
+#define BPS_115K
+#else
+#ifdef __FreeBSD__
+#define BPS_115K
+#else
+#ifdef __NetBSD__
+#define BPS_115K
+#else
+#ifdef OS2
+#ifdef __32BIT__
+#define BPS_115K
+#endif /* __32BIT__ */
+#else
+#ifdef BEOSORBEBOX
+#define BPS_115K
+#else
+#ifdef IRIX62
+#define BPS_115K
+#else
+#ifdef SCO_OSR504
+#define BPS_115K
+#endif /* SCO_OSR504 */
+#endif /* IRIX62 */
+#endif /* BEOSORBEBOX */
+#endif /* OS2 */
+#endif /* __NetBSD__ */
+#endif /* __FreeBSD__ */
+#endif /* __386BSD__ */
+#endif /* __linux__ */
+#endif /* HPUX */
+#endif /* QNX */
+#endif /* VMS */
+#endif /* NOB_115K */
+
+#ifndef NOB_230K /* 230400 bps */
+#ifdef BSDI2
+#define BPS_230K
+#else
+#ifdef SCO_OSR504
+#define BPS_230K
+#else
+#ifdef __linux__
+#define BPS_230K
+#else
+#ifdef SOLARIS25
+#define BPS_230K
+#else
+#ifdef OS2
+#ifdef __32BIT__
+#define BPS_230K
+#endif /* __32BIT__ */
+#else
+#undef BPS_230K
+#endif /* OS2 */
+#endif /* SOLARIS25 */
+#endif /* __linux__ */
+#endif /* SCO_OSR504 */
+#endif /* BSDI2 */
+#endif /* NOB_230K */
+
+#ifndef NOB_460K /* 460800 bps */
+#ifdef SCO_OSR504
+#define BPS_460K
+#else
+#ifdef __linux__
+#define BPS_460K
+#else
+#ifdef OS2
+#ifdef __32BIT__
+#define BPS_460K
+#endif /* __32BIT__ */
+#else
+#undef BPS_460K
+#endif /* __linux__ */
+#endif /* SCO_OSR504 */
+#endif /* OS2 */
+#endif /* NOB_460K */
+
+#ifndef NOB_921K /* 921600 bps */
+#ifdef SCO_OSR504
+#define BPS_921K
+#endif /* SCO_OSR504 */
+#endif /* NOB_921K */
+
+#ifdef BPS_921K /* Maximum speed defined */
+#define MAX_SPD 921600L
+#else
+#ifdef BPS_460K
+#define MAX_SPD 460800L
+#else
+#ifdef BPS_230K
+#define MAX_SPD 230400L
+#else
+#ifdef BPS_115K
+#define MAX_SPD 115200L
+#else
+#ifdef BPS_76K
+#define MAX_SPD 76800L
+#else
+#ifdef BPS_57K
+#define MAX_SPD 57600L
+#else
+#ifdef BPS_38K
+#define MAX_SPD 38400L
+#else
+#ifdef BPS_28K
+#define MAX_SPD 28800L
+#else
+#ifdef BPS_19K
+#define MAX_SPD 19200L
+#else
+#ifdef BPS_14K
+#define MAX_SPD 14400L
+#else
+#define MAX_SPD 9600L
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif /* TTSPDLIST */
+
+#ifndef CONGSPD /* Systems that can call congspd() */
+#ifdef UNIX
+#define CONGSPD
+#endif /* UNIX */
+#ifdef VMS
+#define CONGSPD
+#endif /* VMS */
+#ifdef STRATUS
+#define CONGSPD
+#endif /* STRATUS */
+#endif /* CONGSPD */
+
+/* Types of flow control available */
+
+#define CK_XONXOFF /* Everybody can do this, right? */
+
+#ifdef AMIGA /* Commodore Amiga */
+#define CK_RTSCTS /* has RTS/CTS */
+#endif /* AMIGA */
+
+#ifdef SUN4S5 /* SunOS in System V environment */
+#define CK_RTSCTS
+#else /* SunOS 4.0/4.1 in BSD environment */
+#ifdef SUNOS4 /* SunOS 4.0+later supports RTS/CTS */
+#ifdef SUNOS41 /* Easy in 4.1 and later */
+#define CK_RTSCTS
+#else /* Harder in 4.0 */
+#ifndef __GNUC__ /* (see tthflow() in ckutio.c) */
+#ifndef GNUC
+#define CK_RTSCTS /* Only if not using GNU gcc */
+#endif /* __GNUC__ */
+#endif /* GNUC */
+#endif /* SUNOS41 */
+#endif /* SUNOS4 */
+#endif /* SUN4S5 */
+
+#ifdef BSD44 /* And in 4.4 BSD, including BSDI */
+#define CK_RTSCTS
+#endif /* BSD44 */
+
+#ifdef TERMIOX /* Sys V R4 <termiox.h> */
+#ifndef CK_RTSCTS
+#define CK_RTSCTS
+#endif /* CK_RTSCTS */
+#ifndef CK_DTRCD
+#define CK_DTRCD
+#endif /* CK_DTRCD */
+#else
+#ifdef STERMIOX /* Sys V R4 <sys/termiox.h> */
+#ifndef CK_RTSCTS
+#define CK_RTSCTS
+#endif /* CK_RTSCTS */
+#ifndef CK_DTRCD
+#define CK_DTRCD
+#endif /* CK_DTRCD */
+#endif /* STERMIOX */
+#endif /* TERMIOX */
+
+#ifdef OXOS /* Olivetti X/OS R2 struct termios */
+#define CK_RTSCTS /* Ditto. */
+#define CK_DTRCD
+#endif /* OXOS */
+
+#ifdef AIXRS /* RS/6000 with AIX 3.x */
+#define CK_RTSCTS /* Has its own peculiar method... */
+#endif /* AIXRS */
+
+#ifdef __linux__ /* Linux */
+#define CK_RTSCTS
+#endif /* __linux__ */
+/*
+ Hardware flow control is not defined in POSIX.1. Nevertheless, a certain
+ style API for hardware flow control, using tcsetattr() and the CRTSCTS
+ bit(s), seems to be gaining currency on POSIX-based UNIX systems. The
+ following code defines the symbol POSIX_CRTSCTS for such systems.
+*/
+#ifdef CK_RTSCTS
+#ifdef __bsdi__ /* BSDI, a.k.a. BSD/386 */
+#define POSIX_CRTSCTS
+#endif /* __bsdi__ */
+#ifdef __linux__ /* Linux */
+#define POSIX_CRTSCTS
+#endif /* __linux__ */
+#ifdef __NetBSD__ /* NetBSD */
+#define POSIX_CRTSCTS
+#endif /* __NetBSD__ */
+#ifdef __OpenBSD__
+#define POSIX_CRTSCTS
+#endif /* __OpenBSD__ */
+#ifdef BEOSORBEBOX /* BeBOX */
+#define POSIX_CRTSCTS
+/* BEBOX defines CRTSFL as (CTSFLOW & RTSFLOW) */
+#define CRTSCTS CRTSFL
+#endif /* BEOSORBEBOX */
+#ifdef IRIX52 /* IRIX 5.2 and later */
+#define POSIX_CRTSCTS
+#define CRTSCTS CNEW_RTSCTS /* See <sys/termios.h> */
+#endif /* IRIX52 */
+#endif /* CK_RTSCTS */
+
+/* Implementations that have implemented the ttsetflow() function. */
+
+#ifndef CK_TTSETFLOW
+#ifdef UNIX
+#define CK_TTSETFLOW
+#endif /* UNIX */
+#ifdef OS2
+#define CK_TTSETFLOW
+#endif /* OS2 */
+#endif /* CK_TTSETFLOW */
+
+#ifdef CK_TTSETFLOW
+_PROTOTYP( int ttsetflow, (int) );
+#endif /* CK_TTSETFLOW */
+/*
+ Systems where we can expand tilde at the beginning of file or directory names
+*/
+#ifdef POSIX
+#ifndef DTILDE
+#define DTILDE
+#endif /* DTILDE */
+#endif /* POSIX */
+#ifdef BSD4
+#ifndef DTILDE
+#define DTILDE
+#endif /* DTILDE */
+#endif /* BSD4 */
+#ifdef ATTSV
+#ifndef DTILDE
+#define DTILDE
+#endif /* DTILDE */
+#endif /* ATTSV */
+#ifdef OSK
+#ifndef DTILDE
+#define DTILDE
+#endif /* DTILDE */
+#endif /* OSK */
+#ifdef HPUX /* I don't know why this is */
+#ifndef DTILDE /* necessary, since -DHPUX */
+#define DTILDE /* automatically defines ATTSV */
+#endif /* DTILDE */ /* (see above) ... */
+#endif /* HPUX */
+
+/*
+ This is mainly for the benefit of ckufio.c (UNIX and OS/2 file support).
+ Systems that have an atomic rename() function, so we don't have to use
+ link() and unlink().
+*/
+#ifdef POSIX
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* POSIX */
+
+#ifdef OS2
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* OS2 */
+
+#ifdef SUNOS41
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* SUNOS41 */
+
+#ifdef SVR4
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* SVR4 */
+
+#ifdef AIXRS
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* AIXRS */
+
+#ifdef BSD44
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* BSD44 */
+
+#ifdef NORENAME /* Allow for compile-time override */
+#ifdef RENAME
+#undef RENAME
+#endif /* RENAME */
+#endif /* NORENAME */
+
+#ifdef STRATUS /* Stratus VOS */
+#ifndef RENAME
+#define RENAME
+#endif /* RENAME */
+#endif /* STRATUS */
+
+/* Line delimiter for text files */
+
+/*
+ If the system uses a single character for text file line delimitation,
+ define NLCHAR to the value of that character. For text files, that
+ character will be converted to CRLF upon output, and CRLF will be converted
+ to that character on input during text-mode (default) packet operations.
+*/
+#ifdef MAC /* Macintosh */
+#define NLCHAR 015
+#else
+#ifdef OSK /* OS-9/68K */
+#define NLCHAR 015
+#else /* All Unix-like systems */
+#define NLCHAR 012
+#endif /* OSK */
+#endif /* MAC */
+
+/*
+ At this point, if there's a system that uses ordinary CRLF line
+ delimitation AND the C compiler actually returns both the CR and
+ the LF when doing input from a file, then #undef NLCHAR.
+*/
+#ifdef OS2 /* OS/2 */
+#undef NLCHAR
+#endif /* OS2 */
+
+#ifdef GEMDOS /* Atari ST */
+#undef NLCHAR
+#endif /* GEMDOS */
+
+/*
+ VMS file formats are so complicated we need to do all the conversion
+ work in the CKVFIO module, so we tell the rest of C-Kermit not to fiddle
+ with the bytes.
+*/
+
+#ifdef vms
+#undef NLCHAR
+#endif /* vms */
+
+/* The device name of a job's controlling terminal */
+/* Special for VMS, same for all Unixes (?), not used by Macintosh */
+
+#ifdef BEOS
+#define CTTNAM dftty
+#else
+#ifdef vms
+#define CTTNAM "SYS$INPUT:" /* (4 Jan 2002) Was TT: */
+#else
+#ifdef datageneral
+#define CTTNAM "@output"
+#else
+#ifdef OSK
+extern char myttystr[];
+#define CTTNAM myttystr
+#else
+#ifdef OS2
+#define CTTNAM "con"
+#else
+#ifdef UNIX
+#define CTTNAM "/dev/tty"
+#else
+#ifdef GEMDOS
+#define CTTNAM "aux:"
+#else
+#ifdef STRATUS
+extern char myttystr[];
+#define CTTNAM myttystr
+#else /* Anyone else... */
+#define CTTNAM "stdout" /* This is a kludge used by Mac */
+#endif /* STRATUS */
+#endif /* GEMDOS */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* OSK */
+#endif /* datageneral */
+#endif /* vms */
+#endif /* BEOS */
+
+#ifndef HAVECTTNAM
+#ifdef UNIX
+#define HAVECTTNAM
+#else
+#ifdef VMS
+#define HAVECTTNAM
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* HAVECTTNAM */
+
+#ifndef ZFCDAT /* zfcdat() function available? */
+#ifdef UNIX
+#define ZFCDAT
+#else
+#ifdef STRATUS
+#define ZFCDAT
+#else
+#ifdef GEMDOS
+#define ZFCDAT
+#else
+#ifdef AMIGA
+#define ZFCDAT
+#else
+#ifdef OS2
+#define ZFCDAT
+#else
+#ifdef datageneral
+#define ZFCDAT
+#else
+#ifdef VMS
+#define ZFCDAT
+#endif /* VMS */
+#endif /* datageneral */
+#endif /* OS2 */
+#endif /* AMIGA */
+#endif /* GEMDOS */
+#endif /* STRATUS */
+#endif /* UNIX */
+#endif /* ZFCDAT */
+
+#ifdef SUNS4S5
+#define tolower _tolower
+#define toupper _toupper
+#endif /* SUNS4S5 */
+
+/* Error number */
+
+#ifdef _CRAY
+#ifdef _CRAYCOM /* Cray Computer Corp. */
+extern int errno;
+#else /* _CRAYCOM */
+#include <errno.h> /* Cray Research UNICOS defines */
+ /* errno as a function. */
+#endif /* _CRAYCOM */ /* OK for UNICOS 6.1 and 7.0. */
+#else /* _CRAY */
+#ifdef STRATUS /* Stratus VOS */
+#include <errno.h>
+#else /* not STRATUS */
+#ifndef VMS
+#ifndef OS2
+#ifdef __GLIBC__
+/*
+ "glibc uses threads, kermit uses glibc; errno access is in Thread Local
+ Storage (TLS) from glibc-3.2.2. ...a thread specific errno is being run in
+ thread local storage relative to the %gs segment register, so some means to
+ revector gets/puts needs to be done." - Jeff Johnson, Red Hat, Feb 2003.
+*/
+#include <errno.h>
+#else
+/*
+ The following declaration would cause problems for VMS and OS/2, in which
+ errno is an "extern volatile int noshare"...
+*/
+ extern int errno; /* Needed by most modules. */
+#endif /* __GLIBC__ */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* STRATUS */
+#endif /* _CRAY */
+
+#ifdef pdp11 /* Try to make some space on PDP-11 */
+#ifndef NODIAL
+#define NODIAL
+#endif /* NODIAL */
+#ifndef NOCURSES
+#define NOCURSES
+#endif /* NOCURSES */
+#ifndef NOBIGBUF
+#define NOBIGBUF
+#endif /* NOBIGBUF */
+#endif /* pdp11 */
+
+#ifndef NOBIGBUF
+#ifndef BIGBUFOK /* Platforms with lots of memory */
+
+#ifdef QNX /* QNX */
+#ifndef QNX16 /* But not 16-bit versions */
+#define BIGBUFOK
+#endif /* QNX16 */
+#endif /* QNX */
+
+#ifdef BSD44
+#define BIGBUFOK
+#endif /* BSD44 */
+
+#ifdef STRATUS /* Stratus VOS */
+#define BIGBUFOK
+#endif /* STRATUS */
+
+#ifdef sparc /* SPARC processors */
+#define BIGBUFOK
+#endif /* sparc */
+
+#ifdef mips /* MIPS processors */
+#define BIGBUFOK
+#endif /* mips */
+
+#ifdef HPUX9 /* HP-UX 9.x */
+#define BIGBUFOK
+#endif /* HPUX9 */
+
+#ifdef HPUX10 /* HP-UX 10.0 PA-RISC */
+#define BIGBUFOK
+#endif /* HPUX10 */
+
+#ifdef NEXT /* NeXTSTEP */
+#ifdef mc68000 /* on NEXT platforms... */
+#define BIGBUFOK
+#endif /* mc68000 */
+#endif /* NEXT */
+
+#ifdef LINUX /* Linux in 1998 should be OK */
+#ifndef BIGBUFOK
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* LINUX */
+
+#ifdef OS2 /* 32-bit OS/2 2.x and above */
+#ifdef __32BIT__
+#define BIGBUFOK
+#endif /* __32BIT__ */
+#ifdef NT
+#define BIGBUFOK
+#endif /* NT */
+#endif /* OS2 */
+
+#ifdef Plan9 /* Plan 9 is OK */
+#define BIGBUFOK
+#endif /* Plan9 */
+
+#ifdef VMS /* Any VMS is OK */
+#ifndef BIGBUFOK
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* VMS */
+
+#ifdef __alpha /* DEC 64-bit Alpha, e.g. OSF/1 */
+#ifndef BIGBUFOK /* Might already be defined for VMS */
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* __alpha */
+
+#ifdef sgi /* SGI with IRIX 4.0 or later */
+#ifndef BIGBUFOK
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* sgi */
+
+#ifdef AIXRS /* AIX on RISC */
+#define BIGBUFOK
+#endif /* AIXRS */
+
+#ifdef CK_SCOV5 /* SCO OSR5 */
+#ifndef BIGBUFOK
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* CK_SCOV5 */
+
+#ifdef SOLARIS /* Solaris x86 */
+#ifndef BIGBUFOK
+#define BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* SOLARIS */
+
+#endif /* BIGBUFOK */
+#endif /* NOBIGBUF */
+
+#ifdef CK_SMALL
+#ifdef BIGBUFOK
+#undef BIGBUFOK
+#endif /* BIGBUFOK */
+#endif /* CK_SMALL */
+
+/* If "memory is no problem" then this improves performance */
+
+#ifdef DEBUG
+#ifdef BIGBUFOK
+#ifndef IFDEBUG
+#define IFDEBUG
+#endif /* IFDEBUG */
+#endif /* BIGBUFOK */
+#endif /* DEBUG */
+
+#ifndef DEBUG
+/* Compile all the debug() statements away. Saves a lot of space and time. */
+#define debug(a,b,c,d)
+#define hexdump(a,b,c)
+/* Now define the debug() macro. */
+#else /* DEBUG */
+_PROTOTYP(int dodebug,(int,char *,char *,long));
+_PROTOTYP(int dohexdump,(CHAR *,CHAR *,int));
+#ifdef IFDEBUG
+/* Use this form to avoid function calls: */
+#ifdef COMMENT
+#define debug(a,b,c,d) if (deblog) dodebug(a,b,(char *)(c),(long)d)
+#define hexdump(a,b,c) if (deblog) dohexdump((CHAR *)(a),(CHAR *)(b),c)
+#else
+#ifdef CK_ANSIC
+#define debug(a,b,c,d) ((void)(deblog?dodebug(a,b,(char *)(c),(long)d):0))
+#define hexdump(a,b,c) ((void)(deblog?dohexdump((CHAR *)(a),(CHAR *)(b),c):0))
+#else
+#define debug(a,b,c,d) (deblog?dodebug(a,b,(char *)(c),(long)d):0)
+#define hexdump(a,b,c) (deblog?dohexdump((CHAR *)(a),(CHAR *)(b),c):0)
+#endif /* CK_ANSIC */
+#endif /* COMMENT */
+#else /* IFDEBUG */
+/* Use this form to save space: */
+#define debug(a,b,c,d) dodebug(a,b,(char *)(c),(long)d)
+#define hexdump(a,b,c) dohexdump((CHAR *)(a),(CHAR *)(b),c)
+#endif /* IFDEBUG */
+#endif /* DEBUG */
+
+/* File System Defaults */
+
+#ifndef UIDBUFLEN /* Length of User ID */
+#ifdef OS2
+#define UIDBUFLEN 256
+#else /* OS2 */
+#ifdef BIGBUFOK
+#define UIDBUFLEN 256
+#else
+#define UIDBUFLEN 64
+#endif /* BIGBUFOK */
+#endif /* OS2 */
+#endif /* UIDBUFLEN */
+
+#ifdef UNIX
+#ifdef PROVX1
+#define MAXWLD 50
+#else
+#ifdef pdp11
+#define MAXWLD 50
+#else
+#ifdef BIGBUFOK
+#define MAXWLD 102400
+#else
+#define MAXWLD 1024
+#endif /* BIGBUFOK */
+#endif /* pdp11 */
+#endif /* PROVX1 */
+#else
+#ifdef VMS
+#define MAXWLD 102400 /* Maximum wildcard filenames */
+#else
+#ifdef datageneral
+#define MAXWLD 500
+#else
+#ifdef STRATUS
+#define MAXWLD 5000
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIX */
+
+#ifdef VMS
+#define DBLKSIZ 512
+#define DLRECL 512
+#else
+#define DBLKSIZ 0
+#define DLRECL 0
+#endif /* VMS */
+
+/* Communication device / network host name length */
+
+#ifdef BIGBUFOK
+#define TTNAMLEN 512
+#else
+#ifdef MAC
+#define TTNAMLEN 256
+#else
+#ifndef CK_SMALL
+#define TTNAMLEN 128
+#else
+#define TTNAMLEN 80
+#endif /* CK_SMALL */
+#endif /* MAC */
+#endif /* BIGBUFOK */
+
+/* Program return codes for DECUS C and UNIX (VMS uses UNIX codes) */
+
+#ifdef decus
+#define GOOD_EXIT IO_NORMAL
+#define BAD_EXIT IO_ERROR
+#else
+#define GOOD_EXIT 0
+#define BAD_EXIT 1
+#endif /* decus */
+
+/* Special hack for Fortune, which doesn't have <sys/file.h>... */
+
+#ifdef FT18
+#define FREAD 0x01
+#define FWRITE 0x10
+#endif /* FT18 */
+
+/* Special hack for OS-9/68k */
+#ifdef OSK
+#ifndef _UCC
+#define SIGALRM 30 /* May always cancel I/O */
+#endif /* _UCC */
+#define SIGARB 1234 /* Arbitrary for I/O */
+SIGTYP (*signal())();
+#endif /* OSK */
+
+#ifdef MINIX
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(c) (putc(c,stdout)!=EOF)&&fflush(stdout)
+#endif /* MINIX */
+
+#ifdef datageneral /* Data General AOS/VS */
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(c) conoc(c)
+#endif /* datageneral */
+
+/* Escape/quote character used by the command parser */
+
+#define CMDQ '\\'
+
+/* Symbols for RS-232 modem signals */
+
+#define KM_FG 1 /* Frame ground */
+#define KM_TXD 2 /* Transmit */
+#define KM_RXD 3 /* Receive */
+#define KM_RTS 4 /* Request to Send */
+#define KM_CTS 5 /* Clear to Send */
+#define KM_DSR 6 /* Data Set Ready */
+#define KM_SG 7 /* Signal ground */
+#define KM_DCD 8 /* Carrier Detect */
+#define KM_DTR 20 /* Data Terminal Ready */
+#define KM_RI 22 /* Ring Indication */
+
+/* Bit mask values for modem signals */
+
+#define BM_CTS 0001 /* Clear to send (From DCE) */
+#define BM_DSR 0002 /* Dataset ready (From DCE) */
+#define BM_DCD 0004 /* Carrier (From DCE) */
+#define BM_RNG 0010 /* Ring Indicator (From DCE) */
+#define BM_DTR 0020 /* Data Terminal Ready (From DTE) */
+#define BM_RTS 0040 /* Request to Send (From DTE) */
+
+/* Codes for full duplex flow control */
+
+#define FLO_NONE 0 /* None */
+#define FLO_XONX 1 /* Xon/Xoff (soft) */
+#define FLO_RTSC 2 /* RTS/CTS (hard) */
+#define FLO_DTRC 3 /* DTR/CD (hard) */
+#define FLO_ETXA 4 /* ETX/ACK (soft) */
+#define FLO_STRG 5 /* String-based (soft) */
+#define FLO_DIAL 6 /* DIALing kludge */
+#define FLO_DIAX 7 /* Cancel dialing kludge */
+#define FLO_DTRT 8 /* DTR/CTS (hard) */
+#define FLO_KEEP 9 /* Keep, i.e. don't touch or change */
+#define FLO_AUTO 10 /* Figure out automatically */
+
+/* Types of connections */
+
+#define CXT_REMOTE 0 /* Remote mode - no connection */
+#define CXT_DIRECT 1 /* Direct serial connection */
+#define CXT_MODEM 2 /* Modem dialout */
+#define CXT_TCPIP 3 /* TCP/IP - Telnet, Rlogin, etc */
+#define CXT_X25 4 /* X.25 peer-to-peer */
+#define CXT_DECNET 5 /* DECnet (CTERM, etc) */
+#define CXT_LAT 6 /* LAT */
+#define CXT_NETBIOS 7 /* NETBIOS */
+#define CXT_NPIPE 8 /* Named Pipe */
+#define CXT_PIPE 9 /* Pipe, Command, PTY, DLL, etc */
+#define CXT_SSH 10 /* SSH */
+#define CXT_MAX 10 /* Highest connection type */
+
+/* Autodownload Detection Options */
+
+#define ADL_PACK 0 /* Auto-Download detect packet */
+#define ADL_STR 1 /* Auto-Download detect string */
+
+/* And finally... */
+
+#ifdef COMMENT /* Make sure this is NOT defined! */
+#undef COMMENT
+#endif /* COMMENT */
+
+/* zstr zattr filinfo were here (moved to top for DECC 5 Jun 2000) */
+
+#ifndef ZFNQFP /* Versions that have zfnqfp() */
+#ifdef UNIX
+#define ZFNQFP
+#else
+#ifdef VMS
+#define ZFNQFP
+#else
+#ifdef OS2
+#define ZFNQFP
+#else
+#ifdef datageneral
+#define ZFNQFP
+#else
+#ifdef STRATUS
+#define ZFNQFP
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+struct zfnfp {
+ int len; /* Length of full pathname */
+ char * fpath; /* Pointer to full pathname */
+ char * fname; /* Pointer to name part */
+};
+#endif /* ZFNQFP */
+
+/* Systems that support FILE TYPE LABELED */
+
+#ifdef VMS
+#define CK_LABELED
+#else
+#ifdef OS2
+#ifdef __32BIT__
+#ifndef NT
+#define CK_LABELED
+#endif /* NT */
+#endif /* __32BIT__ */
+#endif /* OS2 */
+#endif /* VMS */
+
+/* LABELED FILE options bitmask */
+
+#ifdef VMS /* For VMS */
+#define LBL_NAM 1 /* Ignore incoming name if set */
+#define LBL_PTH 2 /* Use complete path if set */
+#define LBL_ACL 4 /* Preserve ACLs if set */
+#define LBL_BCK 8 /* Preserve backup date if set */
+#define LBL_OWN 16 /* Preserve ownership if set */
+
+#else
+
+#ifdef OS2 /* Ditto for OS/2 */
+#define LBL_NOR 0x0000 /* Normal file */
+#define LBL_ARC 0x0020 /* Archive */
+#define LBL_DIR 0x0010 /* Directory */
+#define LBL_HID 0x0002 /* Hidden file */
+#define LBL_RO 0x0001 /* Read only file */
+#define LBL_SYS 0x0004 /* System file */
+#define LBL_EXT 0x0040 /* Extended */
+#endif /* OS2 */
+#endif /* VMS */
+
+/*
+ Data types. First the header file for data types so we can pick up the
+ types used for pids, uids, and gids. Override this section by putting
+ -DCKTYP_H=xxx on the command line to specify the header file where your
+ system defines these types.
+*/
+#ifndef STRATUS
+#ifdef __ALPHA
+#ifdef MULTINET
+#define CK_TGV_AXP
+#endif /* MULTINET */
+#endif /* __ALPHA */
+
+#ifdef CK_TGV_AXP /* Alpha, VMS, MultiNet */
+/*
+ Starting in DECC 5.0, <stdlib.h> no longer includes <types.h>.
+ But before that an elaborate workaround is required, which results in
+ including <types.h> sometimes but not others, evidently depending on whether
+ <types.h> protects itself against multiple inclusion, which in turn probably
+ differentiates between DECC <types.h> and TGV <types.h>. Unfortunately I
+ don't remember the details. (fdc, 25 Oct 96)
+*/
+#ifdef COMMENT
+/*
+ Previously the test here was for DEC version prior to 4.0, but since the
+ test involved an "#if" statement, it was not portable and broke some non-VMS
+ builds. In any case, condition was never satisfied, so the result of
+ commenting this section out is the same as the previous "#if" condition.
+*/
+#ifndef __TYPES_LOADED
+#define __TYPES_LOADED /* Work around bug in .h files */
+#endif /* __TYPES_LOADED */
+#endif /* COMMENT */
+#include <sys/types.h>
+#ifdef IF_DOT_H
+#ifndef MULTINET
+#include <if.h> /* Needed to put up u_int typedef */
+#endif /* MULTINET */
+#else /* IF_DOT_H */
+#ifdef NEEDUINT
+typedef unsigned int u_int;
+#endif /* NEEDUINT */
+#endif /* IF_DOT_H */
+#else /* !CK_TGV_AXP */
+#ifdef OSK /* OS-9 */
+#include <types.h>
+#else /* General case, not OS-9 */
+#ifndef CKTYP_H
+#ifndef VMS
+#ifndef MAC
+#ifndef AMIGA
+#define CKTYP_H <sys/types.h>
+#endif /* AMIGA */
+#endif /* MAC */
+#endif /* VMS */
+#endif /* CKTYP_H */
+
+#ifdef GEMDOS
+#undef CKTYP_H
+#include <types.h>
+#endif /* GEMDOS */
+
+#ifdef OS2
+#undef CKTYP_H
+#include <sys/types.h>
+#endif /* OS2 */
+
+#ifdef CKTYP_H /* Include it. */
+#ifdef COHERENT /* Except for COHERENT */
+#include <unistd.h>
+#include <sys/types.h>
+#else
+#ifdef datageneral /* AOS/VS */
+#include <sys/types.h>
+#else /* All others */
+#ifdef __bsdi__ /* BSDI */
+#ifdef POSIX
+#undef _POSIX_SOURCE
+#endif /* POSIX */
+#endif /* __bsdi__ */
+#include CKTYP_H
+#ifdef __bsdi__
+#ifdef POSIX
+#define _POSIX_SOURCE
+#endif /* POSIX */
+#endif /* __bsdi__ */
+#endif /* datageneral */
+#endif /* COHERENT */
+#endif /* CKTYP_H */
+
+#endif /* OSK */
+#endif /* CK_TGV_AXP */
+#endif /* STRATUS */ /* End of types.h section */
+
+/*
+ Data type for pids. If your system uses a different type, put something
+ like -DPID_T=pid_t on command line, or override here.
+*/
+#ifndef PID_T
+#define PID_T int
+#endif /* PID_T */
+/*
+ Data types for uids and gids. Same deal as for pids.
+ Wouldn't be nice if there was a preprocessor test to find out if a
+ typedef existed?
+*/
+#ifdef VMS
+/* Not used in VMS so who cares */
+#define UID_T int
+#define GID_T int
+#endif /* VMS */
+
+#ifdef POSIX
+/* Or would it be better (or worse?) to use _POSIX_SOURCE here? */
+#ifndef UID_T
+#define UID_T uid_t
+#endif /* UID_T */
+#ifndef GID_T
+#define GID_T gid_t
+#endif /* GID_T */
+#else /* Not POSIX */
+#ifdef SVR4
+/* SVR4 and later have uid_t and gid_t. */
+/* SVR3 and earlier use int, or unsigned short, or.... */
+#ifndef UID_T
+#define UID_T uid_t
+#endif /* UID_T */
+#ifndef GID_T
+#define GID_T gid_t
+#endif /* GID_T */
+#else /* Not SVR4 */
+#ifdef BSD43
+#ifndef UID_T
+#define UID_T uid_t
+#endif /* UID_T */
+#ifndef GID_T
+#define GID_T gid_t
+#endif /* GID_T */
+#else /* Not BSD43 */
+/* Default these to int for older UNIX versions */
+#ifndef UID_T
+#define UID_T int
+#endif /* UID_T */
+#ifndef GID_T
+#define GID_T int
+#endif /* GID_T */
+#endif /* BSD43 */
+#endif /* SVR4 */
+#endif /* POSIX */
+
+/*
+ getpwuid() arg type, which is not necessarily the same as UID_T,
+ e.g. in SCO UNIX SVR3, it's int.
+*/
+#ifndef PWID_T
+#define PWID_T UID_T
+#endif /* PWID_T */
+
+#ifdef CK_REDIR
+#ifdef NEXT
+#define MACHWAIT
+#else
+#ifdef MACH
+#define MACHWAIT
+#endif /* MACH */
+#endif /* NEXT */
+
+#ifdef MACHWAIT /* WAIT_T argument for wait() */
+#include <sys/wait.h>
+#define CK_WAIT_H
+typedef union wait WAIT_T;
+#else
+#ifdef POSIX
+#ifdef OSF
+/* OSF wait.h defines BSD wait if _BSD is defined so hide _BSD from wait.h */
+#ifdef _BSD
+#define CK_OSF_BSD
+#undef _BSD
+#endif /* _BSD */
+#endif /* OSF */
+#include <sys/wait.h>
+#define CK_WAIT_H
+#ifndef WAIT_T
+typedef int WAIT_T;
+#endif /* WAIT_T */
+#ifdef CK_OSF_BSD /* OSF/1: Restore _BSD definition */
+#define _BSD
+#undef CK_OSF_BSD
+#endif /* CK_OSF_BSD */
+#else /* !POSIX */
+typedef int WAIT_T;
+#endif /* POSIX */
+#endif /* MACHWAIT */
+#else
+typedef int WAIT_T;
+#endif /* CK_REDIR */
+
+/* Assorted other blah_t's handled here... */
+
+#ifndef SIZE_T
+#define SIZE_T size_t
+#endif /* SIZE_T */
+
+/* Forward declarations of system-dependent functions callable from all */
+/* C-Kermit modules. */
+
+/* File-related functions from system-dependent file i/o module */
+
+#ifndef CKVFIO_C
+/* For some reason, this does not agree with DEC C */
+_PROTOTYP( int zkself, (void) );
+#endif /* CKVFIO_C */
+_PROTOTYP( int zopeni, (int, char *) );
+_PROTOTYP( int zopeno, (int, char *, struct zattr *, struct filinfo *) );
+_PROTOTYP( int zclose, (int) );
+#ifndef MAC
+_PROTOTYP( int zchin, (int, int *) );
+#endif /* MAC */
+_PROTOTYP( int zxin, (int, char *, int) );
+_PROTOTYP( int zsinl, (int, char *, int) );
+_PROTOTYP( int zinfill, (void) );
+_PROTOTYP( int zsout, (int, char*) );
+_PROTOTYP( int zsoutl, (int, char*) );
+_PROTOTYP( int zsoutx, (int, char*, int) );
+_PROTOTYP( int zchout, (int, char) );
+_PROTOTYP( int zoutdump, (void) );
+_PROTOTYP( int zsyscmd, (char *) );
+_PROTOTYP( int zshcmd, (char *) );
+#ifdef UNIX
+_PROTOTYP( int zsetfil, (int, int) );
+_PROTOTYP( int zchkpid, (unsigned long) );
+#endif /* UNIX */
+#ifdef CKEXEC
+_PROTOTYP( VOID z_exec, (char *, char **, int) );
+#endif /* CKEXEC */
+_PROTOTYP( int chkfn, (int) );
+_PROTOTYP( long zchki, (char *) );
+#ifdef VMSORUNIX
+_PROTOTYP( long zgetfs, (char *) );
+#else
+#ifdef OS2
+_PROTOTYP( long zgetfs, (char *) );
+#else
+#define zgetfs(a) zchki(a)
+#endif /* OS2 */
+#endif /* VMSORUNIX */
+_PROTOTYP( int iswild, (char *) );
+_PROTOTYP( int isdir, (char *) );
+_PROTOTYP( int zchko, (char *) );
+_PROTOTYP( int zdelet, (char *) );
+_PROTOTYP( VOID zrtol, (char *,char *) );
+_PROTOTYP( VOID zltor, (char *,char *) );
+_PROTOTYP( VOID zstrip, (char *,char **) );
+#ifdef VMS
+_PROTOTYP( char * zrelname, (char *, char *) );
+#endif /* VMS */
+_PROTOTYP( int zchdir, (char *) );
+_PROTOTYP( char * zhome, (void) );
+_PROTOTYP( char * zgtdir, (void) );
+_PROTOTYP( int zxcmd, (int, char *) );
+#ifndef MAC
+_PROTOTYP( int zclosf, (int) );
+#endif /* MAC */
+#ifdef NZXPAND
+_PROTOTYP( int nzxpand, (char *, int) );
+#else /* NZXPAND */
+_PROTOTYP( int zxpand, (char *) );
+#endif /* NZXPAND */
+_PROTOTYP( int znext, (char *) );
+#ifdef ZXREWIND
+_PROTOTYP( int zxrewind, (void) );
+#endif /* ZXREWIND */
+_PROTOTYP( int zchkspa, (char *, long) );
+_PROTOTYP( VOID znewn, (char *, char **) );
+_PROTOTYP( int zrename, (char *, char *) );
+_PROTOTYP( int zcopy, (char *, char *) );
+_PROTOTYP( int zsattr, (struct zattr *) );
+_PROTOTYP( int zfree, (char *) );
+_PROTOTYP( char * zfcdat, (char *) );
+_PROTOTYP( int zstime, (char *, struct zattr *, int) );
+#ifdef CK_PERMS
+_PROTOTYP( char * zgperm, (char *) );
+_PROTOTYP( char * ziperm, (char *) );
+#endif /* CK_PERMS */
+_PROTOTYP( int zmail, (char *, char *) );
+_PROTOTYP( int zprint, (char *, char *) );
+_PROTOTYP( char * tilde_expand, (char *) );
+_PROTOTYP( int zmkdir, (char *) ) ;
+_PROTOTYP( int zfseek, (long) ) ;
+#ifdef ZFNQFP
+_PROTOTYP( struct zfnfp * zfnqfp, (char *, int, char * ) ) ;
+#else
+#define zfnqfp(a,b,c) ckstrncpy(c,a,b)
+#endif /* ZFNQFP */
+_PROTOTYP( int zvuser, (char *) ) ;
+_PROTOTYP( int zvpass, (char *) ) ;
+_PROTOTYP( VOID zvlogout, (void) ) ;
+#ifdef OS2
+_PROTOTYP( int os2setlongname, ( char * fn, char * ln ) ) ;
+_PROTOTYP( int os2getlongname, ( char * fn, char ** ln ) ) ;
+_PROTOTYP( int os2rexx, ( char *, char *, int ) ) ;
+_PROTOTYP( int os2rexxfile, ( char *, char *, char *, int) ) ;
+_PROTOTYP( int os2geteas, (char *) ) ;
+_PROTOTYP( int os2seteas, (char *) ) ;
+_PROTOTYP( char * get_os2_vers, (void) ) ;
+_PROTOTYP( int do_label_send, (char *) ) ;
+_PROTOTYP( int do_label_recv, (void) ) ;
+#ifdef OS2MOUSE
+_PROTOTYP( unsigned long os2_mouseon, (void) );
+_PROTOTYP( unsigned long os2_mousehide, (void) );
+_PROTOTYP( unsigned long os2_mouseshow, (void) );
+_PROTOTYP( unsigned long os2_mouseoff, (void) );
+_PROTOTYP( void os2_mouseevt, (void *) );
+_PROTOTYP( int mousebuttoncount, (void));
+#endif /* OS2MOUSE */
+#endif /* OS2 */
+
+/* Functions from system-dependent terminal i/o module */
+
+_PROTOTYP( int ttopen, (char *, int *, int, int) ); /* tty functions */
+#ifndef MAC
+_PROTOTYP( int ttclos, (int) );
+#endif /* MAC */
+_PROTOTYP( int tthang, (void) );
+_PROTOTYP( int ttres, (void) );
+_PROTOTYP( int ttpkt, (long, int, int) );
+#ifndef MAC
+_PROTOTYP( int ttvt, (long, int) );
+#endif /* MAC */
+_PROTOTYP( int ttsspd, (int) );
+_PROTOTYP( long ttgspd, (void) );
+_PROTOTYP( int ttflui, (void) );
+_PROTOTYP( int ttfluo, (void) );
+_PROTOTYP( int ttpushback, (CHAR *, int) );
+_PROTOTYP( int ttpeek, (void) );
+_PROTOTYP( int ttgwsiz, (void) );
+_PROTOTYP( int ttchk, (void) );
+_PROTOTYP( int ttxin, (int, CHAR *) );
+_PROTOTYP( int ttxout, (CHAR *, int) );
+_PROTOTYP( int ttol, (CHAR *, int) );
+_PROTOTYP( int ttoc, (char) );
+_PROTOTYP( int ttinc, (int) );
+_PROTOTYP( int ttscarr, (int) );
+_PROTOTYP( int ttgmdm, (void) );
+_PROTOTYP( int ttsndb, (void) );
+_PROTOTYP( int ttsndlb, (void) );
+#ifdef UNIX
+_PROTOTYP( char * ttglckdir, (void) );
+#endif /* UNIX */
+#ifdef PARSENSE
+#ifdef UNIX
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) );
+#else
+#ifdef VMS
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) );
+#else
+#ifdef STRATUS
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) );
+#else
+#ifdef OS2
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) );
+#else
+#ifdef OSK
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) );
+#else
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR) );
+#endif /* OSK */
+#endif /* OS2 */
+#endif /* STRATUS */
+#endif /* VMS */
+#endif /* UNIX */
+#else /* ! PARSENSE */
+_PROTOTYP( int ttinl, (CHAR *, int, int, CHAR) );
+#endif /* PARSENSE */
+
+/* XYZMODEM support */
+
+/*
+ CK_XYZ enables the various commands and data structures.
+ XYZ_INTERNAL means these protocols are built-in; if not defined,
+ then they are external. XYZ_DLL is used to indicate a separate
+ loadable library containing the XYZmodem protocol code.
+*/
+#ifdef pdp11 /* No room for this in PDP-11 */
+#define NOCKXYZ
+#endif /* pdp11 */
+
+#ifndef NOCKXYZ /* Alternative protocols */
+#ifndef CK_XYZ
+#ifdef UNIX
+#define CK_XYZ
+#else
+#ifdef OS2
+#define CK_XYZ
+#ifndef NOXYZDLL
+#define XYZ_INTERNAL /* Internal and DLL */
+#define XYZ_DLL
+#endif /* NOXYZDLL */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* CK_XYZ */
+#endif /* NOCKXYZ */
+
+#ifdef XYZ_INTERNAL /* This ensures that XYZ_INTERNAL */
+#ifndef CK_XYZ /* is defined only if CK_XYZ is too */
+#undef XYZ_INTERNAL
+#endif /* CK_XYZ */
+#endif /* XYZ_INTERNAL */
+#ifdef XYZ_DLL /* This ensures XYZ_DLL is defined */
+#ifndef XYZ_INTERNAL /* only if XYZ_INTERNAL is too */
+#undef XYZ_DLL
+#endif /* XYZ_INTERNAL */
+#endif /* XYZ_DLL */
+
+/* Console functions */
+
+_PROTOTYP( int congm, (void) );
+#ifdef COMMENT
+_PROTOTYP( VOID conint, (SIGTYP (*)(int, int), SIGTYP (*)(int, int)) );
+#else
+_PROTOTYP( VOID conint, (SIGTYP (*)(int), SIGTYP (*)(int)) );
+#endif /* COMMENT */
+_PROTOTYP( VOID connoi, (void) );
+_PROTOTYP( int concb, (char) );
+#ifdef CONGSPD
+_PROTOTYP( long congspd, (void) );
+#endif /* CONGSPD */
+_PROTOTYP( int conbin, (char) );
+_PROTOTYP( int conres, (void) );
+_PROTOTYP( int conoc, (char) );
+_PROTOTYP( int conxo, (int, char *) );
+_PROTOTYP( int conol, (char *) );
+_PROTOTYP( int conola, (char *[]) );
+_PROTOTYP( int conoll, (char *) );
+_PROTOTYP( int conchk, (void) );
+_PROTOTYP( int coninc, (int) );
+_PROTOTYP( char * conkbg, (void) );
+_PROTOTYP( int psuspend, (int) );
+_PROTOTYP( int priv_ini, (void) );
+_PROTOTYP( int priv_on, (void) );
+_PROTOTYP( int priv_off, (void) );
+_PROTOTYP( int priv_can, (void) );
+_PROTOTYP( int priv_chk, (void) );
+_PROTOTYP( int priv_opn, (char *, int) );
+
+_PROTOTYP( int sysinit, (void) ); /* Misc Kermit functions */
+_PROTOTYP( int syscleanup, (void) );
+_PROTOTYP( int msleep, (int) );
+_PROTOTYP( VOID rtimer, (void) );
+_PROTOTYP( int gtimer, (void) );
+#ifdef GFTIMER
+_PROTOTYP( VOID rftimer, (void) );
+_PROTOTYP( CKFLOAT gftimer, (void) );
+#endif /* GFTIMER */
+_PROTOTYP( VOID ttimoff, (void) );
+_PROTOTYP( VOID ztime, (char **) );
+_PROTOTYP( int parchk, (CHAR *, CHAR, int) );
+_PROTOTYP( VOID doexit, (int, int) );
+_PROTOTYP( int askmore, (void) );
+_PROTOTYP( VOID fatal, (char *) );
+_PROTOTYP( VOID fatal2, (char *, char *) );
+#ifdef VMS
+_PROTOTYP( int ck_cancio, (void) );
+#endif /* VMS */
+
+/* Key mapping support */
+
+#ifdef NOICP
+#ifndef NOSETKEY
+#define NOSETKEY
+#endif /* NOSETKEY */
+#endif /* NOICP */
+
+#ifdef MAC
+#ifndef NOSETKEY
+#define NOSETKEY
+#endif /* NOSETKEY */
+#endif /* MAC */
+
+_PROTOTYP( int congks, (int) );
+#ifdef OS2
+/* OS2 requires these definitions even if SET KEY is not being supported */
+#define KMSIZE 8916
+typedef ULONG KEY;
+typedef CHAR *MACRO;
+extern int wideresult;
+#else /* Not OS2 */
+#ifndef NOSETKEY
+/*
+ Catch-all for systems where we don't know how to read keyboard scan
+ codes > 255.
+*/
+#define KMSIZE 256
+/* Note: CHAR (i.e. unsigned char) is very important here. */
+typedef CHAR KEY;
+typedef CHAR * MACRO;
+#define congks coninc
+#endif /* NOSETKEY */
+#endif /* OS2 */
+
+#ifndef OS2
+#ifndef NOKVERBS /* No \Kverbs unless... */
+#define NOKVERBS
+#endif /* NOKVERBS */
+#endif /* OS2 */
+
+#ifndef NOKVERBS
+#ifdef OS2
+/*
+ Note: this value chosen to be bigger than PC BIOS key modifier bits,
+ but still fit in 16 bits without affecting sign.
+
+ As of K95 1.1.5, this no longer fits in 16 bits, good thing we are 32 bit.
+*/
+#define F_MACRO 0x2000 /* Bit indicating a macro indice */
+#define IS_MACRO(x) (x & F_MACRO)
+#define F_KVERB 0x4000 /* Bit indicating a keyboard verb */
+#define IS_KVERB(x) (x & F_KVERB) /* Test this bit */
+#endif /* OS2 */
+#endif /* NOKVERBS */
+
+#define F_ESC 0x8000 /* Bit indicating ESC char combination */
+#define IS_ESC(x) (x & F_ESC)
+#define F_CSI 0x10000 /* Bit indicating CSI char combination */
+#define IS_CSI(x) (x & F_CSI)
+
+#ifdef NOSPL /* This might be overkill.. */
+#ifndef NOKVERBS /* Not all \Kverbs require */
+#define NOKVERBS /* the script programming language. */
+#endif /* NOKVERBS */
+#ifndef NOTAKEARGS
+#define NOTAKEARGS
+#endif /* NOTAKEARGS */
+#endif /* NOSPL */
+
+/*
+ Function prototypes for system and library functions.
+*/
+#ifdef _POSIX_SOURCE
+#ifndef VMS
+#ifndef MAC
+#define CK_ANSILIBS
+#endif /* MAC */
+#endif /* VMS */
+#endif /* _POSIX_SOURCE */
+
+#ifdef NEXT
+#define CK_ANSILIBS
+#endif /* NEXT */
+
+#ifdef SVR4
+#define CK_ANSILIBS
+#endif /* SVR4 */
+
+#ifdef STRATUS /* Stratus VOS uses ANSI libraries */
+#define CK_ANSILIBS
+#endif /* STRATUS */
+
+#ifdef OS2
+#define CK_ANSILIBS
+#ifndef NOCURSES
+#define MYCURSES
+#endif /* NOCURSES */
+#define CK_RTSCTS
+#ifdef __IBMC__
+#define S_IFMT 0xF000
+#define timezone _timezone
+#endif /* __IBMC__ */
+#include <fcntl.h>
+#include <io.h>
+#ifdef __EMX__
+#ifndef __32BIT__
+#define __32BIT__
+#endif /* __32BIT__ */
+#include <sys/timeb.h>
+#else /* __EMX__ */
+#include <direct.h>
+#undef SIGALRM
+#ifndef SIGUSR1
+#define SIGUSR1 7
+#endif /* SIGUSR1 */
+#define SIGALRM SIGUSR1
+_PROTOTYP( unsigned alarm, (unsigned) );
+_PROTOTYP( unsigned sleep, (unsigned) );
+#endif /* __EMX__ */
+_PROTOTYP( unsigned long zdskspace, (int) );
+_PROTOTYP( int zchdsk, (int) );
+_PROTOTYP( int conincraw, (int) );
+_PROTOTYP( int ttiscom, (int f) );
+_PROTOTYP( int IsFileNameValid, (char *) );
+_PROTOTYP( void ChangeNameForFAT, (char *) );
+_PROTOTYP( char *GetLoadPath, (void) );
+#endif /* OS2 */
+
+/* Fullscreen file transfer display items... */
+
+#ifndef NOCURSES
+#ifdef CK_NCURSES /* CK_NCURSES implies CK_CURSES */
+#ifndef CK_CURSES
+#define CK_CURSES
+#endif /* CK_CURSES */
+#endif /* CK_NCURSES */
+
+#ifdef MYCURSES /* MYCURSES implies CK_CURSES */
+#ifndef CK_CURSES
+#define CK_CURSES
+#endif /* CK_CURSES */
+#endif /* MYCURSES */
+#endif /* NOCURSES */
+
+#ifdef NOCURSES
+#ifdef CK_CURSES
+#undef CK_CURSES
+#endif /* CK_CURSES */
+#ifndef NODISPLAY
+#define NODISPLAY
+#endif /* NODISPLAY */
+#endif /* NOCURSES */
+
+#ifdef CK_CURSES
+/*
+ The CK_WREFRESH symbol is defined if the curses library provides
+ clearok() and wrefresh() functions, which are used in repainting
+ the screen.
+*/
+#ifdef NOWREFRESH /* Override CK_WREFRESH */
+
+#ifdef CK_WREFRESH /* If this is defined, */
+#undef CK_WREFRESH /* undefine it. */
+#endif /* CK_WREFRESH */
+
+#else /* !NOWREFRESH */ /* No override... */
+
+#ifndef CK_WREFRESH /* If CK_WREFRESH not defined */
+/*
+ Automatically define it for systems known to have it ...
+*/
+#ifdef VMS /* DEC (Open)VMS has it */
+#define CK_WREFRESH
+#else
+#ifdef ultrix /* DEC ULTRIX has it */
+#else
+#ifdef SVR3 /* System V has it */
+#define CK_WREFRESH
+#else
+#ifdef BSD44 /* 4.4 BSD has it */
+#define CK_WREFRESH
+#else
+#ifdef NEXT /* Define it for NeXTSTEP */
+#define CK_WREFRESH
+#else
+#ifdef SUNOS4 /* SunOS 4.x... */
+#define CK_WREFRESH
+#else
+#ifdef SOLARIS25 /* Solaris 2.5 and later */
+#define CK_WREFRESH
+#else
+#ifdef AIXRS /* RS/6000 AIX ... */
+#define CK_WREFRESH
+#else
+#ifdef RTAIX /* RT PC AIX ... */
+#define CK_WREFRESH
+#else
+#ifdef OSF /* DEC OSF/1 ... */
+#define CK_WREFRESH
+
+/* Add more here, or just define CK_WREFRESH on the CC command line... */
+
+#endif /* OSF */
+#endif /* RTAIX */
+#endif /* AIXRS */
+#endif /* SOLARIS25 */
+#endif /* SUNOS4 */
+#endif /* NEXT */
+#endif /* BSD44 */
+#endif /* SVR3 */
+#endif /* ultrix */
+#endif /* VMS */
+
+#else /* CK_WREFRESH is defined */
+
+/* This is within an ifdef CK_CURSES block. The following is not needed */
+
+#ifndef CK_CURSES /* CK_WREFRESH implies CK_CURSES */
+#define CK_CURSES
+#endif /* CK_CURSES */
+
+#endif /* CK_WREFRESH */
+#endif /* NOWREFRESH */
+
+#ifndef TRMBUFL
+#ifdef BIGBUFOK
+#define TRMBUFL 16384
+#else
+#ifdef DYNAMIC
+#define TRMBUFL 8192
+#else
+#define TRMBUFL 1024
+#endif /* BIGBUFOK */
+#endif /* DYNAMIC */
+#endif /* TRMBUFL */
+#endif /* CK_CURSES */
+
+/*
+ Whether to use ckmatch() in all its glory for C-Shell-like patterns.
+ If CKREGEX is NOT defined, all but * and ? matching are removed from
+ ckmatch(). NOTE: Defining CKREGEX does not necessarily mean that ckmatch()
+ regexes are used for filename matching. That depends on whether zxpand()
+ in ck?fio.c calls ckmatch(). NOTE 2: REGEX is a misnomer -- these are not
+ regular expressions in the computer-science sense (in which, e.g. "a*b"
+ matches 0 or more 'a' characters followed by 'b') but patterns (in which
+ "a*b" matches 'a' followed by 0 or more non-b characters, followed by b).
+*/
+#ifndef NOCKREGEX
+#ifndef CKREGEX
+#define CKREGEX
+#endif /* CKREGEX */
+#endif /* NOCKREGEX */
+
+/* Learned-script feature */
+
+#ifndef NOLEARN
+#ifdef NOSPL
+#define NOLEARN
+#else
+#ifdef NOLOCAL
+#define NOLEARN
+#endif /* NOLOCAL */
+#endif /* NOSPL */
+#endif /* NOLEARN */
+
+#ifdef NOLEARN
+#ifdef CKLEARN
+#undef CKLEARN
+#endif /* CKLEARN */
+#else /* !NOLEARN */
+#ifndef CKLEARN
+#ifdef OS2ORUNIX
+/* In UNIX this can work only with ckucns.c builds */
+#define CKLEARN
+#else
+#ifdef VMS
+#define CKLEARN
+#endif /* VMS */
+#endif /* OS2ORUNIX */
+#endif /* CKLEARN */
+#endif /* NOLEARN */
+
+#ifdef CKLEARN
+#ifndef LEARNBUFSIZ
+#define LEARNBUFSIZ 128
+#endif /* LEARNBUFSIZ */
+#endif /* CKLEARN */
+
+#ifndef IKSDONLY
+#ifndef CKTIDLE /* Pseudo-keepalive in CONNECT */
+#ifdef OS2 /* In K95 */
+#define CKTIDLE
+#else
+#ifdef UNIX /* In UNIX but only ckucns versions */
+#ifndef NOLEARN
+#ifndef NOSELECT
+#define CKTIDLE
+#endif /* NOSELECT */
+#endif /* NOLEARN */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* CKTIDLE */
+#endif /* IKSDONLY */
+
+#ifdef CK_ANSILIBS
+/*
+ String library functions.
+ For ANSI C, get prototypes from <string.h>.
+ Otherwise, skip the prototypes.
+*/
+#include <string.h>
+
+/*
+ Prototypes for other commonly used library functions, such as
+ malloc, free, getenv, atol, atoi, and exit. Otherwise, no prototypes.
+*/
+#include <stdlib.h>
+#ifdef DIAB /* DIAB DS90 */
+/* #include <commonC.h> */
+#include <sys/wait.h>
+#define CK_WAIT_H
+#ifdef COMMENT
+extern void exit(int status);
+extern void _exit(int status);
+extern int uname(struct utsname *name);
+#endif /* COMMENT */
+extern int chmod(char *path, int mode);
+extern int ioctl(int fildes, int request, ...);
+extern int rdchk(int ttyfd);
+extern int nap(int m);
+#ifdef COMMENT
+extern int getppid(void);
+#endif /* COMMENT */
+extern int _filbuf(FILE *stream);
+extern int _flsbuf(char c,FILE *stream);
+#endif /* DIAB */
+
+/*
+ Prototypes for UNIX functions like access, alarm, chdir, sleep, fork,
+ and pause. Otherwise, no prototypes.
+*/
+#ifdef VMS
+#include <unixio.h>
+#endif /* VMS */
+
+#ifdef NEXT
+#ifndef NEXT33
+#include <libc.h>
+#endif /* NEXT33 */
+#else /* NoT NeXT */
+#ifndef AMIGA
+#ifndef OS2
+#ifdef STRATUS
+#include <c_utilities.h>
+#else /* !STRATUS */
+#ifndef OSKXXC
+#include <unistd.h>
+#endif /* OSKXXC */
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif /* HAVE_CRYPT_H */
+#endif /* STRATUS */
+#endif /* OS2 */
+#endif /* AMIGA */
+#endif /* NEXT */
+
+#else /* Not ANSI libs... */
+
+#ifdef MAC
+#include <String.h>
+#include <StdLib.h>
+#endif /* MAC */
+
+#ifdef HPUX
+#ifndef HPUXPRE65
+#include <unistd.h>
+#endif /* HPUXPRE65 */
+#endif /* HPUX */
+
+#ifdef SUNOS41
+#include <unistd.h>
+#include <stdlib.h>
+#else
+#ifndef MAC
+/*
+ It is essential that these are declared correctly!
+ Which is not always easy. Take malloc() for instance ...
+*/
+#ifdef PYRAMID
+#ifdef SVR4
+#ifdef __STDC__
+#define SIZE_T_MALLOC
+#endif /* __STDC__ */
+#endif /* SVR4 */
+#endif /* PYRAMID */
+/*
+ Maybe some other environments need the same treatment for malloc.
+ If so, define SIZE_T_MALLOC for them here or in compiler CFLAGS.
+*/
+#ifdef SIZE_T_MALLOC
+_PROTOTYP( void * malloc, (size_t) );
+#else
+_PROTOTYP( char * malloc, (unsigned int) );
+#endif /* SIZE_T_MALLOC */
+
+_PROTOTYP( char * getenv, (char *) );
+_PROTOTYP( long atol, (char *) );
+#endif /* !MAC */
+#endif /* SUNOS41 */
+#endif /* CK_ANSILIBS */
+
+/*
+ <sys/param.h> generally picks up NULL, MAXPATHLEN, and MAXNAMLEN
+ and seems to present on all Unixes going back at least to SCO Xenix
+ with the exception(s) noted.
+*/
+#ifndef NO_PARAM_H /* 2001-11-03 */
+#ifndef UNIX /* Non-Unixes don't have it */
+#define NO_PARAM_H
+#else
+#ifdef TRS16 /* Tandy Xenix doesn't have it */
+#define NO_PARAM_H
+#endif /* TRS16 */
+#endif /* UNIX */
+#endif /* NO_PARAM_H */
+
+#ifndef NO_PARAM_H
+#ifndef INCL_PARAM_H
+#define INCL_PARAM_H
+#endif /* INCL_PARAM_H */
+#include <sys/param.h>
+#endif /* NO_PARAM_H */
+
+#ifndef NULL /* In case NULL is still not defined */
+#define NULL 0L
+/* or #define NULL 0 */
+/* or #define NULL ((char *) 0) */
+/* or #define NULL ((void *) 0) */
+#endif /* NULL */
+
+/* Maximum length for a fully qualified filename, not counting \0 at end. */
+/*
+ This is a rough cut, and errs on the side of being too big. We don't
+ want to pull in hundreds of header files looking for many and varied
+ symbols, for fear of introducing unnecessary conflicts.
+*/
+#ifndef CKMAXPATH
+#ifdef MAXPATHLEN /* (it probably isn't) */
+#define CKMAXPATH MAXPATHLEN
+#else
+#ifdef PATH_MAX /* POSIX */
+#define CKMAXPATH PATH_MAX
+#else
+#ifdef MAC
+#define CKMAXPATH 63
+#else
+#ifdef pdp11
+#define CKMAXPATH 255
+#else
+#ifdef UNIX /* Even though some are way less... */
+#define CKMAXPATH 1024
+#else
+#ifdef VMS
+#define CKMAXPATH 675 /* (derivation is complicated...) */
+#else
+#ifdef STRATUS
+#define CKMAXPATH 256 /* == $MXPL from PARU.H */
+#else
+#ifdef datageneral
+#define CKMAXPATH 256 /* == $MXPL from PARU.H */
+#else
+#define CKMAXPATH 255
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* pdp11 */
+#endif /* MAC */
+#endif /* PATH_MAX */
+#endif /* MAXPATHLEN */
+#endif /* CKMAXPATH */
+
+/* Maximum length for the name of a tty device */
+
+#ifndef DEVNAMLEN
+#define DEVNAMLEN CKMAXPATH
+#endif /* DEVNAMLEN */
+
+/* Directory (path segment) separator */
+/* Not fully general - Tricky for VMS, Amiga, ... */
+
+#ifndef DIRSEP
+#ifdef UNIX
+#define DIRSEP '/'
+#define ISDIRSEP(c) ((c)=='/')
+#else
+#ifdef OS2
+#define DIRSEP '/'
+#define ISDIRSEP(c) ((c)=='/'||(c)=='\\')
+#else
+#ifdef datageneral
+#define DIRSEP ':'
+#define ISDIRSEP(c) (((c)==':')||((c)=='^')||((c)=='='))
+#else
+#ifdef STRATUS
+#define DIRSEP '>'
+#define ISDIRSEP(c) ((c)=='>')
+#else
+#ifdef VMS
+#define DIRSEP ']' /* (not really) */
+#define ISDIRSEP(c) ((c)==']'||(c)==':')
+#else
+#ifdef MAC
+#define DIRSEP ':'
+#define ISDIRSEP(c) ((c)==':')
+#else
+#ifdef AMIGA
+#define DIRSEP '/'
+#define ISDIRSEP(c) ((c)=='/'||(c)==':')
+#else
+#ifdef GEMDOS
+#define DIRSEP '\\'
+#define ISDIRSEP(c) ((c)=='\\'||(c)==':')
+#else
+#define DIRSEP '/'
+#define ISDIRSEP(c) ((c)=='/')
+#endif /* GEMDOS */
+#endif /* AMIGA */
+#endif /* MAC */
+#endif /* VMS */
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* DIRSEP */
+
+/* FILE package parameters */
+
+#ifdef pdp11
+#define NOCHANNELIO
+#else
+
+#ifndef CKMAXOPEN
+#ifdef QNX
+#define CKMAXOPEN 390
+#else
+#ifdef VMS
+#define CKMAXOPEN 64
+#else
+#ifdef OPEN_MAX
+#define CKMAXOPEN OPEN_MAX
+#else
+#ifdef FOPEN_MAX
+#define CKMAXOPEN FOPEN_MAX
+#else
+#define CKMAXOPEN 64
+#endif /* FOPEN_MAX */
+#endif /* OPEN_MAX */
+#endif /* VMS */
+#endif /* QNX */
+#endif /* CKMAXOPEN */
+
+/* Maximum channels for FOPEN = CKMAXOPEN minus logs, stdio, etc */
+
+#ifndef Z_MINCHAN
+#define Z_MINCHAN 16
+#endif /* Z_MINCHAN */
+
+#ifndef Z_MAXCHAN
+#define Z_MAXCHAN (CKMAXOPEN-ZNFILS-5)
+#endif /* Z_MAXCHAN */
+#endif /* pdp11 */
+
+/* New-format nzltor() and nzrtol() functions that handle pathnames */
+
+#ifndef NZLTOR
+#ifdef UNIX
+#define NZLTOR
+#else
+#ifdef VMS
+#define NZLTOR
+#else
+#ifdef OS2
+#define NZLTOR
+#else
+#ifdef STRATUS
+#define NZLTOR
+#endif /* STRATUS */
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* NZLTOR */
+
+#ifdef NZLTOR
+_PROTOTYP( VOID nzltor, (char *, char *, int, int, int) );
+_PROTOTYP( VOID nzrtol, (char *, char *, int, int, int) );
+#endif /* NZLTOR */
+
+/* Implementations with a zrmdir() function */
+
+#ifndef ZRMDIR
+#ifdef OS2
+#define ZRMDIR
+#else /* OS2 */
+#ifdef UNIX
+#define ZRMDIR
+#else
+#ifdef VMS
+#define ZRMDIR
+#else /* VMS */
+#ifdef STRATUS
+#define ZRMDIR
+#endif /* STRATUS */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* ZRMDIR */
+
+#ifdef ZRMDIR
+_PROTOTYP( int zrmdir, (char *) );
+#endif /* ZRMDIR */
+
+#ifndef FILECASE
+#ifdef UNIXOROSK
+#define FILECASE 1
+#else
+#define FILECASE 0
+#endif /* UNIXOROSK */
+#ifndef CKCMAI
+extern int filecase;
+#endif /* CKCMAI */
+#endif /* FILECASE */
+
+/* Funny names for library functions department... */
+
+#ifdef ZILOG
+#define setjmp setret
+#define longjmp longret
+#define jmp_buf ret_buf
+#define getcwd curdir
+#endif /* ZILOG */
+
+#ifdef STRATUS
+/* The C-runtime conflicts with things we do in Stratus VOS ckltio.c ... */
+#define printf vosprtf
+_PROTOTYP( int vosprtf, (char *fmt, ...) );
+#define perror(txt) printf("%s\n", txt)
+/* char_varying is a string type from PL/I that VOS uses extensively */
+#define CV char_varying
+#endif /* STRATUS */
+
+#ifdef NT
+extern int OSVer;
+#define isWin95() (OSVer==VER_PLATFORM_WIN32_WINDOWS)
+#else
+#define isWin95() (0)
+#endif /* NT */
+
+#ifndef BPRINT
+#ifdef OS2
+#define BPRINT
+#endif /* OS2 */
+#endif /* BPRINT */
+
+#ifndef SESLIMIT
+#ifdef OS2
+#define SESLIMIT
+#endif /* OS2 */
+#endif /* SESLIMIT */
+
+#ifndef NOTERM
+#ifndef PCTERM
+#ifdef NT
+#define PCTERM
+#endif /* NT */
+#endif /* PCTERM */
+#endif /* NOTERM */
+
+#ifdef BEOSORBEBOX
+#define query ckquery
+#endif /* BEOSORBEBOX */
+
+#ifndef PTYORPIPE /* NETCMD and/or NETPTY defined */
+#ifdef NETCMD
+#define PTYORPIPE
+#else
+#ifdef NETPTY
+#define PTYORPIPE
+#endif /* NETPTY */
+#endif /* NETCMD */
+#endif /* PTYORPIPE */
+
+/* mktemp() and mkstemp() */
+
+#ifndef NOMKTEMP
+#ifndef MKTEMP
+#ifdef OS2ORUNIX
+#define MKTEMP
+#endif /* OS2ORUNIX */
+#endif /* MKTEMP */
+
+#ifdef MKTEMP
+#ifndef NOMKSTEMP
+#ifndef MKSTEMP
+#ifdef BSD44
+#define MKSTEMP
+#else
+#ifdef __linux__
+#define MKSTEMP
+#endif /* __linux__ */
+#endif /* BSD44 */
+#endif /* MKSTEMP */
+#endif /* NOMKSTEMP */
+#endif /* MKTEMP */
+#endif /* NOMKTEMP */
+
+/* Platforms that have memcpy() -- only after all headers included */
+
+#ifndef USE_MEMCPY
+#ifdef VMS
+#define USE_MEMCPY
+#else
+#ifdef NEXT
+#define USE_MEMCPY
+#else
+#ifdef OS2
+#define USE_MEMCPY
+#else
+#ifdef __linux__
+#define USE_MEMCPY
+#else
+#ifdef SOLARIS
+#define USE_MEMCPY
+#else
+#ifdef SUNOS4
+#define USE_MEMCPY
+#else
+#ifdef AIXRS
+#define USE_MEMCPY
+#else
+#ifdef HPUX
+#define USE_MEMCPY
+#else
+#ifdef POSIX
+#define USE_MEMCPY
+#else
+#ifdef SVR4
+#define USE_MEMCPY
+#else
+#ifdef OSF
+#define USE_MEMCPY
+#else
+#ifdef datageneral
+#define USE_MEMCPY
+#else
+#ifdef STRATUS
+#define USE_MEMCPY
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* OSF */
+#endif /* SVR4 */
+#endif /* POSIX */
+#endif /* HPUX */
+#endif /* AIXRS */
+#endif /* SUNOS4 */
+#endif /* SOLARIS */
+#endif /* __linux__ */
+#endif /* OS2 */
+#endif /* NEXT */
+#endif /* VMS */
+#endif /* USE_MEMCPY */
+
+#ifndef USE_MEMCPY
+#define memcpy(a,b,c) ckmemcpy((a),(b),(c))
+#else
+#ifdef CK_SCO32V4
+/* Because the prototype isn't picked up in the normal header files */
+_PROTOTYP( void *memcpy, (void *, const void *, size_t));
+#endif /* CK_SCO32V4 */
+#endif /* USE_MEMCPY */
+
+/* User authentication for IKS -- So far K95 and UNIX only */
+
+#ifdef NOICP
+#ifndef NOLOGIN
+#define NOLOGIN
+#endif /* NOLOGIN */
+#endif /* NOICP */
+
+#ifndef NOLOGIN
+#ifdef OS2ORUNIX
+#ifndef CK_LOGIN
+#define CK_LOGIN
+#ifndef NOSHADOW
+#ifdef CK_SCOV5
+#define CK_SHADOW
+#endif /* CK_SCOV5 */
+#endif /* NOSHADOW */
+#endif /* CK_LOGIN */
+#ifdef NT
+#define NTCREATETOKEN
+#endif /* NT */
+#endif /* OS2ORUNIX */
+#else /* NOLOGIN */
+#ifdef CK_LOGIN
+#undef CK_LOGIN
+#endif /* CK_LOGIN */
+#endif /* NOLOGIN */
+
+#ifdef OS2
+#define CKSPINNER
+#endif /* OS2 */
+
+#ifdef CK_LOGIN /* Telnet protocol required */
+#ifndef TNCODE /* for login to IKSD. */
+#define TNCODE
+#endif /* TNCODE */
+#endif /* CK_LOGIN */
+
+#ifdef CK_AUTHENTICATION
+#ifdef NOSENDUID
+#undef NOSENDUID
+#endif /* NOSENDUID */
+#endif /* CK_AUTHENTICATION */
+
+#ifdef TNCODE /* Should TELNET send user ID? */
+#ifndef NOSENDUID
+#ifndef CKSENDUID
+#define CKSENDUID
+#endif /* CKSENDUID */
+#endif /* NOSENDUID */
+#endif /* TNCODE */
+
+/* UNIX platforms that don't have getusershell() */
+
+#ifdef UNIX
+#ifndef NOGETUSERSHELL
+#ifdef IRIX
+#define NOGETUSERSHELL
+#else
+#ifdef PTX
+#define NOGETUSERSHELL
+#else
+#ifdef AIXRS
+#define NOGETUSERSHELL
+#else
+#ifdef SINIX
+#define NOGETUSERSHELL
+#else
+#ifdef UNIXWARE
+#define NOGETUSERSHELL
+#else
+#ifdef COHERENT
+#define NOGETUSERSHELL
+#endif /* COHERENT */
+#endif /* UNIXWARE */
+#endif /* SINIX */
+#endif /* AIXRS */
+#endif /* PTX */
+#endif /* IRIX */
+#endif /* NOGETUSERSHELL */
+#endif /* UNIX */
+
+#ifdef CK_LOGIN
+#ifdef NT
+#ifndef NOSYSLOG
+#ifndef CKSYSLOG
+#define CKSYSLOG
+#endif /* CKSYSLOG */
+#endif /* NOSYSLOG */
+#endif /* NT */
+#ifdef UNIX
+#ifndef NOSYSLOG
+#ifndef CKSYSLOG
+#define CKSYSLOG
+#endif /* CKSYSLOG */
+#endif /* NOSYSLOG */
+#ifndef NOWTMP
+#ifndef CKWTMP
+#define CKWTMP
+#endif /* CKWTMP */
+#endif /* NOWTMP */
+#ifndef NOGETUSERSHELL
+#ifndef GETUSERSHELL
+#define GETUSERSHELL
+#endif /* GETUSERSHELL */
+#endif /* NOGETUSERSHELL */
+#endif /* UNIX */
+_PROTOTYP( int ckxlogin, (CHAR *, CHAR *, CHAR *, int));
+_PROTOTYP( int ckxlogout, (VOID));
+#endif /* CK_LOGIN */
+
+#ifndef NOZLOCALTIME /* zlocaltime() available. */
+#ifdef OS2ORUNIX
+#define ZLOCALTIME
+_PROTOTYP( char * zlocaltime, (char *) );
+#endif /* OS2ORUNIX */
+#endif /* NOZLOCALTIME */
+
+#ifdef CKSYSLOG /* Syslogging levels */
+#define SYSLG_NO 0 /* No logging */
+#define SYSLG_LI 1 /* Login/out */
+#define SYSLG_DI 2 /* Dialing out */
+#define SYSLG_AC 3 /* Making any kind of connection */
+#define SYSLG_PR 4 /* Protocol Operations */
+#define SYSLG_FC 5 /* File creation */
+#define SYSLG_FA 6 /* File reading */
+#define SYSLG_CM 7 /* Top-level commands */
+#define SYSLG_CX 8 /* All commands */
+#define SYSLG_DB 9 /* Debug */
+#define SYSLGMAX 9 /* Highest level */
+#define SYSLG_DF SYSLG_FA /* Default level */
+/* Logging function */
+_PROTOTYP(VOID cksyslog,(int, int, char *, char *, char *));
+#endif /* CKSYSLOG */
+#ifndef CKCMAI
+extern int ckxlogging, ckxsyslog, ikdbopen;
+#endif /* CKCMAI */
+
+#ifndef CK_KEYTAB
+#define CK_KEYTAB
+/* Keyword Table Template */
+
+/* Note: formerly defined in ckucmd.h but now more widely used */
+
+struct keytab { /* Keyword table */
+ char *kwd; /* Pointer to keyword string */
+ int kwval; /* Associated value */
+ int flgs; /* Flags (as defined above) */
+};
+#endif /* CK_KEYTAB */
+
+#ifdef NETPTY
+_PROTOTYP( int do_pty, (char *));
+_PROTOTYP( VOID end_pty, (void));
+#endif /* NETPTY */
+
+#ifdef CKROOT
+_PROTOTYP( int zsetroot, (char *) );
+_PROTOTYP( char * zgetroot, (void) );
+_PROTOTYP( int zinroot, (char *) );
+#endif /* CKROOT */
+
+/* Local Echo Buffer prototypes */
+_PROTOTYP( VOID le_init, (void) );
+_PROTOTYP( VOID le_clean, (void));
+_PROTOTYP( int le_inbuf, (void));
+_PROTOTYP( int le_putstr, (CHAR *));
+_PROTOTYP( int le_puts, (CHAR *, int));
+_PROTOTYP( int le_putchar, (CHAR));
+_PROTOTYP( int le_getchar, (CHAR *));
+
+/* #ifndef NOHTTP */
+#ifndef NOCMDATE2TM
+#ifndef CMDATE2TM
+#ifdef OS2ORUNIX
+#define CMDATE2TM
+#endif /* OS2ORUNIX */
+#ifdef VMS
+#define CMDATE2TM
+#endif /* VMS */
+#endif /* CMDATE2TM */
+#endif /* NOCMDATE2TM */
+
+#ifdef CMDATE2TM
+_PROTOTYP( struct tm * cmdate2tm, (char *,int));
+#endif /* CMDATE2TM */
+/* #endif */ /* NOHTTP */
+
+#ifndef NOSETTIME /* This would be set in CFLAGS */
+#ifdef SVR4ORPOSIX /* Defined in IEEE 1003.1-1996 */
+#ifndef UTIMEH /* and in SVID for SVR4 */
+#define UTIMEH
+#endif /* UTIMEH */
+#else /* SVR4ORPOSIX */
+#ifdef OSF /* Verified by Lucas Hart */
+#ifndef UTIMEH
+#define UTIMEH
+#endif /* UTIMEH */
+#else /* OSF */
+#ifdef SUNOS41 /* Verified by Lucas Hart */
+#ifndef UTIMEH
+#define UTIMEH
+#endif /* UTIMEH */
+#else /* SUNOS41 */
+#ifdef OS2
+#ifndef SYSUTIMEH
+#define SYSUTIMEH
+#endif /* SYSUTIMEH */
+#else /* OS2 */
+#ifdef VMS
+#ifndef UTIMEH
+#define UTIMEH
+#endif /* UTIMEH */
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* SUNOS41 */
+#endif /* OSF */
+#endif /* SVR4ORPOSIX */
+#endif /* NOSETTIME */
+
+#ifdef NEWFTP
+_PROTOTYP( int ftpisconnected, (void));
+_PROTOTYP( int ftpisloggedin, (void));
+_PROTOTYP( int ftpissecure, (void));
+#endif /* NEWFTP */
+
+_PROTOTYP( int readpass, (char *, char *, int));
+_PROTOTYP( int readtext, (char *, char *, int));
+
+#ifdef OS2
+_PROTOTYP(int ck_auth_loaddll, (VOID));
+_PROTOTYP(int ck_auth_unloaddll, (VOID));
+#endif /* OS2 */
+
+#ifdef NT
+_PROTOTYP(DWORD ckGetLongPathname,(LPCSTR lpFileName,
+ LPSTR lpBuffer, DWORD cchBuffer));
+#endif /* NT */
+
+#include "ckclib.h"
+
+/* End of ckcdeb.h */
+#endif /* CKCDEB_H */
diff --git a/ckermit-8.0.211/ckcfn2.c b/ckermit-8.0.211/ckcfn2.c
new file mode 100644
index 0000000..06b97a2
--- /dev/null
+++ b/ckermit-8.0.211/ckcfn2.c
@@ -0,0 +1,3297 @@
+/* C K C F N 2 -- System-independent Kermit protocol support functions... */
+
+/* ...Part 2 (continued from ckcfns.c) */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+/*
+ Note -- if you change this file, please amend the version number and date at
+ the top of ckcfns.c accordingly.
+*/
+
+#include "ckcsym.h" /* Compilation options */
+#include "ckcdeb.h" /* Debugging and other symbols */
+#include "ckcasc.h" /* ASCII symbols */
+#include "ckcker.h" /* Kermit symbols */
+#include "ckcxla.h" /* Translation */
+#include "ckcnet.h" /* IKS and VMS #define TCPSOCKET */
+#ifdef TCPSOCKET /* For TELNET business in spack() */
+extern int tn_nlm, ttnproto, tn_b_nlm;
+#endif /* TCPSOCKET */
+
+extern int parity, network, local, interrupted, fatalio, wasclosed;
+
+int kstartactive = 0; /* Flag for kstart() in a packet */
+
+static CHAR p_tbl[] = { /* Even parity table for dopar(). */
+ (CHAR) '\000', /* ANSI C casts '\ooo' constants */
+ (CHAR) '\201', /* to signed char, so we have to */
+ (CHAR) '\202', /* cast back to unsigned char... */
+ (CHAR) '\003',
+ (CHAR) '\204',
+ (CHAR) '\005',
+ (CHAR) '\006',
+ (CHAR) '\207',
+ (CHAR) '\210',
+ (CHAR) '\011',
+ (CHAR) '\012',
+ (CHAR) '\213',
+ (CHAR) '\014',
+ (CHAR) '\215',
+ (CHAR) '\216',
+ (CHAR) '\017',
+ (CHAR) '\220',
+ (CHAR) '\021',
+ (CHAR) '\022',
+ (CHAR) '\223',
+ (CHAR) '\024',
+ (CHAR) '\225',
+ (CHAR) '\226',
+ (CHAR) '\027',
+ (CHAR) '\030',
+ (CHAR) '\231',
+ (CHAR) '\232',
+ (CHAR) '\033',
+ (CHAR) '\234',
+ (CHAR) '\035',
+ (CHAR) '\036',
+ (CHAR) '\237',
+ (CHAR) '\240',
+ (CHAR) '\041',
+ (CHAR) '\042',
+ (CHAR) '\243',
+ (CHAR) '\044',
+ (CHAR) '\245',
+ (CHAR) '\246',
+ (CHAR) '\047',
+ (CHAR) '\050',
+ (CHAR) '\251',
+ (CHAR) '\252',
+ (CHAR) '\053',
+ (CHAR) '\254',
+ (CHAR) '\055',
+ (CHAR) '\056',
+ (CHAR) '\257',
+ (CHAR) '\060',
+ (CHAR) '\261',
+ (CHAR) '\262',
+ (CHAR) '\063',
+ (CHAR) '\264',
+ (CHAR) '\065',
+ (CHAR) '\066',
+ (CHAR) '\267',
+ (CHAR) '\270',
+ (CHAR) '\071',
+ (CHAR) '\072',
+ (CHAR) '\273',
+ (CHAR) '\074',
+ (CHAR) '\275',
+ (CHAR) '\276',
+ (CHAR) '\077',
+ (CHAR) '\300',
+ (CHAR) '\101',
+ (CHAR) '\102',
+ (CHAR) '\303',
+ (CHAR) '\104',
+ (CHAR) '\305',
+ (CHAR) '\306',
+ (CHAR) '\107',
+ (CHAR) '\110',
+ (CHAR) '\311',
+ (CHAR) '\312',
+ (CHAR) '\113',
+ (CHAR) '\314',
+ (CHAR) '\115',
+ (CHAR) '\116',
+ (CHAR) '\317',
+ (CHAR) '\120',
+ (CHAR) '\321',
+ (CHAR) '\322',
+ (CHAR) '\123',
+ (CHAR) '\324',
+ (CHAR) '\125',
+ (CHAR) '\126',
+ (CHAR) '\327',
+ (CHAR) '\330',
+ (CHAR) '\131',
+ (CHAR) '\132',
+ (CHAR) '\333',
+ (CHAR) '\134',
+ (CHAR) '\335',
+ (CHAR) '\336',
+ (CHAR) '\137',
+ (CHAR) '\140',
+ (CHAR) '\341',
+ (CHAR) '\342',
+ (CHAR) '\143',
+ (CHAR) '\344',
+ (CHAR) '\145',
+ (CHAR) '\146',
+ (CHAR) '\347',
+ (CHAR) '\350',
+ (CHAR) '\151',
+ (CHAR) '\152',
+ (CHAR) '\353',
+ (CHAR) '\154',
+ (CHAR) '\355',
+ (CHAR) '\356',
+ (CHAR) '\157',
+ (CHAR) '\360',
+ (CHAR) '\161',
+ (CHAR) '\162',
+ (CHAR) '\363',
+ (CHAR) '\164',
+ (CHAR) '\365',
+ (CHAR) '\366',
+ (CHAR) '\167',
+ (CHAR) '\170',
+ (CHAR) '\371',
+ (CHAR) '\372',
+ (CHAR) '\173',
+ (CHAR) '\374',
+ (CHAR) '\175',
+ (CHAR) '\176',
+ (CHAR) '\377'
+};
+
+/* D O P A R -- Add an appropriate parity bit to a character */
+
+CHAR
+#ifdef CK_ANSIC
+dopar(register CHAR ch)
+#else
+dopar(ch) register CHAR ch;
+#endif /* CK_ANSIC */
+ {
+ register unsigned int a;
+ if (!parity
+#ifdef TCPSOCKET
+ || (network && (ttnproto == NP_TELNET) && (TELOPT_ME(TELOPT_BINARY)))
+#ifndef NOXFER
+ || (!local && sstelnet) /* TELNET BINARY MODE */
+#endif /* NOXFER */
+#endif /* TCPSOCKET */
+ ) return((CHAR) (ch & 255)); else a = ch & 127;
+ switch (parity) {
+ case 'e': return(p_tbl[a]); /* Even */
+ case 'm': return((CHAR) (a | 128)); /* Mark */
+ case 'o': return((CHAR) (p_tbl[a] ^ 128)); /* Odd */
+ case 's': return((CHAR) a); /* Space */
+ default: return((CHAR) a); /* Something illegal */
+ }
+}
+
+#ifndef NOXFER /* Rest of this file... */
+
+#define NEWDPL /* New dynamic packet length method */
+
+#ifdef VMS
+extern int batch;
+#else
+extern int backgrd;
+#endif /* VMS */
+
+#ifdef DYNAMIC
+extern struct pktinfo *s_pkt; /* array of pktinfo structures */
+extern struct pktinfo *r_pkt; /* array of pktinfo structures */
+#else
+extern struct pktinfo s_pkt[]; /* array of pktinfo structures */
+extern struct pktinfo r_pkt[]; /* array of pktinfo structures */
+#endif /* DYNAMIC */
+
+extern int sseqtbl[], rseqtbl[], sbufuse[], sacktbl[], wslots, winlo, wslotn,
+ sbufnum, rbufnum, pktpaus, reliable;
+
+#ifdef STREAMING
+static int dontsend = 0;
+extern int streaming;
+#endif /* STREAMING */
+
+extern int ttprty; /* from ck*tio.c */
+extern int autopar;
+
+extern int spsiz, spmax, rpsiz, timint, timef, npad, bestlen, maxsend;
+extern int rpt, rptq, rptflg, capas, spsizf, en_fin, tsecs, flow;
+extern int pktnum, sndtyp, rcvtyp, bctr, bctu, bctl, rsn, rln, maxtry, size;
+extern int osize, maxsize, spktl, rpktl, nfils, stdouf, fsecs;
+extern int turn, turnch, displa, pktlog, seslog, xflg, mypadn;
+extern int hcflg, server, cxseen, czseen, discard, slostart;
+extern int nakstate, quiet, success, xitsta, what, filestatus;
+extern int spackets, rpackets, timeouts, retrans, crunched, urpsiz;
+extern int carrier, fdispla, srvidl;
+
+#ifdef GFTIMER
+extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
+#endif /* GFTIMER */
+
+extern long filcnt, filrej, ffc, flci, flco, tlci, tlco, tfc, speed;
+extern long filcps, tfcps;
+
+extern char *cmarg, filnam[];
+
+extern CHAR padch, mypadc, eol, seol, ctlq, sstate;
+extern CHAR *recpkt, *data, myinit[];
+extern CHAR *srvptr, stchr, mystch, *rdatap;
+extern CHAR padbuf[];
+extern CHAR * epktmsg;
+extern int epktrcvd, epktsent;
+
+#ifdef OS2 /* AUTODOWNLOAD parameters */
+extern int adl_kmode, adl_zmode; /* Match Packet to signal download */
+extern char * adl_kstr; /* KERMIT Download String */
+extern char * adl_zstr; /* ZMODEM Download String */
+#endif /* OS2 */
+
+#ifdef CK_AUTODL
+CHAR ksbuf[96] = { NUL, NUL }; /* Autodownload "Kermit Start" buf */
+#endif /* CK_AUTODL */
+
+int numerrs = 0; /* Number of packet errors so far */
+int rcvtimo = 0; /* Timeout for receiving a packet */
+int idletmo = 0; /* Flag for idle timeout */
+
+long filcps = 0L; /* CPS most recent file transferred */
+long tfcps = 0L; /* CPS most recent transaction */
+long xfsecs = 0L; /* Elapsed time for most recent file */
+#ifdef GFTIMER
+CKFLOAT fpxfsecs = 0.0; /* Ditto, but floating point */
+#endif /* GFTIMER */
+
+#ifdef CK_TIMERS
+int rrttbl[64], srttbl[64]; /* Packet timestamp tables */
+extern int rttflg;
+#define RTT_SCALE 1000
+long
+ rttsamples, /* Round trip time samples */
+ rttdelay, /* RTT delay */
+ pktintvl, /* Interpacket arrival time */
+ rttvariance, /* RTT variance */
+ rttstddev; /* RTT standard deviation */
+#endif /* CK_TIMERS */
+
+/* CRC generation tables */
+
+long crcta[16] = { 0L, 010201L, 020402L, 030603L, 041004L,
+ 051205L, 061406L, 071607L, 0102010L, 0112211L, 0122412L, 0132613L, 0143014L,
+ 0153215L, 0163416L, 0173617L
+};
+
+long crctb[16] = { 0L, 010611L, 021422L, 031233L, 043044L,
+ 053655L, 062466L, 072277L, 0106110L, 0116701L, 0127532L, 0137323L, 0145154L,
+ 0155745L, 0164576L, 0174367L
+};
+
+#ifdef CK_TIMERS
+/*
+ Round-trip timer calculations adapted from Tim Kientzle's article,
+ "Improving Kermit Performance", Dr Dobb's Journal, February 1996.
+*/
+
+
+/* R T T I N I T -- Initialize timers at start of transaction */
+
+VOID
+rttinit() { /* Initialize round-trip timing */
+ int i;
+
+ if (timint == 0)
+ return;
+
+ rttsamples = 0L; /* Samples (packets) */
+ rttvariance = 0L; /* Variance in delay */
+ rttdelay = (long) timint * RTT_SCALE; /* Delay */
+ pktintvl = (long) timint * RTT_SCALE; /* Delay */
+ rttstddev = (long) timint * RTT_SCALE; /* Standard deviation of delay */
+
+ /* Tables of timestamps indexed by packet sequence number */
+
+ for (i = 0; i < 64; i++) {
+ rrttbl[i] = -1; /* Time each packet was received */
+ srttbl[i] = -1; /* Time each packet was sent */
+ }
+ rcvtimo = timint; /* Initial timeout is what user said */
+}
+
+/* G E T R T T -- Get packet round trip time */
+/*
+ Call with nakstate == 0 if file sender, nonzero if receiver,
+ and n == packet sequence number of the packet we just received.
+
+ Returns:
+ -1 on failure with rcvtimo set to timint (what the user said), or:
+ 0 on success with rcvtimo set to dynamically calculated value:
+ 1 <= rcvtimo <= timint * 3.
+*/
+int
+getrtt(nakstate, n) int nakstate, n; {
+ extern int mintime, maxtime;
+ static int prevz = 0, prevr = 0;
+ int x, y, yy, z = 0, zz = 0; /* How long did it take to get here? */
+
+ rcvtimo = timint; /* Default timeout is what user said */
+
+ if (timint == 0) /* We're not timing out. */
+ return(0);
+
+ if (!rttflg) /* Not supposed to be doing this? */
+ return(-1); /* So don't */
+
+ if (!RTT_SCALE) /* Paranoia... */
+ return(-1);
+
+ /* rtimer() (reset timer) is not called until 1st data packet */
+#ifdef GFTIMER
+ /* rftimer(); */
+#endif /* GFTIMER */
+ /* S (F [ A ] D* Z)* B */
+
+ /* NOTE: we calculate both the round-trip time AND the packet */
+ /* arrival rate. We don't use the RTT for anything, we just display it. */
+ /* Timeouts are based on the packet arrival rate. */
+
+ if (spackets > 3) { /* Don't start till 4th packet */
+ if (nakstate) { /* File receiver */
+ x = rrttbl[n]; /* Time when I got packet n */
+ y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
+ yy = srttbl[n > 0 ? n - 1 : 63]; /* Time when I sent ACK(n-1) */
+ if (x > -1 && y > -1) { /* Be careful */
+ z = x - y; /* Packet rate */
+ zz = x - yy; /* Round trip time */
+ z++; /* So sender & receiver differ */
+ debug(F101,"RTT RECV","",z);
+ } else { /* This shouldn't happen */
+ debug(F101,"RTT RECV ERROR spackets","",spackets);
+ debug(F101,"RTT RECV ERROR sequence","",n);
+ return(-1);
+ }
+ } else { /* File sender */
+ x = rrttbl[n]; /* Time when I got ACK(n) */
+ y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */
+ yy = srttbl[n]; /* Time when I sent n */
+ if (x > -1 && y > -1) {
+ z = x - y; /* Packet rate */
+ zz = x - yy; /* Round trip time */
+ debug(F101,"RTT SEND","",z);
+ } else {
+ debug(F100,"RTT SEND ERROR","",0);
+ return(-1);
+ }
+ }
+ if (z < 1) /* For fast connections */
+ z = RTT_SCALE / 2; /* Convert to scale... */
+ else
+ z *= RTT_SCALE;
+ debug(F101,"RTT z scaled","",z);
+
+ if (zz < 1) /* For fast connections */
+ zz = RTT_SCALE / 2; /* Convert to scale... */
+ else
+ zz *= RTT_SCALE;
+
+ rttdelay = zz; /* Round trip time of this packet */
+#ifdef COMMENT
+/*
+ This was used in C-Kermit 7.0 (and 6.0?) but not only is it overkill,
+ it also can produce ridiculously long timeouts under certain conditions.
+ Replaced in 8.0 by a far simpler and more aggressive strategy.
+*/
+ if (rttsamples++ == 0L) { /* First sample */
+ pktintvl = z;
+ } else { /* Subsequent samples */
+ long oldavg = pktintvl;
+ long rttdiffsq;
+
+ if (rttsamples > 30) /* Use real average for first 30 */
+ rttsamples = 30; /* then decaying average. */
+
+ /* Average delay, difference squared, variance, std deviation */
+
+ pktintvl += (z - pktintvl) / rttsamples;
+ rttdiffsq = (z - oldavg) * (z - oldavg);
+ rttvariance += (rttdiffsq - rttvariance) / rttsamples;
+ debug(F101,"RTT stddev1","",rttstddev);
+ if (rttstddev < 1L) /* It can be zero, in which case */
+ rttstddev = RTT_SCALE / 3; /* set it to something small... */
+ rttstddev = (rttstddev + rttvariance / rttstddev) / 2;
+ }
+ debug(F101,"RTT stddev2","",rttstddev);
+ debug(F101,"RTT delay ","",pktintvl);
+ rcvtimo = (pktintvl + (3L * rttstddev)) / RTT_SCALE + 1;
+ if (rpackets < 32) /* Allow for slow start */
+ rcvtimo += rcvtimo + 2;
+ else if (rpackets < 64)
+ rcvtimo += rcvtimo / 2 + 1;
+ /* On a reliable link, don't try too hard to time out. */
+ /* Especially on fast local network connections. */
+ if (server && what == W_NOTHING) /* Server command wait */
+ rcvtimo = rcvtimo; /* == srvtim */
+ else if (reliable == SET_ON && rcvtimo > 0) /* Reliable */
+ rcvtimo = rcvtimo +15; /* and not server command wait */
+ else /* Not reliable or server cmd wait */
+ rcvtimo = rcvtimo;
+ if (rcvtimo < mintime) /* Lower bound */
+ rcvtimo = mintime;
+ if (maxtime > 0) { /* User specified an upper bound */
+ if (rcvtimo > maxtime)
+ rcvtimo = maxtime;
+ } else if (maxtime == 0) { /* User didn't specify */
+ if (rcvtimo > timint * 6)
+ rcvtimo = timint * 6;
+ }
+#else /* COMMENT */
+#ifdef CKFLOAT
+ {
+ CKFLOAT x;
+ x = (CKFLOAT)(prevz + z + z) / 3.0;
+ rcvtimo = (int)((((CKFLOAT)x * 2.66) / RTT_SCALE) + 0.5);
+ debug(F101,"RTT rcvtimo (float)","",rcvtimo);
+ }
+#else
+ rcvtimo = (prevz + z + z) / RTT_SCALE;
+ debug(F101,"RTT rcvtimo (int)","",rcvtimo);
+#endif /* CKFLOAT */
+#endif /* COMMENT */
+
+ zz = (rttdelay + 500) / 1000;
+ if (rcvtimo > (zz * 3))
+ rcvtimo = zz * 3;
+
+ if (rcvtimo < 1)
+ rcvtimo = 1;
+ if (mintime > 0) {
+ if (rcvtimo < mintime) /* Lower bound */
+ rcvtimo = mintime;
+ }
+ if (maxtime > 0) { /* Upper bound */
+ if (rcvtimo > maxtime)
+ rcvtimo = maxtime;
+ }
+ if (rcvtimo == (prevr - 1))
+ rcvtimo++;
+
+ debug(F101,"RTT final rcvtimo","",rcvtimo);
+ }
+ prevz = z;
+ prevr = rcvtimo;
+ return(0);
+}
+#endif /* CK_TIMERS */
+
+/* I N P U T -- Attempt to read packet number 'pktnum'. */
+
+/*
+ This is the function that feeds input to Kermit's finite state machine,
+ in the form of a character in the range 32-126, normally a packet type
+ (uppercase letter) or pseudo-packet-type (lowercase letter).
+
+ If a special start state is in effect, that state is returned as if it were
+ the type of an incoming packet.
+*/
+int
+input() {
+ int type = 0, acktype; /* Received packet type */
+ int x, y, k; /* Workers */
+ int z, pi, nf; /* Worker, packet index, NAK flag */
+ int nak2ack = 0;
+
+ debug(F000,"input sstate","",sstate);
+ debug(F101,"input nakstate","",nakstate);
+ debug(F000,"input sndtyp","",sndtyp);
+ debug(F101,"input xitsta","",xitsta);
+ debug(F101,"input what","",what);
+
+ while (1) { /* Big loop... */
+/*
+ It is ttchk()'s responsibility to tell us if the connection is broken,
+ and to do so instantly and nondestructively -- no blocking, etc, that would
+ slow down file transfer.
+*/
+ if (ttchk() < 0) {
+ debug(F100,"input CONNECTION BROKEN","",0);
+ fatalio = 1;
+ return('q');
+ }
+ if (sstate != 0) { /* If a start state is in effect, */
+ type = sstate; /* return it like a packet type, */
+ sstate = 0; /* and then nullify it. */
+ numerrs = 0; /* (PWP) no errors so far */
+ return(type);
+ }
+ if (nakstate) { /* This section for file receiver. */
+ if (wslots > 1) { /* If we're doing windows, */
+ x = rseqtbl[winlo]; /* see if desired packet already in. */
+ debug(F101,"input winlo","",winlo);
+ debug(F101,"input rseqtbl[winlo]","",rseqtbl[winlo]);
+ if (x > -1) { /* Already there? */
+ if (r_pkt[x].pk_seq == winlo) { /* (double check) */
+ rsn = winlo; /* Yes, return its info */
+ debug(F101,"input return pre-stashed packet","",rsn);
+ dumprbuf();
+ rdatap = r_pkt[x].pk_adr; /* like rpack would do. */
+ rln = (int)strlen((char *) rdatap);
+ type = r_pkt[x].pk_typ;
+ break;
+ }
+ }
+ }
+ type = rpack(); /* Try to read a packet. */
+ debug(F101,"input rpack","",type);
+
+ while (type == 'e') { /* Handle echoes */
+ debug(F101,"input echo discarded","",type);
+ type = rpack();
+ }
+#ifdef DEBUG
+ if (deblog) {
+ if (type == 'D')
+ debug(F011,"input type D=",(char *)rdatap,39);
+ else
+ debug(F000,"input type",(char *)rdatap,type);
+ }
+#endif /* DEBUG */
+#ifndef OLDCHKINT
+ if (type == 'z') {
+ epktrcvd = 1;
+ errpkt((CHAR *)"User cancelled.");
+ type = 'E';
+ break;
+ }
+#endif /* OLDCHKINT */
+ if (type < -1) {
+ char * s;
+ s = (type == -2) ?
+ "FAILED - Interrupted" :
+ "FAILED - Connection lost";
+
+ xxscreen(SCR_PT,'q',0L,s);
+ dologend();
+ return('q'); /* Ctrl-C or connection lost */
+ }
+ if (type < 0) { /* Receive window full */
+ /* Another thing to do here would be to delete */
+ /* the highest packet and NAK winlo. But that */
+ /* shouldn't be necessary since the other Kermit */
+ /* should not have sent a packet outside the window. */
+#ifdef COMMENT
+ char foo[256];
+ ckmakxmsg(foo,256,"Receive window full (rpack): wslots=",
+ ckitoa(wslots)," winlo=",ckitoa(winlo)," pktnum=",
+ ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
+ errpkt((CHAR *)foo);
+ debug(F100,foo,"",0);
+#else
+ errpkt((CHAR *)"Receive window full");
+ debug(F101,"rpack receive window full","",0);
+ debug(F101," wslots","",wslots);
+ debug(F101," winlo","",winlo);
+ debug(F101," pktnum","",pktnum);
+#endif
+ dumprbuf();
+ type = 'E';
+ break;
+ }
+ dumprbuf();
+
+#ifdef OLDCHKINT
+ if (chkint() < 0) { /* Check for console interrupts. */
+ errpkt((CHAR *)"User cancelled."); /* (old way) */
+ type = 'E';
+ break;
+ }
+#endif /* OLDCHKINT */
+
+#ifdef STREAMING
+ if (streaming) { /* Streaming */
+ if (type == 'Q' || type == 'T') { /* Errors are fatal. */
+ crunched++; /* For statistics */
+ errpkt((CHAR *)"Transmission error on reliable link.");
+ type = 'E';
+ }
+ }
+#endif /* STREAMING */
+ if (type == 'E') {
+ debug(F101,"input got E, nakstate","",nakstate);
+ break; /* Error packet */
+ }
+ if (type == 'Q') { /* Crunched packet. */
+ crunched++;
+ numerrs++;
+/*
+ Packet arrived damaged. It was most likely the packet we were expecting
+ next, so we send a NAK for that packet. Prior to 5A(189), we always
+ NAK'd winlo here, but that was bad because if two (or more) different
+ packets were damaged, we would keep NAKing the first one and never NAK the
+ other ones, which could result in a lengthy series of timeouts. Now we
+ NAK the oldest as-yet-unNAK'd missing packet.
+*/
+#ifdef CK_TIMERS
+ rcvtimo++; /* Stretch the timeout a little */
+#endif /* CK_TIMERS */
+ z = (winlo + wslots) % 64; /* Search from winlo to z */
+ debug(F101,"ZZZ crunched z","",z);
+ nf = 0; /* NAK flag not set yet */
+ for (x = winlo; x != z; x = (x + 1) % 64) {
+ debug(F101,"ZZZ x","",x);
+ if (rseqtbl[x] > -1) /* Have I received packet x? */
+ continue; /* Yes, go on. */
+ debug(F101,"ZZZ x not recd yet","",x);
+ pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
+ if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
+ debug(F101,"ZZZ x not NAK'd yet","",x);
+ nack(x); /* No, NAK it now. */
+ nf = 1; /* Flag that I did. */
+ break;
+ }
+ }
+ if (!nf) { /* If we didn't NAK anything above, */
+ debug(F101,"ZZZ NAKing winlo","",winlo);
+ if (nack(winlo) < 0) { /* we have to NAK winlo (again) */
+ errpkt((CHAR *)"Too many retries."); /* Too many */
+ type = 'E';
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (type == 'T') { /* Timeout */
+#ifndef OS2
+ /* K95 does this its own way */
+ if (server && srvidl) {
+ idletmo = 1;
+ debug(F101,"SERVER IDLE TIMEOUT","",srvidl);
+ return('q');
+ }
+#endif /* OS2 */
+#ifdef CK_TIMERS
+ rcvtimo++; /* Stretch the timeout a little */
+#endif /* CK_TIMERS */
+ timeouts++;
+ debug(F101,"input receive-state timeout, winlo","",winlo);
+ /* NAK only the packet at window-low */
+ debug(F101,"input sending NAK for winlo","",winlo);
+ x = ttchk();
+ if (x > 0) /* Don't give up if there is still */
+ continue; /* something to read. */
+ else if (x < 0) {
+ dologend();
+ fatalio = 1;
+ return('q'); /* Connection Lost */
+ }
+ if (nack(winlo) < 0) {
+ debug(F101,"input sent too many naks","",winlo);
+ errpkt((CHAR *)"Too many retries.");
+ type = 'E';
+ break;
+ } else continue;
+ }
+ if (rsn == winlo) { /* Got the packet we want, done. */
+#ifdef CK_TIMERS
+ if (rttflg && timint) /* Dynamic round trip timers? */
+ getrtt(nakstate, rsn); /* yes, do it. */
+#endif /* CK_TIMERS */
+ debug(F101,"input rsn=winlo","",rsn);
+ break;
+ }
+
+ /* Got a packet out of order. */
+
+ debug(F101,"input out of sequence, rsn","",rsn);
+ k = rseqtbl[rsn]; /* Get window slot of this packet. */
+ debug(F101,"input rseqtbl[rsn]","",k);
+ if (k < 0) {
+ debug(F101,"input recv can't find index for rcvd pkt","",rsn);
+ /* Was "Internal error 21" */
+ /* This should not happen */
+ errpkt((CHAR *)"Sliding windows protocol error.");
+ type = 'E';
+ break;
+ }
+ y = chkwin(rsn,winlo,wslots); /* See what window it's in. */
+ debug(F101,"input recv chkwin","",y);
+ if (y == 1) { /* From previous window. */
+#ifdef STREAMING
+ if (!streaming) /* NO RESEND IF STREAMING! */
+#endif /* STREAMING */
+ resend(rsn); /* Resend the ACK (might have data) */
+ freerpkt(rsn); /* Get rid of received packet */
+ continue; /* Back to wait for another packet */
+ } else { /* In this window or out of range */
+ if (y < 0) /* If out of range entirely, */
+ freerpkt(rsn); /* release its buffer */
+
+#ifdef STREAMING
+ if (streaming) { /* Streaming (this shouldn't happen) */
+ errpkt((CHAR *)"Sequence error on reliable link.");
+ type = 'E';
+ break;
+ }
+#endif /* STREAMING */
+
+/* If our receive window is full, NAK window-low */
+
+ if (rbufnum < 1) { /* Receive window full? */
+ if (nack(winlo) < 0) { /* No choice, must NAK winlo. */
+ errpkt((CHAR *)"Too many retries."); /* Too many */
+ type = 'E';
+ break;
+ } else continue;
+ }
+/*
+ Receive window not full. This is a packet in the current window but it is
+ not the desired packet at winlo. So therefore there are gaps before this
+ packet. So we find the "lowest" unNAK'd missing packet, if any, between
+ winlo and this one, and NAK it. If there are no as-yet-unNAK'd missing
+ packets in the window, then we send nothing and go wait for another packet.
+ In theory, this could result in a timeout, but in practice it is likely that
+ the already-NAK'd missing packets are already on their way. Note, we do not
+ NAK ahead of ourselves, as that only creates unnecessary retransmissions.
+*/
+ for (x = winlo; x != rsn; x = (x + 1) % 64) {
+ if (rseqtbl[x] > -1) /* Have I received packet x? */
+ continue; /* Yes, check next sequence number. */
+ pi = sseqtbl[x]; /* No, have I NAK'd it yet? */
+ if (pi < 0 || s_pkt[pi].pk_rtr == 0) {
+ nack(x); /* No, NAK it now. */
+ break;
+ }
+ }
+ }
+/*!!!*/
+ } else { /* Otherwise file sender... */
+
+#ifdef STREAMING
+ if (streaming && sndtyp == 'D') {
+ debug(F101,"STREAMING input streaming","",streaming);
+ debug(F000,"STREAMING input sndtyp","",sndtyp);
+ rsn = winlo;
+ type = 'Y'; /* Pretend we got an ACK */
+ }
+#endif /* STREAMING */
+ if (!nak2ack) { /* NAK(n+1) = ACK(n) */
+ if (wslots > 1) { /* Packet at winlo already ACK'd? */
+ if (sacktbl[winlo]) { /* If so, */
+ sacktbl[winlo] = 0; /* Turn off the ACK'd flag */
+ winlo = (winlo + 1) % 64; /* Rotate the window */
+ type = 'Y'; /* And return ACK */
+ debug(F101,
+ "input send returning pre-stashed ACK","",
+ winlo-1);
+ break;
+ }
+ }
+#ifdef STREAMING
+ if (!(streaming && sndtyp == 'D')) { /* Not streaming | data */
+ type = rpack(); /* Try to read an acknowledgement */
+ } else { /* Streaming and in Data phase */
+ type = 'Y'; /* Assume all is normal */
+ if (chkint() < 0) /* Check for console interrupts. */
+ type = 'z';
+ else if (ttchk() > 4 + bctu) /* Check for return traffic */
+ type = rpack();
+ debug(F000,"input streaming type","",type);
+ }
+#endif /* STREAMING */
+ debug(F111,"input send",(char *) rdatap,(int) type);
+ while (type == 'e') { /* Handle echoes */
+ debug(F000,"echo discarded","",type);
+ type = rpack();
+ }
+#ifndef OLDCHKINT
+ if (type == 'z') {
+ epktrcvd = 1;
+ errpkt((CHAR *)"User cancelled.");
+ type = 'E';
+ break;
+ }
+#endif /* OLDCHKINT */
+ if (type < -1) {
+ xxscreen(SCR_PT,'q',0L,
+ ((char *)((type == -2) ?
+ "Interrupted" :
+ "Connection lost"))
+ );
+ if (type != -2)
+ dologend();
+ return('q'); /* Ctrl-C or connection lost */
+ }
+ if (type == -1) {
+#ifdef COMMENT
+ char foo[256];
+ ckmakxmsg(foo,256,
+ "Receive window full (error 18): wslots=",
+ ckitoa(wslots),
+ " winlo=",ckitoa(winlo)," pktnum=",
+ ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL);
+ errpkt((CHAR *)foo);
+ debug(F100,foo,"",0);
+#else
+ errpkt((CHAR *)"Receive window full"); /* was "internal */
+ debug(F101," wslots","",wslots); /* error 18" */
+ debug(F101," winlo","",winlo);
+ debug(F101," pktnum","",pktnum);
+#endif /* COMMENT */
+ dumprbuf();
+ type = 'E';
+ break;
+ }
+ dumprbuf(); /* Debugging */
+
+#ifdef OLDCHKINT
+ if (chkint() < 0) { /* Check for console interrupts. */
+ errpkt((CHAR *)"User cancelled.");
+ return(type = 'E');
+ }
+#endif /* OLDCHKINT */
+
+ /* Got a packet */
+
+#ifdef STREAMING
+ if (streaming) { /* Streaming */
+ if (type == 'Q' || type == 'T') { /* Errors are fatal. */
+ crunched++; /* For statistics */
+ errpkt((CHAR *)"Transmission error on reliable link.");
+ type = 'E';
+ }
+ }
+#endif /* STREAMING */
+ if (type == 'E') {
+ debug(F101,"input send got E, nakstate","",nakstate);
+ break; /* Error packet */
+ }
+ if (type == 'Q') { /* Crunched packet */
+ crunched++; /* For statistics */
+ numerrs++; /* For packet resizing */
+ x = resend(winlo); /* Resend window-low */
+ if (x < 0) {
+ type = 'E';
+ errpkt((CHAR *)"Too many retries");
+ break;
+ }
+ continue;
+ }
+ if (type == 'T') { /* Timeout waiting for ACKs. */
+ timeouts++; /* Count it */
+ numerrs++; /* Count an error too */
+ debug(F101,"input send state timeout, winlo","",winlo);
+
+ /* Retransmit the oldest un-ACK'd packet. */
+
+ debug(F101,"input send resending winlo","",winlo);
+ if (resend(winlo) < 0) { /* Check retries */
+ debug(F101,"input send too many resends","",maxtry);
+ errpkt((CHAR *)"Too many retries");
+ return(type = 'E');
+ }
+#ifdef NEWDPL
+ /* Reduce prevailing packet length */
+ x = sseqtbl[winlo]; /* Get length of packet we want ACKd */
+ if (x > -1) { /* Only if we have a valid index */
+ if (s_pkt[x].pk_typ == 'D') { /* only for D packets */
+ spsiz = (s_pkt[x].pk_len + 8) >> 1; /* halve it */
+ if (spsiz < 20) spsiz = 20; /* within reason */
+ debug(F101,"input T cut packet length","",spsiz);
+ }
+ }
+#endif /* NEWDPL */
+ continue;
+ }
+ }
+ /* Got an actual normal packet */
+
+ nak2ack = 0; /* Unset this flag. */
+ y = chkwin(rsn,winlo,wslots); /* Is it in the window? */
+ debug(F101,"input send rsn","",rsn);
+ debug(F101,"input send winlo","",winlo);
+ debug(F101,"input send chkwin","",y);
+
+ if (type == 'Y') { /* Got an ACK */
+ if (y == 0) { /* In current window */
+ if (spackets < 4) /* Error counter doesn't count */
+ numerrs = 0; /* until data phase. */
+ sacktbl[rsn]++; /* Mark the packet as ACK'd */
+ x = sseqtbl[rsn]; /* Get ACK'd packet's buffer index */
+ debug(F101,"bestlen ack x","",x);
+#ifdef NEWDPL
+ if (x > -1) {
+ acktype = s_pkt[x].pk_typ; /* Get type */
+ debug(F000,"bestlen ack type","",acktype);
+
+ if (acktype == 'D') { /* Adjust data packet length */
+ if (spsiz > bestlen) {
+ bestlen = spsiz;
+ debug(F101,"bestlen B","",bestlen);
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"bestlen retry","",s_pkt[x].pk_rtr);
+ debug(F101,"bestlen len","",s_pkt[x].pk_len);
+ debug(F101,"bestlen spackets","",spackets);
+ }
+#endif /* DEBUG */
+ /* Set new best length */
+ if (s_pkt[x].pk_rtr == 0 &&
+ s_pkt[x].pk_len + 8 > bestlen) {
+ bestlen = s_pkt[x].pk_len + 8;
+ if (bestlen > spmax)
+ bestlen = spmax;
+ debug(F101,"bestlen A","",bestlen);
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"bestlen wslots","",wslots);
+ debug(F101,"bestlen maxsend","",maxsend);
+ }
+#endif /* DEBUG */
+ /* Slow start */
+ if (slostart &&
+ (maxsend <= spmax) &&
+ (rpackets < 11) &&
+ (numerrs == 0)) {
+ spsiz = spsiz << 1;
+ debug(F101,"bestlen spsiz A","",spsiz);
+
+ /* Creep up to best length */
+ } else if ((spackets > 5) &&
+ (spsiz < bestlen - 8)) {
+ spsiz += (bestlen - spsiz) / 3;
+ debug(F101,"bestlen spsiz B","",spsiz);
+
+ /* Push the envelope */
+ } else if ((spackets % (wslots + 1) == 0) &&
+ (spackets > 6) &&
+ (bestlen < spmax - 8) &&
+ (spsiz < spmax)) {
+ spsiz += (spmax - bestlen) / 3;
+ debug(F101,"bestlen spsiz C","",spsiz);
+ }
+ /* But not too far */
+ if (spsiz > spmax) {
+ spsiz = spmax;
+ debug(F101,"bestlen spsiz D","",spsiz);
+ }
+ }
+ }
+#endif /* NEWDPL */
+
+#ifdef CK_TIMERS
+ if (rttflg && timint) /* If doing dynamic timers */
+ getrtt(nakstate, rsn); /* call routine to set it. */
+#endif /* CK_TIMERS */
+/*
+ NOTE: The following statement frees the buffer of the ACK we just got.
+ But the upper layers still need the data, like if it's the ACK to an I,
+ S, F, D, Z, or just about any kind of packet. So for now, freerbuf()
+ deallocates the buffer, but does not erase the data or destroy the pointer
+ to it. There's no other single place where these receive buffers can be
+ correctly freed (?) ...
+*/
+ freerpkt(rsn); /* Free the ACK's buffer */
+ freesbuf(rsn); /* *** Free the sent packet's buffer */
+ if (rsn == winlo) { /* Got the one we want */
+ sacktbl[winlo] = 0;
+ winlo = (winlo + 1) % 64;
+ debug(F101,"input send rotated send window","",winlo);
+ break; /* Return the ACK */
+ } else {
+ debug(F101,"input send mark pkt","",rsn);
+ continue; /* Otherwise go read another packet */
+ }
+ } else if (y == 1 && wslots < 2) { /* (190) ACK for previous */
+ numerrs++; /* == NAK for current, count error */
+ debug(F101,"input send ACK for previous","",rsn);
+ freerpkt(rsn); /* Free NAK's buffer */
+ x = resend(winlo); /* Resend current packet */
+ if (x < 0) {
+ type = 'E';
+ errpkt((CHAR *)"Too many retries");
+ break;
+ } else continue; /* Resend ok, go read another packet */
+ } else { /* Other cases, just ignore */
+ debug(F101,"input send ACK out of window","",rsn);
+ freerpkt(rsn);
+ continue;
+ }
+ }
+ if (type == 'N') { /* NAK */
+ numerrs++; /* Count an error */
+#ifdef STREAMING
+ if (streaming) { /* Streaming */
+ errpkt((CHAR *)"NAK received on reliable link.");
+ type = 'E';
+ break;
+ }
+#endif /* STREAMING */
+
+ debug(F101,"input send NAK","",rsn);
+#ifdef NEWDPL
+ /* Reduce prevailing packet length */
+ x = sseqtbl[rsn]; /* Length of packet that was NAK'd */
+ if (x > -1) { /* If it's a Data packet we've sent */
+ if (s_pkt[x].pk_typ == 'D') {
+ spsiz = (s_pkt[x].pk_len + 8) >> 1; /* Halve length */
+#ifdef COMMENT
+ /* This might be a good idea -- haven't tried it ... */
+ if (bestlen > 0 && spsiz > bestlen)
+ spsiz = bestlen;
+#endif /* COMMENT */
+ if (spsiz < 20) spsiz = 20;
+ debug(F101,"input N cut packet length","",spsiz);
+ }
+ }
+#endif /* NEWDPL */
+ freerpkt(rsn); /* Free buffer where NAK lies. */
+ if (y == 0) { /* In current window */
+ debug(F100," in window","",0);
+ k = sseqtbl[rsn]; /* Get pointer to NAK'd packet. */
+ if (k < 0 || (k > -1 && s_pkt[k].pk_typ == ' ')) {
+ x = resend(winlo); /* Packet we haven't sent yet. */
+ } else {
+ x = resend(rsn); /* Resend requested packet. */
+ }
+ if (x < 0) { /* Resend error is fatal. */
+ type = 'E';
+ errpkt((CHAR *)"Too many retries");
+ break;
+ } else continue; /* Resend ok, go read another packet */
+ } else if ((rsn == (pktnum + 1) % 64)) { /* NAK for next pkt */
+ if (wslots > 1) {
+ debug( F101,"NAK for next packet, windowing","",rsn);
+ x = resend(winlo); /* Resend window-low */
+ if (x < 0) {
+ type = 'E';
+ errpkt((CHAR *)"Too many retries");
+ break;
+ }
+ continue; /* Go back and read another pkt */
+ }
+ debug(F101,"NAK for next packet, no windowing","",rsn);
+ x = (rsn == 0) ? 63 : rsn - 1;
+ if (x == 0 && (sndtyp == 'S' || sndtyp == 'I')) {
+ resend(0); /* ACK for S or I packet missing */
+ continue; /* so resend the S or I */
+ }
+ rsn = x; /* Else, treat NAK(n+1) as ACK(n) */
+ nak2ack = 1; /* Go back and process the ACK */
+ continue;
+ } else if (y > 0) { /* NAK for pkt we can't resend */
+ debug(F101," NAK out of window","",rsn); /* bad... */
+ type = 'E';
+ errpkt((CHAR *)"NAK out of window");
+ break;
+ } else continue; /* Ignore other NAKs */
+ } /* End of file-sender NAK handler */
+
+ if (rsn == winlo) { /* Not ACK, NAK, timeout, etc. */
+ debug(F000,"input send unexpected type","",type);
+ break;
+ }
+ } /* End of file-sender section */
+ } /* End of input() while() loop */
+/*
+ When the window size is 1 and we have the packet we want, there can not
+ possibly be anything waiting for us on the connection that is useful to us.
+ However, there might be redundant copies of a packet we already got, which
+ would cause needless cycles of repeated packets. Therefore we flush the
+ communications input buffer now to try to get rid of undesired and unneeded
+ packets that we have not read yet.
+*/
+ if (wslotn == 1 /* (not wslots!) */
+#ifdef STREAMING
+ && !streaming /* But not when streaming */
+#endif /* STREAMING */
+ ) {
+ debug(F100,"input about to flush","",0);
+ ttflui(); /* Got what we want, clear input buffer. */
+ }
+#ifndef NEWDPL
+ if (!nakstate) /* When sending */
+ rcalcpsz(); /* recalculate size every packet */
+#endif /* NEWDPL */
+ if (type == 'E')
+ xitsta |= (what ? what : 1); /* Remember what failed. */
+ debug(F101,"input winlo","",winlo);
+ debug(F101,"input rsn","",rsn);
+ debug(F000,"input returning type","",type);
+ return(rcvtyp = type); /* Success, return packet type. */
+}
+
+#ifdef PARSENSE
+/* P A R C H K -- Check if Kermit packet has parity */
+
+/*
+ Call with s = pointer to packet, start = packet start character, n = length.
+ Returns 0 if packet has no parity, -1 on error, or, if packet has parity:
+ 'e' for even, 'o' for odd, 'm' for mark. Space parity cannot be sensed.
+ So a return value of 0 really means either space or none.
+ Returns -2 if parity has already been checked during this protocol operation.
+*/
+int
+#ifdef CK_ANSIC
+parchk(CHAR *s, CHAR start, int n)
+#else
+parchk(s,start,n) CHAR *s, start; int n;
+#endif /* CK_ANSIC */
+/* parchk */ {
+ CHAR s0, s1, s2, s3;
+
+ debug(F101,"parchk n","",n);
+ debug(F101,"parchk start","",start);
+
+ s0 = s[0] & 0x7f; /* Mark field (usually Ctrl-A) */
+
+ if (s0 != start || n < 5) return(-1); /* Not a valid packet */
+
+/* Look at packet control fields, which never have 8th bit set */
+/* First check for no parity, most common case. */
+
+ if (((s[0] | s[1] | s[2] | s[3]) & 0x80) == 0)
+ return(0); /* No parity or space parity */
+
+/* Check for mark parity */
+
+ if (((s[0] & s[1] & s[2] & s[3]) & 0x80) == 0x80)
+ return('m'); /* Mark parity */
+
+/* Packet has some kind of parity */
+/* Make 7-bit copies of control fields */
+
+ s1 = s[1] & 0x7f; /* LEN */
+ s2 = s[2] & 0x7f; /* SEQ */
+ s3 = s[3] & 0x7f; /* TYPE */
+
+/* Check for even parity */
+
+ if ((s[0] == p_tbl[s0]) &&
+ (s[1] == p_tbl[s1]) &&
+ (s[2] == p_tbl[s2]) &&
+ (s[3] == p_tbl[s3]))
+ return('e');
+
+/* Check for odd parity */
+
+ if ((s[0] != p_tbl[s0]) &&
+ (s[1] != p_tbl[s1]) &&
+ (s[2] != p_tbl[s2]) &&
+ (s[3] != p_tbl[s3]))
+ return('o');
+
+/* Otherwise it's probably line noise. Let checksum calculation catch it. */
+
+ return(-1);
+}
+#endif /* PARSENSE */
+
+/*
+ Check to make sure timeout intervals are long enough to allow maximum
+ length packets to get through before the timer goes off. If not, the
+ timeout interval is adjusted upwards.
+
+ This routine is called at the beginning of a transaction, before we
+ know anything about the delay characteristics of the line. It works
+ only for serial communication devices; it trusts the speed reported by
+ the operating system.
+
+ Call with a timout interval. Returns it, adjusted if necessary.
+*/
+int
+chktimo(timo,flag) int timo, flag; {
+ long cps, z; int x, y;
+#ifdef STREAMING
+ debug(F101,"chktimo streaming","",streaming);
+ if (streaming)
+ return(0);
+#endif /* STREAMING */
+
+ debug(F101,"chktimo timo","",timo); /* Timeout before adjustment */
+ debug(F101,"chktimo flag","",flag);
+
+ if (flag) /* Don't change timeout if user */
+ return(timo); /* gave SET SEND TIMEOUT command. */
+ debug(F101,"chktimo spmax","",spmax);
+ debug(F101,"chktimo urpsiz","",urpsiz);
+
+ if (!network) { /* On serial connections... */
+ speed = ttgspd(); /* Get current speed. */
+ if (speed > 0L) {
+ cps = speed / 10L; /* Convert to chars per second */
+ if (cps > 0L) {
+ long plen; /* Maximum of send and rcv pkt size */
+ z = cps * (long) timo; /* Chars per timeout interval */
+ z -= z / 10L; /* Less 10 percent */
+ plen = spmax;
+ if (urpsiz > spmax) plen = urpsiz;
+ debug(F101,"chktimo plen","",plen);
+ if (z < plen) { /* Compare with packet size */
+ x = (int) ((long) plen / cps); /* Adjust if necessary */
+ y = x / 10; /* Add 10 percent for safety */
+ if (y < 2) y = 2; /* Or 2 seconds, whichever is more */
+ x += y;
+ if (x > timo) /* If this is greater than current */
+ timo = x; /* timeout, change the timeout */
+ debug(F101,"chktimo new timo","",timo);
+ }
+ }
+ }
+ }
+ return(timo);
+}
+
+/* S P A C K -- Construct and send a packet */
+
+/*
+ spack() sends a packet of the given type, sequence number n, with len data
+ characters pointed to by d, in either a regular or extended- length packet,
+ depending on len. Returns the number of bytes actually sent, or else -1
+ upon failure. Uses global npad, padch, mystch, bctu, data. Leaves packet
+ fully built and null-terminated for later retransmission by resend().
+ Updates global sndpktl (send-packet length).
+
+ NOTE: The global pointer "data" is assumed to point into the 7th position
+ of a character array (presumably in packet buffer for the current packet).
+ It was used by getpkt() to build the packet data field. spack() fills in
+ the header to the left of the data pointer (the data pointer is defined
+ in getsbuf() in ckcfn3.c). If the address "d" is the same as "data", then
+ the packet's data field has been built "in place" and need not be copied.
+*/
+int
+#ifdef CK_ANSIC
+spack(char pkttyp, int n, int len, CHAR *d)
+#else
+spack(pkttyp,n,len,d) char pkttyp; int n, len; CHAR *d;
+#endif /* CK_ANSIC */
+/* spack */ {
+ register int i;
+ int ix, j, k, x, lp, longpkt, copy, loglen;
+
+#ifdef GFTIMER
+ CKFLOAT t1 = 0.0, t2 = 0.0;
+#endif /* GFTIMER */
+
+ register CHAR *cp, *mydata;
+ unsigned crc;
+
+ copy = (d != data); /* Flag whether data must be copied */
+
+#ifdef DEBUG
+ if (deblog) { /* Save lots of function calls! */
+ debug(F101,"spack n","",n);
+ if (pkttyp != 'D') { /* Data packets would be too long */
+ debug(F111,"spack data",data,data);
+ debug(F111,"spack d",d,d);
+ }
+ debug(F101,"spack len","",len);
+ debug(F101,"spack copy","",copy);
+ }
+#endif /* DEBUG */
+
+ longpkt = (len + bctl + 2) > 94; /* Decide whether it's a long packet */
+ mydata = data - 7 + (longpkt ? 0 : 3); /* Starting position of header */
+ k = sseqtbl[n]; /* Packet structure info for pkt n */
+#ifdef DEBUG
+ if (deblog) { /* Save 2 more function calls... */
+ debug(F101,"spack mydata","",mydata);
+ debug(F101,"spack sseqtbl[n]","",k);
+ if (k < 0) {
+#ifdef STREAMING
+ if (!streaming)
+#endif /* STREAMING */
+ debug(F101,"spack sending packet out of window","",n);
+ }
+ }
+#endif /* DEBUG */
+ if (k > -1) {
+ s_pkt[k].pk_adr = mydata; /* Remember address of packet. */
+ s_pkt[k].pk_seq = n; /* Record sequence number */
+ s_pkt[k].pk_typ = pkttyp; /* Record packet type */
+ }
+ spktl = 0; /* Initialize length of this packet */
+ i = 0; /* and position in packet. */
+
+/* Now fill the packet */
+
+ mydata[i++] = mystch; /* MARK */
+ lp = i++; /* Position of LEN, fill in later */
+
+ mydata[i++] = tochar(n); /* SEQ field */
+ mydata[i++] = pkttyp; /* TYPE field */
+ j = len + bctl; /* Length of data + block check */
+ if (longpkt) { /* Long packet? */
+ int x; /* Yes, work around SCO Xenix/286 */
+#ifdef CKTUNING
+ unsigned int chk;
+#endif /* CKTUNING */
+ x = j / 95; /* compiler bug... */
+ mydata[lp] = tochar(0); /* Set LEN to zero */
+ mydata[i++] = tochar(x); /* Extended length, high byte */
+ mydata[i++] = tochar(j % 95); /* Extended length, low byte */
+#ifdef CKTUNING
+ /* Header checksum - skip the function calls and loops */
+ chk = (unsigned) mydata[lp] +
+ (unsigned) mydata[lp+1] +
+ (unsigned) mydata[lp+2] +
+ (unsigned) mydata[lp+3] +
+ (unsigned) mydata[lp+4] ;
+ mydata[i++] = tochar((CHAR) ((((chk & 0300) >> 6) + chk) & 077));
+#else
+ mydata[i] = '\0'; /* Terminate for header checksum */
+ mydata[i++] = tochar(chk1(mydata+lp,5));
+#endif /* CKTUNING */
+ } else mydata[lp] = tochar(j+2); /* Normal LEN */
+/*
+ When sending a file, the data is already in the right place. If it weren't,
+ it might make sense to optimize this section by using memcpy or bcopy
+ (neither of which are portable), but only if our packets were rather long.
+ When receiving, we're only sending ACKs so it doesn't matter. So count the
+ following loop as a sleeping dog.
+*/
+ if (copy) /* Data field built in place? */
+ for ( ; len--; i++) mydata[i] = *d++; /* No, must copy. */
+ else /* Otherwise, */
+ i += len; /* Just skip past data field. */
+ mydata[i] = '\0'; /* Null-terminate for checksum calc. */
+
+ switch (bctu) { /* Block check */
+ case 1: /* 1 = 6-bit chksum */
+ ix = i - lp; /* Avoid "order of operation" error */
+ mydata[i++] = tochar(chk1(mydata+lp,ix));
+ break;
+ case 2: /* 2 = 12-bit chksum */
+ j = chk2(mydata+lp,i-lp);
+ mydata[i++] = (unsigned)tochar((j >> 6) & 077);
+ mydata[i++] = (unsigned)tochar(j & 077);
+ break;
+ case 3: /* 3 = 16-bit CRC */
+ crc = chk3(mydata+lp,i-lp);
+ mydata[i++] = (unsigned)tochar(((crc & 0170000)) >> 12);
+ mydata[i++] = (unsigned)tochar((crc >> 6) & 077);
+ mydata[i++] = (unsigned)tochar(crc & 077);
+ break;
+ case 4: /* 2 = 12-bit chksum, blank-free */
+ j = chk2(mydata+lp,i-lp);
+ mydata[i++] =
+ (unsigned)(tochar((unsigned)(((j >> 6) & 077) + 1)));
+ mydata[i++] = (unsigned)(tochar((unsigned)((j & 077) + 1)));
+ break;
+ }
+ loglen = i;
+ mydata[i++] = seol; /* End of line (packet terminator) */
+#ifdef TCPSOCKET
+/*
+ If TELNET connection and packet terminator is carriage return,
+ we must stuff either LF or NUL, according to SET TELNET NEWLINE-MODE
+ (tn_nlm), to meet the TELNET NVT specification, unless user said RAW.
+
+ If NEWLINE-MODE is set to LF instead of CR, we still send CR-NUL
+ on a NVT connection and CR on a binary connection.
+*/
+ if (
+#ifdef STREAMING
+ !dontsend &&
+#endif /* STREAMING */
+ ((network && ttnproto == NP_TELNET) || (!local && sstelnet))
+ && seol == CR) {
+ switch (TELOPT_ME(TELOPT_BINARY) ? tn_b_nlm : tn_nlm) {
+ case TNL_CR: /* NVT or BINARY */
+ break;
+ case TNL_CRNUL:
+ mydata[i++] = NUL;
+ break;
+ case TNL_CRLF:
+ mydata[i++] = LF;
+ break;
+ }
+ }
+#endif /* TCPSOCKET */
+ mydata[i] = '\0'; /* Terminate string */
+ if (
+#ifdef STREAMING
+ !dontsend &&
+#endif /* STREAMING */
+ pktlog
+ ) /* Save a function call! */
+ logpkt('s',n,mydata,loglen); /* Log the packet */
+
+ /* (PWP) add the parity quickly at the end */
+ if (parity) {
+ switch (parity) {
+ case 'e': /* Even */
+ for (cp = &mydata[i-1]; cp >= mydata; cp--)
+ *cp = p_tbl[*cp];
+ break;
+ case 'm': /* Mark */
+ for (cp = &mydata[i-1]; cp >= mydata; cp--)
+ *cp |= 128;
+ break;
+ case 'o': /* Odd */
+ for (cp = &mydata[i-1]; cp >= mydata; cp--)
+ *cp = p_tbl[*cp] ^ 128;
+ break;
+ case 's': /* Space */
+ for (cp = &mydata[i-1]; cp >= mydata; cp--)
+ *cp &= 127;
+ break;
+ }
+ }
+ if (pktpaus) msleep(pktpaus); /* Pause if requested */
+ x = 0;
+
+ if (npad) {
+#ifdef STREAMING
+ if (dontsend)
+ x = 0;
+ else
+#endif /* STREAMING */
+ x = ttol(padbuf,npad); /* Send any padding */
+ }
+ if (x > -1) {
+#ifdef CK_TIMERS
+ if (timint > 0) {
+ if (pkttyp == 'N')
+ srttbl[n > 0 ? n-1 : 63] = gtimer();
+ else
+ srttbl[n] = gtimer();
+ }
+#endif /* CK_TIMERS */
+ spktl = i; /* Remember packet length */
+ if (k > -1)
+ s_pkt[k].pk_len = spktl; /* also in packet info structure */
+
+#ifdef DEBUG
+#ifdef GFTIMER
+/*
+ This code shows (in the debug log) how long it takes write() to execute.
+ Sometimes on a congested TCP connection, it can surprise you -- 90 seconds
+ or more...
+*/
+ if (
+#ifdef STREAMING
+ !dontsend &&
+#endif /* STREAMING */
+ deblog
+ )
+ t1 = gftimer();
+#endif /* GFTIMER */
+#endif /* DEBUG */
+
+#ifdef STREAMING
+ if (dontsend) {
+ debug(F000,"STREAMING spack skipping","",pkttyp);
+ x = 0;
+ } else
+#endif /* STREAMING */
+ x = ttol(mydata,spktl); /* Send the packet */
+ }
+#ifdef STREAMING
+ if (!dontsend) {
+#endif /* STREAMING */
+ debug(F101,"spack spktl","",spktl);
+ debug(F101,"spack ttol returns","",x);
+ if (x < 0) { /* Failed. */
+ if (local && x < -1) {
+ xxscreen(SCR_ST,ST_ERR,0L,"FAILED: Connection lost");
+ /* We can't send an E packet because the connection is lost. */
+ epktsent = 1; /* So pretend we sent one. */
+ fatalio = 1; /* Remember we got a fatal i/o error */
+ dologend();
+ ckstrncpy((char *)epktmsg,"Connection lost",PKTMSGLEN);
+ }
+ return(x);
+ }
+ if (spktl > maxsend) /* Keep track of longest packet sent */
+ maxsend = spktl;
+#ifdef DEBUG
+#ifdef GFTIMER
+ if (deblog) { /* Log elapsed time for write() */
+ t2 = gftimer();
+ debug(F101,"spack ttol msec","",(long)((t2-t1)*1000.0));
+ }
+#endif /* GFTIMER */
+#endif /* DEBUG */
+#ifdef STREAMING
+ }
+#endif /* STREAMING */
+
+ sndtyp = pkttyp; /* Remember packet type for echos */
+#ifdef STREAMING
+ if (!dontsend) { /* If really sent, */
+ spackets++; /* count it. */
+ flco += spktl; /* Count the characters */
+ tlco += spktl; /* for statistics... */
+#ifdef DEBUG
+ if (deblog) { /* Save two function calls! */
+ dumpsbuf(); /* Dump send buffers to debug log */
+ debug(F111,"spack calling screen, mydata=",mydata,n);
+ }
+#endif /* DEBUG */
+ }
+#endif /* STREAMING */
+ if (local) {
+ int x = 0;
+ if (fdispla != XYFD_N) x = 1;
+ if ((fdispla == XYFD_B) && (pkttyp == 'D' || pkttyp == 'Y')) x = 0;
+ if (x)
+ xxscreen(SCR_PT,pkttyp,(long)n,(char *)mydata); /* Update screen */
+ }
+ return(spktl); /* Return length */
+}
+
+/* C H K 1 -- Compute a type-1 Kermit 6-bit checksum. */
+
+int
+chk1(pkt,len) register CHAR *pkt; register int len; {
+ register unsigned int chk;
+#ifdef CKTUNING
+#ifdef COMMENT
+ register unsigned int m; /* Avoid function call */
+ m = (parity) ? 0177 : 0377;
+ for (chk = 0; len-- > 0; pkt++)
+ chk += *pkt & m;
+#else
+ chk = 0;
+ while (len-- > 0) chk += (unsigned) *pkt++;
+#endif /* COMMENT */
+#else
+ chk = chk2(pkt,len);
+#endif /* CKTUNING */
+ chk = (((chk & 0300) >> 6) + chk) & 077;
+ debug(F101,"chk1","",chk);
+ return((int) chk);
+}
+
+/* C H K 2 -- Compute the numeric sum of all the bytes in the packet. */
+
+unsigned int
+chk2(pkt,len) register CHAR *pkt; register int len; {
+ register long chk;
+#ifdef COMMENT
+ register unsigned int m;
+ m = (parity) ? 0177 : 0377;
+ for (chk = 0; len-- > 0; pkt++)
+ chk += *pkt & m;
+#else
+ /* Parity has already been stripped */
+ chk = 0L;
+ while (len-- > 0) chk += (unsigned) *pkt++;
+#endif /* COMMENT */
+ debug(F101,"chk2","",(unsigned int) (chk & 07777));
+ return((unsigned int) (chk & 07777));
+}
+
+/* C H K 3 -- Compute a type-3 Kermit block check. */
+/*
+ Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup
+ table. Assumes the argument string contains no embedded nulls.
+*/
+#ifdef COMMENT
+unsigned int
+chk3(pkt,parity,len) register CHAR *pkt; int parity; register int len; {
+ register long c, crc;
+ register unsigned int m;
+ m = (parity) ? 0177 : 0377;
+ for (crc = 0; len-- > 0; pkt++) {
+ c = crc ^ (long)(*pkt & m);
+ crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]);
+ }
+ return((unsigned int) (crc & 0xFFFF));
+}
+#else
+unsigned int
+chk3(pkt,len) register CHAR *pkt; register int len; {
+ register long c, crc;
+ for (crc = 0; len-- > 0; pkt++) {
+ c = crc ^ (long)(*pkt);
+ crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]);
+ }
+ debug(F101,"chk3","",(unsigned int) (crc & 0xFFFF));
+ return((unsigned int) (crc & 0xFFFF));
+}
+#endif /* COMMENT */
+
+/* N X T P K T -- Next Packet */
+/*
+ Get packet number of next packet to send and allocate a buffer for it.
+ Returns:
+ 0 on success, with global pktnum set to the packet number;
+ -1 on failure to allocate buffer (fatal);
+ -2 if resulting packet number is outside the current window.
+*/
+int
+nxtpkt() { /* Called by file sender */
+ int j, n, x;
+
+ debug(F101,"nxtpkt pktnum","",pktnum);
+ debug(F101,"nxtpkt winlo ","",winlo);
+ n = (pktnum + 1) % 64; /* Increment packet number mod 64 */
+ debug(F101,"nxtpkt n","",n);
+#ifdef STREAMING
+ if (!streaming) {
+ x = chkwin(n,winlo,wslots); /* Don't exceed window boundary */
+ debug(F101,"nxtpkt chkwin","",x);
+ if (x)
+ return(-2);
+ j = getsbuf(n); /* Get a buffer for packet n */
+ if (j < 0) {
+ debug(F101,"nxtpkt getsbuf failure","",j);
+ return(-1);
+ }
+ }
+#endif /* STREAMING */
+ pktnum = n;
+ return(0);
+}
+
+/* Functions for sending ACKs and NAKs */
+
+/* Note, we should only ACK the packet at window-low (winlo) */
+/* However, if an old packet arrives again (e.g. because the ACK we sent */
+/* earlier was lost), we ACK it again. */
+
+int
+ack() { /* Acknowledge the current packet. */
+ return(ackns(winlo,(CHAR *)""));
+}
+
+#ifdef STREAMING
+int
+fastack() { /* Acknowledge packet n */
+ int j, k, n, x;
+ n = winlo;
+
+ k = rseqtbl[n]; /* First find received packet n. */
+ debug(F101,"STREAMING fastack k","",k);
+ freesbuf(n); /* Free current send-buffer, if any */
+ if ((j = getsbuf(n)) < 0) {
+ /* This can happen if we have to re-ACK an old packet that has */
+ /* already left the window. It does no harm. */
+ debug(F101,"STREAMING fastack can't getsbuf","",n);
+ }
+ dontsend = 1;
+ x = spack('Y',n,0,(CHAR *)""); /* Now send it (but not really) */
+ dontsend = 0;
+ if (x < 0) return(x);
+ debug(F101,"STREAMING fastack x","",x);
+ if (k > -1)
+ freerbuf(k); /* don't need it any more */
+ if (j > -1)
+ freesbuf(j); /* and don't need to keep ACK either */
+ winlo = (winlo + 1) % 64;
+ return(0);
+}
+#endif /* STREAMING */
+
+int
+ackns(n,s) int n; CHAR *s; { /* Acknowledge packet n */
+ int j, k, x;
+ debug(F111,"ackns",s,n);
+
+ k = rseqtbl[n]; /* First find received packet n. */
+ debug(F101,"ackns k","",k);
+ freesbuf(n); /* Free current send-buffer, if any */
+ if ((j = getsbuf(n)) < 0) {
+ /* This can happen if we have to re-ACK an old packet that has */
+ /* already left the window. It does no harm. */
+ debug(F101,"ackns can't getsbuf","",n);
+ }
+ x = spack('Y',n,(int)strlen((char *)s),s); /* Now send it. */
+ if (x < 0) return(x);
+ debug(F101,"ackns winlo","",winlo);
+ debug(F101,"ackns n","",n);
+ if (n == winlo) { /* If we're acking winlo */
+ if (k > -1)
+ freerbuf(k); /* don't need it any more */
+ if (j > -1)
+ freesbuf(j); /* and don't need to keep ACK either */
+ winlo = (winlo + 1) % 64;
+ }
+ return(0);
+}
+
+int
+ackn(n) int n; { /* Send ACK for packet number n */
+ return(ackns(n,(CHAR *)""));
+}
+
+int
+ack1(s) CHAR *s; { /* Send an ACK with data. */
+ if (!s) s = (CHAR *)"";
+ debug(F110,"ack1",(char *)s,0);
+ return(ackns(winlo,s));
+}
+
+/* N A C K -- Send a Negative ACKnowledgment. */
+/*
+ Call with the packet number, n, to be NAK'd.
+ Returns -1 if that packet has been NAK'd too many times, otherwise 0.
+ Btw, it is not right to return 0 under error conditions. This is
+ done because the -1 code is used for cancelling the file transfer.
+ More work is needed here.
+*/
+int
+nack(n) int n; {
+ int i, x;
+
+ if (n < 0 || n > 63) {
+ debug(F101,"nack bad pkt num","",n);
+ return(0);
+ } else debug(F101,"nack","",n);
+ if ((i = sseqtbl[n]) < 0) { /* If necessary */
+ if (getsbuf(n) < 0) { /* get a buffer for this NAK */
+ debug(F101,"nack can't getsbuf","",n);
+ return(0);
+ } else i = sseqtbl[n]; /* New slot number */
+ }
+ if (maxtry > 0 && s_pkt[i].pk_rtr++ > maxtry) /* How many? */
+ return(-1); /* Too many... */
+
+/* Note, don't free this buffer. Eventually an ACK will come, and that */
+/* will set it free. If not, well, it's back to ground zero anyway... */
+
+ x = spack('N',n,0,(CHAR *) ""); /* NAKs never have data. */
+ return(x);
+}
+
+#ifndef NEWDPL /* This routine no longer used */
+/*
+ * (PWP) recalculate the optimal packet length in the face of errors.
+ * This is a modified version of the algorithm by John Chandler in Kermit/370,
+ * see "Dynamic Packet Size Control", Kermit News, V2 #1, June 1988.
+ *
+ * This implementation minimizes the total overhead equation, which is
+ *
+ * Total chars = file_chars + (header_len * num_packs)
+ * + (errors * (header_len + packet_len))
+ *
+ * Differentiate with respect to number of chars, solve for packet_len, get:
+ *
+ * packet_len = sqrt (file_chars * header_len / errors)
+ */
+
+/*
+ (FDC) New super-simple algorithm. If there was an error in the most recent
+ packet exchange, cut the send-packet size in half, down to a minimum of 20.
+ If there was no error, increase the size by 5/4, up to the maximum negotiated
+ length. Seems to be much more responsive than previous algorithm, which took
+ forever to recover the original packet length, and it also went crazy under
+ certain conditions.
+
+ Here's another idea for packet length resizing that keeps a history of the
+ last n packets. Push a 1 into the left end of an n-bit shift register if the
+ current packet is good, otherwise push a zero. The current n-bit value, w, of
+ this register is a weighted sum of the noise hits for the last n packets, with
+ the most recent weighing the most. The current packet length is some function
+ of w and the negotiated packet length, like:
+
+ (2^n - w) / (2^n) * (negotiated length)
+
+ If the present resizing method causes problems, think about this one a little
+ more.
+*/
+VOID
+rcalcpsz() {
+
+#ifdef COMMENT
+/* Old way */
+ register long x, q;
+ if (numerrs == 0) return; /* bounds check just in case */
+
+ /* overhead on a data packet is npad+5+bctr, plus 3 if extended packet */
+ /* an ACK is 5+bctr */
+
+ /* first set x = per packet overhead */
+ if (wslots > 1) /* Sliding windows */
+ x = (long) (npad+5+bctr); /* packet only, don't count ack */
+ else /* Stop-n-wait */
+ x = (long) (npad+5+3+bctr+5+bctr); /* count packet and ack. */
+
+ /* then set x = packet length ** 2 */
+ x = x * ( ffc / (long) numerrs); /* careful of overflow */
+
+ /* calculate the long integer sqrt(x) quickly */
+ q = 500;
+ q = (q + x/q) >> 1;
+ q = (q + x/q) >> 1;
+ q = (q + x/q) >> 1;
+ q = (q + x/q) >> 1; /* should converge in about 4 steps */
+ if ((q > 94) && (q < 130)) /* break-even point for long packets */
+ q = 94;
+ if (q > spmax) q = spmax; /* maximum bounds */
+ if (q < 10) q = 10; /* minimum bounds */
+ spsiz = q; /* set new send packet size */
+ debug(F101,"rcalcpsiz","",q);
+#else
+/* New way */
+ debug(F101,"rcalcpsiz numerrs","",numerrs);
+ debug(F101,"rcalcpsiz spsiz","",spsiz);
+ if (spackets < 3) {
+ numerrs = 0;
+ return;
+ }
+ if (numerrs)
+ spsiz = spsiz / 2;
+ else
+ spsiz = (spsiz / 4) * 5;
+ if (spsiz < 20) spsiz = 20;
+ if (spsiz > spmax) spsiz = spmax;
+ debug(F101,"rcalcpsiz new spsiz","",spsiz);
+ numerrs = 0;
+#endif /* COMMENT */
+}
+#endif /* NEWDPL */
+
+/* R E S E N D -- Retransmit packet n. */
+
+/*
+ Returns 0 or positive on success (the number of retries for packet n).
+ On failure, returns a negative number, and an error message is placed
+ in recpkt.
+*/
+int
+resend(n) int n; { /* Send packet n again. */
+ int j, k, x;
+#ifdef GFTIMER
+ CKFLOAT t1 = 0.0, t2 = 0.0;
+#endif /* GFTIMER */
+
+ debug(F101,"resend seq","",n);
+
+ k = chkwin(n,winlo,wslots); /* See if packet in current window */
+ j = -1; /* Assume it's lost */
+ if (k == 0) j = sseqtbl[n]; /* See if we still have a copy of it */
+ if (k != 0 || j < 0) { /* If not.... */
+ if (nakstate && k == 1) {
+/*
+ Packet n is in the previous window and we are the file receiver.
+ We already sent the ACK and deallocated its buffer so we can't just
+ retransmit the ACK. Rather than give up, we try some tricks...
+*/
+ if (n == 0 && spackets < 63 && myinit[0]) { /* ACK to Send-Init */
+/*
+ If the packet number is 0, and we're at the beginning of a protocol
+ operation (spackets < 63), then we have to resend the ACK to an I or S
+ packet, complete with parameters in the data field. So we take a chance and
+ send a copy of the parameters in an ACK packet with block check type 1.
+*/
+ int bctlsav; /* Temporary storage */
+ int bctusav;
+ bctlsav = bctl; /* Save current block check length */
+ bctusav = bctu; /* and type */
+ bctu = bctl = 1; /* Set block check to 1 */
+ x = spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit);
+ if (x < 0) return(x);
+ logpkt('#',n,(CHAR *)"<reconstructed>",0); /* Log it */
+ bctu = bctusav; /* Restore block check type */
+ bctl = bctlsav; /* and length */
+
+ } else { /* Not the first packet */
+/*
+ It's not the first packet of the protocol operation. It's some other packet
+ that we have already ACK'd and forgotten about. So we take a chance and
+ send an empty ACK using the current block-check type. Usually this will
+ work out OK (like when acking Data packets), and no great harm will be done
+ if it was some other kind of packet (F, etc). If we are requesting an
+ interruption of the file transfer, the flags are still set, so we'll catch
+ up on the next packet.
+*/
+ x = spack('Y',n,0,(CHAR *) "");
+ if (x < 0) return(x);
+ }
+ retrans++;
+ xxscreen(SCR_PT,'%',(long)pktnum,"Retransmission");
+ return(0);
+ } else {
+/*
+ Packet number is not in current or previous window. We seem to hit this
+ code occasionally at the beginning of a transaction, for apparently no good
+ reason. Let's just log it for debugging, send nothing, and try to proceed
+ with the protocol rather than killing it.
+*/
+ debug(F101,"resend PKT NOT IN WINDOW","",n);
+ debug(F101,"resend k","",k);
+ return(0);
+ }
+ }
+
+/* OK, it's in the window and it's not lost. */
+
+ debug(F101,"resend pktinfo index","",k);
+
+ if (maxtry > 0 && s_pkt[j].pk_rtr++ > maxtry) { /* Over retry limit */
+ xitsta |= what;
+ return(-1);
+ }
+ debug(F101,"resend retry","",s_pkt[j].pk_rtr); /* OK so far */
+ dumpsbuf(); /* (debugging) */
+ if (s_pkt[j].pk_typ == ' ') { /* Incompletely formed packet */
+ if (nakstate) { /* (This shouldn't happen any more) */
+ nack(n);
+ retrans++;
+ xxscreen(SCR_PT,'%',(long)pktnum,"(resend)");
+ return(s_pkt[j].pk_rtr);
+ } else { /* No packet to resend! */
+#ifdef COMMENT
+/*
+ This happened (once) while sending a file with 2 window slots and typing
+ X to the sender to cancel the file. But since we're cancelling anyway,
+ there's no need to give a scary message.
+*/
+ sprintf((char *)epktmsg,
+ "resend logic error: NPS, n=%d, j=%d.",n,j);
+ return(-2);
+#else
+/* Just ignore it. */
+ return(0);
+#endif /* COMMENT */
+ }
+ }
+#ifdef DEBUG
+#ifdef GFTIMER
+ if (deblog) t1 = gftimer();
+#endif /* GFTIMER */
+#endif /* DEBUG */
+
+ /* Everything ok, send the packet */
+#ifdef CK_TIMERS
+ if (timint > 0)
+ srttbl[n] = gtimer(); /* Update the timer */
+#endif /* CK_TIMERS */
+ x = ttol(s_pkt[j].pk_adr,s_pkt[j].pk_len);
+
+#ifdef DEBUG
+#ifdef GFTIMER
+ if (deblog) {
+ t2 = gftimer();
+ debug(F101,"resend ttol msec","",(long)((t2-t1)*1000.0));
+ }
+#endif /* GFTIMER */
+#endif /* DEBUG */
+ debug(F101,"resend ttol returns","",x);
+
+ retrans++; /* Count a retransmission */
+ xxscreen(SCR_PT,'%',(long)pktnum,"(resend)"); /* Tell user about resend */
+ logpkt('S',n,s_pkt[j].pk_adr, s_pkt[j].pk_len); /* Log the resent packet */
+ return(s_pkt[j].pk_rtr); /* Return the number of retries. */
+}
+
+/* E R R P K T -- Send an Error Packet */
+
+int
+errpkt(reason) CHAR *reason; { /* ...containing the reason given */
+ extern int rtimo, state, justone;
+ int x, y;
+ czseen = 1; /* Also cancels batch */
+ state = 0; /* Reset protocol state */
+ debug(F110,"errpkt",reason,0);
+ tlog(F110,"Protocol Error:",(char *)reason,0L);
+ xxscreen(SCR_EM,0,0L,reason);
+ encstr(reason);
+ x = spack('E',pktnum,size,data);
+ ckstrncpy((char *)epktmsg,(char *)reason,PKTMSGLEN);
+ y = quiet; quiet = 1; epktsent = 1; /* Close files silently. */
+ clsif(); clsof(1);
+ quiet = y;
+/*
+ I just sent an E-packet. I'm in local mode, I was receiving a file,
+ I'm not a server, and sliding windows are in use. Therefore, there are
+ likely to be a bunch of packets already "in the pipe" on their way to me
+ by the time the remote sender gets the E-packet. So the next time I
+ CONNECT or try to start another protocol operation, I am likely to become
+ terribly confused by torrents of incoming material. To prevent this,
+ the following code soaks up packets from the connection until there is an
+ error or timeout, without wasting too much time waiting.
+
+ Exactly the same problem occurs when I am in remote mode or if I am
+ in server mode with the justone flag set. In remote mode not only
+ does the packet data potentially get echo'd back to the sender which
+ is confusing to the user in CONNECT mode, but it also may result in the
+ host performing bizarre actions such as suspending the process if ^Z is
+ unprefixed, etc.
+
+ Furthermore, thousands of packets bytes in the data stream prevent the
+ client from being able to process Telnet Kermit Option negotiations
+ properly.
+*/
+#ifdef STREAMING
+ /* Because streaming sets the timeout to 0... */
+ if (streaming) {
+ timint = rcvtimo = rtimo;
+ streaming = 0;
+ }
+#endif /* STREAMING */
+ if (what & W_RECV &&
+ (!server || (server && justone)) &&
+ (wslots > 1
+#ifdef STREAMING
+ || streaming
+#endif /* STREAMING */
+ )) {
+#ifdef GFTIMER
+ CKFLOAT oldsec, sec = (CKFLOAT) 0.0;
+#else
+ int oldsec, sec = 0;
+#endif /* GFTIMER */
+ debug(F101,"errpkt draining","",wslots);
+ xxscreen(SCR_ST,ST_MSG,0l,"Draining incoming packets, wait...");
+ while (x > -1) { /* Don't bother if no connection */
+ oldsec = sec;
+#ifdef GFTIMER
+ sec = gftimer();
+ if (oldsec != (CKFLOAT) 0.0)
+ timint = rcvtimo = (int) (sec - oldsec + 0.5);
+#else
+ sec = gtimer();
+ if (oldsec != 0)
+ timint = rcvtimo = sec - oldsec + 1;
+#endif /* GFTIMER */
+ if (timint < 1)
+ timint = rcvtimo = 1;
+ msleep(50); /* Allow a bit of slop */
+ x = rpack(); /* Read a packet */
+ if (x == 'T' || x == 'z') /* Timed out means we're done */
+ break;
+ xxscreen(SCR_PT,x,rsn,""); /* Let user know */
+ }
+ xxscreen(SCR_ST,ST_MSG,0l,"Drain complete.");
+ }
+ if ((x = (what & W_KERMIT)))
+ xitsta |= x; /* Remember what failed. */
+ success = 0;
+ return(y);
+}
+
+/* scmd() -- Send a packet of the given type */
+
+int
+#ifdef CK_ANSIC
+scmd(char t, CHAR *dat)
+#else
+scmd(t,dat) char t; CHAR *dat;
+#endif /* CK_ANSIC */
+/* scmd */ {
+ int x;
+ extern char * srimsg;
+ debug(F000,"scmd",dat,t);
+ if (encstr(dat) < 0) { /* Encode the command string */
+ srimsg = "String too long";
+ return(-1);
+ }
+ x = spack(t,pktnum,size,data);
+ debug(F101,"scmd spack","",x);
+ return(x);
+}
+
+/* Compose and Send GET packet */
+
+struct opktparm { /* O-Packet item list */
+ CHAR * opktitem;
+ struct opktparm * opktnext;
+};
+
+struct opktparm * opkthead = NULL; /* Linked list of O-packet fields */
+int opktcnt = 0; /* O-Packet counter */
+char * srimsg = NULL; /* GET-Packet error message */
+
+/* S O P K T -- Send O-Packet */
+/*
+ Sends one O-Packet each time called, using first-fit method of filling
+ the packet from linked list of parameters pointed to by opkthead.
+ To be called repeatedly until list is empty or there is an error.
+ Returns:
+ -1 on failure.
+ 0 on success and no more fields left to send.
+ 1 on success but with more fields left to be sent.
+*/
+
+int
+sopkt() {
+ int n = 0; /* Field number in this packet */
+ int rc = 0; /* Return code */
+ int len = 0; /* Data field length */
+ char c = NUL;
+ struct opktparm * o = NULL;
+ struct opktparm * t = NULL;
+ struct opktparm * prev = NULL;
+ CHAR * dsave = data;
+ int x, ssave = spsiz;
+
+ srimsg = NULL; /* Error message */
+ o = opkthead; /* Point to head of list */
+ if (!o) { /* Oops, no list... */
+ srimsg = "GET Packet Internal Error 1";
+ debug(F100,"sopkt NULL list","",0);
+ return(-1);
+ }
+ while (o) { /* Go thru linked list... */
+ c = *(o->opktitem); /* Parameter code */
+ debug(F000,"sopkt",o->opktitem,c);
+ x = encstr((CHAR *)o->opktitem);
+ debug(F111,"sopkt encstr",dsave,x);
+ if (x < 0) { /* Encode this item */
+ if (n == 0) { /* Failure, first field in packet */
+ debug(F100,"sopkt overflow","",0);
+ spsiz = ssave; /* Restore these */
+ data = dsave;
+ o = opkthead; /* Free linked list */
+ while (o) {
+ if (o->opktitem) free(o->opktitem);
+ t = o->opktnext;
+ free((char *)o);
+ o = t;
+ }
+ opkthead = NULL;
+ srimsg = "GET Packet Too Long for Server";
+ return(-1); /* Fail */
+ } else { /* Not first field in packet */
+ debug(F110,"sopkt leftover",o->opktitem,0);
+ prev = o; /* Make this one the new previous */
+ o = o->opktnext; /* Get next */
+ c = NUL; /* So we know we're not done */
+ *data = NUL; /* Erase any partial encoding */
+ continue; /* We can try this one again later */
+ }
+ }
+ n++; /* Encoding was successful */
+ debug(F111,"sopkt field",data,x);
+ len += x; /* Total data field length */
+ data += x; /* Set up for next field... */
+ spsiz -= x;
+ free(o->opktitem); /* Free item just encoded */
+ if (o == opkthead) { /* If head */
+ opkthead = o->opktnext; /* Move head to next */
+ free((char *)o); /* Free this list node */
+ o = opkthead;
+ } else { /* If not head */
+ o = o->opktnext; /* Get next */
+ prev->opktnext = o; /* Link previous to next */
+ }
+ if (c == '@') /* Loop exit */
+ break;
+ if (!o && !opkthead) { /* Set up End Of Parameters Field */
+ o = (struct opktparm *)malloc(sizeof(struct opktparm));
+ if (o) {
+ opkthead = o;
+ if (!(o->opktitem = (CHAR *)malloc(3))) {
+ free((char *)o);
+ srimsg = "GET Packet Internal Error 8";
+ return(-1);
+ }
+ ckstrncpy((char *)(o->opktitem), "@ ", 3);
+ debug(F111,"sopkt o->opktitem",o->opktitem,
+ strlen((char *)(o->opktitem)));
+ o->opktnext = NULL;
+ }
+ }
+ }
+ data = dsave; /* Restore globals */
+ spsiz = ssave;
+ debug(F110,"sopkt data",data,0);
+ debug(F101,"sopkt opktcnt","",opktcnt);
+ if (opktcnt++ > 0) {
+ if (nxtpkt() < 0) { /* Get next packet number and buffer */
+ srimsg = "GET Packet Internal Error 9";
+ return(-1);
+ }
+ }
+ debug(F101,"sopkt pktnum","",pktnum);
+ rc = spack((char)'O',pktnum,len,data); /* Send O-Packet */
+ debug(F101,"sopkt spack","",rc);
+ if (rc < 0) /* Failed */
+ srimsg = "Send Packet Failure"; /* Set message */
+ else /* Succeeded */
+ rc = (c == '@') ? 0 : 1; /* 1 = come back for more, 0 = done */
+ debug(F101,"sopkt rc","",rc);
+ return(rc);
+}
+
+/* S R I N I T -- Send GET packet */
+/*
+ Sends the appropriate GET-Class packet.
+ Returns:
+ -1 on error
+ 0 if packet sent successfully and we can move on to the next state
+ 1 if an O-packet was sent OK but more O packets still need to be sent.
+*/
+int
+srinit(reget, retrieve, opkt) int reget, retrieve, opkt; {
+ int x = 0, left = 0;
+ extern int oopts, omode;
+ CHAR * p = NULL;
+#ifdef RECURSIVE
+ extern int recursive;
+ debug(F101,"srinit recursive","",recursive);
+#endif /* RECURSIVE */
+ debug(F101,"srinit reget","",reget);
+ debug(F101,"srinit retrieve","",retrieve);
+ debug(F101,"srinit opkt","",opkt);
+ debug(F101,"srinit oopts","",oopts);
+ debug(F101,"srinit omode","",omode);
+ debug(F110,"srinit cmarg",cmarg,0);
+ srimsg = NULL;
+
+ opktcnt = 0;
+ if (!cmarg) cmarg = "";
+ if (!*cmarg) {
+ srimsg = "GET with no filename";
+ debug(F100,"srinit null cmarg","",0);
+ return(-1);
+ }
+ if (opkt) { /* Extended GET is totally different */
+ char buf[16];
+ struct opktparm * o = NULL;
+ struct opktparm * prev = NULL;
+
+ buf[0] = NUL;
+
+ /* Build O-Packet fields and send (perhaps first) O-Packet */
+
+ if (oopts > -1) { /* Write Option flags */
+ o = (struct opktparm *)malloc(sizeof(struct opktparm));
+ if (!o) {
+ srimsg = "GET Packet Internal Error 2";
+ debug(F100,"srinit malloc fail O1","",0);
+ return(-1);
+ }
+ sprintf(buf,"Ox%d",oopts); /* safe */
+ x = (int) strlen(buf+2);
+ buf[1] = tochar(x);
+ o->opktitem = (CHAR *)malloc(x + 3);
+ if (!o->opktitem) {
+ srimsg = "GET Packet Internal Error 3";
+ debug(F100,"srinit malloc fail O2","",0);
+ return(-1);
+ }
+ ckstrncpy((char *)(o->opktitem),buf,x+3);
+ o->opktnext = NULL;
+ if (!opkthead)
+ opkthead = o;
+ prev = o;
+ }
+ if (omode > -1) { /* If Xfer Mode specified, write it */
+ o = (struct opktparm *)malloc(sizeof(struct opktparm));
+ if (!o) {
+ srimsg = "GET Packet Internal Error 4";
+ debug(F100,"srinit malloc fail M1","",0);
+ return(-1);
+ }
+ sprintf(buf,"Mx%d",omode); /* safe */
+ x = (int) strlen(buf+2);
+ buf[1] = tochar(x);
+ o->opktitem = (CHAR *)malloc(x + 3);
+ if (!o->opktitem) {
+ srimsg = "GET Packet Internal Error 5";
+ debug(F100,"srinit malloc fail O2","",0);
+ return(-1);
+ }
+ ckstrncpy((char *)(o->opktitem),buf,x+3);
+ o->opktnext = NULL;
+ if (!opkthead)
+ opkthead = o;
+ else
+ prev->opktnext = o;
+ prev = o;
+ }
+
+ /* Same deal for oname and opath eventually but not needed now... */
+
+ x = strlen(cmarg); /* Now do filename */
+ if (x > spsiz - 4) {
+ srimsg = "GET Packet Too Long for Server";
+ return(-1);
+ }
+ o = (struct opktparm *)malloc(sizeof(struct opktparm));
+ if (!o) {
+ srimsg = "GET Packet Internal Error 6";
+ debug(F100,"srinit malloc fail F1","",0);
+ return(-1);
+ }
+ left = x + 6;
+ o->opktitem = (CHAR *)malloc(left + 1);
+ if (!o->opktitem) {
+ srimsg = "GET Packet Internal Error 7";
+ debug(F100,"srinit malloc fail F2","",0);
+ return(-1);
+ }
+ p = o->opktitem;
+ *p++ = 'F';
+ left--;
+ if (x > 94) { /* Too long for normal length */
+ *p++ = SYN; /* Escape length with Ctrl-V */
+ *p++ = tochar(x / 95);
+ *p++ = tochar(x % 95);
+ left -= 3;
+ } else { /* Normal encoding for 94 or less */
+ *p++ = tochar(x);
+ left--;
+ }
+ ckstrncpy((char *)p,cmarg,left); /* Copy the filename */
+ o->opktnext = NULL;
+ if (!opkthead)
+ opkthead = o;
+ else
+ prev->opktnext = o;
+ prev = o;
+
+ /* End of Parameters */
+
+ prev->opktnext = NULL; /* End of list. */
+ return(sopkt());
+ }
+
+ /* Not Extended GET */
+
+ if (encstr((CHAR *)cmarg) < 0) { /* Encode the filename. */
+ srimsg = "GET Packet Too Long for Server";
+ return(-1);
+ }
+ if (retrieve) { /* Send the packet. */
+#ifdef RECURSIVE
+ if (recursive)
+ x = spack((char)'W',pktnum,size,data); /* GET /DELETE /RECURSIVE */
+ else
+#endif /* RECURSIVE */
+ x = spack((char)'H',pktnum,size,data); /* GET /DELETE */
+ }
+#ifdef RECURSIVE
+ else if (recursive)
+ x = spack((char)'V',pktnum,size,data); /* GET /RECURSIVE */
+#endif /* RECURSIVE */
+ else
+ x = spack((char)(reget ? 'J' : 'R'),pktnum,size,data); /* GET */
+ if (x < 0)
+ srimsg = "Send Packet Failure";
+ return(x < 0 ? x : 0);
+}
+
+
+/* K S T A R T -- Checks for a Kermit packet while in terminal mode. */
+
+/* (or command mode...) */
+
+#ifdef CK_AUTODL
+int
+#ifdef CK_ANSIC
+kstart(CHAR ch)
+#else
+kstart(ch) CHAR ch;
+#endif /* CK_ANSIC */
+/* kstart */ {
+ static CHAR * p = NULL;
+
+#ifdef OS2
+ static CHAR * pk = NULL;
+#endif /* OS2 */
+ ch &= 0177; /* Strip 8th bit */
+
+ /* Because we're in cooked mode at the command prompt... */
+
+ if (ch == LF) {
+ debug(F110,"kstart","ch == LF",0);
+ if ((what == W_COMMAND || what == W_INIT || what == W_NOTHING)) {
+ if (eol == CR) {
+ ch = eol;
+ debug(F110,"kstart","ch = CR",0);
+ }
+ }
+ }
+
+#ifdef OS2
+ if (adl_kmode == ADL_STR) {
+ if (!ch)
+ return(0);
+ if (!pk)
+ pk = adl_kstr;
+
+ if (ch == *pk) {
+ pk++;
+ if (*pk == '\0') {
+ pk = adl_kstr;
+ debug(F100, "kstart Kermit Start String","",0);
+ return(PROTO_K + 1);
+ }
+ } else
+ pk = adl_kstr;
+ }
+#endif /* OS2 */
+
+ if (ch == stchr) { /* Start of packet */
+ kstartactive = 1;
+ p = ksbuf;
+ *p = ch;
+ debug(F101,"kstart SOP","",ch);
+ } else if (ch == eol) { /* End of packet */
+ kstartactive = 0;
+ if (p) {
+ debug(F101,"kstart EOL","",ch);
+ p++;
+ if (p - ksbuf < 94 ) {
+ int rc = 0;
+ *p++ = ch;
+ *p = NUL;
+ rc = chkspkt((char *)ksbuf);
+ debug(F111,"kstart EOP chkspkt", ksbuf, rc);
+ p = NULL;
+ if (!rc) return(0);
+ if (rc == 2) rc = -1;
+ debug(F111,"kstart ksbuf",ksbuf,rc);
+ return(rc);
+ } else {
+ debug(F110,"kstart","p - ksbuf >= 94",0);
+ p = NULL;
+ }
+ }
+ } else if (p) {
+ if (ch < SP)
+ kstartactive = 0;
+ p++;
+ if (p - ksbuf < 94) {
+ *p = ch;
+ } else {
+ p = NULL;
+ debug(F110,"kstart","p - ksbuf >= 94",0);
+ }
+ }
+ return(0);
+}
+
+#ifdef CK_XYZ
+
+/* Z S T A R T -- Checks for a ZMODEM packet while in terminal mode. */
+
+int
+#ifdef CK_ANSIC
+zstart(CHAR ch)
+#else
+zstart(ch) CHAR ch;
+#endif /* CK_ANSIC */
+/* zstart */ {
+ static CHAR * matchstr = (CHAR *) "\030B00";
+ /* "rz\r**\030B00000000000000\r\033J\021"; */
+ static CHAR * p = NULL;
+ extern int inserver;
+
+ if (inserver)
+ return(0);
+
+ if (!ch)
+ return(0);
+ if (!p) {
+#ifdef OS2
+ p = adl_zmode == ADL_PACK ? matchstr : adl_zstr;
+#else
+ p = matchstr;
+#endif /* OS2 */
+ }
+ if (ch == *p) {
+ p++;
+ if (*p == '\0') {
+#ifdef OS2
+ if (adl_zmode == ADL_PACK) {
+ p = matchstr;
+ debug(F100, "zstart Zmodem SOP","",0);
+ } else {
+ p = adl_zstr;
+ debug(F100, "zstart Zmodem Start String","",0);
+ }
+#else
+ p = matchstr;
+ debug(F100, "zstart Zmodem SOP","",0);
+#endif /* OS2 */
+ return(PROTO_Z + 1);
+ }
+ } else {
+#ifdef OS2
+ p = adl_zmode == ADL_PACK ? matchstr : adl_zstr;
+#else
+ p = matchstr;
+#endif /* OS2 */
+ }
+ return(0);
+}
+#endif /* CK_XYZ */
+
+#ifndef NOICP
+#ifdef CK_APC
+/* A U T O D O W N */
+
+#ifdef CK_ANSIC
+VOID
+autodown(int ch)
+#else
+VOID
+autodown(ch) int ch;
+#endif /* CK_ANSIC */
+/* autodown */ {
+
+/* The Kermit and Zmodem Auto-download calls go here */
+
+ extern int justone; /* From protocol module */
+ extern int debses, protocol, apcactive, autodl, inautodl;
+#ifdef DCMDBUF
+ extern char *apcbuf;
+#else
+ extern char apcbuf[];
+#endif /* DCMDBUF */
+#ifdef OS2
+ extern int apclength, term_io;
+#endif /* OS2 */
+ int k = 0;
+
+ if ((autodl || inautodl
+#ifdef IKS_OPTION
+ || TELOPT_SB(TELOPT_KERMIT).kermit.me_start
+#endif /* IKS_OPTION */
+ ) && !debses) {
+#ifdef CK_XYZ
+#ifdef XYZ_INTERNAL
+ extern int p_avail;
+#else
+ int p_avail = 1;
+#endif /* XYZ_INTERNAL */
+ if (p_avail && zstart((CHAR) ch)) {
+ debug(F100, "Zmodem download","",0);
+#ifdef OS2
+#ifndef NOTERM
+ apc_command(APC_LOCAL,"receive /protocol:zmodem");
+#endif /* NOTERM */
+#else /* OS2 */
+ ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN);
+ apcactive = APC_LOCAL;
+#endif /* OS2 */
+ return;
+ }
+#endif /* CK_XYZ */
+
+ /* First try... */
+ k = kstart((CHAR) ch);
+ if (
+#ifdef NOSERVER
+ k > 0
+#else /* NOSERVER */
+ k
+#endif /* NOSERVER */
+ ) { /* We saw a valid S or I packet */
+ if (k < 0) { /* Stuff RECEIVE into APC buffer */
+ justone = 1;
+ switch (protocol) {
+#ifdef CK_XYZ
+ case PROTO_G:
+ ckstrncpy(apcbuf,
+ "set proto kermit, server, set protocol g",
+ APCBUFLEN
+ );
+ break;
+ case PROTO_X:
+ ckstrncpy(apcbuf,
+ "set proto kermit,server,set proto xmodem",
+ APCBUFLEN
+ );
+ break;
+ case PROTO_XC:
+ ckstrncpy(apcbuf,
+ "set proto kermit,server,set proto xmodem-crc",
+ APCBUFLEN
+ );
+ break;
+ case PROTO_Y:
+ ckstrncpy(apcbuf,
+ "set proto kermit,server, set protocol y",
+ APCBUFLEN
+ );
+ break;
+ case PROTO_Z:
+ ckstrncpy(apcbuf,
+ "set proto kermit,server,set proto zmodem",
+ APCBUFLEN
+ );
+ break;
+#endif /* CK_XYZ */
+ case PROTO_K:
+ ckstrncpy(apcbuf,"server",APCBUFLEN);
+ break;
+ }
+ } else {
+ justone = 0;
+ ckstrncpy(apcbuf,"receive /protocol:kermit",APCBUFLEN);
+ }
+#ifdef OS2
+#ifndef NOTERM
+ apc_command(APC_LOCAL,apcbuf);
+#endif /* NOTERM */
+#else /* OS2 */
+ ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN);
+ apcactive = APC_LOCAL;
+#endif /* OS2 */
+ return;
+ }
+ }
+}
+#endif /* CK_APC */
+#endif /* NOICP */
+
+/* C H K S P K T -- Check if buf contains a valid S or I packet */
+
+int
+chkspkt(buf) char *buf; {
+ int buflen;
+ int len = -1;
+ CHAR chk;
+ char type = 0;
+ char *s = buf;
+
+ if (!buf) return(0);
+ buflen = strlen(buf);
+ if (buflen < 5) return(0); /* Too short */
+ if (*s++ != stchr) return(0); /* SOH */
+ len = xunchar(*s++); /* Length */
+ if (len < 0) return(0);
+ if (*s++ != SP) return(0); /* Sequence number */
+ type = *s++; /* Type */
+ if (type != 'S' && type != 'I')
+ return(0);
+ if (buflen < len + 2) return(0);
+ s += (len - 3); /* Position of checksum */
+ chk = (CHAR) (*s); /* Checksum */
+ *s = NUL;
+ if (xunchar(chk) != chk1((CHAR *)(buf+1),buflen-2)) /* Check it */
+ return(0);
+ *s = chk;
+ return(type == 'S' ? 1 : 2);
+}
+#endif /* CK_AUTODL */
+
+/* R P A C K -- Read a Packet */
+
+/*
+ rpack reads a packet and returns the packet type, or else Q if the
+ packet was invalid, or T if a timeout occurred. Upon successful return,
+ sets the values of global rsn (received sequence number), rln (received
+ data length), and rdatap (pointer to null-terminated data field), and
+ returns the packet type. NOTE: This is an inner-loop function so must be
+ efficient. Protect function calls by if-tests where possible, e.g.
+ "if (pktlog) logpkt(...);".
+*/
+int
+rpack() {
+ register int i, j, x, lp; /* Local variables */
+#ifdef CKTUNING
+ unsigned int chk;
+#endif /* CKTUNING */
+ int k, type, chklen;
+ unsigned crc;
+ CHAR pbc[5]; /* Packet block check */
+ CHAR *sohp; /* Pointer to SOH */
+ CHAR e; /* Packet end character */
+
+#ifdef GFTIMER
+ CKFLOAT t1 = 0.0, t2 = 0.0;
+#endif /* GFTIMER */
+
+ debug(F101,"rpack pktnum","",pktnum);
+
+#ifndef OLDCHKINT
+ if (chkint() < 0) /* Check for console interrupts. */
+ return('z');
+#endif /* OLDCHKINT */
+
+ k = getrbuf(); /* Get a new packet input buffer. */
+ debug(F101,"rpack getrbuf","",k);
+ if (k < 0) { /* Return like this if none free. */
+ return(-1);
+ }
+ recpkt = r_pkt[k].bf_adr;
+ *recpkt = '\0'; /* Clear receive buffer. */
+ sohp = recpkt; /* Initialize pointers to it. */
+ rdatap = recpkt;
+ rsn = rln = -1; /* In case of failure. */
+ e = (turn) ? turnch : eol; /* Use any handshake char for eol */
+
+/* Try to get a "line". */
+
+#ifdef CK_AUTODL
+ debug(F110,"rpack ksbuf",ksbuf,0);
+ if (ksbuf[0]) { /* Kermit packet already */
+ int x; /* collected for us in CONNECT mode */
+ CHAR *s1 = recpkt, *s2 = ksbuf;
+ j = 0;
+ while (*s2) { /* Copy and get length */
+ *s1++ = *s2++; /* No point optimizing this since */
+ j++; /* it's never more than ~20 chars */
+ }
+ *s1 = NUL;
+#ifdef PARSENSE
+ x = parchk(recpkt, stchr, j); /* Check parity */
+ debug(F000,"autodownload parity","",parity);
+ debug(F000,"autodownload parchk","",x);
+ if (x > -1 && parity != x) {
+ autopar = 1;
+ parity = x;
+ }
+#endif /* PARSENSE */
+ ksbuf[0] = NUL; /* Don't do this next time! */
+
+ } else { /* Normally go read a packet */
+#endif /* CK_AUTODL */
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"rpack timint","",timint);
+ debug(F101,"rpack rcvtimo","",rcvtimo);
+#ifdef STREAMING
+ debug(F101,"rpack streaming","",streaming);
+#endif /* STREAMING */
+#ifdef GFTIMER
+ /* Measure how long it takes to read a packet */
+ t1 = gftimer();
+#endif /* GFTIMER */
+ }
+#endif /* DEBUG */
+
+/* JUST IN CASE (otherwise this could clobber streaming) */
+
+ if ((timint == 0
+#ifdef STREAMING
+ || streaming
+#endif /* STREAMING */
+ ) && (rcvtimo != 0)) {
+ debug(F101,"rpack timint 0 || streaming but rcvtimo","",rcvtimo);
+ rcvtimo = 0;
+ }
+
+#ifdef PARSENSE
+#ifdef UNIX
+/*
+ So far the final turn argument is only for ck[uvdl]tio.c. Should be added
+ to the others too. (turn == handshake character.)
+*/
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+#ifdef VMS
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+#ifdef datageneral
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+#ifdef STRATUS
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+#ifdef OS2
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+#ifdef OSK
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn);
+#else
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr);
+#endif /* OSK */
+#endif /* OS2 */
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIX */
+ if (parity != 0 && parity != 's' && ttprty != 0) {
+ if (parity != ttprty) autopar = 1;
+ parity = ttprty;
+ }
+#else /* !PARSENSE */
+ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e);
+#endif /* PARSENSE */
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"rpack ttinl len","",j);
+#ifdef GFTIMER
+ t2 = gftimer();
+ debug(F101,"rpack ttinl msec","",(long)((t2-t1)*1000.0));
+#endif /* GFTIMER */
+ }
+#endif /* DEBUG */
+
+#ifdef STREAMING
+ if (streaming && sndtyp == 'D' && j == 0)
+ return('Y');
+#endif /* STREAMING */
+
+ if (j < 0) {
+ /* -1 == timeout, -2 == ^C, -3 == connection lost or fatal i/o */
+ debug(F101,"rpack: ttinl fails","",j); /* Otherwise, */
+ freerbuf(k); /* Free this buffer */
+ if (j < -1) { /* Bail out if ^C^C typed. */
+ if (j == -2) {
+ interrupted = 1;
+ debug(F101,"rpack ^C server","",server);
+ debug(F101,"rpack ^C en_fin","",en_fin);
+ } else if (j == -3) {
+ fatalio = 1;
+ debug(F101,"rpack fatalio","",en_fin);
+ }
+ return(j);
+ }
+ if (nakstate) /* j == -1 is a read timeout */
+ xxscreen(SCR_PT,'T',(long)winlo,"");
+ else
+ xxscreen(SCR_PT,'T',(long)pktnum,"");
+ logpkt('r',-1,(CHAR *)"<timeout>",0);
+ if (flow == 1) ttoc(XON); /* In case of Xoff blockage. */
+ return('T');
+ }
+#ifdef CK_AUTODL
+ }
+#endif /* CK_AUTODL */
+
+ rpktl = j;
+ tlci += j; /* All OK, Count the characters. */
+ flci += j;
+
+/* Find start of packet */
+
+#ifndef PARSENSE
+ for (i = 0; (recpkt[i] != stchr) && (i < j); i++)
+ sohp++; /* Find mark */
+ if (i++ >= j) { /* Didn't find it. */
+ logpkt('r',-1,"<timeout>",0);
+ freerbuf(k);
+ return('T');
+ }
+#else
+ i = 1; /* ttinl does this for us */
+#endif /* PARSENSE */
+
+ rpackets++; /* Count received packet. */
+ lp = i; /* Remember LEN position. */
+ if ((j = xunchar(recpkt[i++])) == 0) { /* Get packet length. */
+ if ((j = lp+5) > MAXRP) { /* Long packet */
+ return('Q'); /* Too long */
+ }
+
+#ifdef CKTUNING
+ /* Save some function-call and loop overhead... */
+#ifdef COMMENT
+ /* ttinl() already removed parity */
+ if (parity)
+#endif /* COMMENT */
+ chk = (unsigned) ((unsigned) recpkt[i-1] +
+ (unsigned) recpkt[i] +
+ (unsigned) recpkt[i+1] +
+ (unsigned) recpkt[i+2] +
+ (unsigned) recpkt[i+3]
+ );
+#ifdef COMMENT
+ else
+ chk = (unsigned) ((unsigned) (recpkt[i-1] & 077) +
+ (unsigned) (recpkt[i] & 077) +
+ (unsigned) (recpkt[i+1] & 077) +
+ (unsigned) (recpkt[i+2] & 077) +
+ (unsigned) (recpkt[i+3] & 077)
+ );
+#endif /* COMMENT */
+ if (xunchar(recpkt[j]) != ((((chk & 0300) >> 6) + chk) & 077))
+#else
+ x = recpkt[j]; /* Header checksum. */
+ recpkt[j] = '\0'; /* Calculate & compare. */
+ if (xunchar(x) != chk1(recpkt+lp,5))
+#endif /* CKTUNING */
+ {
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:hdr>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet header");
+ return('Q');
+ }
+#ifndef CKTUNING
+ recpkt[j] = x; /* Checksum ok, put it back. */
+#endif /* CKTUNING */
+ rln = xunchar(recpkt[j-2]) * 95 + xunchar(recpkt[j-1]) - bctl;
+ j = 3; /* Data offset. */
+ } else if (j < 3) {
+ debug(F101,"rpack packet length less than 3","",j);
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:len>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet length");
+ return('Q');
+ } else {
+ rln = j - bctl - 2; /* Regular packet */
+ j = 0; /* No extended header */
+ }
+ rsn = xunchar(recpkt[i++]); /* Sequence number */
+ if (pktlog) /* Save a function call! */
+ logpkt('r',rsn,sohp,rln+bctl+j+4);
+ if (rsn < 0 || rsn > 63) {
+ debug(F101,"rpack bad sequence number","",rsn);
+ freerbuf(k);
+ if (pktlog)
+ logpkt('r',rsn,(CHAR *)"<crunched:seq>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Bad sequence number");
+ return('Q');
+ }
+/*
+ If this packet has the same type as the packet just sent, assume it is
+ an echo and ignore it. Don't even bother with the block check calculation:
+ even if the packet is corrupted, we don't want to NAK an echoed packet.
+ Nor must we NAK an ACK or NAK.
+*/
+ type = recpkt[i++]; /* Get packet's TYPE field */
+ if (type == sndtyp || (nakstate && (type == 'N' /* || type == 'Y' */ ))) {
+ debug(F000,"rpack echo","",type); /* If it's an echo */
+ freerbuf(k); /* Free this buffer */
+ logpkt('#',rsn,(CHAR *)"<echo:ignored>",0);
+ return('e'); /* Return special (lowercase) code */
+ }
+/*
+ Separate the data from the block check, accounting for the case where
+ a packet was retransmitted after the block check switched.
+*/
+ if (type == 'I' || type == 'S') { /* I & S packets always have type 1 */
+ chklen = 1;
+ rln = rln + bctl - 1;
+ } else if (type == 'N') { /* A NAK packet never has data */
+ chklen = xunchar(recpkt[lp]) - 2;
+ rln = rln + bctl - chklen;
+ } else chklen = bctl;
+#ifdef DEBUG
+ if (deblog) { /* Save 2 function calls */
+ debug(F101,"rpack bctl","",bctl);
+ debug(F101,"rpack chklen","",chklen);
+ }
+#endif /* DEBUG */
+ i += j; /* Buffer index of DATA field */
+ rdatap = recpkt+i; /* Pointer to DATA field */
+ if ((j = rln + i) > r_pkt[k].bf_len) { /* Make sure it fits */
+ debug(F101,"packet too long","",j);
+ freerbuf(k);
+ logpkt('r',rsn,(CHAR *)"<overflow>",0);
+ return('Q');
+ }
+ for (x = 0; x < chklen; x++) /* Copy the block check */
+ pbc[x] = recpkt[j+x]; /* 3 bytes at most. */
+ pbc[x] = '\0'; /* Null-terminate block check string */
+ recpkt[j] = '\0'; /* and the packet Data field. */
+
+ if (chklen == 2 && bctu == 4) { /* Adjust for Blank-Free-2 */
+ chklen = 4; /* (chklen is now a misnomer...) */
+ debug(F100,"rpack block check B","",0);
+ }
+ switch (chklen) { /* Check the block check */
+ case 1: /* Type 1, 6-bit checksum */
+ if (xunchar(*pbc) != chk1(recpkt+lp,j-lp)) {
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"checked chars",recpkt+lp,0);
+ debug(F101,"block check (1)","",(int) xunchar(*pbc));
+ debug(F101,"should be (1)","",chk1(recpkt+lp,j-lp));
+ }
+#endif /* DEBUG */
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:chk1>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
+ return('Q');
+ }
+ break;
+ case 2: /* Type 2, 12-bit checksum */
+ x = xunchar(*pbc) << 6 | xunchar(pbc[1]);
+ if (x != chk2(recpkt+lp,j-lp)) { /* No match */
+ if (type == 'E') { /* Allow E packets to have type 1 */
+ recpkt[j++] = pbc[0];
+ recpkt[j] = '\0';
+ if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp))
+ break;
+ else
+ recpkt[--j] = '\0';
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"checked chars",recpkt+lp,0);
+ debug(F101,"block check (2)","", x);
+ debug(F101,"should be (2)","", (int) chk2(recpkt+lp,j-lp));
+ }
+#endif /* DEBUG */
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:chk2>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
+ return('Q');
+ }
+ break;
+ case 3: /* Type 3, 16-bit CRC */
+ crc = (xunchar(pbc[0]) << 12)
+ | (xunchar(pbc[1]) << 6)
+ | (xunchar(pbc[2]));
+ if (crc != chk3(recpkt+lp,j-lp)) {
+ if (type == 'E') { /* Allow E packets to have type 1 */
+ recpkt[j++] = pbc[0];
+ recpkt[j++] = pbc[1];
+ recpkt[j] = '\0';
+ if (xunchar(pbc[2]) == chk1(recpkt+lp,j-lp))
+ break;
+ else { j -=2; recpkt[j] = '\0'; }
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"checked chars",recpkt+lp,0);
+ debug(F101,"block check (3)","",crc);
+ debug(F101,"should be (3)","",(int) chk3(recpkt+lp,j-lp));
+ }
+#endif /* DEBUG */
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:chk3>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"CRC error");
+ return('Q');
+ }
+ break;
+ case 4: /* Type 4 = Type 2, no blanks. */
+ x = (unsigned)((xunchar(*pbc) - 1) << 6) |
+ (unsigned)(xunchar(pbc[1]) - 1);
+ if (x != chk2(recpkt+lp,j-lp)) {
+ if (type == 'E') { /* Allow E packets to have type 1 */
+ recpkt[j++] = pbc[0];
+ recpkt[j] = '\0';
+ if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp))
+ break;
+ else
+ recpkt[--j] = '\0';
+ }
+ debug(F101,"bad type B block check","",x);
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:chkb>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error");
+ return('Q');
+ }
+ break;
+ default: /* Shouldn't happen... */
+ freerbuf(k);
+ logpkt('r',-1,(CHAR *)"<crunched:chkx>",0);
+ xxscreen(SCR_PT,'%',(long)pktnum,"(crunched)");
+ return('Q');
+ }
+ debug(F101,"rpack block check OK","",rsn);
+
+/* Now we can believe the sequence number, and other fields. */
+/* Here we violate strict principles of layering, etc, and look at the */
+/* packet sequence number. If there's already a packet with the same */
+/* number in the window, we remove this one so that the window will not */
+/* fill up. */
+
+ if ((x = rseqtbl[rsn]) != -1) { /* Already a packet with this number */
+ retrans++; /* Count it for statistics */
+ debug(F101,"rpack got dup","",rsn);
+ logpkt('r',rsn,(CHAR *)"<duplicate>",0);
+ freerbuf(x); /* Free old buffer, keep new packet. */
+ r_pkt[k].pk_rtr++; /* Count this as a retransmission. */
+ }
+
+/* New packet, not seen before, enter it into the receive window. */
+
+#ifdef CK_TIMERS
+ if (timint > 0)
+ rrttbl[rsn] = gtimer(); /* Timestamp */
+#endif /* CK_TIMERS */
+
+ rseqtbl[rsn] = k; /* Make back pointer */
+ r_pkt[k].pk_seq = rsn; /* Record in packet info structure */
+ r_pkt[k].pk_typ = type; /* Sequence, type,... */
+ r_pkt[k].pk_adr = rdatap; /* pointer to data buffer */
+ if (local) { /* Save a function call! */
+ int x = 0;
+ if (fdispla != XYFD_N) x = 1;
+ if (fdispla == XYFD_B && (type == 'D' || sndtyp == 'D')) x = 0;
+ if (x) /* Update screen */
+ xxscreen(SCR_PT,(char)type,(long)rsn,(char *)sohp);
+ }
+ return(type); /* Return packet type */
+}
+
+/* L O G P K T -- Log packet number n, pointed to by s. */
+
+/* c = 's' (send) or 'r' (receive) */
+
+VOID
+#ifdef CK_ANSIC
+logpkt(char c,int n, CHAR *s, int len)
+#else
+logpkt(c,n,s,len) char c; int n; CHAR *s; int len;
+#endif /* CK_ANSIC */
+/* logpkt */ {
+ char plog[20];
+ if (!s) s = (CHAR *)"";
+ if (pktlog) if (chkfn(ZPFILE) > 0) {
+ if (n < 0) /* Construct entry header */
+ sprintf(plog,"%c-xx-%02d-",c,(gtimer()%60)); /* safe */
+ else
+ sprintf(plog,"%c-%02d-%02d-",c,n,(gtimer()%60)); /* safe */
+ if (zsoutx(ZPFILE,plog,(int)strlen(plog)) < 0) {
+ pktlog = 0;
+ return;
+ } else {
+ if (len == 0)
+ len = strlen((char *)s);
+ if (len > 0) {
+ char * p; /* Make SOP printable */
+ int x; /* so we can look at logs without */
+ p = dbchr(*s); /* triggering autodownload. */
+ x = strlen(dbchr(*s));
+ if (*s < 32 || (*s > 127 && *s < 160)) {
+ if (zsoutx(ZPFILE,p,x) < 0) {
+ pktlog = 0;
+ return;
+ } else {
+ len--;
+ s++;
+ }
+ }
+ }
+ if (zsoutx(ZPFILE,(char *)s,len) < 0) {
+ pktlog = 0;
+ return;
+ } else if (zsoutx(ZPFILE,
+#ifdef UNIX
+ "\n", 1
+#else
+#ifdef datageneral
+ "\n", 1
+#else
+#ifdef OSK
+ "\r", 1
+#else
+#ifdef MAC
+ "\r", 1
+#else
+ "\015\012", 2
+#endif /* MAC */
+#endif /* OSK */
+#endif /* datageneral */
+#endif /* UNIX */
+ ) < 0) {
+ pktlog = 0;
+ }
+ }
+ }
+}
+
+/* T S T A T S -- Record statistics in transaction log */
+
+VOID
+tstats() {
+ char *tp = NULL;
+#ifdef GFTIMER
+ CKFLOAT xx; /* Elapsed time divisor */
+#endif /* GFTIMER */
+
+ debug(F101,"tstats xfsecs","",xfsecs);
+ debug(F101,"tstats filcnt","",filcnt);
+ if (filcnt == 1) { /* Get timing for statistics */
+ tsecs = xfsecs; /* Single file, we already have it */
+#ifdef GFTIMER
+ debug(F101,"tstats fpxfsecs","",(int)fpxfsecs);
+ fptsecs = fpxfsecs;
+#endif /* GFTIMER */
+ } else { /* Multiple files */
+ tsecs = gtimer(); /* Get current time */
+#ifdef GFTIMER
+ fptsecs = gftimer();
+#endif /* GFTIMER */
+ }
+#ifdef GFTIMER
+ if (fptsecs <= GFMINTIME) /* Calculate CPS */
+ fptsecs = (CKFLOAT) GFMINTIME;
+ debug(F101,"tstats fptsecs","",(int)fptsecs);
+ xx = (CKFLOAT) tfc / fptsecs;
+ if (sizeof(long) <= 4) { /* doesn't account for 16-bit longs */
+ if (xx > 2147483647.0)
+ tfcps = 2147483647L; /* 31 bits */
+ else
+ tfcps = (long) xx;
+ } else
+ tfcps = (long) xx;
+#else
+ if (tsecs < 2L)
+ tsecs = 1L;
+ debug(F101,"tstats tsecs","",tsecs);
+ tfcps = tfc / tsecs;
+#endif /* GFTIMER */
+
+ ztime(&tp); /* Get time stamp */
+ tlog(F100,"","",0L); /* Leave a blank line */
+ tlog(F110,"Transaction complete",tp,0L); /* Record it */
+
+ if (filcnt < 1) return; /* If no files, done. */
+
+/* If multiple files, record character totals for all files */
+
+ if (filcnt > 1) {
+ tlog(F101," files transferred ","",filcnt - filrej);
+ tlog(F101," total file characters ","",tfc);
+ tlog(F101," communication line in ","",tlci);
+ tlog(F101," communication line out ","",tlco);
+ }
+
+/* Record timing info for one or more files */
+
+#ifdef GFTIMER
+ if (filcnt - filrej == 1) {
+ tlog(F101," elapsed time (seconds) ","",(long) fpxfsecs);
+ tlog(F101," effective data rate ","",filcps);
+ } else {
+ tlog(F101," elapsed time (seconds) ","",(long) fptsecs);
+ tlog(F101," effective data rate ","",(long) xx);
+ }
+#else
+ tlog(F101," elapsed time (seconds) ","",(long) tsecs);
+ if (tsecs > 0) {
+ long lx;
+ lx = (tfc * 10L) / (long) tsecs;
+ tlog(F101," effective data rate ","",lx/10L);
+ }
+#endif /* GFTIMER */
+ tlog(F100,"","",0L); /* Leave a blank line */
+}
+
+/* F S T A T S -- Record file statistics in transaction log */
+
+VOID
+fcps() {
+#ifdef GFTIMER
+ double xx;
+ fpxfsecs = gftimer() - fpfsecs;
+ if (fpxfsecs <= GFMINTIME)
+ fpxfsecs = (CKFLOAT) GFMINTIME;
+ xx = (CKFLOAT) ffc / fpxfsecs;
+ if (sizeof(long) <= 4) {
+ if (xx > 2147483647.0)
+ tfcps = 2147483647L; /* 31 bits */
+ else
+ filcps = (long) xx;
+ } else
+ filcps = (long) xx;
+ if (sizeof(int) >= 4)
+ xfsecs = (int) fpxfsecs;
+ else if (fpxfsecs < 32768.0)
+ xfsecs = (int) fpxfsecs;
+ else
+ xfsecs = 32767;
+#else /* GFTIMER */
+ xfsecs = gtimer() - fsecs;
+ if (xfsecs < 1L) xfsecs = 1L;
+ filcps = ffc / xfsecs;
+#endif /* GFTIMER */
+}
+
+VOID
+fstats() {
+ tfc += ffc;
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"fstats tfc","",tfc);
+ debug(F101,"fstats what","",what);
+ debug(F110,"fstats epktmsg",epktmsg,0);
+ }
+#endif /* DEBUG */
+#ifdef TLOG
+ if (!discard && !cxseen && !czseen && what != W_NOTHING && !*epktmsg)
+ tlog(F101," complete, size","",ffc);
+#endif /* TLOG */
+}
+
+#endif /* NOXFER */
diff --git a/ckermit-8.0.211/ckcfn3.c b/ckermit-8.0.211/ckcfn3.c
new file mode 100644
index 0000000..c499421
--- /dev/null
+++ b/ckermit-8.0.211/ckcfn3.c
@@ -0,0 +1,2559 @@
+/* C K C F N 3 -- Packet buffer management for C-Kermit */
+
+/* (plus assorted functions tacked on at the end) */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+/*
+ Note -- if you change this file, please amend the version number and date at
+ the top of ckcfns.c accordingly.
+*/
+
+#include "ckcsym.h"
+#include "ckcdeb.h"
+#include "ckcasc.h"
+#include "ckcker.h"
+#include "ckcxla.h"
+
+/* C K M K D I R -- Create a directory */
+/*
+ Call with:
+ int fc = 0 to create, nonzero to remove, a directory.
+ char * s = pointer to name of directory to create or remove.
+ char ** r = address of pointer to return name or message.
+ int m = 1 to print error messages, 0 to be silent.
+ int cvt = 1 means convert s from standard format to local format;
+ 0 means use s as is.
+ Returns:
+ 0 on success (directory was created or removed).
+ -1 when attempt to create the directory failed.
+ -2 on internal error (e.g. no code for creating directories).
+ On success, the name is pointed to by p.
+ On failure, the reason is pointed to by p.
+*/
+#ifdef CK_MKDIR
+static char ckmkdbuf[CKMAXPATH+1];
+#else
+#ifdef datageneral
+static char ckmkdbuf[CKMAXPATH+1];
+#endif /* datageneral */
+#endif /* CK_MKDIR */
+
+#ifdef CK_MKDIR
+int
+ckmkdir(fc,s,r,m,cvt) int fc; char * s; char ** r; int m; int cvt; {
+ int x, rc = -2;
+ char tmpbuf[CKMAXPATH+1];
+ char buf2[CKMAXPATH+1];
+ if (!s) s = "";
+ debug(F110,"ckmkdir 1 fc",s,fc);
+ if (!*s) {
+ ckmakmsg(ckmkdbuf,
+ CKMAXPATH+1,
+ (fc == 0) ? "mkdir" : "rmdir",
+ ": no name given",
+ NULL,
+ NULL
+ );
+ *r = ckmkdbuf;
+ return(-2);
+ }
+#ifdef datageneral
+/* Come back and make this nicer later if anybody notices */
+ if (fc == 0) { /* mkdir */
+ rc = createdir(s,0);
+ } else { /* rmdir */
+ /* AOS/VS rmdir() is a no-op. */
+ ckmakmsg(tmpbuf,CKMAXPATH+1,"delete ",s,NULL,NULL);
+ debug(F110,"ckmkdir 2",tmpbuf,0);
+ rc = system(tmpbuf);
+ }
+ *r = NULL;
+#else /* not datageneral */
+
+/* First make sure the name has an acceptable directory-name format */
+
+#ifdef VMS
+ {
+ char *p = s;
+ int lb = 0, rb = 0, sl = 0;
+ while (*p) {
+ if (*p == '[' || *p == '<') lb++; /* Count brackets */
+ else if (*p == ']' || *p == '>') rb++;
+ else if (*p == '/') sl++; /* and slashes */
+ p++;
+ }
+ if (lb != 1 && rb != 1 && sl == 0 && p > s && *(p-1) != ':') {
+ /* Probably just a word - convert to VMS format */
+ ckmakmsg(buf2,
+ CKMAXPATH+1,
+ "[",
+ (*s == '.') ? "" : ".",
+ s,
+ "]"
+ );
+ s = buf2;
+ } else if (lb == 0 && rb == 0 && sl != 0 && p > s && *(p-1) != ':') {
+ int flag = 0;
+ /* Seems to be in UNIX format */
+ x = strlen(s);
+ if (x > 0 && s[x-1] != '/')
+ flag = 1;
+ ckmakmsg(buf2,CKMAXPATH+1,s,flag ? "/" : "",NULL,NULL);
+ s = buf2;
+ }
+ if (s == buf2) {
+ ckstrncpy(tmpbuf,s,CKMAXPATH+1);
+ s = tmpbuf;
+ }
+ debug(F110,"ckmkdir 2+VMS",s,0);
+ }
+#else
+#ifdef UNIXOROSK
+#ifdef DTILDE
+ s = tilde_expand(s);
+#endif /* DTILDE */
+ ckstrncpy(tmpbuf,s,CKMAXPATH+1);
+ s = tmpbuf;
+ x = strlen(s);
+ if (x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */
+ s[x] = '/';
+ s[x+1] = NUL;
+ debug(F110,"ckmkdir 2+UNIXOROSK",s,0);
+ }
+#else /* UNIXOROSK */
+#ifdef OS2
+ ckstrncpy(tmpbuf,s,CKMAXPATH+1);
+ s = tmpbuf;
+ x = strlen(s);
+ if (fc == 0 && x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */
+ s[x] = '/';
+ s[x+1] = NUL;
+ debug(F110,"ckmkdir 2+OS2",s,0);
+ }
+#endif /* OS2 */
+#endif /* UNIXOROSK */
+#endif /* VMS */
+#ifdef NZLTOR
+ /* Server is calling us, so convert to local format if necessary */
+ if (cvt) {
+ nzrtol(s,(char *)buf2,1,PATH_ABS,CKMAXPATH);
+ s = buf2;
+ debug(F110,"ckmkdir 3",s,0);
+ }
+#endif /* NZLTOR */
+ debug(F110,"ckmkdir 4",s,0);
+ if (fc == 0) { /* Making */
+#ifdef CK_MKDIR
+ rc = zmkdir(s);
+#else
+#ifdef NT
+ rc = _mkdir(s);
+#else
+ rc = mkdir(s,0777);
+#endif /* NT */
+#endif /* CK_MKDIR */
+ } else { /* Removing */
+#ifdef ZRMDIR
+ rc = zrmdir(s);
+#else
+#ifdef NT
+ rc = _rmdir(s);
+#else
+#ifdef OSK
+ rc = -2;
+#else
+ rc = rmdir(s);
+#endif /* OSK */
+#endif /* NT */
+#endif /* ZRMDIR */
+ }
+#endif /* datageneral */
+ debug(F101,"ckmkdir rc","",rc);
+ if (rc == -2) {
+ ckmakmsg(ckmkdbuf,
+ CKMAXPATH,
+ "Directory ",
+ (fc == 0) ? "creation" : "removal",
+ "not implemented in this version of C-Kermit",
+ NULL
+ );
+ *r = ckmkdbuf;
+ if (m) printf("%s\n",*r);
+ } else if (rc < 0) {
+ if (m) perror(s);
+ ckmakmsg(ckmkdbuf,CKMAXPATH,s,": ",ck_errstr(),NULL);
+ *r = ckmkdbuf;
+ } else if (fc == 0 && zfnqfp(s,CKMAXPATH,ckmkdbuf)) {
+ *r = ckmkdbuf;
+ } else if (fc != 0) {
+ ckmakmsg(ckmkdbuf,CKMAXPATH,s,": removed",NULL,NULL);
+ *r = ckmkdbuf;
+ }
+ return(rc);
+}
+#endif /* CK_MKDIR */
+
+#ifndef NOXFER /* Rest of this file... */
+
+#ifndef NODISPO
+#ifdef pdp11
+#define NODISPO
+#endif /* pdpd11 */
+#endif /* NODISPO */
+
+extern int pipesend;
+#ifdef PIPESEND
+extern char ** sndfilter;
+#endif /* PIPESEND */
+
+extern int unkcs, wmax, wcur, discard, bctu, bctl, local, fdispla, what,
+ sendmode, opnerr, dest, epktrcvd, epktsent, filestatus, eofmethod, dispos;
+extern long sendstart, calibrate, fncnv, fnrpath;
+
+extern char * ofn2;
+extern char * rfspec, * sfspec, * prfspec, * psfspec, * rrfspec, * prrfspec;
+extern char ofn1[];
+extern int ofn1x;
+extern char * ofperms;
+
+#ifdef VMS
+extern int batch;
+#else
+extern int backgrd;
+#endif /* VMS */
+
+extern int xflg, remfile, remappd;
+extern CHAR *data;
+extern char filnam[];
+#ifndef NOFRILLS
+extern int rprintf, rmailf; /* REMOTE MAIL, PRINT */
+char optbuf[OPTBUFLEN]; /* Options for MAIL or REMOTE PRINT */
+#endif /* NOFRILLS */
+extern int wslots;
+extern int fblksiz, frecl, forg, frecfm, fncact, fncsav, fcctrl, lf_opts;
+extern CHAR * srvcmd;
+extern int srvcmdlen;
+
+extern int binary, spsiz;
+extern int pktnum, cxseen, czseen, nfils, stdinf;
+extern int memstr, stdouf, keep, sndsrc, hcflg;
+extern int server, en_cwd, en_mai, en_pri;
+
+/* Attributes in/out enabled flags */
+
+extern int
+ atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
+ attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
+
+#ifdef CK_PERMS
+extern int atlpri, atlpro, atgpri, atgpro;
+#endif /* CK_PERMS */
+
+#ifdef STRATUS
+extern int atfrmi, atfrmo, atcrei, atcreo, atacti, atacto;
+#endif /* STRATUS */
+
+#ifdef datageneral
+extern int quiet;
+#endif /* datageneral */
+
+extern long fsize, filcnt, ffc, tfc;
+
+#ifndef NOCSETS
+_PROTOTYP (VOID setxlate, (void));
+extern int tcharset, fcharset;
+extern int ntcsets, xlatype, xfrxla;
+extern struct csinfo tcsinfo[], fcsinfo[];
+#endif /* NOCSETS */
+
+/* Variables global to Kermit that are defined in this module */
+
+#ifdef CKXXCHAR /* DOUBLE / IGNORE char table */
+int dblflag = 0;
+int ignflag = 0;
+short dblt[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+#endif /* CKXXCHAR */
+
+int winlo; /* packet number at low window edge */
+
+int sbufnum; /* number of free buffers */
+int dum001 = 1234; /* protection... */
+int sbufuse[MAXWS]; /* buffer in-use flag */
+int dum003 = 1111;
+int rbufnum; /* number of free buffers */
+int dum002 = 4321; /* more protection */
+int rbufuse[MAXWS]; /* buffer in-use flag */
+int sseqtbl[64]; /* sequence # to buffer # table */
+int rseqtbl[64]; /* sequence # to buffer # table */
+int sacktbl[64]; /* sequence # ack table */
+
+int o_isopen = 0, i_isopen = 0; /* Input & output files are open */
+
+#ifdef DYNAMIC
+struct pktinfo *s_pkt = NULL; /* array of pktinfo structures */
+struct pktinfo *r_pkt = NULL; /* array of pktinfo structures */
+#else
+struct pktinfo s_pkt[MAXWS]; /* array of pktinfo structures */
+struct pktinfo r_pkt[MAXWS]; /* array of pktinfo structures */
+#endif /* DYNAMIC */
+
+#ifdef DEBUG
+char xbuf[200]; /* For debug logging */
+#endif /* DEBUG */
+
+#ifdef DYNAMIC
+CHAR *bigsbuf = NULL, *bigrbuf = NULL;
+#else
+char bigsbt[8]; /* Protection (shouldn't need this). */
+ /* BUT DON'T REMOVE IT! */
+CHAR bigsbuf[SBSIZ + 5]; /* Send-packet buffer area */
+char bigrbt[8]; /* Safety padding */
+CHAR bigrbuf[RBSIZ + 5]; /* Receive-packet area */
+#endif
+int bigsbsiz = SBSIZ; /* Sizes of big send & rcv buffers. */
+int bigrbsiz = RBSIZ;
+
+#ifdef VMS
+int zchkpath(char *s);
+#endif /* VMS */
+
+/* FUNCTIONS */
+
+VOID
+dofast() {
+ long maxbufsiz = RBSIZ; /* Configuration parameters */
+ int maxpktsiz = MAXSP;
+ extern int spsizf, /* For bug in IRIX Telnet server */
+ rpsiz, urpsiz, spsizr, spmax, wslotr;
+ extern struct ck_p ptab[];
+
+ if (maxpktsiz < 40) /* Long packet length */
+ maxpktsiz = 40;
+ else if (maxpktsiz > 4000)
+ maxpktsiz = 4000;
+ wslotr = maxbufsiz / maxpktsiz;
+ if (wslotr > MAXWS) /* Window slots */
+ wslotr = MAXWS;
+ if (wslotr > 30)
+ wslotr = 30;
+ else if (wslotr < 1)
+ wslotr = 1;
+ urpsiz = adjpkl(maxpktsiz,wslotr,maxbufsiz);
+ ptab[PROTO_K].rpktlen = urpsiz;
+ rpsiz = (urpsiz > 94) ? 94 : urpsiz; /* Max non-long packet length */
+ debug(F111,"dofast","uprsiz",urpsiz);
+#ifdef IRIX
+#ifndef IRIX65
+ /* IRIX Telnet server chops off writes longer than 4K */
+ spsiz = spmax = spsizr = urpsiz;
+ debug(F101,"doarg Q IRIX spsiz","",spsiz);
+ spsizf = 1;
+#endif /* IRIX65 */
+#endif /* IRIX */
+#ifdef CK_SPEED
+ setprefix(PX_CAU); /* Cautious unprefixing */
+#endif /* CK_SPEED */
+}
+
+
+/* For sanity, use "i" for buffer slots, "n" for packet numbers. */
+
+/* I N I B U F S */
+
+/*
+ Allocates the big send and receive buffers.
+ Call with size for big send buffer (s) and receive buffer (r).
+ These sizes can be different.
+ Attempts to allocate buffers of the requested size, but if it can't,
+ it will allocate smaller ones.
+ Sets global variables bigsbsiz and bigrbsiz to the actual sizes,
+ and bigsbuf and bigrbuf pointing to the actual buffers.
+ Designed to be called more than once.
+ Returns 0 on success, -1 on failure.
+*/
+
+CHAR *bigbufp = NULL;
+
+int
+inibufs(s,r) int s, r; {
+#ifdef DYNAMIC
+ unsigned
+ int size;
+#ifdef OS2
+ unsigned /* Don't you wish everybody had unsigned long... */
+#endif /* OS2 */
+ long z;
+ int x;
+
+ debug(F101,"inibufs s","",s);
+ debug(F101,"inibufs r","",r);
+
+ if (s < 80 || r < 80) return(-1); /* Validate arguments. */
+
+ if (!s_pkt) { /* Allocate packet info structures */
+ if (!(s_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
+ fatal("ini_pkts: no memory for s_pkt");
+ }
+ for (x = 0; x < MAXWS; x++)
+ s_pkt[x].pk_adr = NULL; /* Initialize addresses */
+
+ if (!r_pkt) {
+ if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS)))
+ fatal("ini_pkts: no memory for s_pkt");
+ }
+ for (x = 0; x < MAXWS; x++)
+ r_pkt[x].pk_adr = NULL; /* Initialize addresses */
+
+ if (!srvcmd) { /* Allocate srvcmd buffer */
+ srvcmd = (CHAR *) malloc(r + 100);
+ if (!srvcmd) return(-1);
+ srvcmdlen = r + 99;
+ *srvcmd = NUL;
+ }
+ if (bigbufp) { /* Free previous buffers, if any. */
+ free(bigbufp);
+ bigbufp = NULL;
+ }
+ size = s + r + 40; /* Combined requested size + padding */
+ z = (unsigned) s + (unsigned) r + 40;
+ debug(F101,"inibufs size 1","",size);
+ debug(F101,"inibufs size z","",z);
+ if ((long) size != z) {
+ debug(F100,"inibufs overflow","",0);
+ size = 65535;
+ }
+
+ /* Try to get the space. If malloc fails, try to get a little less. */
+ /* (Obviously, this algorithm can be refined.) */
+
+ while (!(bigbufp = (CHAR *) malloc(size))) {
+ debug(F101,"inibufs bigbuf malloc failed","",size);
+ size = (size * 2) / 3; /* Failed, cut size by 1/3. */
+ if (size < 200) /* Try again until too small. */
+ return(-1);
+ }
+ debug(F101,"inibufs size 2","",size); /* OK, we got some space. */
+
+/*
+ Now divide the allocated space between the send and receive buffers in the
+ requested proportion. The natural formula would be (s / (s + r)) * size
+ (for the send buffer), but that doesn't work with integer arithmetic and we
+ can't use floating point because some machines don't have it. This can be
+ rearranged as (s * size) / (s + r). But (s * size) can be VERY large, too
+ large for 32 bits. So let's do it this way. This arithmetic works for
+ buffer sizes up to about 5,000,000.
+*/
+#define FACTOR 20L
+ z = ( (long) s * FACTOR ) / ( (long) s + (long) r );
+ x = ( z * ( (long) size / FACTOR ) );
+ if (x < 0) return(-1); /* Catch overflow */
+
+ bigsbsiz = x - 5; /* Size of send buffer */
+ bigsbuf = bigbufp; /* Address of send buffer */
+ debug(F101,"inibufs bigsbsiz","",bigsbsiz);
+
+ bigrbsiz = size - x - 5; /* Size of receive buffer */
+ bigrbuf = bigbufp + x; /* Addresss of receive buffer */
+ debug(F101,"inibufs bigrbsiz","",bigrbsiz);
+
+ return(0); /* Success */
+#else /* No dynamic allocation */
+ bigsbsiz = SBSIZ; /* Just use the symbols */
+ bigrbsiz = RBSIZ; /* ... */
+ return(0); /* Success. */
+#endif /* DYNAMIC */
+}
+
+
+/* M A K E B U F -- Makes and clears a new buffers. */
+
+/* Call with: */
+/* slots: number of buffer slots to make, 1 to 32 */
+/* bufsiz: size of the big buffer */
+/* buf: address of the big buffer */
+/* xx: pointer to array of pktinfo structures for these buffers */
+
+/* Subdivides the big buffer into "slots" buffers. */
+
+/* Returns: */
+/* -1 if too many or too few slots requested, */
+/* -2 if slots would be too small. */
+/* n (positive) on success = size of one buffer. */
+/* with pktinfo structure initialized for this set of buffers. */
+
+int
+makebuf(slots,bufsiz,buf,xx)
+/* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; {
+
+ CHAR *a;
+ int i, size;
+
+ debug(F101,"makebuf","",slots);
+ debug(F101,"makebuf bufsiz","",bufsiz);
+ debug(F101,"makebuf MAXWS","",MAXWS);
+
+ if (slots > MAXWS || slots < 1) return(-1);
+ if (bufsiz < slots * 10 ) return(-2);
+
+ size = bufsiz / slots; /* Divide up the big buffer. */
+ a = buf; /* Address of first piece. */
+
+ for (i = 0; i < slots; i++) {
+ struct pktinfo *x = &xx[i];
+ x->bf_adr = a; /* Address of this buffer */
+ x->bf_len = size; /* Length of this buffer */
+ x->pk_len = 0; /* Length of data field */
+ x->pk_typ = ' '; /* packet type */
+ x->pk_seq = -1; /* packet sequence number */
+ x->pk_rtr = 0; /* retransmissions */
+ *a = '\0'; /* Clear the buffer */
+ a += size; /* Position to next buffer slot */
+ }
+ return(size);
+}
+
+/* M A K S B U F -- Makes the send-packet buffer */
+
+int
+mksbuf(slots) int slots; {
+ int i, x;
+ sbufnum = 0;
+ if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) {
+ debug(F101,"mksbuf makebuf return","",x);
+ return(x);
+ }
+ debug(F101,"mksbuf makebuf return","",x);
+ for (i = 0; i < 64; i++) { /* Initialize sequence-number- */
+ sseqtbl[i] = -1; /* to-buffer-number table. */
+ sacktbl[i] = 0;
+ }
+ for (i = 0; i < MAXWS; i++)
+ sbufuse[i] = 0; /* Mark each buffer as free */
+ sbufnum = slots;
+ wcur = 0;
+ return(x);
+}
+
+/* M A K R B U F -- Makes the receive-packet buffer */
+
+int
+mkrbuf(slots) int slots; {
+ int i, x;
+ rbufnum = 0;
+ if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) {
+ debug(F101,"mkrbuf makebuf return","",x);
+ return(x);
+ }
+ debug(F101,"mkrbuf makebuf return","",x);
+ for (i = 0; i < 64; i++) { /* Initialize sequence-number- */
+ rseqtbl[i] = -1; /* to-buffer-number table. */
+ }
+ for (i = 0; i < MAXWS; i++)
+ rbufuse[i] = 0; /* Mark each buffer as free */
+ rbufnum = slots;
+ wcur = 0;
+ return(x);
+}
+
+/* W I N D O W -- Resize the window to n */
+
+int
+window(n) int n; {
+ debug(F101,"window","",n);
+ if (n < 1 || n > MAXWS) return(-1);
+ if (mksbuf(n) < 0) return(-1);
+ if (mkrbuf(n) < 0) return(-1);
+ wslots = n;
+#ifdef DEBUG
+ if (deblog) dumpsbuf();
+ if (deblog) dumprbuf();
+#endif /* DEBUG */
+ return(0);
+}
+
+/* G E T S B U F -- Allocate a send-buffer. */
+
+/* Call with packet sequence number to allocate buffer for. */
+/* Returns: */
+/* -4 if argument is invalid (negative, or greater than 63) */
+/* -3 if buffers were thought to be available but really weren't (bug!) */
+/* -2 if the number of free buffers is negative (bug!) */
+/* -1 if no free buffers. */
+/* 0 or positive, packet sequence number, with buffer allocated for it. */
+
+int
+getsbuf(n) int n; { /* Allocate a send-buffer */
+ int i;
+ CHAR * p = NULL;
+ if (n < 0 || n > 63) {
+ debug(F101,"getsbuf bad arg","",n);
+ return(-4); /* Bad argument */
+ }
+ debug(F101,"getsbuf packet","",n);
+ /* debug(F101,"getsbuf, sbufnum","",sbufnum); */
+ if (sbufnum == 0) return(-1); /* No free buffers. */
+ if (sbufnum < 0) return(-2); /* Shouldn't happen. */
+ for (i = 0; i < wslots; i++) /* Find the first one not in use. */
+ if (sbufuse[i] == 0) { /* Got one? */
+ sbufuse[i] = 1; /* Mark it as in use. */
+ sbufnum--; /* One less free buffer. */
+ *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
+ s_pkt[i].pk_seq = n; /* Put in the sequence number */
+ sseqtbl[n] = i; /* Back pointer from sequence number */
+ sacktbl[n] = 0; /* ACK flag */
+ s_pkt[i].pk_len = 0; /* Data field length now zero. */
+ s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
+ s_pkt[i].pk_rtr = 0; /* Zero the retransmission count */
+ p = s_pkt[i].bf_adr + 7; /* Set global "data" address. */
+ debug(F101,"getsbuf p","",0);
+ data = p;
+ if (!data) {
+ debug(F100,"getsbuf data == NULL","",0);
+ return(-3);
+ }
+ if ((what & (W_SEND|W_REMO)) && (++wcur > wmax))
+ wmax = wcur; /* For statistics. */
+ /* debug(F101,"getsbuf wcur","",wcur); */
+ return(n); /* Return its index. */
+ }
+ sbufnum = 0; /* Didn't find one. */
+ return(-3); /* Shouldn't happen! */
+}
+
+int
+getrbuf() { /* Allocate a receive buffer */
+ int i;
+#ifdef COMMENT
+ /* This code is pretty stable by now... */
+ /* Looks like we might need this after all */
+ debug(F101,"getrbuf rbufnum","",rbufnum);
+ debug(F101,"getrbuf wslots","",wslots);
+ debug(F101,"getrbuf dum002","",dum002);
+ debug(F101,"getrbuf dum003","",dum003);
+#endif /* COMMENT */
+ if (rbufnum == 0) return(-1); /* No free buffers. */
+ if (rbufnum < 0) return(-2); /* Shouldn't happen. */
+ for (i = 0; i < wslots; i++) /* Find the first one not in use. */
+ if (rbufuse[i] == 0) { /* Got one? */
+ rbufuse[i] = 1; /* Mark it as in use. */
+ *r_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
+ rbufnum--; /* One less free buffer. */
+ debug(F101,"getrbuf new rbufnum","",rbufnum);
+ if ((what & W_RECV) && (++wcur > wmax))
+ wmax = wcur; /* For statistics. */
+ /* debug(F101,"getrbuf wcur","",wcur); */
+ return(i); /* Return its index. */
+ }
+ /* debug(F101,"getrbuf foulup","",i); */
+ rbufnum = 0; /* Didn't find one. */
+ return(-3); /* Shouldn't happen! */
+}
+
+/* F R E E S B U F -- Free send-buffer for given packet sequence number */
+
+/* Returns: */
+/* 1 upon success */
+/* -1 if specified buffer does not exist */
+
+int
+freesbuf(n) int n; { /* Release send-buffer for packet n. */
+ int i;
+
+ debug(F101,"freesbuf","",n);
+ if (n < 0 || n > 63) /* No such packet. */
+ return(-1);
+ i = sseqtbl[n]; /* Get the window slot number. */
+ if (i > -1 && i <= wslots) {
+ sseqtbl[n] = -1; /* If valid, remove from seqtbl */
+ sbufnum++; /* and count one more free buffer */
+ sbufuse[i] = 0; /* and mark it as free, */
+ if (what & (W_SEND|W_REMO)) /* decrement active slots */
+ wcur--; /* for statistics and display. */
+ } else {
+ debug(F101," sseqtbl[n]","",sseqtbl[n]);
+ return(-1);
+ }
+
+/* The following is done only so dumped buffers will look right. */
+
+ if (1) {
+ *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */
+ s_pkt[i].pk_seq = -1; /* Invalidate the sequence number */
+ s_pkt[i].pk_len = 0; /* Data field length now zero. */
+ s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
+ s_pkt[i].pk_rtr = 0; /* And the retries field. */
+ }
+ return(1);
+}
+
+int
+freerbuf(i) int i; { /* Release receive-buffer slot "i". */
+ int n;
+
+/* NOTE !! Currently, this function frees the indicated buffer, but */
+/* does NOT erase the data. The program counts on this. Will find a */
+/* better way later.... */
+
+ /* debug(F101,"freerbuf, slot","",i); */
+ if (i < 0 || i >= wslots) { /* No such slot. */
+ debug(F101,"freerbuf no such slot","",i);
+ return(-1);
+ }
+ n = r_pkt[i].pk_seq; /* Get the packet sequence number */
+ debug(F101,"freerbuf packet","",n);
+ if (n > -1 && n < 64) /* If valid, remove from seqtbl */
+ rseqtbl[n] = -1;
+ if (rbufuse[i] != 0) { /* If really allocated, */
+ rbufuse[i] = 0; /* mark it as free, */
+ rbufnum++; /* and count one more free buffer. */
+ if (what & W_RECV) /* Keep track of current slots */
+ wcur--; /* for statistics and display */
+ debug(F101,"freerbuf rbufnum","",rbufnum);
+ }
+
+/* The following is done only so dumped buffers will look right. */
+
+ if (1) {
+ /* *r_pkt[i].bf_adr = '\0'; */ /* Zero the buffer data field */
+ r_pkt[i].pk_seq = -1; /* And from packet list */
+ r_pkt[i].pk_len = 0; /* Data field length now zero. */
+ r_pkt[i].pk_typ = ' '; /* Blank the packet type too. */
+ r_pkt[i].pk_rtr = 0; /* And the retries field. */
+ }
+ return(1);
+}
+
+/* This is like freerbuf, except it's called with a packet sequence number */
+/* rather than a packet buffer index. */
+
+VOID
+freerpkt(seq) int seq; {
+ int k;
+ debug(F101,"freerpkt seq","",seq);
+ k = rseqtbl[seq];
+ /* debug(F101,"freerpkt k","",k); */
+ if (k > -1) {
+ k = freerbuf(k);
+ /* debug(F101,"freerpkt freerbuf","",k); */
+ }
+}
+
+
+/* C H K W I N -- Check if packet n is in window. */
+
+/* Returns: */
+/* 0 if it is in the current window, */
+/* +1 if it would have been in previous window (e.g. if ack was lost), */
+/* -1 if it is outside any window (protocol error), */
+/* -2 if either of the argument packet numbers is out of range. */
+
+/* Call with packet number to check (n), lowest packet number in window */
+/* (bottom), and number of slots in window (slots). */
+
+int
+chkwin(n,bottom,slots) int n, bottom, slots; {
+ int top, prev;
+
+ debug(F101,"chkwin packet","",n);
+ debug(F101,"chkwin winlo","",bottom);
+ debug(F101,"chkwin slots","",slots);
+
+/* First do the easy and common cases, where the windows are not split. */
+
+ if (n < 0 || n > 63 || bottom < 0 || bottom > 63)
+ return(-2);
+
+ if (n == bottom) return(0); /* In a perfect world... */
+
+ top = bottom + slots; /* Calculate window top. */
+ if (top < 64 && n < top && n >= bottom)
+ return(0); /* In current window. */
+
+ prev = bottom - slots; /* Bottom of previous window. */
+ if (prev > -1 && n < bottom && n > prev)
+ return(1); /* In previous. */
+
+/* Now consider the case where the current window is split. */
+
+ if (top > 63) { /* Wraparound... */
+ top -= 64; /* Get modulo-64 sequence number */
+ if (n < top || n >= bottom) {
+ return(0); /* In current window. */
+ } else { /* Not in current window. */
+ if (n < bottom && n >= prev) /* Previous window can't be split. */
+ return(1); /* In previous window. */
+ else
+ return(-1); /* Not in previous window. */
+ }
+ }
+
+/* Now the case where current window not split, but previous window is. */
+
+ if (prev < 0) { /* Is previous window split? */
+ prev += 64; /* Yes. */
+ if (n < bottom || n >= prev)
+ return(1); /* In previous window. */
+ } else { /* Previous window not split. */
+ if (n < bottom && n >= prev)
+ return(1); /* In previous window. */
+ }
+
+/* It's not in the current window, and not in the previous window... */
+
+ return(-1); /* So it's not in any window. */
+}
+
+int
+dumpsbuf() { /* Dump send-buffers */
+#ifdef DEBUG
+ int j, x, z; /* to debug log. */
+
+ if (! deblog) return(0);
+ x = zsoutl(ZDFILE,"SEND BUFFERS:");
+ if (x < 0) {
+ deblog = 0;
+ return(0);
+ }
+ x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
+ if (x < 0) {
+ deblog = 0;
+ return(0);
+ }
+ for (j = 0; j < wslots; j++) {
+ if (!sbufuse[j])
+ continue;
+ z = ((unsigned long)(s_pkt[j].bf_adr)) & 0xffff;
+
+ sprintf(xbuf, /* safe (200) */
+ "%4d%6d%10d%5d%6d%4c%5d%6d\n",
+ j,
+ sbufuse[j],
+ /* Avoid warnings when addresses are bigger than ints */
+ z,
+ s_pkt[j].bf_len,
+ s_pkt[j].pk_len,
+ s_pkt[j].pk_typ,
+ s_pkt[j].pk_seq,
+ s_pkt[j].pk_rtr
+ );
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+ if (s_pkt[j].pk_adr) {
+ x = (int)strlen((char *) s_pkt[j].pk_adr);
+ if (x)
+ sprintf(xbuf, /* safe (checked) */
+ "[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : "");
+ else
+ sprintf(xbuf,"[(empty string)]\n"); /* safe (200) */
+ } else {
+ sprintf(xbuf,"[(null pointer)]\n"); /* safe (200) */
+ }
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+ }
+ sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo); /* safe (200) */
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+#endif /* DEBUG */
+ return(0);
+}
+int
+dumprbuf() { /* Dump receive-buffers */
+#ifdef DEBUG
+ int j, x, z;
+ if (! deblog) return(0);
+ if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) {
+ deblog = 0;
+ return(0);
+ }
+ x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries");
+ if (x < 0) {
+ deblog = 0;
+ return(0);
+ }
+ for ( j = 0; j < wslots; j++ ) {
+ if (!rbufuse[j])
+ continue;
+ z = ((unsigned long)(r_pkt[j].bf_adr)) & 0xffff;
+ sprintf(xbuf, /* 200, safe */
+ "%4d%6d%10d%5d%6d%4c%5d%6d\n",
+ j,
+ rbufuse[j],
+ /* Avoid warnings when addresses are bigger than ints */
+ z,
+ r_pkt[j].bf_len,
+ r_pkt[j].pk_len,
+ r_pkt[j].pk_typ,
+ r_pkt[j].pk_seq,
+ r_pkt[j].pk_rtr
+ );
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+ x = (int)strlen((char *)r_pkt[j].bf_adr);
+ sprintf(xbuf, /* safe (checked) */
+ "[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : "");
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+ }
+ sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo); /* safe (200) */
+ if (zsout(ZDFILE,xbuf) < 0) {
+ deblog = 0;
+ return(0);
+ }
+#endif /* DEBUG */
+ return(0);
+}
+
+/* S A T T R -- Send an Attribute Packet */
+
+/*
+ Sends attribute packet(s) for the current file. If the info will not
+ fit into one packet, it can be called repeatedly until all the fields
+ that will fit are sent.
+
+ Call with:
+ xp == 0 if we're sending a real file (F packet), or:
+ xp != 0 for screen data (X packet).
+ And:
+ flag == 1 for first A packet
+ flag == 0 for subsequent A packets.
+ Returns:
+ 1 or greater if an A packet was sent, or:
+ 0 if an S-packet was not sent because there was no data to send or
+ there was no data left that was short enough to send, or:
+ -1 on any kind of error.
+*/
+
+/* (don't) #define TSOFORMAT */
+/* which was only for making C-Kermit send TSO-Kermit-like A packets */
+/* to try to track down a problem somebody reported... */
+
+int
+sattr(xp, flag) int xp, flag; { /* Send Attributes */
+
+ static int max; /* Maximum length for Attributes */
+ static short done[95]; /* Field-complete array */
+ static struct zattr x; /* File attribute struct */
+ static char xdate[24];
+
+ extern char * cksysid;
+
+ /* Some extra flags are used because the "done" array is sparse */
+
+ int i, j, rc, aln, left = 0, numset = 0, xbin = 0; /* Workers */
+ int notafile = 0;
+ char *tp, c;
+
+ notafile = sndarray || pipesend ||
+#ifdef PIPESEND
+ sndfilter ||
+#endif /* PIPESEND */
+ calibrate;
+
+ debug(F101,"sattr flag","",flag);
+ if (!flag) /* No more attributes to send */
+ if (done[xunchar('@')])
+ return(0);
+
+ /* Initialize Attribute mechanism */
+
+ if (flag) { /* First time here for this file? */
+ initattr(&x); /* Blank out all the fields. */
+ for (j = 0; j < 95; j++) /* Init array of completed fields */
+ done[j] = 0;
+ max = maxdata(); /* Get maximum data field length */
+ if (notafile || xp == 1) { /* Is it not a real file? */
+ extern char * zzndate();
+ char * p;
+ int i;
+#ifdef CALIBRATE
+ if (calibrate) { /* Calibration run... */
+ x.lengthk = calibrate / 1024L; /* We know the length */
+ x.length = calibrate;
+ }
+#endif /* CALIBRATE */
+ x.systemid.val = cksysid; /* System ID */
+ x.systemid.len = (int)strlen(cksysid);
+ ckstrncpy(xdate,zzndate(),24);
+ xdate[8] = SP;
+ ztime(&p);
+ for (i = 11; i < 19; i++) /* copy hh:mm:ss */
+ xdate[i - 2] = p[i]; /* to xdate */
+ xdate[17] = NUL; /* terminate */
+ x.date.val = xdate;
+ x.date.len = 17;
+ debug(F111,"sattr notafile date",x.date.val,x.date.len);
+ } else { /* Real file */
+ rc = zsattr(&x); /* Get attributes for this file */
+ debug(F101,"sattr zsattr","",rc);
+ if (rc < 0) /* Can't get 'em so don't send 'em */
+ return(0);
+ debug(F101,"sattr init max","",max);
+ }
+ }
+ if (nxtpkt() < 0) /* Got 'em, get next packet number */
+ return(-1); /* Bad news if we can't */
+
+ i = 0; /* Init data field character number */
+
+ /* Do each attribute using first-fit method, marking as we go */
+ /* This is rather long and repititious - could be done more cleverly */
+
+ if (atsido && !done[xunchar(c = '.')]) { /* System type */
+ if (max - i >= x.systemid.len + 2) { /* Enough space ? */
+ data[i++] = c; /* Yes, add parameter */
+ data[i++] = tochar(x.systemid.len); /* Add length */
+ for (j = 0; j < x.systemid.len; j++) /* Add data */
+ data[i++] = x.systemid.val[j];
+ numset++; /* Count that we did at least one */
+ done[xunchar(c)] = 1; /* Mark this attribute as done */
+ } else /* No */
+ left++; /* so mark this one left to do */
+ }
+#ifdef STRATUS
+ if (atcreo && !done[xunchar(c = '$')]) { /* Creator */
+ if (max - i >= x.creator.len + 2) { /* Enough space ? */
+ data[i++] = c;
+ data[i++] = tochar(x.creator.len);
+ for (j = 0; j < x.creator.len; j++)
+ data[i++] = x.creator.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+ if (atacto && !done[xunchar(c = '%')]) { /* File account */
+ if (max - i >= x.account.len + 2) {
+ data[i++] = c;
+ data[i++] = tochar(x.account.len);
+ for (j = 0; j < x.account.len; j++)
+ data[i++] = x.account.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+ if (atfrmo && !done[xunchar(c = '/')]) { /* Packet data format */
+ if (max - i >= x.recfm.len + 2) {
+ data[i++] = c;
+ data[i++] = tochar(x.recfm.len); /* Copy from attr structure */
+ for (j = 0; j < x.recfm.len; j++)
+ data[i++] = x.recfm.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+#endif /* STRATUS */
+
+ xbin = /* Is the transfer in binary mode? */
+#ifdef VMS
+ binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */
+ !strncmp(x.recfm.val,"F",1) /* or RECFM=Fxxxxxx */
+#else
+ binary /* User said SET FILE TYPE BINARY */
+#endif /* VMS */
+ ;
+
+ if (attypo && !done[xunchar(c = '"')]) { /* File type */
+ if (max - i >= 5) { /* Max length for this field */
+ data[i++] = c;
+ if (xbin) { /* Binary */
+ data[i++] = tochar(2); /* Two characters */
+ data[i++] = 'B'; /* B for Binary */
+ data[i++] = '8'; /* 8-bit bytes (note assumption...) */
+#ifdef CK_LABELED
+ if (binary != XYFT_L
+#ifdef VMS
+ && binary != XYFT_I
+#endif /* VMS */
+ )
+ binary = XYFT_B;
+#endif /* CK_LABELED */
+ } else { /* Text */
+#ifdef TSOFORMAT
+ data[i++] = tochar(1); /* One character */
+ data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */
+#else
+ data[i++] = tochar(3); /* Three characters */
+ data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */
+ data[i++] = 'M'; /* M for carriage return */
+ data[i++] = 'J'; /* J for linefeed */
+#endif /* TSOFORMAT */
+
+#ifdef VMS
+ binary = XYFT_T; /* We automatically detected text */
+#endif /* VMS */
+ }
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+
+#ifdef TSOFORMAT
+ if (attypo && !xbin && !done[xunchar(c = '/')]) { /* Record format */
+ if (max - i >= 5) {
+ data[i++] = c;
+ data[i++] = tochar(3); /* Three characters */
+ data[i++] = 'A'; /* A = variable with CRLFs */
+ data[i++] = 'M'; /* M for carriage return */
+ data[i++] = 'J'; /* J for linefeed */
+ }
+ }
+#endif /* TSOFORMAT */
+
+ if (attypo && !xbin && !done[xunchar(c = '*')]) { /* Text encoding */
+#ifdef NOCSETS
+ if (max - i >= 3) {
+ data[i++] = c;
+ data[i++] = tochar(1); /* Length of value is 1 */
+ data[i++] = 'A'; /* A for ASCII */
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+#else
+ if (tcharset == TC_TRANSP || !xfrxla) { /* Transfer character set */
+ if (max - i >= 3) {
+ data[i++] = c; /* Encoding */
+ data[i++] = tochar(1); /* Length of value is 1 */
+ data[i++] = 'A'; /* A for ASCII (i.e. text) */
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ } else {
+ tp = tcsinfo[tcharset].designator;
+ if (!tp) tp = "";
+ aln = strlen(tp);
+ if (aln > 0) {
+ if (max - i >= aln + 2) {
+ data[i++] = c; /* Encoding */
+ data[i++] = tochar(aln+1); /* Length of designator. */
+ data[i++] = 'C'; /* Text in specified charset. */
+ for (j = 0; j < aln; j++) /* Copy designator */
+ data[i++] = *tp++; /* Example: *&I6/100 */
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ } else
+ done[xunchar(c)] = 1;
+ }
+#endif /* NOCSETS */
+ }
+ if (atdato && !done[xunchar(c = '#')] && /* Creation date, if any */
+ (aln = x.date.len) > 0) {
+ if (max - i >= aln + 2) {
+ data[i++] = c;
+ data[i++] = tochar(aln);
+ for (j = 0; j < aln; j++)
+ data[i++] = x.date.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+ /* File length in K */
+ if (atleno && !done[xunchar(c = '!')] && x.lengthk > -1L) {
+ sprintf((char *) &data[i+2],"%ld",x.lengthk); /* safe */
+ aln = (int)strlen((char *)(data+i+2));
+ if (max - i >= aln + 2) {
+ data[i] = c;
+ data[i+1] = tochar(aln);
+ i += aln + 2;
+ numset++;
+ done[xunchar(c)] = 1;
+ } else {
+ data[i] = NUL;
+ left++;
+ }
+ }
+ /* File length in bytes */
+ if (atleno && !done[xunchar(c = '1')] && x.length > -1L) {
+ sprintf((char *) &data[i+2],"%ld",x.length); /* safe */
+ aln = (int)strlen((char *)(data+i+2));
+ if (max - i >= aln + 2) {
+ data[i] = c;
+ data[i+1] = tochar(aln);
+ i += aln + 2;
+ numset++;
+ done[xunchar(c)] = 1;
+ } else {
+ data[i] = NUL;
+ left++;
+ }
+ }
+#ifdef CK_PERMS
+ if (atlpro && !done[xunchar(c = ',')] && /* Local protection */
+ (aln = x.lprotect.len) > 0 && !notafile && xp == 0) {
+ if (max - i >= aln + 2) {
+ data[i++] = c;
+ data[i++] = tochar(aln);
+ for (j = 0; j < aln; j++)
+ data[i++] = x.lprotect.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+ if (atgpro && !done[xunchar(c = '-')] && /* Generic protection */
+ (aln = x.gprotect.len) > 0 && !notafile && xp == 0) {
+ if (max - i >= aln + 2) {
+ data[i++] = c;
+ data[i++] = tochar(aln);
+ for (j = 0; j < aln; j++)
+ data[i++] = x.gprotect.val[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+#endif /* CK_PERMS */
+ if (atblko && fblksiz && !done[xunchar(c = '(')] &&
+ !notafile && xp == 0) { /* Blocksize */
+ sprintf((char *) &data[i+2],"%d",fblksiz); /* safe */
+ aln = (int)strlen((char *)(data+i+2));
+ if (max - i >= aln + 2) {
+ data[i] = c;
+ data[i+1] = tochar(aln);
+ i += aln + 2;
+ numset++;
+ done[xunchar(c)] = 1;
+ } else {
+ data[i] = NUL;
+ left++;
+ }
+ }
+#ifndef NOFRILLS
+ if ((rprintf || rmailf) && atdiso && /* MAIL, or REMOTE PRINT? */
+ !done[xunchar(c = '+')]) {
+ aln = (int) strlen(optbuf) + 1; /* Options, if any */
+ if (max - i >= aln + 2) {
+ data[i++] = c; /* Disposition */
+ data[i++] = tochar(aln); /* Options, if any */
+ if (rprintf)
+ data[i++] = 'P'; /* P for Print */
+ else
+ data[i++] = 'M'; /* M for Mail */
+ for (j = 0; optbuf[j]; j++) /* Copy any options */
+ data[i++] = optbuf[j];
+ numset++;
+ done[xunchar(c)] = 1;
+ } else {
+ data[i] = NUL;
+ left++;
+ }
+ }
+#endif /* NOFRILLS */
+#ifdef CK_RESEND
+ if (sendmode == SM_RESEND && !done[xunchar(c = '+')]) {
+ if (max - i >= 3) {
+ data[i++] = c; /* Disposition */
+ data[i++] = tochar(1);
+ data[i++] = 'R'; /* is RESEND */
+ numset++;
+ done[xunchar(c)] = 1;
+ } else
+ left++;
+ }
+#endif /* CK_RESEND */
+
+ /* End of Attributes -- to be sent only after sending all others */
+
+ debug(F111,"sattr","@",i);
+ debug(F101,"sattr numset","",numset);
+ debug(F101,"sattr left","",left);
+
+ if ((left == 0 || numset == 0) && !done[xunchar(c = '@')]) {
+ if (max - i >= 3) {
+ data[i++] = c; /* End of Attributes */
+ data[i++] = SP; /* Length 0 */
+ data[i] = NUL; /* Make sure it's null-terminated */
+ numset++;
+ done[xunchar(c)] = 1;
+ }
+ }
+
+ /* Finished - send the packet off if we have anything in it */
+
+ if (numset) {
+ data[i] = NUL; /* Terminate last good field */
+ debug(F111,"sattr sending",data,left);
+ aln = (int)strlen((char *)data); /* Get overall length of attributes */
+ return(spack('A',pktnum,aln,data)); /* Send it */
+ } else
+ return(0);
+}
+
+static char *refused = "";
+
+static char *reason[] = {
+ "size", "type", "date", "creator", "account", "area", "password",
+ "blocksize", "access", "encoding", "disposition", "protection",
+ "protection", "origin", "format",
+ "sys-dependent", /* 0 */
+ "size", /* 1 */
+ "2", /* 2 */
+ "3", /* 3 */
+ "4", /* 4 */
+ "5", /* 5 */
+ "6", /* 6 */
+ "7", /* 7 */
+ "8", /* 8 */
+ "9", /* 9 */
+ ":", /* : */
+ ";", /* ; */
+ "<", /* < */
+ "=", /* = */
+ ">", /* > */
+ "name", /* ? */
+ "@"
+};
+static int nreason = sizeof(reason) / sizeof(char *);
+int rejection = -1;
+
+char *
+getreason(s) char *s; { /* Decode attribute refusal reason */
+ char c, *p;
+ if (rejection == 1) /* Kludge for SET FIL COLL DISCARD */
+ return("name"); /* when other Kermit doesn't... */
+ p = s;
+ if (*p++ != 'N') return(""); /* Should start with N */
+ else if ((c = *p) > SP) { /* get reason, */
+ rejection = c; /* remember it, */
+ c -= '!'; /* get offset */
+ p = ((unsigned int) ((CHAR) c) <= (unsigned int) nreason) ?
+ reason[c] :
+ "unknown";
+ }
+ return(p);
+}
+
+int
+rsattr(s) CHAR *s; { /* Read response to attribute packet */
+ debug(F111,"rsattr",s,*s);
+ if (*s == 'N') { /* If it's 'N' followed by anything, */
+ refused = getreason((char *)s); /* they are refusing, get reason. */
+ debug(F110,"rsattr refused",refused,0);
+ tlog(F110," refused:",refused,0L);
+ return(-1);
+ }
+#ifdef CK_RESEND
+ if (sendmode == SM_RESEND && *s == '1') { /* RESEND length */
+ int n; long z; CHAR *p;
+ p = s + 1;
+ n = xunchar(*p++);
+ debug(F101,"rsattr RESEND n","",n);
+ z = 0L;
+ while (n-- > 0) /* We assume the format is good. */
+ z = 10L * z + (long) (*p++ - '0');
+ debug(F101,"rsattr RESEND z","",z);
+ if (z > 0L) sendstart = z;
+ debug(F101,"rsattr RESEND sendstart","",sendstart);
+ if (sendstart > 0L)
+ if (zfseek(sendstart) < 0) /* Input file is already open. */
+ return(0);
+#ifdef CK_CURSES
+ if (fdispla == XYFD_C)
+ xxscreen(SCR_FS,0,fsize,""); /* Refresh file transfer display */
+#endif /* CK_CURSES */
+ }
+#endif /* CK_RESEND */
+ refused = "";
+ return(0);
+}
+
+long rs_len = 0L; /* Length of file being resent to */
+
+/*
+ Get attributes from incoming A packet. Returns:
+ 0 on success, file is to be accepted
+ -1 on failure, file is to be refused
+*/
+int
+gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */
+ char c, d;
+ char *ff;
+ int aln, i;
+
+#ifndef NOCSETS
+ extern int r_cset, axcset[];
+#endif /* NOCSETS */
+
+#define ABUFL 40 /* Temporary buffer for conversions */
+ char abuf[ABUFL+1];
+#define RFBUFL 10 /* Record-format buffer */
+ static char rfbuf[RFBUFL+1];
+#define FTBUFL 10 /* File type buffer */
+ static char ftbuf[FTBUFL+1];
+#define DTBUFL 40 /* File creation date */
+ static char dtbuf[DTBUFL+1];
+#define TSBUFL 10 /* Transfer syntax */
+ static char tsbuf[TSBUFL+1];
+#define IDBUFL 10 /* System ID */
+ static char idbuf[IDBUFL+1];
+#ifndef DYNAMIC
+#define DSBUFL 100 /* Disposition */
+ static char dsbuf[DSBUFL+1];
+#define SPBUFL 512 /* System-dependent parameters */
+ static char spbuf[SPBUFL+1];
+#else
+#define DSBUFL 100 /* Disposition */
+ static char *dsbuf = NULL;
+#define SPBUFL 512 /* System-dependent parameters */
+ static char *spbuf = NULL;
+#endif /* DYNAMIC */
+#define RPBUFL 20 /* Attribute reply */
+ static char rpbuf[RPBUFL+1];
+
+#ifdef CK_PERMS
+ static char lprmbuf[CK_PERMLEN+1];
+ static char gprmbuf[2];
+#endif /* CK_PERMS */
+
+ char *rp; /* Pointer to reply buffer */
+ int retcode; /* Return code */
+
+ d = SP; /* Initialize disposition */
+ ff = filnam; /* Filename returned by rcvfil */
+ if (fncact == XYFX_R && ofn1x && ofn1[0]) /* But watch out for FC=RENAME */
+ ff = ofn1; /* because we haven't renamed it yet */
+
+/* Fill in the attributes we have received */
+
+ rp = rpbuf; /* Initialize reply buffer */
+ *rp++ = 'N'; /* for negative reply. */
+ *rp = NUL;
+ retcode = 0; /* Initialize return code. */
+
+ if (dest == DEST_P) { /* SET DESTINATION PRINTER */
+#ifdef DYNAMIC
+ if (!dsbuf)
+ if ((dsbuf = malloc(DSBUFL+1)) == NULL)
+ fatal("gtattr: no memory for dsbuf");
+#endif /* DYNAMIC */
+ dsbuf[0] = 'P';
+ dsbuf[1] = '\0';
+ yy->disp.val = dsbuf;
+ yy->disp.len = 1;
+ }
+ while (c = *s++) { /* Get attribute tag */
+ aln = xunchar(*s++); /* Length of attribute string */
+ switch (c) {
+ case '!': /* File length in K */
+ for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+ abuf[i] = *s++;
+ abuf[i] = '\0'; /* Terminate with null */
+ if (i < aln) s += (aln - i); /* If field was too long for buffer */
+ yy->lengthk = atol(abuf); /* Convert to number */
+ break;
+
+ case '/': /* Record format */
+ rfbuf[1] = NUL;
+ rfbuf[2] = NUL;
+ for (i = 0; (i < aln) && (i < RFBUFL); i++) /* Copy it */
+ rfbuf[i] = *s++;
+ rfbuf[i] = NUL; /* Terminate with null */
+ yy->recfm.val = rfbuf; /* Pointer to string */
+ yy->recfm.len = i; /* Length of string */
+ if ((rfbuf[0] != 'A') ||
+ (rfbuf[1] && rfbuf[1] != 'M') ||
+ (rfbuf[2] && rfbuf[2] != 'J')) {
+ debug(F110,"gattr bad recfm",rfbuf,0);
+ *rp++ = c;
+ retcode = -1;
+ }
+ break;
+
+ case '"': /* File type (text, binary, ...) */
+ for (i = 0; (i < aln) && (i < FTBUFL); i++)
+ ftbuf[i] = *s++; /* Copy it into a static string */
+ ftbuf[i] = '\0';
+ if (i < aln) s += (aln - i);
+ /* TYPE attribute is enabled? */
+ if (attypi) {
+ yy->type.val = ftbuf; /* Pointer to string */
+ yy->type.len = i; /* Length of string */
+ debug(F111,"gattr file type", ftbuf, i);
+ debug(F101,"gattr binary 1","",binary);
+ /* Unknown type? */
+ if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I')
+#ifdef CK_LABELED
+/* ... Or our FILE TYPE is LABELED and the incoming file is text... */
+ || (binary == XYFT_L && *ftbuf == 'A' && !xflg)
+#endif /* CK_LABELED */
+ ) {
+ retcode = -1; /* Reject the file */
+ *rp++ = c;
+ if (!opnerr) tlog(F100," refused: type","",0);
+ break;
+ }
+/*
+ The following code moved here from opena() so we set binary mode
+ as soon as requested by the attribute packet. That way when the first
+ data packet comes, the mode of transfer can be displayed correctly
+ before opena() is called.
+*/
+ if (yy->type.val[0] == 'A') { /* Check received attributes. */
+#ifdef VMS
+ if (binary != XYFT_I) /* VMS IMAGE overrides this */
+#endif /* VMS */
+ binary = XYFT_T; /* Set current type to Text. */
+ debug(F101,"gattr binary 2","",binary);
+ } else if (yy->type.val[0] == 'B') {
+#ifdef CK_LABELED
+ if (binary != XYFT_L
+#ifdef VMS
+ && binary != XYFT_U /* VMS special case */
+#endif /* VMS */
+ )
+#endif /* CK_LABELED */
+#ifdef MAC
+ if (binary != XYFT_M) /* If not MacBinary... */
+#endif /* MAC */
+ binary = XYFT_B;
+ debug(F101,"gattr binary 3","",binary);
+ }
+ }
+ break;
+
+ case '#': /* File creation date */
+ for (i = 0; (i < aln) && (i < DTBUFL); i++)
+ dtbuf[i] = *s++; /* Copy it into a static string */
+ if (i < aln) s += (aln - i);
+ dtbuf[i] = '\0';
+ if (atdati && !xflg) { /* Real file and dates enabled */
+ yy->date.val = dtbuf; /* Pointer to string */
+ yy->date.len = i; /* Length of string */
+ if (fncact == XYFX_U) { /* Receiving in update mode? */
+ if (zstime(ff,yy,1) > 0) { /* Compare dates */
+ *rp++ = c; /* Discard if older, reason = date. */
+ if (!opnerr) tlog(F100," refused: date","",0);
+ retcode = -1; /* Rejection notice. */
+ }
+ }
+ }
+ break;
+
+ case '(': /* File Block Size */
+ for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+ abuf[i] = *s++;
+ abuf[i] = '\0'; /* Terminate with null */
+ if (i < aln) s += (aln - i);
+ if (atblki)
+ yy->blksize = atol(abuf); /* Convert to number */
+ break;
+
+ case '*': /* Encoding (transfer syntax) */
+ for (i = 0; (i < aln) && (i < TSBUFL); i++)
+ tsbuf[i] = *s++; /* Copy it into a static string */
+ if (i < aln) s += (aln - i);
+ tsbuf[i] = '\0';
+#ifndef NOCSETS
+ xlatype = XLA_NONE; /* Assume no translation */
+#endif /* NOCSETS */
+ if (atenci) {
+ char * ss;
+ yy->encoding.val = tsbuf; /* Pointer to string */
+ yy->encoding.len = i; /* Length of string */
+ debug(F101,"gattr encoding",tsbuf,i);
+ ss = tsbuf+1;
+ switch (*tsbuf) {
+#ifndef NOCSETS
+ case 'A': /* Normal, nothing special */
+ tcharset = TC_TRANSP; /* Transparent chars untranslated */
+ debug(F110,"gattr sets tcharset TC_TRANSP","A",0);
+ break;
+ case 'C': /* Specified character set */
+ if (!xfrxla) { /* But translation disabled */
+ tcharset = TC_TRANSP;
+ debug(F110,"gattr sets tcharset TC_TRANSP","C",0);
+ break;
+ }
+#ifdef UNICODE
+ if (!strcmp("I196",ss)) /* Treat I196 (UTF-8 no level) */
+ ss = "I190"; /* as I190 (UTF-8 Level 1) */
+#endif /* UNICODE */
+ if (!strcmp("I6/204",ss)) /* Treat "Latin-1 + Euro" */
+ ss = "I6/100"; /* as I6/100 (regular Latin-1) */
+ for (i = 0; i < ntcsets; i++) {
+ if (!strcmp(tcsinfo[i].designator,ss))
+ break;
+ }
+ debug(F101,"gattr xfer charset lookup","",i);
+ if (i == ntcsets) { /* If unknown character set, */
+ debug(F110,"gattr: xfer charset unknown",ss,0);
+ if (!unkcs) { /* and SET UNKNOWN DISCARD, */
+ retcode = -1; /* reject the file. */
+ *rp++ = c;
+ if (!opnerr)
+ tlog(F100," refused: character set","",0);
+ }
+ } else {
+ tcharset = tcsinfo[i].code; /* it's known, use it */
+ debug(F101,"gattr switch tcharset","",tcharset);
+ debug(F101,"gattr fcharset","",fcharset);
+ if (r_cset == XMODE_A) { /* Automatic switching? */
+ if (tcharset > -1 && tcharset <= MAXTCSETS) {
+ int x;
+ x = axcset[tcharset];
+ if (x > 0 && x <= MAXFCSETS) {
+ fcharset = x;
+ debug(F101,"gattr switch fcharset","",x);
+ }
+ }
+ }
+ /* Set up translation type and function */
+ setxlatype(tcharset,fcharset);
+ }
+ break;
+#endif /* NOCSETS */
+ default: /* Something else. */
+ debug(F110,"gattr unk encoding attribute",tsbuf,0);
+ if (!unkcs) { /* If SET UNK DISC */
+ retcode = -1;
+ *rp++ = c;
+ if (!opnerr) tlog(F100," refused: encoding","",0);
+ }
+ break;
+ }
+ }
+ break;
+
+ case '+': /* Disposition */
+#ifdef DYNAMIC
+ if (!dsbuf)
+ if ((dsbuf = malloc(DSBUFL+1)) == NULL)
+ fatal("gtattr: no memory for dsbuf");
+#endif /* DYNAMIC */
+ for (i = 0; (i < aln) && (i < DSBUFL); i++)
+ dsbuf[i] = *s++; /* Copy it into a separate string */
+ dsbuf[i] = '\0';
+ if (i < aln) s += (aln - i);
+ rs_len = 0;
+ if (atdisi) { /* We are doing this attribute */
+ /* Copy it into the attribute structure */
+ yy->disp.val = dsbuf; /* Pointer to string */
+ yy->disp.len = i; /* Length of string */
+ d = *dsbuf;
+#ifndef NODISPO
+/*
+ Define NODISPO to disable receipt of mail or print files and of RESEND.
+*/
+ if (
+#ifndef datageneral /* MAIL supported only for */
+#ifndef OS2 /* UNIX, VMS, and OS-9 */
+#ifndef MAC
+#ifndef GEMDOS
+#ifndef AMIGA
+ d != 'M' && /* MAIL */
+#endif /* AMIGA */
+#endif /* GEMDOS */
+#endif /* MAC */
+#endif /* OS/2 */
+#endif /* datageneral */
+#ifdef CK_RESEND
+ d != 'R' && /* RESEND */
+#endif /* CK_RESEND */
+ d != 'P') { /* PRINT */
+ retcode = -1; /* Unknown/unsupported disposition */
+ *rp++ = c;
+ if (!opnerr) tlog(F101," refused: bad disposition","",d);
+ }
+ dispos = d;
+ debug(F000,"gattr dispos","",dispos);
+ switch (d) {
+#ifndef NOFRILLS
+ case 'M':
+ if (!en_mai) {
+ retcode = -1;
+ *rp++ = c;
+ if (!opnerr) tlog(F100," refused: mail disabled","",0);
+ dispos = 0;
+ }
+ break;
+#endif /* NOFRILLS */
+ case 'P':
+ if (!en_pri) {
+ retcode = -1;
+ *rp++ = c;
+ if (!opnerr)
+ tlog(F100," refused: print disabled","",0);
+ dispos = 0;
+ }
+ break;
+
+ case 'R':
+ dispos = 0;
+#ifdef CK_RESEND
+ rs_len = zgetfs(ff); /* Get length of file */
+ debug(F111,"gattr RESEND",ff,rs_len);
+#ifdef VMS
+ rs_len &= (long) -512; /* Ensure block boundary if VMS */
+ rs_len -= 512; /* In case last block not complete */
+ debug(F111,"gattr rs_len",ff,rs_len);
+#endif /* VMS */
+#ifdef COMMENT
+ if (rs_len < 0L) /* Local file doesn't exist */
+ rs_len = 0L;
+#endif /* COMMENT */
+/*
+ Another possibility here (or later, really) would be to check if the two
+ file lengths are the same, and if so, keep the prevailing collision action
+ as is (note: rs_len == length of existing file; yy->length == fsize ==
+ length of incoming file). This could be complicated, though, since
+ (a) we might not have received the length attribute yet, and in fact it
+ might even be in a subsequent A-packet, yet (b) we have to accept or reject
+ the Recover attribute now. So better to leave as-is. Anyway, it's probably
+ more useful this way.
+*/
+ if (rs_len > 0L) {
+ fncsav = fncact; /* Save collision action */
+ fncact = XYFX_A; /* Switch to APPEND */
+ }
+#else
+ retcode = -1; /* This shouldn't happen */
+ *rp++ = c; /* 'cause it wasn't negotiated. */
+ if (!opnerr) tlog(F100," refused: resend","",0);
+#endif /* CK_RESEND */
+ }
+#else /* NODISPO */
+ retcode = -1;
+ *rp++ = c;
+ if (!opnerr) tlog(F100," refused: NODISPO","",0);
+#endif /* NODISPO */
+ }
+ break;
+
+ case '.': /* Sender's system ID */
+ for (i = 0; (i < aln) && (i < IDBUFL); i++)
+ idbuf[i] = *s++; /* Copy it into a static string */
+ idbuf[i] = '\0';
+ if (i < aln) s += (aln - i);
+ if (atsidi) {
+ yy->systemid.val = idbuf; /* Pointer to string */
+ yy->systemid.len = i; /* Length of string */
+ }
+ break;
+
+ case '0': /* System-dependent parameters */
+#ifdef DYNAMIC
+ if (!spbuf && !(spbuf = malloc(SPBUFL)))
+ fatal("gattr: no memory for spbuf");
+#endif /* DYNAMIC */
+ for (i = 0; (i < aln) && (i < SPBUFL); i++)
+ spbuf[i] = *s++; /* Copy it into a static string */
+ spbuf[i] = '\0';
+ if (i < aln) s += (aln - i);
+ if (atsysi) {
+ yy->sysparam.val = spbuf; /* Pointer to string */
+ yy->sysparam.len = i; /* Length of string */
+ }
+ break;
+
+ case '1': /* File length in bytes */
+ for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */
+ abuf[i] = *s++;
+ abuf[i] = '\0'; /* Terminate with null */
+ if (i < aln) s += (aln - i);
+ yy->length = atol(abuf); /* Convert to number */
+ debug(F111,"gattr length",abuf,(int) yy->length);
+ break;
+
+
+#ifdef CK_PERMS
+ case ',': /* System-dependent protection code */
+ for (i = 0; (i < aln) && (i < CK_PERMLEN); i++)
+ lprmbuf[i] = *s++; /* Just copy it - decode later */
+ lprmbuf[i] = '\0'; /* Terminate with null */
+ if (i < aln) s += (aln - i);
+ if (atlpri) {
+ yy->lprotect.val = (char *)lprmbuf;
+ yy->lprotect.len = i;
+ } else
+ lprmbuf[0] = NUL;
+ break;
+
+ case '-': /* Generic "world" protection code */
+ gprmbuf[0] = NUL; /* Just 1 byte by definition */
+ for (i = 0; i < aln; i++) /* But allow for more... */
+ if (i == 0) gprmbuf[0] = *s++;
+ gprmbuf[1] = NUL;
+ if (atgpri) {
+ yy->gprotect.val = (char *)gprmbuf;
+ yy->gprotect.len = gprmbuf[0] ? 1 : 0;
+ } else
+ gprmbuf[0] = NUL;
+ break;
+#endif /* CK_PERMS */
+
+ default: /* Unknown attribute */
+ s += aln; /* Just skip past it */
+ break;
+ }
+ }
+
+ /* Check file length now, because we also need to know the file type */
+ /* in case zchkspa() differentiates text and binary (VMS version does) */
+
+ if (atleni) { /* Length attribute enabled? */
+ if (yy->length > -1L) { /* Length-in-bytes attribute rec'd? */
+ if (!zchkspa(ff,(yy->length))) { /* Check space */
+ retcode = -1; /* Not enuf */
+ *rp++ = '1';
+ if (!opnerr) tlog(F100," refused: length bytes","",0);
+ }
+ } else if (yy->lengthk > -1L) { /* Length in K attribute rec'd? */
+ if (!zchkspa(ff,(yy->lengthk * 1024))) {
+ retcode = -1; /* Check space */
+ *rp++ = '!';
+ if (!opnerr) tlog(F100," refused: length K","",0);
+ }
+ }
+ }
+ if (yy->length > -1L) { /* Remember the file size */
+ fsize = yy->length;
+ } else if (yy->lengthk > -1L) {
+ fsize = yy->lengthk * 1024L;
+ } else fsize = -1L;
+
+#ifdef DEBUG
+ if (deblog) {
+ sprintf(abuf,"%ld",fsize); /* safe */
+ debug(F110,"gattr fsize",abuf,0);
+ }
+#endif /* DEBUG */
+
+ if (retcode == 0) rp = rpbuf; /* Null reply string if accepted */
+ *rp = '\0'; /* End of reply string */
+
+#ifdef CK_RESEND
+ if (d == 'R') { /* Receiving a RESEND? */
+ debug(F101,"gattr RESEND","",retcode);
+ /* We ignore retcodes because this overrides */
+ if (binary != XYFT_B) { /* Reject if not binary */
+ retcode = -1; /* in case type field came */
+ ckstrncpy(rpbuf,"N+",RPBUFL); /* after the disposition field */
+ debug(F111,"gattr RESEND not binary",rpbuf,binary);
+ } else { /* Binary mode */
+ retcode = 0; /* Accept the file */
+ discard = 0; /* If SET FILE COLLISION DISCARD */
+ sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */
+ rpbuf[0] = '1'; /* '1' means Length in Bytes */
+ rpbuf[1] = tochar((int)strlen(rpbuf+2)); /* Length of length */
+ debug(F111,"gattr RESEND OK",rpbuf,retcode);
+ }
+ }
+#endif /* CK_RESEND */
+ if (retcode == 0 && discard != 0) { /* Do we still have a discard flag? */
+ ckstrncpy(rpbuf,"N?",RPBUFL); /* Yes, must be filename collision */
+ retcode = -1; /* "?" = name (reply-only code) */
+ }
+ yy->reply.val = rpbuf; /* Add it to attribute structure */
+ yy->reply.len = (int)strlen(rpbuf);
+ if (retcode < 0) { /* If we are rejecting */
+ discard = 1; /* remember to discard the file */
+ rejection = rpbuf[1]; /* and use the first reason given. */
+ if (fncsav != -1) {
+ fncact = fncsav;
+ fncsav = -1;
+ }
+ }
+ debug(F111,"gattr return",rpbuf,retcode);
+ return(retcode);
+}
+
+/* I N I T A T T R -- Initialize file attribute structure */
+
+int
+initattr(yy) struct zattr *yy; {
+ yy->lengthk = yy->length = -1L;
+ yy->type.val = "";
+ yy->type.len = 0;
+ yy->date.val = "";
+ yy->date.len = 0;
+ yy->encoding.val = "";
+ yy->encoding.len = 0;
+ yy->disp.val = "";
+ yy->disp.len = 0;
+ yy->systemid.val = "";
+ yy->systemid.len = 0;
+ yy->sysparam.val = "";
+ yy->sysparam.len = 0;
+ yy->creator.val = "";
+ yy->creator.len = 0;
+ yy->account.val = "";
+ yy->account.len = 0;
+ yy->area.val = "";
+ yy->area.len = 0;
+ yy->password.val = "";
+ yy->password.len = 0;
+ yy->blksize = -1L;
+ yy->xaccess.val = "";
+ yy->xaccess.len = 0;
+#ifdef CK_PERMS
+ if (!ofperms) ofperms = "";
+ debug(F110,"initattr ofperms",ofperms,0);
+ yy->lprotect.val = ofperms;
+ yy->lprotect.len = 0 - strlen(ofperms); /* <-- NOTE! */
+ /*
+ A negative length indicates that we have a permissions string but it has
+ been inherited from a previously existing file rather than picked up
+ from an incoming A-packet.
+ */
+#else
+ yy->lprotect.val = "";
+ yy->lprotect.len = 0;
+#endif /* CK_PERMS */
+ yy->gprotect.val = "";
+ yy->gprotect.len = 0;
+ yy->recfm.val = "";
+ yy->recfm.len = 0;
+ yy->reply.val = "";
+ yy->reply.len = 0;
+#ifdef OS2
+ yy->longname.len = 0 ;
+ yy->longname.val = "" ;
+#endif /* OS2 */
+ return(0);
+}
+
+/* A D E B U -- Write attribute packet info to debug log */
+
+int
+adebu(f,zz) char *f; struct zattr *zz; {
+#ifdef DEBUG
+ if (deblog == 0) return(0);
+ debug(F110,"Attributes for incoming file ",f,0);
+ debug(F101," length in K","",(int) zz->lengthk);
+ debug(F111," file type",zz->type.val,zz->type.len);
+ debug(F111," creation date",zz->date.val,zz->date.len);
+ debug(F111," creator",zz->creator.val,zz->creator.len);
+ debug(F111," account",zz->account.val,zz->account.len);
+ debug(F111," area",zz->area.val,zz->area.len);
+ debug(F111," password",zz->password.val,zz->password.len);
+ debug(F101," blksize","",(int) zz->blksize);
+ debug(F111," access",zz->xaccess.val,zz->xaccess.len);
+ debug(F111," encoding",zz->encoding.val,zz->encoding.len);
+ debug(F111," disposition",zz->disp.val,zz->disp.len);
+ debug(F111," lprotection",zz->lprotect.val,zz->lprotect.len);
+ debug(F111," gprotection",zz->gprotect.val,zz->gprotect.len);
+ debug(F111," systemid",zz->systemid.val,zz->systemid.len);
+ debug(F111," recfm",zz->recfm.val,zz->recfm.len);
+ debug(F111," sysparam",zz->sysparam.val,zz->sysparam.len);
+ debug(F101," length","",(int) zz->length);
+ debug(F110," reply",zz->reply.val,0);
+#endif /* DEBUG */
+ return(0);
+}
+
+/* O P E N A -- Open a file, with attributes. */
+/*
+ This function tries to open a new file to put the arriving data in. The
+ filename is the one in the srvcmd buffer. File collision actions are:
+ OVERWRITE (the existing file is overwritten), RENAME (the new file is
+ renamed), BACKUP (the existing file is renamed), DISCARD (the new file is
+ refused), UPDATE (the incoming file replaces the existing file only if the
+ incoming file has a newer creation date).
+
+ Returns 0 on failure, nonzero on success.
+*/
+extern char *rf_err;
+
+int
+opena(f,zz) char *f; struct zattr *zz; {
+ int x, dispos = 0;
+ static struct filinfo fcb; /* Must be static! */
+
+ debug(F110,"opena f",f,0);
+ debug(F101,"opena discard","",discard);
+
+ adebu(f,zz); /* Write attributes to debug log */
+
+ ffc = 0L; /* Init file-character counter */
+
+#ifdef PIPESEND
+ if (pipesend) /* Receiving to a pipe - easy. */
+ return(openo(f,zz,&fcb)); /* Just open the pipe. */
+#endif /* PIPESEND */
+
+ /* Receiving to a file - set up file control structure */
+
+ fcb.bs = fblksiz; /* Blocksize */
+#ifndef NOCSETS
+ fcb.cs = fcharset; /* Character set */
+#else
+ fcb.cs = 0; /* Character set */
+#endif /* NOCSETS */
+ fcb.rl = frecl; /* Record Length */
+ fcb.fmt = frecfm; /* Record Format */
+ fcb.org = forg; /* Organization */
+ fcb.cc = fcctrl; /* Carriage control */
+ fcb.typ = binary; /* Type */
+ debug(F101,"opena xflg","",xflg);
+ debug(F101,"opena remfile","",remfile);
+ debug(F101,"opena remappd","",remappd);
+ if (xflg && remfile && remappd) /* REMOTE output redirected with >> */
+ fcb.dsp = XYFZ_A;
+ else
+ fcb.dsp = (fncact == XYFX_A) ? XYFZ_A : XYFZ_N; /* Disposition */
+ debug(F101,"opena disp","",fcb.dsp);
+ fcb.os_specific = ""; /* OS-specific info */
+#ifdef CK_LABELED
+ fcb.lblopts = lf_opts; /* Labeled file options */
+#else
+ fcb.lblopts = 0;
+#endif /* CK_LABELED */
+
+ if (zz->disp.len > 0) { /* Incoming file has a disposition? */
+ debug(F111,"open disposition",zz->disp.val,zz->disp.len);
+ dispos = (int) (*(zz->disp.val));
+ }
+ if (!dispos && xflg && remfile && remappd) /* REMOTE redirect append ? */
+ dispos = fcb.dsp;
+
+ debug(F101,"opena dispos","",dispos);
+
+ if (!dispos) { /* No special disposition? */
+ if (fncact == XYFX_B && ofn1x && ofn2) { /* File collision = BACKUP? */
+ if (zrename(ofn1,ofn2) < 0) { /* Rename existing file. */
+ debug(F110,"opena rename fails",ofn1,0);
+ rf_err = "Can't create backup file";
+ return(0);
+ } else debug(F110,"opena rename ok",ofn2,0);
+ }
+ } else if (dispos == 'R') { /* Receiving a RESEND */
+ debug(F101,"opena remote len","",zz->length);
+ debug(F101,"opena local len","",rs_len);
+#ifdef COMMENT
+ if (fncact == XYFX_R) /* and file collision = RENAME */
+ if (ofn1x)
+#endif /* COMMENT */
+ if (ofn1[0])
+ f = ofn1; /* use original name. */
+ if (fncact == XYFX_R) /* if file collision is RENAME */
+ ckstrncpy(filnam,ofn1,CKMAXPATH+1); /* restore the real name */
+ xxscreen(SCR_AN,0,0L,f); /* update name on screen */
+ if (zz->length == rs_len) /* Local and remote lengths equal? */
+ return(-17); /* Secret code */
+ }
+ debug(F111,"opena [file]=mode: ",f,fcb.dsp);
+ if (x = openo(f,zz,&fcb)) { /* Try to open the file. */
+#ifdef pdp11
+ tlog(F110," local name:",f,0L); /* OK, open, record local name. */
+ makestr(&prfspec,f); /* New preliminary name */
+#else
+#ifndef ZFNQFP
+ tlog(F110," local name:",f,0L);
+ makestr(&prfspec,f);
+#else
+ { /* Log full local pathname */
+ char *p = NULL, *q = f;
+ if ((p = malloc(CKMAXPATH+1)))
+ if (zfnqfp(filnam, CKMAXPATH, p))
+ q = p;
+ tlog(F110," local name:",q,0L);
+ makestr(&prfspec,q);
+ if (p) free(p);
+ }
+#endif /* ZFNQFP */
+#endif /* pdp11 */
+
+ if (binary) { /* Log file mode in transaction log */
+ tlog(F101," mode: binary","",(long) binary);
+ } else { /* If text mode, check character set */
+ tlog(F100," mode: text","",0L);
+#ifndef NOCSETS
+ if (xfrxla) {
+ if (fcharset > -1 && fcharset <= MAXFCSETS)
+ tlog(F110," file character-set:",fcsinfo[fcharset].name,0L);
+ if (tcharset > -1 && tcharset <= MAXTCSETS)
+ tlog(F110," xfer character-set:",tcsinfo[tcharset].name,0L);
+ } else {
+ tlog(F110," character-set:","transparent",0L);
+ }
+#endif /* NOCSETS */
+ debug(F111,"opena charset",zz->encoding.val,zz->encoding.len);
+ }
+ debug(F101,"opena binary","",binary);
+
+#ifdef COMMENT
+ if (fsize > -1L)
+#endif /* COMMENT */
+ xxscreen(SCR_FS,0,fsize,"");
+
+#ifdef datageneral
+/*
+ Need to turn on multi-tasking console interrupt task here, since multiple
+ files may be received (huh?) ...
+*/
+ if ((local) && (!quiet)) /* Only do this if local & not quiet */
+ consta_mt(); /* Start the async read task */
+#endif /* datageneral */
+
+ } else { /* Did not open file OK. */
+
+ rf_err = ck_errstr(); /* Get system error message */
+ if (*rf_err)
+ xxscreen(SCR_EM,0,0l,rf_err);
+ else
+ xxscreen(SCR_EM,0,0l,"Can't open output file");
+ tlog(F110,"Failure to open",f,0L);
+ tlog(F110,"Error:",rf_err,0L);
+ debug(F110,"opena error",rf_err,0);
+ }
+ return(x); /* Pass on return code from openo */
+}
+
+/* O P E N C -- Open a command (in place of a file) for output */
+
+int
+openc(n,s) int n; char * s; {
+ int x;
+#ifndef NOPUSH
+ x = zxcmd(n,s);
+#else
+ x = 0;
+#endif /* NOPUSH */
+ debug(F111,"openc zxcmd",s,x);
+ o_isopen = (x > 0) ? 1 : 0;
+ return(x);
+}
+
+/* C A N N E D -- Check if current file transfer cancelled */
+
+int
+canned(buf) CHAR *buf; {
+ extern int interrupted;
+ if (*buf == 'X') cxseen = 1;
+ if (*buf == 'Z') czseen = 1;
+ if (czseen || cxseen)
+ interrupted = 1;
+ debug(F101,"canned: cxseen","",cxseen);
+ debug(F101," czseen","",czseen);
+ return((czseen || cxseen) ? 1 : 0);
+}
+
+
+/* O P E N I -- Open an existing file for input */
+
+int
+openi(name) char *name; {
+#ifndef NOSERVER
+ extern int fromgetpath;
+#endif /* NOSERVER */
+ int x, filno;
+ char *name2;
+ extern CHAR *epktmsg;
+
+ epktmsg[0] = NUL; /* Initialize error message */
+ if (memstr || sndarray) { /* Just return if "file" is memory. */
+ i_isopen = 1;
+ return(1);
+ }
+ debug(F110,"openi name",name,0);
+ debug(F101,"openi sndsrc","",sndsrc);
+
+ filno = (sndsrc == 0) ? ZSTDIO : ZIFILE; /* ... */
+ debug(F101,"openi file number","",filno);
+
+#ifndef NOSERVER
+ /* If I'm a server and CWD is disabled and name is not from GET-PATH... */
+
+ if (server && !en_cwd && !fromgetpath) {
+ zstrip(name,&name2);
+ if ( /* ... check if pathname included. */
+#ifdef VMS
+ zchkpath(name)
+#else
+ strcmp(name,name2)
+#endif /* VMS */
+ ) {
+ tlog(F110,name,"access denied",0L);
+ debug(F110,"openi CD disabled",name,0);
+ ckstrncpy((char *)epktmsg,"Access denied",PKTMSGLEN);
+ return(0);
+ } else name = name2;
+ }
+#endif /* NOSERVER */
+
+#ifdef PIPESEND
+ debug(F101,"openi pipesend","",pipesend);
+ if (pipesend) {
+ int x;
+#ifndef NOPUSH
+ x = zxcmd(ZIFILE,name);
+#else
+ x = 0;
+#endif /* NOPUSH */
+ i_isopen = (x > 0) ? 1 : 0;
+ if (!i_isopen)
+ ckstrncpy((char *)epktmsg,"Command or pipe failure",PKTMSGLEN);
+ debug(F111,"openi pipesend zxcmd",name,x);
+ return(i_isopen);
+ }
+#endif /* PIPESEND */
+
+#ifdef CALIBRATE
+ if (calibrate) {
+ i_isopen = 1;
+ return(1);
+ }
+#endif /* CALIBRATE */
+
+ x = zopeni(filno,name); /* Otherwise, try to open it. */
+ debug(F111,"openi zopeni 1",name,x);
+ if (x) {
+ i_isopen = 1;
+ return(1);
+ } else { /* If not found, */
+ char xname[CKMAXPATH]; /* convert the name */
+#ifdef NZLTOR
+ nzrtol(name,xname,fncnv,fnrpath,CKMAXPATH);
+#else
+ zrtol(name,xname); /* to local form and then */
+#endif /* NZLTOR */
+ x = zopeni(filno,xname); /* try opening it again. */
+ debug(F111,"openi zopeni 2",xname,x);
+ if (x) {
+ i_isopen = 1;
+ return(1); /* It worked. */
+ } else {
+ char * s;
+ s = ck_errstr();
+ if (s) if (!s) s = NULL;
+ if (!s) s = "Can't open file";
+ ckstrncpy((char *)epktmsg,s,PKTMSGLEN);
+ tlog(F110,xname,s,0L);
+ debug(F110,"openi failed",xname,0);
+ debug(F110,"openi message",s,0);
+ i_isopen = 0;
+ return(0);
+ }
+ }
+}
+
+/* O P E N O -- Open a new file for output. */
+
+int
+openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; {
+ char *name2;
+#ifdef DTILDE
+ char *dirp;
+#endif /* DTILDE */
+
+ int channel, x;
+
+ if (stdouf) { /* Receiving to stdout? */
+ x = zopeno(ZSTDIO,"",zz,NULL);
+ o_isopen = (x > 0);
+ debug(F101,"openo stdouf zopeno","",x);
+ return(x);
+ }
+ debug(F110,"openo: name",name,0);
+
+ if (cxseen || czseen || discard) { /* If interrupted, get out before */
+ debug(F100," open cancelled","",0); /* destroying existing file. */
+ return(1); /* Pretend to succeed. */
+ }
+ channel = ZOFILE; /* SET DESTINATION DISK or PRINTER */
+
+#ifdef PIPESEND
+ debug(F101,"openo pipesend","",pipesend);
+ if (pipesend) {
+ int x;
+#ifndef NOPUSH
+ x = zxcmd(ZOFILE,(char *)srvcmd);
+#else
+ x = 0;
+#endif /* NOPUSH */
+ o_isopen = x > 0;
+ debug(F101,"openo zxcmd","",x);
+ return(x);
+ }
+#endif /* PIPESEND */
+
+ if (dest == DEST_S) { /* SET DEST SCREEN... */
+ channel = ZCTERM;
+ fcb = NULL;
+ }
+#ifdef DTILDE
+ if (*name == '~') {
+ dirp = tilde_expand(name);
+ if (*dirp) ckstrncpy(name,dirp,CKMAXPATH+1);
+ }
+#endif /* DTILDE */
+ if (server && !en_cwd) { /* If running as server */
+ zstrip(name,&name2); /* and CWD is disabled, */
+ if (strcmp(name,name2)) { /* check if pathname was included. */
+ tlog(F110,name,"authorization failure",0L);
+ debug(F110,"openo CD disabled",name,0);
+ return(0);
+ } else name = name2;
+ }
+ if (zopeno(channel,name,zz,fcb) <= 0) { /* Try to open the file */
+ o_isopen = 0;
+ debug(F110,"openo failed",name,0);
+ /* tlog(F110,"Failure to open",name,0L); */
+ return(0);
+ } else {
+ o_isopen = 1;
+ debug(F110,"openo ok, name",name,0);
+ return(1);
+ }
+}
+
+/* O P E N T -- Open the terminal for output, in place of a file */
+
+int
+opent(zz) struct zattr *zz; {
+ int x;
+ ffc = tfc = 0L;
+ x = zopeno(ZCTERM,"",zz,NULL);
+ debug(F101,"opent zopeno","",x);
+ if (x >= 0) {
+ o_isopen = 1;
+ binary = XYFT_T;
+ } else
+ return(0);
+ return(x);
+}
+
+/* O P E N X -- Open nothing (incoming file to be accepted but ignored) */
+
+int
+ckopenx(zz) struct zattr *zz; {
+ ffc = tfc = 0L; /* Reset counters */
+ o_isopen = 1;
+ debug(F101,"ckopenx fsize","",fsize);
+ xxscreen(SCR_FS,0,fsize,""); /* Let screen display know the size */
+ return(1);
+}
+
+/* C L S I F -- Close the current input file. */
+
+int
+clsif() {
+ extern int xferstat, success;
+ int x = 0;
+
+ fcps(); /* Calculate CPS quickly */
+
+#ifdef datageneral
+ if ((local) && (!quiet)) /* Only do this if local & not quiet */
+ if (nfils < 1) /* More files to send ... leave it on! */
+ connoi_mt();
+#endif /* datageneral */
+
+ debug(F101,"clsif i_isopen","",i_isopen);
+ if (i_isopen) { /* If input file is open... */
+ if (memstr) { /* If input was memory string, */
+ memstr = 0; /* indicate no more. */
+ } else {
+ x = zclose(ZIFILE); /* else close input file. */
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"clsif zclose","",x);
+ debug(F101,"clsif success","",success);
+ debug(F101,"clsif xferstat","",xferstat);
+ debug(F101,"clsif fsize","",fsize);
+ debug(F101,"clsif ffc","",ffc);
+ debug(F101,"clsif cxseen","",cxseen);
+ debug(F101,"clsif czseen","",czseen);
+ debug(F101,"clsif discard","",czseen);
+ }
+#endif /* DEBUG */
+ if ((cxseen || czseen) && !epktsent) { /* If interrupted */
+ xxscreen(SCR_ST,ST_INT,0l,""); /* say so */
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,psfspec,fsize,binary,1,"Interrupted");
+#endif /* TLOG */
+ } else if (discard && !epktsent) { /* If I'm refusing */
+ xxscreen(SCR_ST,ST_REFU,0l,refused); /* say why */
+#ifdef TLOG
+ if (tralog && !tlogfmt) {
+ char buf[128];
+ ckmakmsg(buf,128,"Refused: ",refused,NULL,NULL);
+ doxlog(what,psfspec,fsize,binary,1,buf);
+ }
+#endif /* TLOG */
+ } else if (!epktrcvd && !epktsent && !cxseen && !czseen) {
+ long zz;
+ zz = ffc;
+#ifdef CK_RESEND
+ if (sendmode == SM_RESEND || sendmode == SM_PSEND)
+ zz += sendstart;
+#endif /* CK_RESEND */
+ debug(F101,"clsif fstats","",zz);
+ fstats(); /* Update statistics */
+ if ( /* Was the whole file sent? */
+#ifdef VMS
+ 0 /* Not a reliable check in VMS */
+#else
+#ifdef STRATUS
+ 0 /* Probably not for VOS either */
+#else
+ zz < fsize
+#ifdef CK_CTRLZ
+ && ((eofmethod != XYEOF_Z && !binary) || binary)
+#endif /* CK_CTRLZ */
+#endif /* STRATUS */
+#endif /* VMS */
+ ) {
+ xxscreen(SCR_ST,ST_INT,0l,"");
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,psfspec,fsize,binary,1,"Incomplete");
+#endif /* TLOG */
+ } else {
+#ifdef COMMENT
+ /* Not yet -- we don't have confirmation from the receiver */
+ xxscreen(SCR_ST,ST_OK,0l,"");
+#endif /* COMMENT */
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,psfspec,fsize,binary,0,"");
+#endif /* TLOG */
+ }
+ }
+ }
+ i_isopen = 0;
+ hcflg = 0; /* Reset flags */
+ sendstart = 0L; /* Don't do this again! */
+#ifdef COMMENT
+/*
+ This prevents a subsequent call to clsof() from deleting the file
+ when given the discard flag.
+*/
+ *filnam = '\0'; /* and current file name */
+#endif /* COMMENT */
+ return(x);
+}
+
+
+/* C L S O F -- Close an output file. */
+
+/* Call with disp != 0 if file is to be discarded. */
+/* Returns -1 upon failure to close, 0 or greater on success. */
+
+int
+clsof(disp) int disp; {
+ int x = 0;
+ extern int success;
+
+ fcps(); /* Calculate CPS quickly */
+
+ debug(F101,"clsof disp","",disp);
+ debug(F101,"clsof cxseen","",cxseen);
+ debug(F101,"clsof success","",success);
+
+ debug(F101,"clsof o_isopen","",o_isopen);
+ if (fncsav != -1) { /* Saved file collision action... */
+ fncact = fncsav; /* Restore it. */
+ fncsav = -1; /* Unsave it. */
+ }
+#ifdef datageneral
+ if ((local) && (!quiet)) /* Only do this if local & not quiet */
+ connoi_mt();
+#endif /* datageneral */
+ if (o_isopen && !calibrate) {
+ if ((x = zclose(ZOFILE)) < 0) { /* Try to close the file */
+ tlog(F100,"Failure to close",filnam,0L);
+ xxscreen(SCR_ST,ST_ERR,0l,"Can't close file");
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,prfspec,fsize,binary,1,"Can't close file");
+#endif /* TLOG */
+ } else if (disp) { /* Interrupted or refused */
+ if (keep == 0 || /* If not keeping incomplete files */
+ (keep == SET_AUTO && binary == XYFT_T)
+ ) {
+ if (*filnam && (what & W_RECV)) /* AND we're receiving */
+ zdelet(filnam); /* ONLY THEN, delete it */
+ if (what & W_KERMIT) {
+ debug(F100,"clsof incomplete discarded","",0);
+ tlog(F100," incomplete: discarded","",0L);
+ if (!epktrcvd && !epktsent) {
+ xxscreen(SCR_ST,ST_DISC,0l,"");
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,prfspec,fsize,binary,1,"Discarded");
+#endif /* TLOG */
+ }
+ }
+ } else { /* Keep incomplete copy */
+ debug(F100,"clsof fstats 1","",0);
+ fstats();
+ if (!discard) { /* Unless discarding for other reason... */
+ if (what & W_KERMIT) {
+ debug(F100,"closf incomplete kept","",0);
+ tlog(F100," incomplete: kept","",0L);
+ }
+ }
+ if (what & W_KERMIT) {
+ if (!epktrcvd && !epktsent) {
+ xxscreen(SCR_ST,ST_INC,0l,"");
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,prfspec,fsize,binary,1,"Incomplete");
+#endif /* TLOG */
+ }
+ }
+ }
+ }
+ }
+ if (o_isopen && x > -1 && !disp) {
+ debug(F110,"clsof OK",rfspec,0);
+ makestr(&rfspec,prfspec);
+ makestr(&rrfspec,prrfspec);
+ fstats();
+ if (!epktrcvd && !epktsent && !cxseen && !czseen) {
+ xxscreen(SCR_ST,ST_OK,0L,"");
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,rfspec,fsize,binary,0,"");
+#endif /* TLOG */
+ }
+ }
+ rs_len = 0;
+ o_isopen = 0; /* The file is not open any more. */
+ cxseen = 0; /* Reset per-file interruption flag */
+ return(x); /* Send back zclose() return code. */
+}
+
+#ifdef SUNOS4S5
+tolower(c) char c; { return((c)-'A'+'a'); }
+toupper(c) char c; { return((c)-'a'+'A'); }
+#endif /* SUNOS4S5 */
+#endif /* NOXFER */
diff --git a/ckermit-8.0.211/ckcfns.c b/ckermit-8.0.211/ckcfns.c
new file mode 100644
index 0000000..53ea236
--- /dev/null
+++ b/ckermit-8.0.211/ckcfns.c
@@ -0,0 +1,7108 @@
+char *fnsv = "C-Kermit functions, 8.0.223, 1 May 2003";
+
+char *nm[] = { "Disabled", "Local only", "Remote only", "Enabled" };
+
+/* C K C F N S -- System-independent Kermit protocol support functions. */
+
+/* ...Part 1 (others moved to ckcfn2,3 to make this module smaller) */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+/*
+ System-dependent primitives defined in:
+
+ ck?tio.c -- terminal (communications) i/o
+ cx?fio.c -- file i/o, directory structure
+*/
+#include "ckcsym.h" /* Needed for Stratus VOS */
+#include "ckcasc.h" /* ASCII symbols */
+#include "ckcdeb.h" /* Debug formats, typedefs, etc. */
+#include "ckcker.h" /* Symbol definitions for Kermit */
+#include "ckcxla.h" /* Character set symbols */
+#include "ckcnet.h" /* VMS definition of TCPSOCKET */
+#ifdef OS2
+#ifdef OS2ONLY
+#include <os2.h>
+#endif /* OS2ONLY */
+#include "ckocon.h"
+#endif /* OS2 */
+
+int docrc = 0; /* Accumulate CRC for \v(crc16) */
+long crc16 = 0L; /* File CRC = \v(crc16) */
+int gnferror = 0; /* gnfile() failure reason */
+
+extern CHAR feol;
+extern int byteorder, xflg, what, fmask, cxseen, czseen, nscanfile, sysindex;
+extern int xcmdsrc, dispos, matchfifo;
+extern long ffc;
+extern int inserver;
+
+extern int nolinks;
+#ifdef VMSORUNIX
+extern int zgfs_dir;
+#ifdef CKSYMLINK
+extern int zgfs_link;
+#endif /* CKSYMLINK */
+#endif /* VMSORUNIX */
+
+#ifndef NOXFER
+
+#ifndef NOICP
+#ifndef NOSPL
+extern char * clcmds;
+extern int haveurl;
+#ifdef CK_APC
+extern int apcactive, adl_ask;
+#endif /* CK_APC */
+#endif /* NOSPL */
+#endif /* NOICP */
+
+extern int remfile;
+
+/* (move these prototypes to the appropriate .h files...) */
+
+#ifdef COMMENT
+/* Not used */
+#ifdef VMS
+_PROTOTYP( int getvnum, (char *) );
+#endif /* VMS */
+#endif /* COMMENT */
+
+_PROTOTYP( static int bgetpkt, (int) );
+#ifndef NOCSETS
+_PROTOTYP( int lookup, (struct keytab[], char *, int, int *) );
+#endif /* NOCSETS */
+#ifndef NOSPL
+_PROTOTYP( int zzstring, (char *, char **, int *) );
+#endif /* NOSPL */
+#ifdef OS2ORUNIX
+_PROTOTYP( long zfsize, (char *) );
+#endif /* OS2ORUNIX */
+
+#ifdef OS2
+#include <io.h>
+#ifdef OS2ONLY
+#include <os2.h>
+#endif /* OS2ONLY */
+#endif /* OS2 */
+
+#ifdef VMS
+#include <errno.h>
+#endif /* VMS */
+
+/* Externals from ckcmai.c */
+
+extern int srvcdmsg, srvidl, idletmo;
+extern char * cdmsgfile[];
+extern int spsiz, spmax, rpsiz, timint, srvtim, rtimo, npad, ebq, ebqflg,
+ rpt, rptq, rptflg, capas, keep, fncact, pkttim, autopar, spsizr, xitsta;
+extern int pktnum, bctr, bctu, bctl, clfils, sbufnum, protocol,
+ size, osize, spktl, nfils, ckwarn, timef, spsizf, sndtyp, rcvtyp, success;
+extern int parity, turn, network, whatru, fsecs, justone, slostart,
+ ckdelay, displa, mypadn, moving, recursive, nettype;
+extern long filcnt, flci, flco, tlci, tlco, tfc, fsize, sendstart, rs_len;
+extern long filrej, oldcps, cps, peakcps, ccu, ccp, calibrate, filestatus;
+extern int fblksiz, frecl, frecfm, forg, fcctrl, fdispla, skipbup;
+extern int spackets, rpackets, timeouts, retrans, crunched, wmax, wcur;
+extern int hcflg, binary, fncnv, b_save, f_save, server;
+extern int nakstate, discard, rejection, local, xfermode, interrupted;
+extern int rq, rqf, sq, wslots, wslotn, wslotr, winlo, urpsiz, rln;
+extern int fnspath, fnrpath, eofmethod, diractive, whatru2, wearealike;
+extern int atcapr, atcapb, atcapu;
+extern int lpcapr, lpcapb, lpcapu;
+extern int swcapr, swcapb, swcapu;
+extern int lscapr, lscapb, lscapu;
+extern int rscapr, rscapb, rscapu;
+extern int rptena, rptmin;
+extern int sseqtbl[];
+extern int numerrs, nzxopts;
+extern long rptn;
+extern int maxtry;
+extern int stdouf;
+extern int sendmode;
+extern int carrier, ttprty;
+extern int g_fnrpath;
+#ifdef TCPSOCKET
+extern int ttnproto;
+#endif /* TCPSOCKET */
+
+#ifndef NOSPL
+extern int sndxin, sndxhi, sndxlo;
+#endif /* NOSPL */
+
+extern int g_binary, g_fncnv;
+
+#ifdef GFTIMER
+extern CKFLOAT fpfsecs;
+#endif /* GFTIMER */
+
+#ifdef OS2
+extern struct zattr iattr;
+#endif /* OS2 */
+
+#ifdef PIPESEND
+extern int usepipes;
+#endif /* PIPESEND */
+extern int pipesend;
+
+#ifdef STREAMING
+extern int streamrq, streaming, streamed, streamok;
+#endif /* STREAMING */
+extern int reliable, clearrq, cleared, urclear;
+
+extern int
+ atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
+ attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
+
+extern int bigsbsiz, bigrbsiz;
+extern char *versio;
+extern char *filefile;
+extern char whoareu[], * cksysid;
+
+#ifndef NOSERVER
+extern int ngetpath;
+extern char * getpath[];
+extern int fromgetpath;
+#endif /* NOSERVER */
+
+#ifdef CK_LOGIN
+extern int isguest;
+#endif /* CK_LOGIN */
+
+extern int srvcmdlen;
+extern CHAR *srvcmd, * epktmsg;
+extern CHAR padch, mypadc, eol, seol, ctlq, myctlq, sstate, myrptq;
+extern CHAR *data, padbuf[], stchr, mystch;
+extern CHAR *srvptr;
+extern CHAR *rdatap;
+extern char *cmarg, *cmarg2, **cmlist, filnam[], ofilnam[];
+extern char *rfspec, *prfspec, *rrfspec, *prrfspec, *sfspec, *psfspec, *rfspec;
+extern char fspec[];
+extern int fspeclen;
+
+#ifndef NOMSEND
+extern struct filelist * filehead, * filenext;
+extern int addlist;
+#endif /* NOMSEND */
+
+_PROTOTYP( int lslook, (unsigned int b) ); /* Locking Shift Lookahead */
+_PROTOTYP( int szeof, (CHAR *s) );
+_PROTOTYP( VOID fnlist, (void) );
+#endif /* NOXFER */
+
+/* Character set Translation */
+
+#ifndef NOCSETS
+extern int tcharset, fcharset, dcset7, dcset8;
+extern int fcs_save, tcs_save;
+extern int ntcsets, xlatype, cseqtab[];
+extern struct csinfo tcsinfo[], fcsinfo[];
+extern int r_cset, s_cset, afcset[];
+#ifdef UNICODE
+extern int ucsorder, fileorder;
+#endif /* UNICODE */
+
+_PROTOTYP( CHAR ident, (CHAR) ); /* Identity translation function */
+
+/* Arrays of and pointers to character translation functions */
+
+#ifdef CK_ANSIC
+extern CHAR (*rx)(CHAR); /* Pointer to input character translation function */
+extern CHAR (*sx)(CHAR); /* Pointer to output character translation function */
+extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Send */
+extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Recv */
+#ifdef UNICODE
+extern int (*xut)(USHORT); /* Translation function UCS to TCS */
+extern int (*xuf)(USHORT); /* Translation function UCS to FCS */
+extern USHORT (*xtu)(CHAR); /* Translation function TCS to UCS */
+extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */
+#endif /* UNICODE */
+
+#else /* The same declarations again for non-ANSI comilers... */
+
+extern CHAR (*rx)();
+extern CHAR (*sx)();
+extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
+extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])();
+#ifdef UNICODE
+extern int (*xut)();
+extern int (*xuf)();
+extern USHORT (*xtu)();
+extern USHORT (*xfu)();
+#endif /* UNICODE */
+#endif /* CK_ANSIC */
+#endif /* NOCSETS */
+
+/* (PWP) external def. of things used in buffered file input and output */
+
+#ifdef DYNAMIC
+extern char *zinbuffer, *zoutbuffer;
+#else
+extern char zinbuffer[], zoutbuffer[];
+#endif /* DYNAMIC */
+extern char *zinptr, *zoutptr;
+extern int zincnt, zoutcnt, zobufsize, xfrxla;
+
+extern long crcta[], crctb[]; /* CRC-16 generation tables */
+extern int rseqtbl[]; /* Rec'd-packet sequence # table */
+
+#ifndef NOXFER
+
+/* Criteria used by gnfile()... */
+
+char sndafter[19] = { NUL, NUL };
+char sndbefore[19] = { NUL, NUL };
+char sndnafter[19] = { NUL, NUL };
+char sndnbefore[19] = { NUL, NUL };
+char *sndexcept[NSNDEXCEPT] = { NULL, NULL };
+char *rcvexcept[NSNDEXCEPT] = { NULL, NULL };
+long sndsmaller = -1L;
+long sndlarger = -1L;
+
+/* Variables defined in this module but shared by other modules. */
+
+int xfrbel = 1;
+char * ofperms = ""; /* Output file permissions */
+int autopath = 0; /* SET RECEIVE PATHNAMES AUTO flag */
+
+#ifdef CALIBRATE
+#define CAL_O 3
+#define CAL_M 253
+
+int cal_j = 0;
+
+CHAR
+cal_a[] = {
+ 16, 45, 98, 3, 52, 41, 14, 7, 76,165,122, 11,104, 77,166, 15,
+160, 93, 18, 19,112, 85, 54, 23,232,213, 90, 27, 12, 81,126, 31,
+ 4,205, 34, 35,144, 73,110, 39, 28,133,218, 43,156, 65,102, 47,
+ 84, 61, 50, 51,208,117, 86, 55, 8,245, 74, 59, 44,125,222, 63,
+ 80, 1,162, 67,116,105,206, 71,120, 9,250, 75, 88, 97, 6, 79,
+100,221, 82, 83, 36, 89, 94, 87, 40, 21,106, 91,236,145,150, 95,
+228, 33,130, 99,148,137,198,103,108,169, 42,107,184,129, 78,111,
+ 0, 49,114,115, 32,121,254,119,172, 57,138,123,152,177, 22,127,
+240,193, 2,131,176, 5, 38,135,204,229, 10,139,200,161,174,143,
+128, 17,146,147, 68,153, 30,151, 72,217,170,155, 24,209, 62,159,
+ 64,225,194,163,244,201, 70,167,216,197,234,171,188,109,230,175,
+212,113,178,179,132,185,190,183,136,249,202,187, 92,241,118,191,
+ 48,237, 66,195, 96,233,142,199,248, 37, 58,203, 60, 13,134,207,
+ 20, 29,210,211,164,149,182,215,220, 25, 26,219,124,157,246,223,
+180,141,226,227,192,101,238,231, 56, 69,154,235,252,173, 46,239,
+224,253,242,243,196, 53,214,247,168,181,186,251,140,189,158,255
+};
+#endif /* CALIBRATE */
+
+char * rf_err = "Error receiving file"; /* rcvfil() error message */
+
+#ifdef CK_SPEED
+short ctlp[256] = { /* Control-Prefix table */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* DEL */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C1 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G1 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 /* 255 */
+};
+#endif /* CK_SPEED */
+
+int sndsrc; /* Flag for where to get names of files to send: */
+ /* -1: znext() function */
+ /* 0: stdin */
+ /* >0: list in cmlist or other list */
+ /* -9: calibrate */
+
+int memstr; /* Flag for input from memory string */
+int funcstr; /* Flag for input from function */
+int bestlen = 0;
+int maxsend = 0;
+
+int gnf_binary = 0; /* Prevailing xfer mode for gnfile */
+
+#ifdef pdp11
+#define MYINITLEN 32
+#else
+#define MYINITLEN 100
+#endif /* pdp11 */
+CHAR myinit[MYINITLEN]; /* Copy of my Send-Init data */
+
+/* Variables local to this module */
+
+#ifdef TLOG
+#ifndef XYZ_INTERNAL
+static
+#endif /* XYZ_INTERNAL */
+char *fncnam[] = {
+ "rename", "replace", "backup", "append", "discard", "ask",
+ "update", "dates-differ", ""
+};
+#endif /* TLOG */
+
+static char *memptr; /* Pointer for memory strings */
+
+#ifdef VMS
+extern int batch;
+#else
+extern int backgrd;
+#endif /* VMS */
+
+#ifdef CK_CTRLZ
+static int lastchar = 0;
+#endif /* CK_CTRLZ */
+
+#ifdef CK_ANSIC
+static int (*funcptr)(void); /* Pointer for function strings */
+#else
+static int (*funcptr)();
+#endif /* CK_ANSIC */
+
+#ifdef pdp11
+#define CMDSTRL 50
+static char cmdstr[50]; /* System command string. */
+#else
+#ifdef BIGBUFOK
+#define CMDSTRL 1024
+#else
+#define CMDSTRL 256
+#endif /* BIGBUFOK */
+static char cmdstr[CMDSTRL+1];
+#endif /* pdp11 */
+
+static int drain; /* For draining stacked-up ACKs. */
+
+static int first; /* Flag for first char from input */
+static CHAR t; /* Current character */
+#ifdef COMMENT
+static CHAR next; /* Next character */
+#endif /* COMMENT */
+
+static int ebqsent = 0; /* 8th-bit prefix bid that I sent */
+static int lsstate = 0; /* Locking shift state */
+static int lsquote = 0; /* Locking shift quote */
+
+extern int quiet;
+
+/* E N C S T R -- Encode a string from memory. */
+
+/*
+ Call this instead of getpkt() if source is a string, rather than a file.
+ Note: Character set translation is never done in this case.
+*/
+
+#ifdef COMMENT
+#define ENCBUFL 200
+#ifndef pdp11
+CHAR encbuf[ENCBUFL];
+#else
+/* This is gross, but the pdp11 root segment is out of space */
+/* Will allocate it in ckuusr.c. */
+extern CHAR encbuf[];
+#endif /* pdp11 */
+#endif /* COMMENT */
+
+/*
+ Encode packet data from a string in memory rather than from a file.
+ Returns the length of the encoded string on success, -1 if the string
+ could not be completely encoded into the currently negotiated data
+ field length.
+*/
+int
+#ifdef CK_ANSIC
+encstr(CHAR *s)
+#else /* CK_ANSIC */
+encstr(s) CHAR* s;
+#endif /* CK_ANSIC */
+{
+/*
+ Recoded 30 Jul 94 to use the regular data buffer and the negotiated
+ maximum packet size. Previously we were limited to the length of encbuf[].
+ Also, to return a failure code if the entire encoded string would not fit.
+ Modified 14 Jul 98 to return length of encoded string.
+*/
+ int m, rc, slen; char *p;
+ if (!data) { /* Watch out for null pointers. */
+ debug(F100,"SERIOUS ERROR: encstr data == NULL","",0);
+ return(-1);
+ }
+ if (!s) s = (CHAR *)""; /* Ditto. */
+ slen = strlen((char *)s); /* Length of source string. */
+ debug(F111,"encstr",s,slen);
+ rc = 0; /* Return code. */
+ m = memstr; p = memptr; /* Save these. */
+ memptr = (char *)s; /* Point to the string. */
+ debug(F101,"encstr memptr 1","",memptr);
+ memstr = 1; /* Flag memory string as source. */
+ first = 1; /* Initialize character lookahead. */
+ *data = NUL; /* In case s is empty */
+ debug(F101,"encstr spsiz","",spsiz);
+ rc = getpkt(spsiz,0); /* Fill a packet from the string. */
+ debug(F101,"encstr getpkt rc","",rc);
+ if (rc > -1 && memptr < (char *)(s + slen)) { /* Means we didn't encode */
+ rc = -1; /* the whole string. */
+ debug(F101,"encstr string too big","",size);
+ }
+ debug(F101,"encstr getpkt rc","",rc);
+ memstr = m; /* Restore memory string flag */
+ memptr = p; /* and pointer */
+ first = 1; /* Put this back as we found it. */
+ return(rc);
+}
+
+/* Output functions passed to 'decode': */
+
+int /* Put character in server command buffer */
+#ifdef CK_ANSIC
+putsrv(char c)
+#else
+putsrv(c) register char c;
+#endif /* CK_ANSIC */
+/* putsrv */ {
+ *srvptr++ = c;
+ *srvptr = '\0'; /* Make sure buffer is null-terminated */
+ return(0);
+}
+
+int /* Output character to console. */
+#ifdef CK_ANSIC
+puttrm(char c)
+#else
+puttrm(c) register char c;
+#endif /* CK_ANSIC */
+/* puttrm */ {
+ extern int rcdactive;
+#ifndef NOSPL
+ extern char * qbufp; /* If REMOTE QUERY active, */
+ extern int query, qbufn; /* also store response in */
+ if (query && qbufn++ < 1024) { /* query buffer. */
+ *qbufp++ = c;
+ *qbufp = NUL;
+ }
+ if (!query || !xcmdsrc)
+#endif /* NOSPL */
+/*
+ This routine is used (among other things) for printing the server's answer
+ to a REMOTE command. But REMOTE CD is special because it isn't really
+ asking for an answer from the server. Thus some people want to suppress
+ the confirmation message (e.g. when the server sends back the actual path
+ of the directory CD'd to), and expect to be able to do this with SET QUIET
+ ON. But they would not want SET QUIET ON to suppress the other server
+ replies, which are pointless without their answers. Thus the "rcdactive"
+ flag (REMOTE CD command is active). Thu Oct 10 16:38:21 2002
+*/
+ if (!(quiet && rcdactive)) /* gross, yuk */
+ conoc(c);
+ return(0);
+}
+#endif /* NOXFER */
+
+int /* Output char to file. */
+#ifdef CK_ANSIC
+putmfil(char c) /* Just like putfil but to ZMFILE */
+#else /* rather than ZOFILE... */
+putmfil(c) register char c;
+#endif /* CK_ANSIC */
+/* putmfil */ {
+ debug(F000,"putfil","",c);
+ if (zchout(ZMFILE, (char) (c & fmask)) < 0) {
+ czseen = 1;
+ debug(F101,"putfil zchout write error, setting czseen","",1);
+ return(-1);
+ }
+ return(0);
+}
+
+int /* Output char to nowhere. */
+#ifdef CK_ANSIC
+putnowhere(char c)
+#else
+putnowhere(c) register char c;
+#endif /* CK_ANSIC */
+/* putnowhere */ {
+ return(0);
+}
+
+
+int /* Output char to file. */
+#ifdef CK_ANSIC
+putfil(char c)
+#else
+putfil(c) register char c;
+#endif /* CK_ANSIC */
+/* putfil */ {
+ debug(F000,"putfil","",c);
+ if (zchout(ZOFILE, (char) (c & fmask)) < 0) {
+ czseen = 1; /* If write error... */
+ debug(F101,"putfil zchout write error, setting czseen","",1);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ The following function is a wrapper for putfil(). The only reason for its
+ existence is to be passed as a function pointer to decode(), which treats
+ putfil() itself specially -- bypassing it and using an internal macro
+ instead to speed things up. Use zputfil() instead of putfil() in cases where
+ we do not want this to happen, e.g. when we need to send output to the file
+ with a mixture of zchout() and zsout()/zsoutl() calls (as is the case with
+ incoming short-form REMOTE command replies redirected to a file), which would
+ otherwise result in data written to the file out of order.
+*/
+int
+#ifdef CK_ANSIC
+zputfil(char c)
+#else
+zputfil(c) register char c;
+#endif /* CK_ANSIC */
+/* zputfil */ {
+ return(putfil(c));
+}
+
+#ifndef NOXFER
+
+/* D E C O D E -- Kermit packet decoding procedure */
+
+/*
+ Call with string to be decoded and an output function.
+ Returns 0 on success, -1 on failure (e.g. disk full).
+
+ This is the "inner loop" when receiving files, and must be coded as
+ efficiently as possible. Note some potential problems: if a packet
+ is badly formed, having a prefixed sequence ending prematurely, this
+ function, as coded, could read past the end of the packet. This has
+ never happened, thus the additional (time-consuming) tests have not
+ been added.
+*/
+
+static CHAR *xdbuf; /* Global version of decode()'s buffer pointer */
+ /* for use by translation functions. */
+
+/* Function for pushing a character onto decode()'s input stream. */
+
+VOID
+#ifdef CK_ANSIC
+zdstuff(CHAR c)
+#else
+zdstuff(c) CHAR c;
+#endif /* CK_ANSIC */
+/* zdstuff */ {
+ xdbuf--; /* Back up the pointer. */
+ *xdbuf = c; /* Stuff the character. */
+}
+
+#ifdef CKTUNING
+/*
+ Trimmed-down packet decoder for binary-mode no-parity transfers.
+ decode() is the full version.
+*/
+int
+#ifdef CK_ANSIC
+bdecode(CHAR *buf, int (*fn)(char))
+#else
+bdecode(buf,fn) register CHAR *buf; register int (*fn)();
+#endif /* CK_ANSIC */
+/* bdecode */ {
+ register unsigned int a, a7; /* Various copies of current char */
+ int ccpflg; /* For Ctrl-unprefixing stats */
+ int t; /* Int version of character */
+ int len;
+ long z; /* For CRC calculation */
+ CHAR c; /* Current character */
+
+ if (!binary || parity || fn != putfil) /* JUST IN CASE */
+ return(decode(buf,fn,1));
+ debug(F100,"BDECODE","",0);
+
+ xdbuf = buf; /* Global copy of source pointer. */
+
+ len = rln; /* Number of bytes in data field */
+ while (len > 0) {
+ a = *xdbuf++ & 0xff; /* Get next character */
+ len--;
+ rpt = 0; /* Initialize repeat count. */
+ if (a == rptq && rptflg) { /* Got a repeat prefix? */
+ rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */
+ rptn += rpt;
+ a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
+ len -= 2;
+ }
+ ccpflg = 0; /* Control prefix flag. */
+ if (a == ctlq) { /* If control prefix, */
+ a = *xdbuf++ & 0xFF; /* get its operand */
+ len--;
+ a7 = a & 0x7F; /* and its low 7 bits. */
+ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */
+ a = ctl(a); /* if in control range. */
+ a7 = a & 0x7F;
+ ccpflg = 1; /* Note that we did this */
+ ccp++; /* Count for stats */
+ }
+ } else a7 = a & 0x7f; /* Not control quote */
+ if (a7 < 32 || a7 == 127) /* A bare control character? */
+ if (!ccpflg) ccu++; /* Count it */
+ if (!rpt) rpt = 1;
+ for (; rpt > 0; rpt--) { /* Output the char RPT times */
+#ifdef CALIBRATE
+ if (calibrate) {
+ ffc++;
+ continue;
+ }
+#endif /* CALIBRATE */
+#ifdef OS2
+ if (xflg && !remfile) { /* Write to virtual screen */
+ char _a;
+ _a = a & fmask;
+ t = conoc(_a);
+ if (t < 1)
+ t = -1;
+ } else
+#endif /* OS2 */
+ t = zmchout(a & fmask); /* zmchout is a macro */
+ if (t < 0) {
+ debug(F101,"bdecode write error - errno","",errno);
+ return(-1);
+ }
+ ffc++; /* Count the character */
+ if (docrc && !remfile) { /* Update file CRC */
+ c = a; /* Force conversion to unsigned char */
+ z = crc16 ^ (long)c;
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+ }
+ }
+#ifdef CK_CTRLZ
+ lastchar = a;
+#endif /* CK_CTRLZ */
+ }
+ return(0);
+}
+#endif /* CKTUNING */
+#endif /* NOXFER */
+
+/* P N B Y T E -- Output next byte to file or other destination */
+
+static long offc = 0L;
+
+static int
+#ifdef CK_ANSIC
+pnbyte(CHAR c, int (*fn)(char))
+#else
+pnbyte(c,fn) CHAR c; int (*fn)();
+#endif /* CK_ANSIC */
+/* pnbyte */ {
+ int rc;
+ long z;
+
+#ifdef OS2
+#ifndef NOXFER
+ if (xflg && !remfile) { /* Write to virtual screen */
+ char _a;
+ _a = c & fmask;
+ rc = conoc(_a);
+ if (rc < 1)
+ return(-1);
+ } else
+#endif /* NOXFER */
+#endif /* OS2 */
+ {
+ if (fn == putfil) { /* Execute output function */
+ rc = zmchout(c); /* to-file macro (fast) */
+ } else if (!fn) {
+ rc = putchar(c); /* to-screen macro (fast) */
+ } else {
+ rc = (*fn)(c); /* function call (not as fast) */
+ }
+ if (rc < 0)
+ return(rc);
+ }
+/*
+ Both xgnbyte() and xpnbyte() increment ffc (the file byte counter).
+ During file transfer, only one of these functions is called. However,
+ the TRANSLATE command is likely to call them both. offc, therefore,
+ contains the output byte count, necessary for handling the UCS-2 BOM.
+ NOTE: It might be safe to just test for W_SEND, FTP or not.
+*/
+ if ((what & (W_FTP|W_SEND)) != (W_FTP|W_SEND)) {
+ offc++; /* Count the byte */
+ ffc++; /* Count the byte */
+ }
+#ifndef NOXFER
+ if (docrc && !xflg && !remfile) { /* Update file CRC */
+ z = crc16 ^ (long)c;
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+ }
+#endif /* NOXFER */
+ return(1);
+}
+
+/*
+ X P N B Y T E -- Translate and put next byte to file.
+
+ Only for Unicode. Call with next untranslated byte from incoming
+ byte stream, which can be in any Transfer Character Set: UCS-2, UTF-8,
+ Latin-1, Latin-Hebrew, etc. Translates to the file character set and
+ writes bytes to the output file. Call with character to translate
+ as an int, plus the transfer character set (to translate from) and the
+ file character set (to translate to), or -1,0,0 to reset the UCS-2 byte
+ number (which should be done at the beginning of a file). If the transfer
+ (source) character-set is UCS-2, bytes MUST arrive in Big-Endian order.
+ Returns:
+ -1: On error
+ 0: Nothing to write (mid-sequence)
+ >0: Number of bytes written.
+*/
+#ifdef KANJI
+static int jstate = 0, jx = 0; /* For outputting JIS-7 */
+static char jbuf[16] = { NUL, NUL };
+#endif /* KANJI */
+
+int
+#ifdef CK_ANSIC
+xpnbyte(int a, int tcs, int fcs, int (*fn)(char))
+#else
+xpnbyte(a,tcs,fcs,fn) int a, tcs, fcs; int (*fn)();
+#endif /* CK_ANSIC */
+/* xpnbyte */ {
+#ifdef UNICODE
+ extern int ucsbom; /* Byte order */
+#endif /* UNICODE */
+ /* CHAR c; */ /* Unsigned char worker */
+ static union ck_short uc, eu, sj; /* UCS-2, EUC, and Shift-JIS workers */
+ USHORT ch; /* ditto... */
+ USHORT * us = NULL; /* ditto... */
+ int c7, rc, haveuc = 0; /* Return code and UCS-2 flag */
+ int utferror = 0; /* UTF-8 error */
+ static int bn = 0; /* UCS-2 byte number */
+ int swapping = 0; /* Swapping UCS bytes to output? */
+ /* swapping must be 0 or 1 */
+ if (a == -1 && (tcs | fcs) == 0) { /* Reset in case previous run */
+ bn = 0; /* left bn at 1... */
+ offc = 0L;
+ debug(F101,"xpnbyte RESET","",bn);
+ return(0);
+ }
+ debug(F001,"xpnbyte a","",a);
+
+#ifdef UNICODE
+
+/*
+ byteorder = hardware byte order of this machine.
+ ucsorder = override by SET FILE UCS BYTE-ORDER command.
+ fileorder = byte order of UCS-2 input file detected from BOM.
+ swapping applies only when output charset is UCS-2.
+*/
+ if (ucsorder != 1 && ucsorder != 0) /* Also just in case... */
+ ucsorder = byteorder;
+ if ((byteorder && !ucsorder) || (!byteorder && fileorder))
+ swapping = 1; /* Swapping bytes to output */
+
+#ifdef COMMENT
+ debug(F101,"xpnbyte ucsorder","",ucsorder);
+ debug(F101,"xpnbyte swapping","",swapping);
+#endif /* COMMENT */
+
+ if (tcs == TC_UTF8) { /* 'a' is from a UTF-8 stream */
+ ch = a;
+ if (fcs == TC_UTF8) /* Output is UTF-8 too */
+ return(pnbyte(ch,fn)); /* so just copy. */
+ rc = utf8_to_ucs2(ch,&us); /* Otherwise convert to UCS-2 */
+ if (rc == 0) { /* Done with this sequence */
+ uc.x_short = *us; /* We have a Unicode */
+ haveuc = 1;
+ } else if (rc < 0) { /* Error */
+ debug(F101,"xpnbyte UTF-8 conversion error","",rc);
+ haveuc = 1; /* Replace by U+FFFD */
+ uc.x_short = *us;
+ utferror = 1;
+ } else /* Sequence incomplete */
+ return(0);
+ } else if (tcs == TC_UCS2) { /* 'a' is UCS-2 */
+ /* Here we have incoming UCS-2 in guaranteed Big Endian order */
+ /* so we must exchange bytes if local machine is Little Endian. */
+ switch (bn) { /* Which byte? */
+ case 0: /* High... */
+ uc.x_char[byteorder] = (unsigned)a & 0xff;
+ bn++;
+ return(0); /* Wait for next */
+ case 1: /* Low... */
+ uc.x_char[1-byteorder] = (unsigned)a & 0xff;
+ bn = 0; /* Done with sequence */
+ haveuc = 1; /* Have a Unicode */
+ }
+ } else
+#endif /* UNICODE */
+
+#ifdef KANJI /* Whether UNICODE is defined or not */
+ if (tcs == TC_JEUC) { /* Incoming Japanese EUC */
+ int bad = 0;
+ static int kanji = 0; /* Flags set in case 0 for case 1 */
+ static int kana = 0;
+ switch (bn) { /* Byte number */
+ case 0: /* Byte 0 */
+ eu.x_short = 0;
+ if ((a & 0x80) == 0) {
+ sj.x_short = (unsigned)a & 0xff; /* Single byte */
+ kanji = kana = 0;
+ } else { /* Double byte */
+ c7 = a & 0x7f;
+ if (c7 > 0x20 && c7 < 0x7f) { /* Kanji */
+ eu.x_char[byteorder] = (CHAR) a; /* Store first byte */
+ bn++; /* Set up for second byte */
+ kanji = 1;
+ kana = 0;
+ return(0);
+ } else if (a == 0x8e) { /* SS2 -- Katakana prefix */
+ eu.x_char[byteorder] = (CHAR) a; /* Save it */
+ bn++;
+ kana = 1;
+ kanji = 0;
+ return(0);
+ } else {
+ bad++;
+ }
+ }
+ break;
+ case 1: /* Byte 1 */
+ bn = 0;
+ if (kanji) {
+ eu.x_char[1-byteorder] = (CHAR) a;
+ sj.x_short = eu_to_sj(eu.x_short);
+ break;
+ } else if (kana) {
+ sj.x_short = (CHAR) (a | 0x80);
+ break;
+ } else { /* (shouldn't happen) */
+ bad++;
+ }
+ }
+ /* Come here with one Shift-JIS character */
+
+#ifdef UNICODE
+ if (bad) {
+ uc.x_short = 0xfffd;
+ } else {
+ uc.x_short = sj_to_un(sj.x_short); /* Convert to Unicode */
+ }
+ haveuc = 1;
+#endif /* UNICODE */
+ } else
+#endif /* KANJI */
+
+#ifdef UNICODE
+ uc.x_short = (unsigned)a & 0xff; /* Latin-1 or whatever... */
+
+ /* Come here with uc = the character to be translated. */
+ /* If (haveuc) it's UCS-2 in native order, otherwise it's a byte. */
+
+ debug(F101,"xpnbyte haveuc","",haveuc);
+
+ if (haveuc) { /* If we have a Unicode... */
+ debug(F001,"xpnbyte uc.x_short","[A]",uc.x_short);
+ debug(F101,"xpnbyte feol","",feol);
+ if (what & W_XFER) { /* If transferring a file */
+ if (feol && uc.x_short == CR) { /* handle eol conversion. */
+ return(0);
+ } else if (feol && uc.x_short == LF) {
+ uc.x_short = feol;
+ }
+ }
+ debug(F001,"xpnbyte uc.x_short","[B]",uc.x_short);
+ if (fcs == FC_UCS2) { /* And FCS is UCS-2 */
+ /* Write out the bytes in the appropriate byte order */
+ int count = 0;
+#ifndef IKSDONLY
+#ifdef OS2
+ extern int k95stdout,wherex[],wherey[];
+ extern unsigned char colorcmd;
+ union {
+ USHORT ucs2;
+ UCHAR bytes[2];
+ } output;
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ if (offc == 0L && ucsbom) { /* Beginning of file? */
+
+#ifndef IKSDONLY
+#ifdef OS2
+ if (fn == NULL && !k95stdout && !inserver) {
+ offc++;
+#ifdef COMMENT
+ /* Don't print the BOM to the display */
+ output.bytes[0] = (!ucsorder ? 0xff : 0xfe);
+ output.bytes[1] = (!ucsorder ? 0xfe : 0xff);
+
+ VscrnWrtUCS2StrAtt(VCMD,
+ &output.ucs2,
+ 1,
+ wherey[VCMD],
+ wherex[VCMD],
+ &colorcmd
+ );
+#endif /* COMMENT */
+ } else
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ {
+ if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0)
+ return(rc); /* BOM */
+ if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0)
+ return(rc);
+ }
+ count += 2;
+ }
+ if (utferror) {
+#ifndef IKSDONLY
+#ifdef OS2
+ if (fn == NULL && !k95stdout && !inserver) {
+ offc++;
+ output.bytes[0] = (!ucsorder ? 0xfd : 0xff);
+ output.bytes[1] = (!ucsorder ? 0xff : 0xfd);
+
+ VscrnWrtUCS2StrAtt(VCMD,
+ &output.ucs2,
+ 1,
+ wherey[VCMD],
+ wherex[VCMD],
+ &colorcmd
+ );
+ } else
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ {
+ if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0)
+ return(rc);
+ }
+ count += 2;
+ }
+#ifndef IKSDONLY
+#ifdef OS2
+ if (fn == NULL && !k95stdout && !inserver) {
+ offc++;
+ output.bytes[0] = uc.x_char[1-swapping];
+ output.bytes[1] = uc.x_char[swapping];
+
+ VscrnWrtUCS2StrAtt(VCMD,
+ &output.ucs2,
+ 1,
+ wherey[VCMD],
+ wherex[VCMD],
+ &colorcmd
+ );
+ } else
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ {
+ if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
+ return(rc);
+ }
+ count += 2;
+ return(count);
+ } else if (fcs == FC_UTF8) { /* Convert to UTF-8 */
+ CHAR * buf = NULL;
+ int i, count;
+ if (utferror) {
+ if ((rc = pnbyte((ucsorder ? 0xbd : 0xff),fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte((ucsorder ? 0xff : 0xbd),fn)) < 0)
+ return(rc);
+ }
+ if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1)
+ return(-1);
+ debug(F011,"xpnbyte buf",buf,count);
+ for (i = 0; i < count; i++)
+ if ((rc = pnbyte(buf[i],fn)) < 0)
+ return(rc);
+ if (utferror)
+ count += 2;
+ return(count);
+ } else { /* Translate UCS-2 to byte */
+ if (uc.x_short == 0x2028 || uc.x_short == 0x2029) {
+ if (utferror)
+ pnbyte(UNK,fn);
+ if (feol)
+ return(pnbyte((CHAR)feol,fn));
+ if ((rc = pnbyte((CHAR)CR,fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte((CHAR)LF,fn)) < 0)
+ return(rc);
+ else
+ return(utferror ? 3 : 2);
+ } else if (xuf) { /* UCS-to-FCS function */
+ int x = 0;
+ if (utferror)
+ pnbyte(UNK,fn);
+ if ((rc = (*xuf)(uc.x_short)) < 0) /* These can fail... */
+ ch = UNK;
+ else
+ ch = (unsigned)((unsigned)rc & 0xffff);
+ x = pnbyte(ch,fn);
+ if (x < 0)
+ return(x);
+ else if (utferror)
+ x++;
+ return(x);
+#ifdef KANJI
+
+/* Also see the non-Unicode Kanji section further down in this function. */
+
+ } else if (fcsinfo[fcs].alphabet == AL_JAPAN) {
+
+ /* Translate UCS-2 to Japanese set */
+ debug(F001,"xpnbyte uc","",uc.x_short);
+ sj.x_short = un_to_sj(uc.x_short); /* First to Shift-JIS */
+ debug(F001,"xpnbyte sj","",sj.x_short);
+
+ switch (fcs) { /* File character set */
+ case FC_SHJIS: /* Shift-JIS -- just output it */
+ if (sj.x_char[byteorder]) /* But not high byte if zero */
+ if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0)
+ return(rc);
+ return(2);
+ case FC_JEUC: /* EUC-JP */
+ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
+ debug(F001,"xpnbyte eu","",eu.x_short);
+ if (eu.x_short == 0xffff) { /* Bad */
+ if ((rc = pnbyte(UNK,fn)) < 0)
+ return(rc);
+ return(1);
+ } else { /* Good */
+ int count = 0; /* Write high byte if not zero */
+ if (eu.x_char[byteorder]) {
+ if ((rc=pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ }
+ /* Always write low byte */
+ if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ return(count);
+ }
+ break;
+
+ case FC_JIS7: /* JIS-7 */
+ case FC_JDEC: /* DEC Kanji */
+ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
+ if (eu.x_short == 0xffff) { /* Bad */
+ debug(F001,"xpnbyte bad eu","",eu.x_short);
+ if ((rc = pnbyte(UNK,fn)) < 0)
+ return(rc);
+ return(1);
+ } else { /* Good */
+ int i;
+ /* Use another name - 'a' hides parameter */
+ /* It's OK as is but causes compiler warnings */
+ char a = eu.x_char[1-byteorder]; /* Low byte */
+ debug(F001,"xpnbyte eu","",eu.x_short);
+ if (eu.x_char[byteorder] == 0) { /* Roman */
+ switch (jstate) {
+ case 1: /* Current state is Katakana */
+ jbuf[0] = 0x0f; /* SI */
+ jbuf[1] = a;
+ jx = 2;
+ break;
+ case 2: /* Current state is Kanji */
+ jbuf[0] = 0x1b; /* ESC */
+ jbuf[1] = 0x28; /* ( */
+ jbuf[2] = 0x4a; /* J */
+ jbuf[3] = a;
+ jx = 4;
+ break;
+ default: /* Current state is Roman */
+ jbuf[0] = a;
+ jx = 1;
+ break;
+ }
+ jstate = 0; /* New state is Roman */
+ } else if (eu.x_char[byteorder] == 0x8e) { /* Kana */
+ jx = 0;
+ switch (jstate) {
+ case 2: /* from Kanji */
+ jbuf[jx++] = 0x1b; /* ESC */
+ jbuf[jx++] = 0x28; /* ( */
+ jbuf[jx++] = 0x4a; /* J */
+ case 0: /* from Roman */
+ jbuf[jx++] = 0x0e; /* SO */
+ default: /* State is already Kana*/
+ jbuf[jx++] = (a & 0x7f); /* and the char */
+ break;
+ }
+ jstate = 1; /* New state is Katakana */
+ } else { /* Kanji */
+ jx = 0;
+ switch (jstate) {
+ case 1: /* Current state is Katakana */
+ jbuf[jx++] = 0x0f; /* SI */
+ case 0: /* Current state is Roman */
+ jbuf[jx++] = 0x1b; /* ESC */
+ jbuf[jx++] = 0x24; /* $ */
+ jbuf[jx++] = 0x42; /* B */
+ default: /* Current state is already Kanji */
+ jbuf[jx++] = eu.x_char[byteorder] & 0x7f;
+ jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f;
+ break;
+ }
+ jstate = 2; /* Set state to Kanji */
+ }
+ for (i = 0; i < jx; i++) /* Output the result */
+ if ((rc = pnbyte(jbuf[i],fn)) < 0)
+ return(rc);
+ return(jx); /* Return its length */
+ }
+ }
+#endif /* KANJI */
+ } else { /* No translation function */
+ int count = 0;
+ if (utferror) {
+ if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0)
+ return(rc);
+ count += 2;
+ }
+ if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
+ return(rc);
+ count += 2;
+ return(count);
+ }
+ }
+ } else { /* Byte to Unicode */
+ if (xtu) { /* TCS-to-UCS function */
+ if (((tcsinfo[tcs].size > 128) && (uc.x_short & 0x80)) ||
+ tcsinfo[tcs].size <= 128)
+ uc.x_short = (*xtu)(uc.x_short);
+ }
+ if (fcs == FC_UCS2) { /* And FCS is UCS-2 */
+ /* Write out the bytes in the appropriate byte order */
+ if (offc == 0 && ucsbom) { /* Beginning of file? */
+ if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0) /* BOM */
+ return(rc);
+ if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0)
+ return(rc);
+ }
+ if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
+ return(rc);
+ if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
+ return(rc);
+ return(2);
+ } else if (fcs == FC_UTF8) { /* Convert to UTF-8 */
+ CHAR * buf = NULL;
+ int i, count;
+ if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1)
+ return(-1);
+ for (i = 0; i < count; i++)
+ if ((rc = pnbyte(buf[i],fn)) < 0)
+ return(rc);
+ return(count);
+ } else {
+ debug(F100,"xpnbyte impossible combination","",0);
+ return(-1);
+ }
+ }
+#else
+#ifdef KANJI
+/*
+ This almost, but not quite, duplicates the Kanji section above.
+ There is no doubt a way to combine the sections more elegantly,
+ but probably only at the expense of additional execution overhead.
+ As matters stand, be careful to reflect any changes in this section
+ to the other Kanji section above.
+*/
+ if (tcs == TC_JEUC) { /* Incoming Japanese EUC */
+ int count = 0;
+ switch (fcs) { /* File character set */
+ case FC_SHJIS: /* Shift-JIS -- just output it */
+ if (sj.x_char[byteorder]) /* But not high byte if zero */
+ if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ return(count);
+ case FC_JEUC: /* EUC-JP */
+ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
+ debug(F001,"xpnbyte FC_JEUC eu","",eu.x_short);
+ if (eu.x_short == 0xffff) { /* Bad */
+ if ((rc = pnbyte(UNK,fn)) < 0)
+ return(rc);
+ return(1);
+ } else { /* Good */
+ int count = 0; /* Write high byte if not zero */
+ if (eu.x_char[byteorder]) {
+ if ((rc = pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ }
+ /* Always write low byte */
+ if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0)
+ return(rc);
+ count++;
+ return(count);
+ }
+ break;
+
+ case FC_JIS7: /* JIS-7 */
+ case FC_JDEC: /* DEC Kanji */
+ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
+ if (eu.x_short == 0xffff) { /* Bad */
+ debug(F001,"xpnbyte FC_JIS7 bad eu","",eu.x_short);
+ if ((rc = pnbyte(UNK,fn)) < 0)
+ return(rc);
+ return(1);
+ } else { /* Good */
+ int i;
+ char a = eu.x_char[1-byteorder]; /* Low byte */
+ debug(F001,"xpnbyte FC_JIS7 eu","",eu.x_short);
+ if (eu.x_char[byteorder] == 0) { /* Roman */
+ switch (jstate) {
+ case 1: /* Current state is Katakana */
+ jbuf[0] = 0x0f; /* SI */
+ jbuf[1] = a;
+ jx = 2;
+ break;
+ case 2: /* Current state is Kanji */
+ jbuf[0] = 0x1b; /* ESC */
+ jbuf[1] = 0x28; /* ( */
+ jbuf[2] = 0x4a; /* J */
+ jbuf[3] = a;
+ jx = 4;
+ break;
+ default: /* Current state is Roman */
+ jbuf[0] = a;
+ jx = 1;
+ break;
+ }
+ jstate = 0; /* New state is Roman */
+ } else if (eu.x_char[byteorder] == 0x8e) { /* Kana */
+ jx = 0;
+ switch (jstate) {
+ case 2: /* from Kanji */
+ jbuf[jx++] = 0x1b; /* ESC */
+ jbuf[jx++] = 0x28; /* ( */
+ jbuf[jx++] = 0x4a; /* J */
+ case 0: /* from Roman */
+ jbuf[jx++] = 0x0e; /* SO */
+ default: /* State is already Kana*/
+ jbuf[jx++] = (a & 0x7f); /* and the char */
+ break;
+ }
+ jstate = 1; /* New state is Katakana */
+ } else { /* Kanji */
+ jx = 0;
+ switch (jstate) {
+ case 1: /* Current state is Katakana */
+ jbuf[jx++] = 0x0f; /* SI */
+ case 0: /* Current state is Roman */
+ jbuf[jx++] = 0x1b; /* ESC */
+ jbuf[jx++] = 0x24; /* $ */
+ jbuf[jx++] = 0x42; /* B */
+ default: /* Current state is already Kanji */
+ jbuf[jx++] = eu.x_char[byteorder] & 0x7f;
+ jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f;
+ break;
+ }
+ jstate = 2; /* Set state to Kanji */
+ }
+ for (i = 0; i < jx; i++) /* Output the result */
+ if ((rc = pnbyte(jbuf[i],fn)) < 0)
+ return(rc);
+ return(jx); /* Return its length */
+ }
+ default:
+ if (sj.x_short < 0x80)
+ return(sj.x_short);
+ else
+ return('?');
+ }
+ }
+#endif /* KANJI */
+#endif /* UNICODE */
+ debug(F100,"xpnbyte BAD FALLTHRU","",0);
+ return(-1);
+}
+
+#ifndef NOXFER
+
+/* D E C O D E -- Kermit Data-packet decoder */
+
+int
+#ifdef CK_ANSIC
+decode(CHAR *buf, int (*fn)(char), int xlate)
+#else
+decode(buf,fn,xlate) register CHAR *buf; register int (*fn)(); int xlate;
+#endif /* CK_ANSIC */
+/* decode */ {
+ register unsigned int a, a7, a8, b8; /* Various copies of current char */
+ int t; /* Int version of character */
+ int ssflg; /* Character was single-shifted */
+ int ccpflg; /* For Ctrl-unprefixing stats */
+ int len;
+ long z;
+ CHAR c;
+/*
+ Catch the case in which we are asked to decode into a file that is not open,
+ for example, if the user interrupted the transfer, but the other Kermit
+ keeps sending.
+*/
+ if ((cxseen || czseen || discard) && (fn == putfil))
+ return(0);
+
+#ifdef COMMENT
+#ifdef CKTUNING
+ if (binary && !parity)
+ return(bdecode(buf,fn));
+#endif /* CKTUNING */
+#endif /* COMMENT */
+ debug(F100,"DECODE","",0);
+
+ xdbuf = buf; /* Make global copy of pointer. */
+ rpt = 0; /* Initialize repeat count. */
+
+ len = rln; /* Number of bytes in data field */
+ while (len > 0) { /* Loop for each byte */
+ a = *xdbuf++ & 0xff; /* Get next character */
+ len--;
+ if (a == rptq && rptflg) { /* Got a repeat prefix? */
+ rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */
+ rptn += rpt;
+ a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
+ len -= 2;
+ }
+ b8 = lsstate ? 0200 : 0; /* 8th-bit value from SHIFT-STATE */
+ if (ebqflg && a == ebq) { /* Have 8th-bit prefix? */
+ b8 ^= 0200; /* Yes, invert the 8th bit's value, */
+ ssflg = 1; /* remember we did this, */
+ a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
+ len--;
+ } else ssflg = 0;
+ ccpflg = 0;
+ if (a == ctlq) { /* If control prefix, */
+ a = *xdbuf++ & 0xFF; /* get its operand */
+ len--;
+ a7 = a & 0x7F; /* and its low 7 bits. */
+ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */
+ a = ctl(a); /* if in control range. */
+ a7 = a & 0x7F;
+ ccpflg = 1; /* Note that we did this */
+ ccp++; /* Count for stats */
+ }
+ } else a7 = a & 0x7f; /* Not control quote */
+ if (a7 < 32 || a7 == 127) { /* Control character? */
+ if (!ccpflg) ccu++; /* A bare one, count it */
+ if (lscapu) { /* If doing locking shifts... */
+ if (lsstate) /* If SHIFTED */
+ a8 = (a & ~b8) & 0xFF; /* Invert meaning of 8th bit */
+ else /* otherwise */
+ a8 = a | b8; /* OR in 8th bit */
+ /* If we're not in a quoted sequence */
+ if (!lsquote && (!lsstate || !ssflg)) {
+ if (a8 == DLE) { /* Check for DLE quote */
+ lsquote = 1; /* prefixed by single shift! */
+ continue;
+ } else if (a8 == SO) { /* Check for Shift-Out */
+ lsstate = 1; /* SHIFT-STATE = SHIFTED */
+ continue;
+ } else if (a8 == SI) { /* or Shift-In */
+ lsstate = 0; /* SHIFT-STATE = UNSHIFTED */
+ continue;
+ }
+ } else lsquote = 0;
+ }
+ }
+ a |= b8; /* OR in the 8th bit */
+ if (rpt == 0) rpt = 1; /* If no repeats, then one */
+#ifndef NOCSETS
+ if (!binary) { /* If in text mode, */
+ if (tcharset != TC_UCS2) {
+ if (feol && a == CR) /* Convert CRLF to newline char */
+ continue;
+ if (feol && a == LF)
+ a = feol;
+ }
+ if (xlatype == XLA_BYTE) /* Byte-for-byte - do it now */
+ if (xlate && rx) a = (*rx)((CHAR) a);
+ }
+#endif /* NOCSETS */
+ /* (PWP) Decoding speedup via buffered output and a macro... */
+ if (fn == putfil) {
+ for (; rpt > 0; rpt--) { /* Output the char RPT times */
+#ifdef CALIBRATE
+ if (calibrate) {
+ ffc++;
+ continue;
+ }
+#endif /* CALIBRATE */
+
+/* Note: The Unicode and Kanji sections can probably be combined now; */
+/* the Unicode method (xpnbyte()) covers Kanji too. */
+
+#ifdef UNICODE
+ if (!binary && xlatype == XLA_UNICODE)
+ t = xpnbyte((unsigned)((unsigned)a & 0xff),
+ tcharset,
+ fcharset,
+ fn
+ );
+ else
+#endif /* UNICODE */
+#ifdef KANJI
+ if (!binary && tcharset == TC_JEUC &&
+ fcharset != FC_JEUC) { /* Translating from J-EUC */
+ if (ffc == 0L) xkanjf();
+ if (xkanji(a,fn) < 0) /* to something else? */
+ return(-1);
+ else t = 1;
+ } else
+#endif /* KANJI */
+ {
+#ifdef OS2
+ if (xflg && !remfile) { /* Write to virtual screen */
+ char _a;
+ _a = a & fmask;
+ t = conoc(_a);
+ if (t < 1)
+ t = -1;
+ } else
+#endif /* OS2 */
+ t = zmchout(a & fmask); /* zmchout is a macro */
+ }
+ if (t < 0) {
+ debug(F101,"decode write errno","",errno);
+ return(-1);
+ }
+#ifdef UNICODE
+ if (xlatype != XLA_UNICODE || binary) {
+ ffc++; /* Count the character */
+ if (docrc && !xflg && !remfile) { /* Update file CRC */
+ c = a; /* Force conversion to unsigned char */
+ z = crc16 ^ (long)c;
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+ }
+ }
+#endif /* UNICODE */
+ }
+ } else { /* Output to something else. */
+ a &= fmask; /* Apply file mask */
+ for (; rpt > 0; rpt--) { /* Output the char RPT times */
+#ifdef CALIBRATE
+ if (calibrate) {
+ ffc++;
+ continue;
+ }
+#endif /* CALIBRATE */
+ if ((*fn)((char) a) < 0) return(-1); /* Send to output func. */
+ }
+ }
+#ifdef CK_CTRLZ
+ lastchar = a;
+#endif /* CK_CTRLZ */
+ }
+ return(0);
+}
+
+/* G E T P K T -- Fill a packet data field */
+
+/*
+ Gets characters from the current source -- file or memory string.
+ Encodes the data into the packet, filling the packet optimally.
+ Set first = 1 when calling for the first time on a given input stream
+ (string or file).
+
+ Call with:
+ bufmax -- current send-packet size
+ xlate -- flag: 0 to skip character-set translation, 1 to translate
+
+ Uses global variables:
+ t -- current character.
+ first -- flag: 1 to start up, 0 for input in progress, -1 for EOF.
+ next -- next character (not used any more).
+ data -- pointer to the packet data buffer.
+ size -- number of characters in the data buffer.
+ memstr - flag that input is coming from a memory string instead of a file.
+ memptr - pointer to string in memory.
+ (*sx)() character set translation function
+
+ Returns:
+ The size as value of the function, and also sets global "size",
+ and fills (and null-terminates) the global data array.
+ Returns:
+ 0 on EOF.
+ -1 on fatal (internal) error.
+ -3 on timeout (e.g. when reading data from a pipe).
+
+ Rewritten by Paul W. Placeway (PWP) of Ohio State University, March 1989.
+ Incorporates old getchx() and encode() inline to reduce function calls,
+ uses buffered input for much-improved efficiency, and clears up some
+ confusion with line termination (CRLF vs LF vs CR).
+
+ Rewritten again by Frank da Cruz to incorporate locking shift mechanism,
+ May 1991. And again in 1998 for efficiency, etc, with a separate
+ bgetpkt() split out for binary-mode no-parity transfers.
+*/
+
+/*
+ Note: Separate Kanji support dates from circa 1991 and now (1999) can most
+ likely be combined with the the Unicode support: the xgnbyte()/xpnbyte()
+ mechanism works for both Unicode and Kanji.
+*/
+#ifdef KANJI
+int
+kgetf(
+#ifdef CK_ANSIC
+ VOID
+#endif /* CK_ANSIC */
+ ) {
+ if (funcstr)
+ return((*funcptr)());
+ else
+ return(zminchar());
+}
+
+int
+kgetm(
+#ifdef CK_ANSIC
+ VOID
+#endif /* CK_ANSIC */
+ ) {
+ int x;
+ if ((x = *memptr++)) return(x);
+ else return(-1);
+}
+#endif /* KANJI */
+
+/*
+ Lookahead function to decide whether locking shift is worth it. Looks at
+ the next four input characters to see if all of their 8th bits match the
+ argument. Call with 0 or 0200. Returns 1 on match, 0 if they don't match.
+ If we don't happen to have at least 4 more characters waiting in the input
+ buffer, returns 1. Note that zinptr points two characters ahead of the
+ current character because of repeat-count lookahead.
+*/
+int
+lslook(b) unsigned int b; { /* Locking Shift Lookahead */
+ int i;
+ if (zincnt < 3) /* If not enough chars in buffer, */
+ return(1); /* force shift-state switch. */
+ b &= 0200; /* Force argument to proper form. */
+ for (i = -1; i < 3; i++) /* Look at next 5 characters to */
+ if (((*(zinptr+i)) & 0200) != b) /* see if all their 8th bits match. */
+ return(0); /* They don't. */
+ return(1); /* They do. */
+}
+
+/* Routine to compute maximum data length for packet to be filled */
+
+int
+maxdata() { /* Get maximum data length */
+ int n, len;
+ debug(F101,"maxdata spsiz 1","",spsiz);
+ if (spsiz < 0) /* How could this happen? */
+ spsiz = DSPSIZ;
+ debug(F101,"maxdata spsiz 2","",spsiz);
+ n = spsiz - 5; /* Space for Data and Checksum */
+ if (n > 92 && n < 96) n = 92; /* "Short" Long packets don't pay */
+ if (n > 92 && lpcapu == 0) /* If long packets needed, */
+ n = 92; /* make sure they've been negotiated */
+ len = n - bctl; /* Space for data */
+ if (n > 92) len -= 3; /* Long packet needs header chksum */
+ debug(F101,"maxdata len 1","",len);
+ if (len < 0) len = 10;
+ debug(F101,"maxdata len 2","",len);
+ return(len);
+}
+
+static CHAR leftover[9] = { '\0','\0','\0','\0','\0','\0','\0','\0','\0' };
+static int nleft = 0;
+
+#ifdef CKTUNING
+/*
+ When CKTUNING is defined we use this special trimmed-down version of getpkt
+ to speed up binary-mode no-parity transfers. When CKTUNING is not defined,
+ or for text-mode or parity transfers, we use the regular getpkt() function.
+ Call just like getpkt() but test first for transfer mode and parity. NOTE:
+ This routine is only to be called when sending a real file -- not for
+ filenames, server responses, etc, because it only reads from the input file.
+ See getpkt() for more detailed commentary.
+*/
+static int
+bgetpkt(bufmax) int bufmax; {
+ register CHAR rt = t, rnext;
+ register CHAR *dp, *odp, *p1, *p2;
+ register int x = 0, a7;
+
+ CHAR xxrc, xxcq; /* Pieces of prefixed sequence */
+
+ long z; /* A long worker (for CRC) */
+
+ if (!binary || parity || memstr) /* JUST IN CASE caller didn't test */
+ return(getpkt(bufmax,!binary));
+
+ if (!data) {
+ debug(F100,"SERIOUS ERROR: bgetpkt data == NULL","",0);
+ return(-1);
+ }
+ dp = data; /* Point to packet data buffer */
+ size = 0; /* And initialize its size */
+ bufmax = maxdata(); /* Get maximum data length */
+
+#ifdef DEBUG
+ if (deblog)
+ debug(F101,"bgetpkt bufmax","",bufmax);
+#endif /* DEBUG */
+
+ if (first == 1) { /* If first character of this file.. */
+ ffc = 0L; /* reset file character counter */
+#ifdef COMMENT
+/* Moved to below */
+ first = 0; /* Next character won't be first */
+#endif /* COMMENT */
+ *leftover = '\0'; /* Discard any interrupted leftovers */
+ nleft = 0;
+
+ /* Get first character of file into rt, watching out for null file */
+
+#ifdef CALIBRATE
+ if (calibrate) {
+#ifdef NORANDOM
+ rt = 17;
+#else
+ rt = cal_a[rand() & 0xff];
+#endif /* NORANDOM */
+ first = 0;
+ } else
+#endif /* CALIBRATE */
+
+ if ((x = zminchar()) < 0) { /* EOF or error */
+ if (x == -3) { /* Timeout. */
+ size = (dp - data);
+ debug(F101,"bgetpkt timeout size","",size);
+ return((size == 0) ? x : size);
+ }
+ first = -1;
+ size = 0;
+ if (x == -2) { /* Error */
+ debug(F100,"bgetpkt: input error","",0);
+ cxseen = 1; /* Interrupt the file transfer */
+ } else {
+ debug(F100,"bgetpkt empty file","",0);
+ }
+ return(0);
+ }
+ first = 0; /* Next char will not be the first */
+ ffc++; /* Count a file character */
+ rt = (CHAR) x; /* Convert int to char */
+ if (docrc && (what & W_SEND)) { /* Accumulate file crc */
+ z = crc16 ^ (long)rt;
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+ }
+ rt &= fmask; /* Apply SET FILE BYTESIZE mask */
+
+ } else if (first == -1 && nleft == 0) { /* EOF from last time */
+
+ return(size = 0);
+ }
+/*
+ Here we handle characters that were encoded for the last packet but
+ did not fit, and so were saved in the "leftover" array.
+*/
+ if (nleft) {
+ for (p1 = leftover; nleft > 0; nleft--) /* Copy leftovers */
+ *dp++ = *p1++;
+ *leftover = '\0'; /* Delete leftovers */
+ nleft = 0;
+ }
+ if (first == -1) /* Handle EOF */
+ return(size = (dp - data));
+
+/* Now fill up the rest of the packet. */
+
+ rpt = 0; /* Initialize character repeat count */
+
+ while (first > -1) { /* Until EOF... */
+#ifdef CALIBRATE
+ if (calibrate) { /* We generate our own "file" */
+ if (ffc >= calibrate) { /* EOF */
+ first = -1;
+ ffc--;
+ } else { /* Generate next character */
+ if (cal_j > CAL_M * ffc)
+ cal_j = cal_a[ffc & 0xff];
+ x = (unsigned)cal_a[(cal_j & 0xff)];
+ if (x == rt) x ^= 2;
+ }
+ ffc++;
+ cal_j += (unsigned int)(ffc + CAL_O);
+ } else
+#endif /* CALIBRATE */
+ if ((x = zminchar()) < 0) { /* Check for EOF */
+ if (x == -3) { /* Timeout. */
+ t = rt;
+ size = (dp-data);
+ debug(F101,"bgetpkt timeout size","",size);
+ return((size == 0) ? x : size);
+ }
+ first = -1; /* Flag eof for next time. */
+ if (x == -2) cxseen = 1; /* If error, cancel this file. */
+ } else {
+ ffc++; /* Count the character */
+ if (docrc && (what & W_SEND)) { /* Accumulate file crc */
+ z = crc16 ^ (long)((CHAR)x & 0xff);
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+ }
+ }
+ rnext = (CHAR) (x & fmask); /* Apply file mask */
+/*
+ At this point, the character we just read is in rnext,
+ and the character we are about to encode into the packet is in rt.
+*/
+ odp = dp; /* Remember where we started. */
+ xxrc = xxcq = NUL; /* Clear these. */
+/*
+ Now encode the character according to the options that are in effect:
+ ctlp[]: whether this control character needs prefixing.
+ rptflg: repeat counts enabled.
+ Other options don't apply in this routine.
+*/
+ if (rptflg && (rt == rnext) && (first == 0)) { /* Got a run... */
+ if (++rpt < 94) { /* Below max, just count */
+ continue; /* go back and get another */
+ } else if (rpt == 94) { /* Reached max, must dump */
+ xxrc = (CHAR) tochar(rpt); /* Put the repeat count here */
+ rptn += rpt; /* Accumulate it for statistics */
+ rpt = 0; /* And reset it */
+ }
+ } else if (rpt > 0) { /* End of run */
+ xxrc = (CHAR)tochar(++rpt); /* The count */
+ rptn += rpt; /* For stats */
+ rpt = 0; /* Reset repeat count */
+ }
+ a7 = rt & 0177; /* Get low 7 bits of character */
+ if (
+#ifdef CK_SPEED
+ ctlp[(unsigned)(rt & 0xff)] /* Lop off any "sign" extension */
+#else
+ (a7 < SP) || (a7 == DEL)
+#endif /* CK_SPEED */
+ ) { /* Do control prefixing if necessary */
+ xxcq = myctlq; /* The prefix */
+ ccp++; /* Count it */
+ rt = (CHAR) ctl(rt); /* Uncontrollify the character */
+ }
+#ifdef CK_SPEED
+ else if ((a7 < SP) || (a7 == DEL)) /* Count an unprefixed one */
+ ccu++;
+#endif /* CK_SPEED */
+
+ if (a7 == myctlq) /* Always prefix the control prefix */
+ xxcq = myctlq;
+
+ if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */
+ xxcq = myctlq; /* prefix it if doing repeat counts */
+
+/* Now construct the prefixed sequence */
+
+ if (xxrc) { /* Repeat count */
+#ifdef COMMENT
+ if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */
+ *dp++ = rt; /* So just do this */
+ } else { /* More than two or prefixed */
+ *dp++ = (CHAR) rptq; *dp++ = xxrc; /* Emit repeat sequence */
+ }
+#else /* CHECK THIS */
+ if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */
+ if (dp == data) {
+ *dp++ = rt; /* So just do this */
+ } else if (*(dp-1) == rt) {
+ *dp++ = (CHAR) rptq;
+ *dp++ = xxrc; /* Emit repeat sequence */
+ } else {
+ *dp++ = rt; /* So just do this */
+ }
+ } else { /* More than two or prefixed */
+ *dp++ = (CHAR) rptq;
+ *dp++ = xxrc; /* Emit repeat sequence */
+ }
+#endif /* COMMENT */
+ }
+ if (xxcq) { *dp++ = myctlq; } /* Control prefix */
+ *dp++ = rt; /* Finally, the character itself */
+ rt = rnext; /* Next character is now current. */
+
+/* Done encoding the character. Now take care of packet buffer overflow. */
+
+ size = dp - data; /* How many bytes we put in buffer. */
+ if (size >= bufmax) { /* If too big, save some for next. */
+ *dp = '\0'; /* Mark the end. */
+ if (size > bufmax) { /* if packet is overfull */
+ /* Copy the part that doesn't fit into the leftover buffer, */
+ /* taking care not to split a prefixed sequence. */
+ int i;
+ nleft = dp - odp;
+ p1 = leftover;
+ p2 = odp;
+ for (i = 0; i < nleft; i++)
+ *p1++ = *p2++;
+ size = odp - data; /* Return truncated packet. */
+ *odp = '\0'; /* Mark the new end */
+ }
+ t = rt; /* Save for next time */
+ return(size);
+ }
+ } /* Otherwise, keep filling. */
+ size = dp - data; /* End of file */
+ *dp = '\0'; /* Mark the end of the data. */
+ return(size); /* Return partially filled last packet. */
+}
+#endif /* CKTUNING */
+
+VOID
+dofilcrc(c) int c; { /* Accumulate file crc */
+ long z;
+ z = crc16 ^ (long)c;
+ crc16 = (crc16 >> 8) ^
+ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
+}
+
+/* For SENDing from an array... */
+
+int
+agnbyte() { /* Get next byte from array */
+#ifndef NOSPL
+ char c;
+ static int save = 0; /* For CRLF */
+ static char ** ap = NULL; /* Array pointer */
+ static char * p = NULL; /* Character pointer */
+ static int i = 0, n = 0; /* Array index and limit */
+ extern int a_dim[]; /* Array dimension */
+
+ if (!ap) { /* First time thru */
+ ap = sndarray; /* Set up array pointers */
+ if (!ap || (i = sndxlo) > a_dim[sndxin]) {
+ sndarray = NULL;
+ ap = NULL;
+ return(-1);
+ }
+ p = ap[i]; /* Point to first element in range */
+ n = sndxhi; /* Index of last element in range */
+ if (sndxhi > a_dim[sndxin]) /* Adjust if necessary */
+ n = a_dim[sndxin];
+ }
+ if (save) { /* If anything saved */
+ c = save; /* unsave it */
+ save = 0; /* and return it */
+ return(c & 0xff);
+ }
+ if (i > n) { /* No more elements */
+ sndarray = NULL;
+ ap = NULL;
+ return(-1);
+ }
+ if (!p) /* Source pointer is NULL */
+ c = NUL; /* this means an empty line */
+ else /* Source pointer not NULL */
+ c = *p++; /* Next char */
+ if (!c) { /* Char is empty? */
+ if (!binary) { /* Text: end of line. */
+ if (feol) { /* Supply NL */
+ c = feol;
+ } else { /* or CRLF */
+ save = LF;
+ c = CR;
+ }
+ p = ap[++i];
+ return(c & 0xff);
+ }
+ while (i++ < n) { /* Binary - get next element */
+ p = ap[i];
+ if (!p) /* Empty line? */
+ continue; /* Ignore it and get another */
+ c = *p++; /* Get next char */
+ if (!c) /* Emtpy char? */
+ continue; /* Ignore it and get another */
+ return(c & 0xff); /* Not empty - return it */
+ }
+ sndarray = NULL;
+ ap = NULL;
+ return(-1); /* Done */
+ }
+ return(c & 0xff); /* Char is not empty */
+#else
+ sndarray = NULL;
+ return(-1);
+#endif /* NOSPL */
+}
+#endif /* NOXFER */
+
+#ifndef NOCSETS
+static CHAR xlabuf[32] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+static int xlacount = 0;
+static int xlaptr = 0;
+/* static USHORT lastucs2 = 0; */
+
+/*
+ X G N B Y T E -- Get next translated byte from the input file.
+
+ Returns the next byte that is to be put into the packet, already translated.
+ This isolates getpkt() from having to know anything about translation,
+ single- vs multibyte character sets, one-to-many vs many-to-one, etc, but it
+ has rather high overhead, so don't call it unless you know translation is
+ needed to or from Unicode, Japanese, or other multibyte character set.
+
+ Call with:
+ fcs: File character set (source, file we are reading from)
+ tcs: Target character set (use an FC_xxx code, not a TC_xxx code)
+ Returns:
+ >= 0: A translated byte suitable for writing.
+ < 0: Fatal error (such as EOF on input source).
+ As of Sat Sep 7 18:37:41 2002:
+ When the output character-set is UCS-2, bytes are ALWAYS returned in
+ big-endian order (previously they could also be returned in LE order
+ under certain conditions, which was just way too confusing).
+*/
+int
+#ifdef CK_ANSIC
+xgnbyte(int tcs, int fcs, int (*fn)(void))
+#else /* CK_ANSIC */
+xgnbyte(tcs,fcs,fn) int tcs, fcs, (*fn)();
+#endif /* CK_ANSIC */
+/* xgnbyte */ {
+ _PROTOTYP( int (*xx), (USHORT) ) = NULL;
+ int haveuc = 0; /* Flag for have Unicode character */
+#ifdef KANJI
+ int havesj = 0; /* Have Shift-JIS character */
+ int haveeu = 0; /* Have EUC-JP character */
+#endif /* KANJI */
+ int rc = -1, x = 0, flag = 0;
+ int utferror = 0;
+ int eolflag = 0;
+ unsigned int xc, thischar;
+ static int swapping = 0;
+ CHAR rt;
+ /* USHORT ch; */
+#ifdef UNICODE
+ union ck_short uc;
+#endif /* UNICODE */
+#ifdef KANJI
+ union ck_short sj, eu; /* Shift-JIS character */
+#endif /* KANJI */
+
+#ifdef KANJI
+ sj.x_short = 0;
+#endif /* KANJI */
+
+#ifdef DEBUG
+ if (deblog && ffc == 0) {
+ debug(F101,"xgnbyte initial swap","",swapping);
+ }
+#endif /* DEBUG */
+
+ if (xlacount-- > 0) { /* We already have some */
+ x = xlabuf[xlaptr++];
+ debug(F001,"xgnbyte from buf","",x);
+ return(x);
+ }
+ if (xlatype != XLA_NONE) { /* Not not translating... */
+ haveuc = 0;
+#ifdef UNICODE
+ if (fcs == FC_UCS2) { /* UCS-2: Read two bytes */
+ if (ffc == 0) /* Beginning of file? */
+ swapping = 0; /* Reset byte-swapping flag */
+ uc.x_short = 0;
+ bomskip:
+ x = fn ? (*fn)() : zminchar(); /* Get first byte */
+ debug(F001,"zminchar swapping","",swapping);
+ debug(F001,"zminchar C0","",x);
+ flag = 1; /* Remember we called zminchar() */
+ if (x > -1) { /* Didn't fail */
+ ffc++; /* Count a file byte */
+ uc.x_char[swapping] = x & 0xff;
+#ifndef NOXFER
+ if (docrc && (what & W_SEND))
+ dofilcrc(x);
+#endif /* NOXFER */
+ x = fn ? (*fn)() : zminchar(); /* Get second byte */
+ if (x > -1) { /* If didn't fail */
+ debug(F001,"zminchar C1","",x);
+ ffc++; /* count another file byte */
+ uc.x_char[1-swapping] = x & 0xff;
+ haveuc = 1; /* And remember we have Unicode */
+#ifndef NOXFER
+ if (docrc && (what & W_SEND))
+ dofilcrc(x);
+#endif /* NOXFER */
+ if (ffc == 2) { /* Second char of file */
+ debug(F001,"xgnbyte 1st UCS2","",uc.x_short);
+ debug(F111,"xgnbyte fileorder","A",fileorder);
+ if (fileorder < 0) /* Byte order of this file */
+ fileorder = ucsorder; /* Default is ucsorder */
+ if (fileorder > 1)
+ fileorder = 1;
+ debug(F111,"xgnbyte fileorder","B",fileorder);
+ if (uc.x_short == (USHORT)0xfeff) {
+ swapping = 0;
+ debug(F101,
+ "xgnbyte UCS2 goodbom swap","",swapping);
+ fileorder = byteorder; /* Note: NOT 0 */
+ goto bomskip;
+ } else if (uc.x_short == (USHORT)0xfffe) {
+ swapping = 1;
+ debug(F101,
+ "xgnbyte UCS2 badbom swap","",swapping);
+ fileorder = (1 - byteorder); /* Note: NOT 1 */
+ goto bomskip;
+ } else if ((byteorder && !fileorder) || /* No BOM */
+ (!byteorder && fileorder > 0)) {
+ /* fileorder might have been set by scanfile() */
+ CHAR c;
+ c = uc.x_char[0];
+ uc.x_char[0] = uc.x_char[1];
+ uc.x_char[1] = c;
+ swapping = 1;
+ debug(F111,"xgnbyte UCS2 noBOM swap","A",swapping);
+ } else {
+ swapping = 0;
+ debug(F111,"xgnbyte UCS2 noBOM swap","B",swapping);
+ }
+ debug(F111,"xgnbyte fileorder","C",fileorder);
+ }
+ } else
+ return(x);
+ } else
+ return(x);
+ debug(F001,"xgnbyte UCS2","",uc.x_short);
+
+ } else if (fcs == FC_UTF8) { /* File is UTF-8 */
+ CHAR ch = 0; /* Data types needed for API... */
+ USHORT * us = NULL;
+ uc.x_short = 0;
+ flag = 1; /* We (will) have called zminchar() */
+ /* Read source bytes */
+ while ((x = fn ? (*fn)() : zminchar()) > -1) {
+ ffc++; /* Got a byte - count it */
+#ifndef NOXFER
+ if (docrc && (what & W_SEND))
+ dofilcrc(x);
+#endif /* NOXFER */
+ ch = x;
+ rc = utf8_to_ucs2(ch,&us); /* Convert to UCS-2 */
+ if (rc == 0) { /* Done */
+ uc.x_short = *us;
+ haveuc = 1;
+ break;
+ } else if (rc < 0) { /* Error */
+ utferror = 1;
+ debug(F101,"xgnbyte UTF-8 input error","",rc);
+ haveuc = 1;
+ uc.x_short = *us;
+ break;
+ }
+ }
+ if (x < 0)
+ return(x);
+ debug(F001,"xgnbyte UTF8->UCS2","",uc.x_short);
+ }
+#endif /* UNICODE */
+
+#ifdef KANJI
+#ifdef UNICODE
+ else
+#endif /* UNICODE */
+ if (fcsinfo[fcs].alphabet == AL_JAPAN) { /* Japanese source file */
+ int c7, x, y;
+ if (fcs == FC_JIS7) { /* If file charset is JIS-7 */
+ if (ffc == 0L) /* If first byte of file */
+ j7init(); /* Initialize JIS-7 parser */
+ x = getj7(); /* Get a JIS-7 byte */
+ } else /* Otherwise */
+ x = fn ? (*fn)() : zminchar(); /* Just get byte */
+ if (x < 0) { /* Propogate EOF or error */
+ debug(F100,"XGNBYTE EOF","",0);
+ return(x);
+ }
+ debug(F001,"XGNBYTE x","",x);
+ ffc++; /* Count */
+#ifndef NOXFER
+ if (docrc && (what & W_SEND)) dofilcrc(x); /* Do CRC */
+#endif /* NOXFER */
+ switch (fcs) { /* What next depends on charset */
+ case FC_SHJIS: /* Shift-JIS */
+ if ((x <= 0x80) || /* Any 7-bit char... */
+ (x >= 0xa0 && x <= 0xdf)) { /* or halfwidth Katakana */
+ sj.x_short = (USHORT) x; /* we read one byte. */
+ } else { /* Anything else */
+ if ((y = fn ? (*fn)() : zminchar()) < 0) /* get another */
+ return(y);
+#ifndef NOXFER
+ if (docrc && (what & W_SEND)) dofilcrc(y);
+#endif /* NOXFER */
+ ffc++;
+ sj.x_char[byteorder] = (CHAR) x;
+ sj.x_char[1-byteorder] = (CHAR) y;
+ }
+ break;
+
+ case FC_JIS7: /* JIS-7 */
+ case FC_JDEC: /* DEC Kanji */
+ case FC_JEUC: /* EUC-JP */
+ if ((x & 0x80) == 0) { /* Convert to Shift-JIS */
+ sj.x_short = (USHORT) x; /* C0 or G0: one byte */
+ eu.x_short = (USHORT) x;
+ haveeu = 1;
+ } else {
+ c7 = x & 0x7f;
+ if (c7 > 0x20 && c7 < 0x7f) { /* Kanji: two bytes */
+ if ((y = (fcs == FC_JEUC) ?
+ (fn ? (*fn)() : zminchar()) :
+ getj7() /* ^^^ */
+ ) < 0)
+ return(y);
+ ffc++;
+#ifndef NOXFER
+ if (docrc && (what & W_SEND)) dofilcrc(y);
+#endif /* NOXFER */
+ eu.x_char[byteorder] = (CHAR) x;
+ eu.x_char[1-byteorder] = (CHAR) y;
+ sj.x_short = eu_to_sj(eu.x_short);
+ haveeu = 1;
+ } else if (x == 0x8e) { /* SS2 Katakana prefix: 2 bytes */
+ if ((y = (fcs == FC_JIS7) ?
+ getj7() : /* ^^^ */
+ (fn ? (*fn)() : zminchar())
+ ) < 0)
+ return(y);
+ ffc++;
+#ifndef NOXFER
+ if (docrc && (what & W_SEND)) dofilcrc(y);
+#endif /* NOXFER */
+ sj.x_short = y | 0x80;
+ debug(F001,"XGNBYTE KANA SJ","",sj.x_short);
+ } else {
+ /* Something that translates to U+FFFD */
+ sj.x_short = UNKSJIS;
+ }
+ }
+ break;
+ }
+ havesj = 1; /* Have Shift-JIS */
+#ifdef UNICODE
+ uc.x_short = sj_to_un(sj.x_short); /* Translate to UCS-2 */
+ haveuc = 1; /* Have Unicode */
+#endif /* UNICODE */
+ flag = 1; /* Have a char */
+ }
+#endif /* KANJI */
+ }
+ if (!flag) { /* If no character was read yet... */
+ if ((x = (fn ? (*fn)() : zminchar())) > -1) /* read one now */
+ ffc++;
+ debug(F101,"xgnbyte zminchar 1","",x);
+ if (x < 0)
+ return(x);
+ haveuc = 0;
+ }
+#ifdef UNICODE
+ if (haveuc) {
+ thischar = uc.x_short;
+ /* lastucs2 = uc.x_short; */
+ } else
+#endif /* UNICODE */
+ thischar = x;
+ debug(F001,"xgnbyte thischar",haveuc ? "[UNICODE]" : "[other]",thischar);
+
+#ifdef CK_CTRLZ /* SET EOF CTRLZ */
+ if (eofmethod == XYEOF_Z && !binary && thischar == 26) {
+ debug(F100,"xgnbyte EOF on Ctrl-Z 1","",0);
+ return(-1);
+ }
+#endif /* CK_CTRLZ */
+
+#ifdef UNICODE
+ if (!haveuc) /* If not Unicode... */
+#endif /* UNICODE */
+ x &= fmask; /* Apply SET FILE BYTESIZE mask */
+
+ switch (xlatype) { /* Translation type... */
+#ifdef UNICODE
+ case XLA_UNICODE: { /* Unicode is involved */
+ xc = 0;
+/*
+ Here we must choose the appropriate translation function. If we are being
+ called by getpkt() (i.e. when transferring a file), we are translating from
+ Unicode to the Transfer Character Set and therefore must use the function
+ pointed to by xut. Otherwise, e.g. during TRANSLATE, CONNECT, TRANSMIT, etc,
+ we are translating from Unicode to the File Character Set and so must call
+ the function pointed to by xuf. There might be a cleaner way to set this
+ up but I don't think so. For example, setxlatype() might have been called
+ too soon and so might not have known whether it was a file transfer or a
+ local operation.
+*/
+ xx = (what & W_SEND) ? xut : xuf;
+ eolflag = 0;
+ if (haveuc) { /* File is Unicode */
+ /* See Unicode TR13, "Converting to Other Character Sets" */
+ if (uc.x_short == 0x2028 || /* Line Separator? */
+ uc.x_short == 0x2029 || /* Paragraph Separator */
+ (feol && (uc.x_short == (USHORT)feol))
+ ) {
+ debug(F001,"xgnbyte uc eol","",uc.x_short);
+ rc = 0;
+ eolflag = 1; /* Don't translate and handle later */
+ }
+ if (xx && !eolflag) { /* UCS-to-TCS function (UCS->byte) */
+ rc = (*xx)(uc.x_short); /* These can fail... */
+ debug(F101,"xgnbyte xx rc","",rc);
+ if (rc < 0) /* If it can't be translated */
+ uc.x_short = UNK; /* Put unknown-character symbol */
+ else
+ uc.x_short = (unsigned)((unsigned)rc & 0xffff);
+ debug(F101,"xgnbyte xx uc","",uc.x_short);
+ }
+#ifdef KANJI
+ if (tcs == FC_JEUC) { /* Translating to EUC-JP */
+ USHORT sj = 0;
+ union ck_short eu;
+ debug(F001,"xgnbyte UCS->EUC UCS","",uc.x_short);
+ if (!havesj) /* If we don't already have it */
+ sj = un_to_sj(uc.x_short); /* convert to Shift-JIS */
+ eu.x_short = sj_to_eu(sj);
+ debug(F001,"xgnbyte UCS->EUC EUC","",eu.x_short);
+ xlaptr = 0;
+ xlacount = 0;
+ if (eolflag) {
+ if (what & W_SEND) {
+ xlabuf[xlacount++] = LF;
+ return(CR);
+ } else {
+ return(feol);
+ }
+ }
+ if (eu.x_char[byteorder]) { /* Two bytes */
+ rc = eu.x_char[byteorder];
+ xlabuf[xlacount++] = eu.x_char[1-byteorder];
+ debug(F001,"xgnbyte UCS->EUC xlabuf[0]","",xlabuf[0]);
+ } else { /* One byte */
+ rc = eu.x_char[1-byteorder];
+ }
+ debug(F101,"xgnbyte UCS->EUC xlacount","",xlacount);
+ debug(F001,"xgnbyte UCS->EUC rc","",rc);
+ return(rc);
+ } else
+#endif /* KANJI */
+ if (tcs != FC_UCS2 && tcs != FC_UTF8) {
+ if (uc.x_short & 0xff00) { /* Decoding error */
+ debug(F001,"xgnbyte decoding error","",uc.x_short);
+ return(-2);
+ } else
+ return((unsigned int)(uc.x_short & 0xff));
+ }
+ xc = uc.x_short;
+
+ } else { /* File is not Unicode */
+ USHORT ch;
+ /* Translate from single FCS byte to UCS-2 */
+/*
+ This is a bit nonobvious... The blah_u() (Blah-to-Unicode) routines are
+ called only with pieces of character sets, in the ISO 2022 sense. So,
+ for example, if ch is a Latin-1 character, we call the translation
+ routine only if it is in the right half; if it's in the left half, it
+ isn't translated, and in fact will give the wrong result if sent to the
+ translation function. That's because those functions were designed for
+ use with the ISO 2022 G0..G3 sets, not for file transfer. On the other
+ hand, if it's a 7-bit character set, we *do* call the translation
+ function. (To put it another way, the left half of any 8-bit character
+ set is ASCII and therefore doesn't need to be translated but 7-bit sets
+ such as ISO 646 German do need translation).
+*/
+ ch = (unsigned)(thischar & 0xff);
+ if (((fcsinfo[fcs].size > 128) && (ch & 0x80)) ||
+ fcsinfo[fcs].size <= 128) {
+ if (xfu) { /* FCS-to-UCS function */
+ ch = (*xfu)(ch);
+ }
+ }
+ xc = ch;
+ }
+ /* At this point we have a UCS-2 character in native format */
+ /* (Big Endian or Little Endian) in xc, which is an unsigned int. */
+
+ debug(F001,"xgnbyte xc","",xc);
+
+ if (tcs == FC_UTF8) { /* Now convert to UTF-8 */
+ USHORT c; /* NOTE: this is FC_UTF8 on purpose! */
+ CHAR * buf = NULL;
+ int i, k = 0, x;
+
+ xlaptr = 0;
+ if (utferror) {
+ xlabuf[k++] = 0xff;
+ xlabuf[k++] = 0xbd;
+ }
+ if (eolflag) { /* We detected EOL in source file */
+ if (what & W_SEND) { /* Convert to CRLF */
+ xlabuf[k++] = LF;
+ xlacount = k;
+ return((unsigned int)CR);
+#ifdef COMMENT
+ } else { /* Or to local line-end */
+ xlacount = k;
+ return((unsigned int)feol);
+#endif /* COMMENT */
+ }
+ }
+ c = xc;
+ if ((x = ucs2_to_utf8(c,&buf)) < 1) {
+ debug(F101,"xgnbyte ucs2_to_utf8 error","",c);
+ return(-2);
+ }
+ debug(F101,"xgnbyte UTF8 buf[0]","",buf[0]);
+ for (i = 1; i < x; i++) {
+ xlabuf[k+i-1] = buf[i];
+ debug(F111,"xgnbyte UTF8 xlabuf",ckitoa(i-1),buf[i]);
+ }
+ xlaptr = 0;
+ xlacount = x - 1;
+ debug(F101,"xgnbyte UTF8 xlacount","",xlacount);
+ return((unsigned int)buf[0]);
+ } else { /* Or keep it as UCS-2 */
+ int k = 0;
+ CHAR c;
+ xlaptr = 0;
+ if (utferror) {
+ xlabuf[k++] = 0xff;
+ xlabuf[k++] = 0xfd;
+ debug(F101,"xgnbyte error","",k);
+ }
+ if (eolflag) { /* We detected EOL in source file */
+ if (what & W_SEND) { /* Convert to CRLF */
+ xlabuf[k++] = CR;
+ xlabuf[k++] = NUL;
+ xlabuf[k++] = LF;
+ xlacount = k;
+ debug(F101,"xgnbyte send CRLF","",k);
+ return(0); /* Return NUL */
+ } else { /* Or to local line-end */
+#ifdef COMMENT
+ /* This bypasses byte swapping that we might need */
+ xlabuf[k++] = (CHAR)feol;
+ xlacount = k;
+ debug(F101,"xgnbyte send feol","",k);
+ return(0); /* Return NUL */
+#else
+ xc = (CHAR)feol;
+#endif /* COMMENT */
+ }
+ }
+ /* In which order should we return the bytes? */
+#ifdef COMMENT
+ if ( (what & W_SEND) || (what & W_FTP) || (fileorder == 0)) {
+#endif /* COMMENT */
+ /* ALWAYS RETURN IN BIG ENDIAN ORDER... 7 Sep 2002 */
+ /* xgnbyte() is almost always used to feed xpnbyte() */
+ /* which requires bytes in BE order. In cases where */
+ /* xgnbyte is used in isolation, the caller can swap */
+ /* bytes itself afterwards. */
+ xlabuf[k++] = (xc >> 8) & 0xff; /* Big Endian */
+ xlabuf[k++] = xc & 0xff;
+ debug(F001,"xgnbyte->UCS2BE",
+ ckitox((int)xlabuf[0]),xlabuf[1]);
+#ifdef COMMENT
+ } else { /* Little Endian */
+ xlabuf[k++] = xc & 0xff;
+ xlabuf[k++] = (xc >> 8) & 0xff;
+ debug(F001,"xgnbyte->UCS2LE",
+ ckitox((int)xlabuf[0]),xlabuf[1]);
+ }
+#endif /* COMMENT */
+ c = xlabuf[0];
+ xlaptr = 1;
+ xlacount = k-1;
+ debug(F101,"xgnbyte c","",c);
+ debug(F101,"xgnbyte xlaptr","",xlaptr);
+ debug(F011,"xgnbyte xlabuf",xlabuf,xlacount);
+ return((unsigned int)c);
+ }
+ }
+#endif /* UNICODE */
+ case XLA_NONE:
+ return((fn ? (*fn)() : zminchar()));
+ case XLA_BYTE: /* Byte-for-Byte translation */
+ rt = x;
+ if (sx)
+ rt = (*sx)(rt);
+#ifdef UNICODE
+ if (utferror) {
+ xlaptr = 0;
+ xlacount = 1;
+ xlabuf[0] = rt;
+ return(UNK);
+ } else
+#endif /* UNICODE */
+ return((unsigned int)rt);
+
+#ifdef KANJI
+ case XLA_JAPAN: /* Come here with Shift-JIS */
+ if (tcs == FC_JEUC) { /* It better be... */
+ xlaptr = 0;
+ xlacount = 0;
+ if (!havesj) {
+ printf("BAD BAD\n");
+ return(-2);
+ }
+ if (!haveeu) /* We might already have EUC too */
+ eu.x_short = sj_to_eu(sj.x_short);
+ if (eu.x_char[byteorder]) {
+ xlabuf[xlacount++] = eu.x_char[1-byteorder];
+ return(eu.x_char[byteorder]);
+ } else {
+ return(eu.x_char[1-byteorder]);
+ }
+ break;
+ }
+#endif /* KANJI */
+
+ default:
+ debug(F101,"xgnbyte bad xlatype","",xlatype);
+ return(-2);
+ }
+#ifdef COMMENT
+/*
+ If there is a return() statement here, some compilers complain
+ about "statement not reached". If there is no return() statement,
+ other compilers complain that "Non-void function should return a value".
+ There is no path through this function that falls through to here.
+*/
+ debug(F100,"xgnbyte switch failure","",0);
+ return(-2);
+#endif /* COMMENT */
+}
+#endif /* NOCSETS */
+
+#ifndef NOXFER
+
+/* G E T P K T -- Fill a packet data field from the indicated source. */
+
+/*
+ Parameters:
+ bufmax: Maximum length of entire packet.
+ xlate: Flag for whether to translate charsets when in text mode.
+ Returns: Number of characters written to packet data field, 0 or more,
+ Or -1 on failure (internal error),
+ or -3 on timeout (e.g. when reading from a pipe).
+
+ This is the full version allowing for parity and text-mode conversions;
+ i.e. it works in all cases. Also see bgetpkt(), a special lean/mean/fast
+ packet encoder that works only for binary-mode no-parity transfers.
+*/
+static int uflag = 0;
+
+int
+getpkt(bufmax,xlate) int bufmax, xlate; { /* Fill one packet buffer */
+ register CHAR rt = t, rnext = NUL; /* Register shadows of the globals */
+ register CHAR *dp, *odp, *odp2, *p1, *p2; /* pointers... */
+ register int x; /* Loop index. */
+ register int a7; /* Low 7 bits of character */
+
+ CHAR xxls, xxdl, xxrc, xxss, xxcq; /* Pieces of prefixed sequence */
+
+ if (binary) xlate = 0; /* We don't translate if binary */
+
+ if (!data) {
+ debug(F100,"SERIOUS ERROR: getpkt data == NULL","",0);
+ return(-1);
+ }
+ dp = data; /* Point to packet data buffer */
+ size = 0; /* And initialize its size */
+/*
+ Assume bufmax is the receiver's total receive-packet buffer length.
+ Our whole packet has to fit into it, so we adjust the data field length.
+ We also decide optimally whether it is better to use a short-format or
+ long-format packet when we're near the borderline.
+*/
+ bufmax = maxdata(); /* Get maximum data length */
+
+ if (first == 1) { /* If first character of this file.. */
+#ifdef UNICODE
+ /* Special end-of-line handling for Unicode */
+ if (tcharset == TC_UCS2 || tcharset == TC_UTF8)
+ uflag = 1;
+#endif /* UNICODE */
+ debug(F101,"getpkt first uflag","",uflag);
+ debug(F101,"getpkt first rt","",rt);
+ if (!memstr && !funcstr) /* and real file... */
+ ffc = 0L; /* reset file character counter */
+#ifdef COMMENT
+ /* Moved to below... */
+ first = 0; /* Next character won't be first */
+#endif /* COMMENT */
+ *leftover = '\0'; /* Discard any interrupted leftovers */
+ nleft = 0;
+#ifndef NOCSETS
+ setxlatype(tcharset,fcharset); /* Set up charset translations */
+#endif /* NOCSETS */
+
+ /* Get first character of file into rt, watching out for null file */
+
+#ifdef CALIBRATE
+ if (calibrate && !memstr) {
+#ifdef NORANDOM
+ x = rt = 53;
+#else
+ x = rt = cal_a[rand() & 0xff];
+#endif /* NORANDOM */
+ first = 0;
+ ffc++;
+ } else
+#endif /* CALIBRATE */
+#ifdef KANJI
+ if (xlate && tcharset == TC_JEUC) { /* Kanji text */
+ x = zkanjf();
+ if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) {
+ first = -1;
+ size = 0;
+ if (x == -2) {
+ debug(F100,"getpkt zkanji: input error","",0);
+ cxseen = 1;
+ } else debug(F100,"getpkt zkanji: empty string/file","",0);
+ return(0);
+ }
+ rt = x;
+ first = 0;
+ if (!memstr) {
+ ffc++;
+ if (docrc && (what & W_SEND)) /* Accumulate file crc */
+ dofilcrc((int)rt);
+ }
+ } else { /* Not Kanji text */
+#endif /* KANJI */
+ if (memstr) { /* Reading data from memory string */
+ /* This will not be Unicode */
+ if ((rt = *memptr++) == '\0') { /* end of string ==> EOF */
+ first = -1;
+ size = 0;
+ debug(F100,"getpkt: empty string","",0);
+ return(0);
+ }
+ first = 0;
+ } else if (funcstr) { /* Reading data from a function */
+ /* This will not be Unicode */
+ if ((x = (*funcptr)()) < 0) { /* End of input */
+ first = -1;
+ size = 0; /* Empty */
+ return(0);
+ }
+ ffc++; /* Count a file character */
+ rt = (CHAR) x; /* Convert int to char */
+ first = 0;
+ debug(F000,"getpkt funcstr","",rt);
+
+ } else { /* Reading data from a file */
+#ifndef NOCSETS
+ if (xlate && !binary) { /* Could be Unicode */
+ if (xlatype == XLA_UNICODE) {
+ /* Get next translated byte */
+ x = xgnbyte(cseqtab[tcharset],fcharset,NULL);
+ debug(F101,"getpkt xgnbyte","",x);
+ } else { /* Not Unicode */
+ x = zminchar(); /* Get next byte, translate below */
+ debug(F101,"getpkt zminchar A","",x);
+ }
+ } else { /* Just get next byte */
+#endif /* NOCSETS */
+ x = zminchar();
+ debug(F101,"getpkt zminchar B","",x);
+#ifndef NOCSETS
+ }
+#endif /* NOCSETS */
+ if (x < 0) { /* End of file or input error */
+ if (x == -3) { /* Timeout. */
+ size = (dp-data);
+ debug(F101,"getpkt timeout size","",size);
+ return((size == 0) ? x : size);
+ }
+ first = -1;
+ size = 0;
+ if (x == -2) { /* Error */
+ debug(F100,"getpkt: input error","",0);
+ cxseen = 1; /* Interrupt the file transfer */
+ } else {
+ debug(F100,"getpkt empty file","",0);
+ }
+ return(0);
+ }
+ first = 0; /* Next character won't be first */
+ rt = (CHAR) x; /* Convert int to char */
+#ifndef NOCSETS
+ if (xlatype != XLA_UNICODE || binary) {
+ ffc++;
+ if (sx)
+ rt = (*sx)(rt);
+ if (docrc && (what & W_SEND))
+ dofilcrc(x);
+ }
+#endif /* NOCSETS */
+#ifdef DEBUG
+ if (deblog)
+ debug(F101,"getpkt 1st char","",rt);
+#endif /* DEBUG */
+ if (/* !haveuc && */ docrc && (what & W_SEND)) /* File CRC */
+ dofilcrc(x);
+ }
+#ifdef KANJI
+ }
+#endif /* KANJI */
+ /* PWP: handling of feol is done later (in the while loop)... */
+
+ } else if ((first == -1) && (nleft == 0)) { /* EOF from last time */
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"getpkt eof crc16","",crc16);
+ debug(F101,"getpkt eof ffc","",ffc);
+ }
+#endif /* DEBUG */
+ return(size = 0);
+ }
+/*
+ Here we handle characters that were encoded for the last packet but
+ did not fit, and so were saved in the "leftover" array.
+*/
+ debug(F101,"getpkt nleft","",nleft);
+ if (nleft) {
+ for (p1 = leftover; nleft > 0; nleft--) /* Copy leftovers */
+ *dp++ = *p1++;
+ *leftover = '\0'; /* Delete leftovers */
+ nleft = 0;
+ }
+ if (first == -1) /* Handle EOF */
+ return(size = (dp - data));
+
+/* Now fill up the rest of the packet. */
+
+ rpt = 0; /* Initialize character repeat count */
+
+ while (first > -1) { /* Until EOF... */
+#ifdef CALIBRATE
+ if (calibrate && !memstr) { /* We generate our own "file" */
+ if (ffc >= calibrate) { /* EOF */
+ first = -1;
+ ffc--;
+ } else { /* Generate next character */
+ if (cal_j > CAL_M * ffc)
+ cal_j = cal_a[ffc & 0xff];
+ x = (unsigned)cal_a[(cal_j & 0xff)];
+ if (x == rt) x ^= 2;
+ }
+ cal_j += (unsigned int)(ffc + CAL_O);
+ ffc++;
+ } else
+#endif /* CALIBRATE */
+#ifdef KANJI
+ if (xlate && tcharset == TC_JEUC) {
+ if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) {
+ first = -1;
+ if (x == -2) cxseen = 1;
+ } else if (!memstr) ffc++;
+ rnext = (CHAR) (x & fmask);
+ } else {
+#endif /* KANJI */
+ if (memstr) { /* Get next char from memory string */
+ if ((x = *memptr++) == '\0') /* End of string means EOF */
+ first = -1; /* Flag EOF for next time. */
+ rnext = (CHAR) (x & fmask); /* Apply file mask */
+ } else if (funcstr) { /* Get next char from function */
+ if ((x = (*funcptr)()) < 0) /* End of string means EOF */
+ first = -1; /* Flag EOF for next time. */
+ rnext = (CHAR) (x & fmask); /* Apply file mask */
+ } else { /* From file... */
+#ifndef NOCSETS
+ if (xlate && !binary) { /* Could be Unicode */
+ if (xlatype == XLA_UNICODE) {
+ /* Get next translated byte */
+ x = xgnbyte(cseqtab[tcharset],fcharset,NULL);
+ } else { /* Not Unicode */
+ x = zminchar(); /* Get next byte, translate below */
+ /* debug(F101,"xgnbyte B zminchar","",x); */
+ }
+ } else { /* Just get next byte */
+#endif /* NOCSETS */
+ x = zminchar();
+ /* debug(F101,"xgnbyte C zminchar","",x); */
+#ifndef NOCSETS
+ }
+#endif /* NOCSETS */
+ if (x < 0) { /* Check for EOF */
+ if (x == -3) { /* Timeout reading from pipe */
+ t = rt;
+ size = (dp-data);
+ debug(F101,"getpkt timeout size","",size);
+ return((size == 0) ? x : size);
+ }
+ first = -1; /* Flag eof for next time. */
+ if (x == -2) cxseen = 1; /* If error, cancel this file. */
+ }
+ rnext = (CHAR) (x & fmask); /* Apply file mask */
+#ifndef NOCSETS
+ if (xlatype != XLA_UNICODE) {
+#endif /* NOCSETS */
+ ffc++;
+#ifndef NOCSETS
+ if (sx)
+ rt = (*sx)(rt);
+#endif /* NOCSETS */
+ if (docrc && (what & W_SEND))
+ dofilcrc(x);
+
+#ifndef NOCSETS
+ }
+#endif /* NOCSETS */
+ }
+#ifdef KANJI
+ }
+#endif /* KANJI */
+/*
+ At this point, the character we just read is in rnext,
+ and the character we are about to encode into the packet is in rt.
+*/
+ odp = dp; /* Remember where we started. */
+ xxls = xxdl = xxrc = xxss = xxcq = NUL; /* Clear these. */
+/*
+ Now encode the character according to the options that are in effect:
+ ctlp[]: whether this control character needs prefixing.
+ binary: text or binary mode.
+ rptflg: repeat counts enabled.
+ ebqflg: 8th-bit prefixing enabled.
+ lscapu: locking shifts enabled.
+*/
+ if (rptflg) { /* Repeat processing is on? */
+ if (!uflag &&
+ /*
+ * If the next char is really CRLF, then we cannot
+ * be doing a repeat (unless CR,CR,LF which becomes
+ * "~ <n-1> CR CR LF", which is OK but not most efficient).
+ * I just plain don't worry about this case. The actual
+ * conversion from NL to CRLF is done after the rptflg if...
+ */
+ (!feol || binary || (feol && (rnext != feol))) &&
+ (rt == rnext) && (first == 0)) { /* Got a run... */
+ if (++rpt < 94) { /* Below max, just count */
+ continue; /* go back and get another */
+ } else if (rpt == 94) { /* Reached max, must dump */
+ xxrc = (CHAR) tochar(rpt); /* Put the repeat count here */
+ rptn += rpt; /* Accumulate it for statistics */
+ rpt = 0; /* And reset it */
+ }
+ } else if (rpt > 1) { /* More than two */
+ xxrc = (CHAR) tochar(++rpt); /* and count. */
+ rptn += rpt;
+ rpt = 0; /* Reset repeat counter. */
+ }
+ /*
+ If (rpt == 1) we must encode exactly two characters.
+ This is done later, after the first character is encoded.
+ */
+ }
+ /* If it's the newline character... */
+ if (!uflag && !binary && feol && (rt == feol)) {
+ if (lscapu && lsstate) { /* If SHIFT-STATE is SHIFTED */
+ if (ebqflg) { /* If single shifts enabled, */
+ *dp++ = (CHAR) ebq; /* insert a single shift. */
+ } else { /* Otherwise must shift in. */
+ *dp++ = myctlq; /* Insert shift-out code */
+ *dp++ = 'O';
+ lsstate = 0; /* Change shift state */
+ }
+ }
+#ifdef CK_SPEED
+ if (ctlp[CR]) {
+ *dp++ = myctlq; /* Insert carriage return directly */
+ *dp++ = 'M';
+ ccp++;
+ } else {
+ *dp++ = CR; /* Perhaps literally */
+ ccu++;
+ }
+#else /* !CK_SPEED */
+ *dp++ = myctlq; /* Insert carriage return directly */
+ *dp++ = 'M';
+ ccp++;
+#endif /* CK_SPEED */
+ rt = LF; /* Now make next char be linefeed. */
+ }
+/*
+ Now handle the 8th bit of the file character. If we have an 8-bit
+ connection, we preserve the 8th bit. If we have a 7-bit connection,
+ we employ either single or locking shifts (if they are enabled).
+*/
+ a7 = rt & 0177; /* Get low 7 bits of character */
+ if (rt & 0200) { /* 8-bit character? */
+ if (lscapu) { /* Locking shifts enabled? */
+ if (!lsstate) { /* Not currently shifted? */
+ x = lslook(0200); /* Look ahead */
+ if (x != 0 || ebqflg == 0) { /* Locking shift decision */
+ xxls = 'N'; /* Need locking shift-out */
+ lsstate = 1; /* and change to shifted state */
+ } else if (ebqflg) { /* Not worth it */
+ xxss = (CHAR) ebq; /* Use single shift */
+ }
+ }
+ rt = (CHAR) a7; /* Replace character by 7-bit value */
+ } else if (ebqflg) { /* 8th bit prefixing is on? */
+ xxss = (CHAR) ebq; /* Insert single shift */
+ rt = (CHAR) a7; /* Replace character by 7-bit value */
+ }
+/*
+ In case we have a 7-bit connection and this is an 8-bit character, AND
+ neither locking shifts nor single shifts are enabled, then the character's
+ 8th bit will be destroyed in transmission, and a block check error will
+ occur.
+*/
+ } else if (lscapu) { /* 7-bit character */
+
+ if (lsstate) { /* Comes while shifted out? */
+ x = lslook(0); /* Yes, look ahead */
+ if (x || ebqflg == 0) { /* Time to shift in. */
+ xxls = 'O'; /* Set shift-in code */
+ lsstate = 0; /* Exit shifted state */
+ } else if (ebqflg) { /* Not worth it, stay shifted out */
+ xxss = (CHAR) ebq; /* Insert single shift */
+ }
+ }
+ }
+ /* If data character is significant to locking shift protocol... */
+ if (lscapu && (a7 == SO || a7 == SI || a7 == DLE))
+ xxdl = 'P'; /* Insert datalink escape */
+
+ if (
+#ifdef CK_SPEED
+ /*
+ Thwart YET ANOTHER unwanted, unneeded, and unloved sign
+ extension. This one was particularly nasty because it prevented
+ 255 (Telnet IAC) from being prefixed on some platforms -- e.g.
+ VMS with VAX C -- but not others, thus causing file transfers to
+ fail on Telnet connections by sending bare IACs. Not to mention
+ the stray memory reference. Signed chars are a BAD idea.
+ */
+ ctlp[(unsigned)(rt & 0xff)] /* Lop off any "sign" extension */
+#else
+ (a7 < SP) || (a7 == DEL)
+#endif /* CK_SPEED */
+ ) { /* Do control prefixing if necessary */
+ xxcq = myctlq; /* The prefix */
+ ccp++; /* Count it */
+ rt = (CHAR) ctl(rt); /* Uncontrollify the character */
+ }
+#ifdef CK_SPEED
+ else if ((a7 < SP) || (a7 == DEL)) /* Count an unprefixed one */
+ ccu++;
+#endif /* CK_SPEED */
+
+ if (a7 == myctlq) /* Always prefix the control prefix */
+ xxcq = myctlq;
+
+ if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */
+ xxcq = myctlq; /* prefix it if doing repeat counts */
+
+ if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th-bit prefix */
+ xxcq = myctlq; /* if doing 8th-bit prefixes */
+
+/* Now construct the entire sequence */
+
+ if (xxls) { *dp++ = myctlq; *dp++ = xxls; } /* Locking shift */
+ odp2 = dp; /* (Save this place) */
+ if (xxdl) { *dp++ = myctlq; *dp++ = xxdl; } /* Datalink escape */
+ if (xxrc) { *dp++ = (CHAR) rptq; *dp++ = xxrc; } /* Repeat count */
+ if (xxss) { *dp++ = (CHAR) ebq; } /* Single shift */
+ if (xxcq) { *dp++ = myctlq; } /* Control prefix */
+ *dp++ = rt; /* Finally, the character itself */
+
+ if (rpt == 1) { /* Exactly two copies? */
+ rpt = 0;
+ p2 = dp; /* Save place temporarily */
+ for (p1 = odp2; p1 < p2; p1++) /* Copy the old chars over again */
+ *dp++ = *p1;
+ if ((p2-data) <= bufmax) odp = p2; /* Check packet bounds */
+ if ((p2-data) < bufmax) odp = p2; /* Check packet bounds */
+ }
+ rt = rnext; /* Next character is now current. */
+
+/* Done encoding the character. Now take care of packet buffer overflow. */
+
+ if ((dp-data) >= bufmax) { /* If too big, save some for next. */
+
+ debug(F000,"getpkt EOP","",rt);
+
+ size = (dp-data); /* Calculate the size. */
+ *dp = '\0'; /* Mark the end. */
+ if (memstr) { /* No leftovers for memory strings */
+ if (rt) /* Char we didn't encode yet */
+ memptr--; /* (for encstr()) */
+ return(size);
+ }
+ if ((dp-data) > bufmax) { /* if packet is overfull */
+ /* copy the part that doesn't fit into the leftover buffer, */
+ /* taking care not to split a prefixed sequence. */
+ int i;
+ nleft = dp - odp;
+ for (i = 0, p1 = leftover, p2 = odp; i < nleft; i++) {
+ *p1++ = *p2++;
+ if (memstr) memptr--; /* (for encstr) */
+ }
+ debug(F111,"getpkt leftover",leftover,size);
+ debug(F101,"getpkt osize","",(odp-data));
+ size = (odp-data); /* Return truncated packet. */
+ *odp = '\0'; /* Mark the new end */
+ }
+ t = rt; /* Save for next time */
+ return(size);
+ }
+ } /* Otherwise, keep filling. */
+ size = (dp-data); /* End of file */
+ *dp = '\0'; /* Mark the end of the data. */
+ debug(F111,"getpkt eof/eot",data,size); /* Fell thru before packet full, */
+ return(size); /* return partially filled last packet. */
+}
+
+/* T I N I T -- Initialize a transaction */
+
+int epktrcvd = 0, epktsent = 0;
+
+/*
+ Call with 1 to reset everything before S/I/Y negotiation, or 0 to
+ reset only the things that are not set in the S/I/Y negotiation.
+ Returns -1 on failure (e.g. to create packet buffers), 0 on success.
+*/
+int
+tinit(flag) int flag; {
+ int x;
+#ifdef CK_TIMERS
+ extern int rttflg;
+#endif /* CK_TIMERS */
+ extern int rcvtimo;
+ extern int fatalio;
+
+ debug(F101,"tinit flag","",flag);
+
+ *epktmsg = NUL;
+ epktrcvd = 0;
+ epktsent = 0;
+ ofperms = "";
+ diractive = 0; /* DIR / REMOTE DIR not active */
+ interrupted = 0; /* Not interrupted */
+ fatalio = 0; /* No fatal i/o error */
+ if (server) {
+ moving = 0;
+ pipesend = 0; /* This takes care of multiple GETs sent to a server. */
+ }
+ bestlen = 0; /* For packet length optimization */
+ maxsend = 0; /* Biggest data field we can send */
+#ifdef STREAMING
+ streamok = 0; /* Streaming negotiated */
+ streaming = 0; /* Streaming being done now */
+#endif /* STREAMING */
+
+ binary = b_save; /* ... */
+ gnf_binary = binary; /* Per-file transfer mode */
+ retrans = 0; /* Packet retransmission count */
+ sndtyp = 0; /* No previous packet */
+ xflg = 0; /* Reset x-packet flag */
+ memstr = 0; /* Reset memory-string flag */
+ memptr = NULL; /* and buffer pointer */
+ funcstr = 0; /* Reset "read from function" flag */
+ funcptr = NULL; /* and function pointer */
+ autopar = 0; /* Automatic parity detection flag */
+
+ /* This stuff is only for BEFORE S/I/Y negotiation, not after */
+
+ if (flag) {
+ bctu = bctl = 1; /* Reset block check type to 1 */
+ myinit[0] = '\0'; /* Haven't sent init string yet */
+ rqf = -1; /* Reset 8th-bit-quote request flag */
+ ebq = MYEBQ; /* Reset 8th-bit quoting stuff */
+ ebqflg = 0; /* 8th bit quoting not enabled */
+ ebqsent = 0; /* No 8th-bit prefix bid sent yet */
+ sq = 'Y'; /* 8th-bit prefix bid I usually send */
+ spsiz = spsizr; /* Initial send-packet size */
+ debug(F101,"tinit spsiz","",spsiz);
+ wslots = 1; /* One window slot */
+ wslotn = 1; /* No window negotiated yet */
+ justone = 0; /* (should this be zero'd here?) */
+ whoareu[0] = NUL; /* Partner's system type */
+ sysindex = -1;
+ wearealike = 0;
+ what = W_INIT; /* Doing nothing so far... */
+ }
+ fncnv = f_save; /* Back to what user last said */
+ pktnum = 0; /* Initial packet number to send */
+ cxseen = czseen = discard = 0; /* Reset interrupt flags */
+ *filnam = '\0'; /* Clear file name */
+ spktl = 0; /* And its length */
+ nakstate = 0; /* Assume we're not in a NAK state */
+ numerrs = 0; /* Transmission error counter */
+ idletmo = 0; /* No idle timeout yet */
+ if (server) { /* If acting as server, */
+ if (srvidl > 0) /* If an idle timeout is given */
+ timint = srvidl;
+ else
+ timint = srvtim; /* use server timeout interval. */
+ } else { /* Otherwise */
+ timint = chktimo(rtimo,timef); /* and use local timeout value */
+ }
+ debug(F101,"tinit timint","",timint);
+
+#ifdef CK_TIMERS
+ if (rttflg && timint > 0) /* Using round-trip timers? */
+ rttinit();
+ else
+#endif /* CK_TIMERS */
+ rcvtimo = timint;
+
+ winlo = 0; /* Packet 0 is at window-low */
+ debug(F101,"tinit winlo","",winlo);
+ x = mksbuf(1); /* Make a 1-slot send-packet buffer */
+ if (x < 0) return(x);
+ x = getsbuf(0); /* Allocate first send-buffer. */
+ debug(F101,"tinit getsbuf","",x);
+ if (x < 0) return(x);
+ dumpsbuf();
+ x = mkrbuf(wslots); /* & a 1-slot receive-packet buffer. */
+ if (x < 0) return(x);
+ lsstate = 0; /* Initialize locking shift state */
+ if (autopath) { /* SET RECEIVE PATHNAMES AUTO fixup */
+ fnrpath = PATH_AUTO;
+ autopath = 0;
+ }
+ return(0);
+}
+
+VOID
+pktinit() { /* Initialize packet sequence */
+ pktnum = 0; /* number & window low. */
+ winlo = 0;
+ debug(F101,"pktinit winlo","",winlo);
+}
+
+/* R I N I T -- Respond to S or I packet */
+
+VOID
+rinit(d) CHAR *d; {
+ char *tp = NULL;
+ ztime(&tp);
+ tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */
+ tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L);
+ tlog(F110,"Collision action:", fncnam[fncact],0);
+ tlog(F100,"","",0);
+ debug(F101,"rinit fncact","",fncact);
+ filcnt = filrej = 0; /* Init file counters */
+ spar(d);
+ ack1(rpar());
+#ifdef datageneral
+ if ((local) && (!quiet)) /* Only do this if local & not quiet */
+ consta_mt(); /* Start the asynch read task */
+#endif /* datageneral */
+}
+
+
+/* R E S E T C -- Reset per-transaction character counters */
+
+VOID
+resetc() {
+ rptn = 0; /* Repeat counts */
+ fsecs = flci = flco = 0L; /* File chars in and out */
+#ifdef GFTIMER
+ fpfsecs = 0.0;
+#endif /* GFTIMER */
+ tfc = tlci = tlco = 0L; /* Total file, line chars in & out */
+ ccu = ccp = 0L; /* Control-char statistics */
+#ifdef COMMENT
+ fsize = -1L; /* File size */
+#else
+ if (!(what & W_SEND))
+ fsize = -1L;
+ debug(F101,"resetc fsize","",fsize);
+#endif /* COMMENT */
+ timeouts = retrans = 0; /* Timeouts, retransmissions */
+ spackets = rpackets = 0; /* Packet counts out & in */
+ crunched = 0; /* Crunched packets */
+ wcur = 0; /* Current window size */
+ wmax = 0; /* Maximum window size used */
+ peakcps = 0; /* Peak chars per second */
+}
+
+/* S I N I T -- Get & verify first file name, then send Send-Init packet */
+/*
+ Returns:
+ 1 if send operation begins successfully
+ 0 if send operation fails
+*/
+#ifdef DYNAMIC
+char *cmargbuf = NULL;
+#else
+char cmargbuf[CKMAXPATH+1];
+#endif /* DYNAMIC */
+char *cmargp[2];
+
+VOID
+fnlist() {
+ if (!calibrate)
+ sndsrc = (nfils < 0) ? -1 : nfils; /* Source for filenames */
+#ifdef DYNAMIC
+ if (!cmargbuf && !(cmargbuf = malloc(CKMAXPATH+1)))
+ fatal("fnlist: no memory for cmargbuf");
+#endif /* DYNAMIC */
+ cmargbuf[0] = NUL; /* Initialize name buffer */
+
+ debug(F101,"fnlist nfils","",nfils);
+ debug(F110,"fnlist cmarg",cmarg,0);
+ debug(F110,"fnlist cmarg2",cmarg2,0);
+ if (!cmarg2) cmarg2 = "";
+ if (nfils == 0) { /* Sending from stdin or memory. */
+ if ((cmarg2 != NULL) && (*cmarg2)) {
+ cmarg = cmarg2; /* If F packet, "as-name" is used */
+ cmarg2 = ""; /* if provided */
+ } else
+ cmarg = "stdin"; /* otherwise just use "stdin" */
+ ckstrncpy(cmargbuf,cmarg,CKMAXPATH+1);
+ cmargp[0] = cmargbuf;
+ cmargp[1] = "";
+ cmlist = cmargp;
+ nfils = 1;
+ }
+}
+
+int
+sinit() {
+ int x; /* Worker int */
+ char *tp, *xp, *m; /* Worker string pointers */
+
+ filcnt = filrej = 0; /* Initialize file counters */
+
+ fnlist();
+
+ xp = "";
+ if (nfils < 0) {
+#ifdef PIPESEND
+ if (usepipes && protocol == PROTO_K && *cmarg == '!') {
+ pipesend = 1;
+ cmarg++;
+ }
+#endif /* PIPESEND */
+ xp = cmarg;
+ } else {
+#ifndef NOMSEND
+ if (addlist)
+ xp = filehead->fl_name;
+ else
+#endif /* NOMSEND */
+ if (filefile)
+ xp = filefile;
+ else if (calibrate)
+ xp = "Calibration";
+ else
+ xp = *cmlist;
+ }
+ debug(F110,"sinit xp",xp,0);
+ x = gnfile(); /* Get first filename. */
+ debug(F111,"sinit gnfile",ckitoa(gnferror),x);
+ if (x == 0) x = gnferror; /* If none, get error reason */
+ m = NULL; /* Error message pointer */
+ debug(F101,"sinit gnfil","",x);
+ switch (x) {
+ case -6: m = "No files meet selection criteria"; break;
+ case -5: m = "Too many files match wildcard"; break;
+ case -4: m = "Cancelled"; break;
+ case -3: m = "Read access denied"; break;
+ case -2: m = "File is not readable"; break;
+#ifdef COMMENT
+ case -1: m = iswild(filnam) ? "No files match" : "File not found";
+ break;
+ case 0: m = "No filespec given!"; break;
+#else
+ case 0:
+ case -1: m = iswild(filnam) ? "No files match" : "File not found";
+ break;
+#endif /* COMMENT */
+ default:
+ break;
+ }
+ debug(F101,"sinit nfils","",nfils);
+ debug(F110,"sinit filnam",filnam,0);
+ if (x < 1) { /* Didn't get a file. */
+ debug(F111,"sinit msg",m,x);
+ if (server) { /* Doing GET command */
+ errpkt((CHAR *)m); /* so send Error packet. */
+ } else if (!local) { /* Doing SEND command */
+ interrupted = 1; /* (To suppress hint) */
+ printf("?%s\r\n",m);
+ } else {
+ xxscreen(SCR_EM,0,0l,m); /* so print message. */
+ }
+ tlog(F110,xp,m,0L); /* Make transaction log entry. */
+ freerbuf(rseqtbl[0]); /* Free the buffer the GET came in. */
+ return(0); /* Return failure code */
+ }
+ if (!local && !server && ckdelay > 0) /* OS-9 sleep(0) == infinite */
+ sleep(ckdelay); /* Delay if requested */
+#ifdef datageneral
+ if ((local) && (!quiet)) /* Only do this if local & not quiet */
+ consta_mt(); /* Start the async read task */
+#endif /* datageneral */
+ freerbuf(rseqtbl[0]); /* Free the buffer the GET came in. */
+ sipkt('S'); /* Send the Send-Init packet. */
+ ztime(&tp); /* Get current date/time */
+ tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */
+ tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L);
+ tlog(F100,"","",0);
+ debug(F111,"sinit ok",filnam,0);
+ return(1);
+}
+
+int
+#ifdef CK_ANSIC
+sipkt(char c) /* Send S or I packet. */
+#else
+sipkt(c) char c;
+#endif
+/* sipkt */ {
+ CHAR *rp; int k, x;
+ extern int sendipkts;
+ debug(F101,"sipkt pktnum","",pktnum); /* (better be 0...) */
+ ttflui(); /* Flush pending input. */
+ /*
+ If this is an I packet and SET SEND I-PACKETS is OFF, don't send it;
+ set sstate to 'Y' which makes the next input() call return 'Y' as if we
+ had received an ACK to the I packet we didn't send. This is to work
+ around buggy Kermit servers that can't handle I packets.
+ */
+ if ((sendipkts == 0) && (c == 'I')) { /* I packet but don't send I pkts? */
+ sstate = 'Y'; /* Yikes! */
+ return(0); /* (see input()..)*/
+ }
+ k = sseqtbl[pktnum]; /* Find slot for this packet */
+ if (k < 0) { /* No slot? */
+ k = getsbuf(winlo = pktnum); /* Make one. */
+ debug(F101,"sipkt getsbuf","",k);
+ }
+ rp = rpar(); /* Get protocol parameters. */
+ if (!rp) rp = (CHAR *)"";
+ x = spack(c,pktnum,(int)strlen((char *)rp),rp); /* Send them. */
+ return(x);
+}
+
+/* X S I N I T -- Retransmit S-packet */
+/*
+ For use in the GET-SEND sequence, when we start to send, but receive another
+ copy of the GET command because the receiver didn't get our S packet.
+ This retransmits the S packet and frees the receive buffer for the ACK.
+ This special case is necessary because packet number zero is being re-used.
+*/
+VOID
+xsinit() {
+ int k;
+ k = rseqtbl[0];
+ debug(F101,"xsinit k","",k);
+ if (k > -1)
+ freerbuf(k);
+ resend(0);
+}
+
+/* R C V F I L -- Receive a file */
+
+/*
+ Incoming filename is in data field of F packet.
+ This function decodes it into the srvcmd buffer, substituting an
+ alternate "as-name", if one was given.
+ Then it does any requested transformations (like converting to
+ lowercase), and finally if a file of the same name already exists,
+ takes the desired collision action.
+ Returns:
+ 1 on success.
+ 0 on failure.
+*/
+char ofn1[CKMAXPATH+4]; /* Buffer for output file name */
+char * ofn2; /* Pointer to backup file name */
+int ofn1x; /* Flag output file already exists */
+long ofn1len = 0L;
+int opnerr; /* Flag for open error */
+
+int /* Returns success ? 1 : 0 */
+rcvfil(n) char *n; {
+ extern int en_cwd;
+ int i, skipthis;
+ char * n2;
+ char * dispo;
+#ifdef OS2ONLY
+ char *zs, *longname, *newlongname, *pn; /* OS/2 long name items */
+#endif /* OS2ONLY */
+#ifdef DTILDE
+ char *dirp;
+#endif /* DTILDE */
+ int dirflg, x, y;
+#ifdef PIPESEND
+ extern char * rcvfilter;
+#endif /* PIPESEND */
+#ifdef CALIBRATE
+ extern int dest;
+ int csave;
+ csave = calibrate; /* So we can decode filename */
+ calibrate = 0;
+#endif /* CALIBRATE */
+
+ ofperms = ""; /* Reset old-file permissions */
+ opnerr = 0; /* No open error (yet) */
+ ofn2 = NULL; /* No new name (yet) */
+ lsstate = 0; /* Cancel locking-shift state */
+ srvptr = srvcmd; /* Decode file name from packet. */
+
+#ifdef UNICODE
+ xpnbyte(-1,0,0,NULL); /* Reset UCS-2 byte counter. */
+#endif /* UNICODE */
+
+ debug(F110,"rcvfil rdatap",rdatap,0);
+ decode(rdatap,putsrv,0); /* Don't xlate charsets. */
+#ifdef CALIBRATE
+ calibrate = csave;
+ if (dest == DEST_N) {
+ calibrate = 1;
+ cmarg2 = "CALIBRATE";
+ }
+#endif /* CALIBRATE */
+ if (*srvcmd == '\0') /* Watch out for null F packet. */
+ ckstrncpy((char *)srvcmd,"NONAME",srvcmdlen);
+ makestr(&prrfspec,(char *)srvcmd); /* New preliminary filename */
+#ifdef DTILDE
+ if (*srvcmd == '~') {
+ dirp = tilde_expand((char *)srvcmd); /* Expand tilde, if any. */
+ if (*dirp != '\0')
+ ckstrncpy((char *)srvcmd,dirp,srvcmdlen);
+ }
+#else
+#ifdef OS2
+ if (isalpha(*srvcmd) && srvcmd[1] == ':' && srvcmd[2] == '\0')
+ ckstrncat((char *)srvcmd,"NONAME",srvcmdlen);
+#endif /* OS2 */
+#endif /* DTILDE */
+
+#ifndef NOICP
+#ifndef NOSPL
+/* File dialog when downloading... */
+ if (
+#ifdef CK_APC
+ (apcactive == APC_LOCAL && adl_ask) || /* Autodownload with ASK */
+#endif /* CK_APC */
+ (clcmds && haveurl) /* Or "kermit:" or "iksd:" URL */
+ ) {
+ int x;
+ char fnbuf[CKMAXPATH+1]; /* Result buffer */
+ char * preface;
+
+ if (clcmds && haveurl)
+ preface = "\r\nIncoming file from Kermit server...\r\n\
+Please confirm output file specification or supply an alternative:";
+ else
+ preface = "\r\nIncoming file from remote Kermit...\r\n\
+Please confirm output file specification or supply an alternative:";
+
+ x = uq_file(preface, /* Preface */
+ NULL, /* Prompt (let uq_file() built it) */
+ 3, /* Output file */
+ NULL, /* Help text */
+ (char *)srvcmd, /* Default */
+ fnbuf, /* Result buffer */
+ CKMAXPATH+1 /* Size of result buffer */
+ );
+ if (x < 1) { /* Refused */
+ rf_err = "Refused by user";
+ return(0);
+ }
+ ckstrncpy((char *)srvcmd,fnbuf,CKMAXPATH+1);
+ if (isabsolute((char *)srvcmd)) { /* User gave an absolute path */
+ g_fnrpath = fnrpath; /* Save current RECEIVE PATHNAMES */
+ fnrpath = PATH_ABS; /* switch to ABSOLUTE */
+ }
+ }
+#endif /* NOSPL */
+#endif /* NOICP */
+
+ if (!ENABLED(en_cwd)) { /* CD is disabled */
+ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */
+ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */
+ rf_err = "Access denied";
+ return(0);
+ }
+ }
+#ifdef COMMENT
+ /* Wrong place for this -- handle cmarg2 first -- see below... */
+
+ if (zchko((char *)srvcmd) < 0) { /* Precheck for write access */
+ debug(F110,"rcvfil access denied",srvcmd,0);
+ rf_err = "Write access denied";
+ discard = opnerr = 1;
+ return(0);
+ }
+ xxscreen(SCR_FN,0,0l,(char *)srvcmd); /* Put it on screen if local */
+ debug(F110,"rcvfil srvcmd 1",srvcmd,0);
+ tlog(F110,"Receiving",(char *)srvcmd,0L); /* Transaction log entry */
+#endif /* COMMENT */
+
+ skipthis = 0; /* This file in our exception list? */
+ for (i = 0; i < NSNDEXCEPT; i++) {
+ if (!rcvexcept[i]) {
+ break;
+ }
+ if (ckmatch(rcvexcept[i],(char *)srvcmd,filecase,1)) {
+ skipthis = 1;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (deblog && skipthis) {
+ debug(F111,"rcvfil rcvexcept",rcvexcept[i],i);
+ debug(F110,"rcvfil skipping",srvcmd,0);
+ }
+#endif /* DEBUG */
+
+ if (skipthis) { /* Skipping this file */
+ discard = 1;
+ rejection = 1;
+ rf_err = "Exception list";
+ debug(F101,"rcvfil discard","",discard);
+ tlog(F100," refused: exception list","",0);
+ return(1);
+ }
+
+ /* File is not in exception list */
+
+ if (!cmarg2) /* No core dumps please */
+ cmarg2 = "";
+ debug(F110,"rcvfil cmarg2",cmarg2,0);
+
+ if (*cmarg2) { /* Check for alternate name */
+#ifndef NOSPL
+ int y; char *s; /* Pass it thru the evaluator */
+ extern int cmd_quoting;
+ if (cmd_quoting) {
+ y = MAXRP;
+ ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* for \v(filename) */
+ s = (char *)srvcmd;
+ zzstring(cmarg2,&s,&y);
+ } else
+ *srvcmd = NUL;
+ if (!*srvcmd) /* If we got something */
+#endif /* NOSPL */
+ ckstrncpy((char *)srvcmd,cmarg2,srvcmdlen);
+ }
+ debug(F110,"rcvfil srvcmd 2",srvcmd,0);
+
+#ifdef PIPESEND
+ /* If it starts with "bang", it's a pipe, not a file. */
+ if (usepipes && protocol == PROTO_K && *srvcmd == '!' && !rcvfilter) {
+ CHAR *s;
+ s = srvcmd+1; /* srvcmd[] is not a pointer. */
+ while (*s) { /* So we have to slide the contents */
+ *(s-1) = *s; /* over 1 space to the left. */
+ s++;
+ }
+ *(s-1) = NUL;
+ pipesend = 1;
+ }
+#endif /* PIPESEND */
+
+#ifdef COMMENT
+/*
+ This is commented out because we need to know whether the name we are
+ using was specified by the local user as an override, or came from the
+ incoming packet. In the former case, we don't do stuff to it (like
+ strip the pathname) that we might do to it in the latter.
+*/
+ cmarg2 = ""; /* Done with alternate name */
+#endif /* COMMENT */
+
+ if ((int)strlen((char *)srvcmd) > CKMAXPATH) /* Watch out for overflow */
+ *(srvcmd + CKMAXPATH - 1) = NUL;
+
+ /* At this point, srvcmd[] contains the incoming filename or as-name. */
+ /* So NOW we check for write access. */
+
+ if (zchko((char *)srvcmd) < 0) { /* Precheck for write access */
+ debug(F110,"rcvfil access denied",srvcmd,0);
+ rf_err = "Write access denied";
+ discard = opnerr = 1;
+ return(0);
+ }
+ xxscreen(SCR_FN,0,0l,(char *)srvcmd); /* Put it on screen if local */
+ debug(F110,"rcvfil srvcmd 1",srvcmd,0);
+ tlog(F110,"Receiving",(char *)srvcmd,0L); /* Transaction log entry */
+
+#ifdef CK_LABELED
+#ifdef VMS
+/*
+ If we have an as-name, this overrides the internal name if we are doing
+ a labeled-mode transfer.
+*/
+ if (*cmarg2) {
+ extern int lf_opts;
+ lf_opts &= ~LBL_NAM;
+ }
+#endif /* VMS */
+#endif /* CK_LABELED */
+
+ debug(F111,"rcvfil pipesend",srvcmd,pipesend);
+
+#ifdef PIPESEND
+ /* Skip all the filename manipulation and collision actions */
+ if (pipesend) {
+ dirflg = 0;
+ ofn1[0] = '!';
+ ckstrncpy(&ofn1[1],(char *)srvcmd,CKMAXPATH+1);
+ ckstrncpy(n,ofn1,CKMAXPATH+1);
+ ckstrncpy(fspec,ofn1,CKMAXPATH+1);
+ makestr(&prfspec,fspec); /* New preliminary filename */
+ debug(F110,"rcvfil pipesend",ofn1,0);
+ goto rcvfilx;
+ }
+#endif /* PIPESEND */
+/*
+ This is to avoid passing email subjects through nzrtol().
+ We haven't yet received the A packet so we don't yet know it's e-mail,
+ so in fact we go ahead and convert it anyway, but later we get the
+ original back from ofilnam[].
+*/
+ dispos = 0;
+ ckstrncpy(ofilnam,(char *)srvcmd,CKMAXPATH+1);
+
+#ifdef NZLTOR
+ if (*cmarg2)
+ ckstrncpy((char *)ofn1,(char *)srvcmd,CKMAXPATH+1);
+ else
+ nzrtol((char *)srvcmd, /* Filename from packet */
+ (char *)ofn1, /* Where to put result */
+ fncnv, /* Filename conversion */
+ fnrpath, /* Pathname handling */
+ CKMAXPATH /* Size of result buffer */
+ );
+#else
+ debug(F101,"rcvfil fnrpath","",fnrpath); /* Handle pathnames */
+ if (fnrpath == PATH_OFF && !*cmarg2) { /* RECEIVE PATHNAMES OFF? */
+ char *t; /* Yes. */
+ zstrip((char *)srvcmd,&t); /* If there is a pathname, strip it */
+ debug(F110,"rcvfil PATH_OFF zstrip",t,0);
+ if (!t) /* Be sure we didn't strip too much */
+ sprintf(ofn1,"FILE%02ld",filcnt);
+ else if (*t == '\0')
+ sprintf(ofn1,"FILE%02ld",filcnt);
+ else
+ ckstrncpy(ofn1,t,CKMAXPATH+1);
+ ckstrncpy((char *)srvcmd,ofn1,srvcmdlen); /* Now copy it back. */
+ }
+/*
+ SET RECEIVE PATHNAMES RELATIVE...
+ The following doesn't belong here but doing it right would require
+ defining and implementing a new file routine for all ck?fio.c modules.
+ So for now...
+*/
+#ifdef UNIXOROSK
+ else if (fnrpath == PATH_REL && !*cmarg2) {
+ if (isabsolute((char *)srvcmd)) {
+ ofn1[0] = '.';
+ ckstrncpy(&of1n[1],(char *)srvcmd,CKMAXPATH+1);
+ ckstrncpy((char *)srvcmd,ofn1,srvcmdlen);
+ debug(F110,"rcvfil PATH_REL",ofn1,0);
+ }
+ }
+#else
+#ifdef OS2
+ else if (fnrpath == PATH_REL && !*cmarg2) {
+ if (isabsolute((char *)srvcmd)) {
+ char *p = (char *)srvcmd;
+ if (isalpha(*p) && *(p+1) == ':')
+ p += 2;
+ if (*p == '\\' || *p == '/')
+ p++;
+ ckstrncpy(ofn1,p,CKMAXPATH+1);
+ ckstrncpy((char *)srvcmd,ofn1,srvcmdlen);
+ debug(F110,"rcvfil OS2 PATH_REL",ofn1,0);
+ }
+ }
+#endif /* OS2 */
+#endif /* UNIXOROSK */
+
+ /* Now srvcmd contains incoming filename with path possibly stripped */
+
+ if (fncnv) /* FILE NAMES CONVERTED? */
+ zrtol((char *)srvcmd,(char *)ofn1); /* Yes, convert to local form */
+ else
+ ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* No, copy literally. */
+#endif /* NZLTOR */
+
+#ifdef PIPESEND
+ if (rcvfilter) {
+ char * p = NULL, * q;
+ int nn = MAXRP;
+ pipesend = 1;
+ debug(F110,"rcvfil rcvfilter ",rcvfilter,0);
+#ifndef NOSPL
+ if ((p = (char *) malloc(nn + 1))) {
+ q = p;
+#ifdef COMMENT
+ /* We have already processed srvcmd and placed it into ofn1 */
+ ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* For \v(filename) */
+#endif /* COMMENT */
+ debug(F110,"rcvfile pipesend filter",rcvfilter,0);
+ zzstring(rcvfilter,&p,&nn);
+ debug(F111,"rcvfil pipename",q,nn);
+ if (nn <= 0) {
+ printf(
+ "?Sorry, receive filter + filename too long, %d max.\n",
+ CKMAXPATH
+ );
+ rf_err = "Name too long";
+ free(q);
+ return(0);
+ }
+ ckstrncpy((char *)srvcmd,q,MAXRP);
+ free(q);
+ }
+#endif /* NOSPL */
+ }
+#endif /* PIPESEND */
+
+ /* Now the incoming filename, possibly converted, is in ofn1[]. */
+
+#ifdef OS2
+ /* Don't refuse the file just because the name is illegal. */
+ if (!IsFileNameValid(ofn1)) { /* Name is OK for OS/2? */
+#ifdef OS2ONLY
+ char *zs = NULL;
+ zstrip(ofn1, &zs); /* Not valid, strip unconditionally */
+ if (zs) {
+ if (iattr.longname.len && /* Free previous longname, if any */
+ iattr.longname.val)
+ free(iattr.longname.val);
+ iattr.longname.len = strlen(zs); /* Store in attribute structure */
+ iattr.longname.val = (char *) malloc(iattr.longname.len + 1);
+ if (iattr.longname.val) /* Remember this (illegal) name */
+ strcpy(iattr.longname.val, zs); /* safe */
+ }
+#endif /* OS2ONLY */
+ debug(F110,"rcvfil: invalid file name",ofn1,0);
+ ChangeNameForFAT(ofn1); /* Change to an acceptable name */
+ debug(F110,"rcvfil: FAT file name",ofn1,0);
+
+ } else { /* Name is OK. */
+
+ debug(F110,"rcvfil: valid file name",ofn1,0);
+#ifdef OS2ONLY
+ if (iattr.longname.len &&
+ iattr.longname.val) /* Free previous longname, if any */
+ free(iattr.longname.val);
+ iattr.longname.len = 0;
+ iattr.longname.val = NULL; /* This file doesn't need a longname */
+#endif /* OS2ONLY */
+ }
+#endif /* OS2 */
+ debug(F110,"rcvfil as",ofn1,0);
+
+/* Filename collision action section. */
+
+ dirflg = /* Is it a directory name? */
+#ifdef CK_TMPDIR
+ isdir(ofn1)
+#else
+ 0
+#endif /* CK_TMPDIR */
+ ;
+ debug(F101,"rcvfil dirflg","",dirflg);
+ ofn1len = zchki(ofn1); /* File already exists? */
+ debug(F111,"rcvfil ofn1len",ofn1,ofn1len);
+ ofn1x = (ofn1len != -1);
+
+ if ( (
+#ifdef UNIX
+ strcmp(ofn1,"/dev/null") && /* It's not the null device? */
+#else
+#ifdef OSK
+ strcmp(ofn1,"/nil") &&
+#endif /* OSK */
+#endif /* UNIX */
+ !stdouf ) && /* Not copying to standard output? */
+ ofn1x || /* File of same name exists? */
+ dirflg ) { /* Or file is a directory? */
+ debug(F111,"rcvfil exists",ofn1,fncact);
+#ifdef CK_PERMS
+ ofperms = zgperm((char *)ofn1); /* Get old file's permissions */
+ debug(F110,"rcvfil perms",ofperms,0);
+#endif /* CK_PERMS */
+
+ debug(F101,"rcvfil fncact","",fncact);
+ switch (fncact) { /* Yes, do what user said. */
+ case XYFX_A: /* Append */
+ ofperms = "";
+ debug(F100,"rcvfil append","",0);
+ if (dirflg) {
+ rf_err = "Can't append to a directory";
+ tlog(F100," error - can't append to directory","",0);
+ discard = opnerr = 1;
+ return(0);
+ }
+ tlog(F110," appending to",ofn1,0);
+ break;
+#ifdef COMMENT
+ case XYFX_Q: /* Query (Ask) */
+ break; /* not implemented */
+#endif /* COMMENT */
+ case XYFX_B: /* Backup (rename old file) */
+ if (dirflg) {
+ rf_err = "Can't rename existing directory";
+ tlog(F100," error - can't rename directory","",0);
+ discard = opnerr = 1;
+ return(0);
+ }
+ znewn(ofn1,&ofn2); /* Get new unique name */
+ tlog(F110," backup:",ofn2,0);
+ debug(F110,"rcvfil backup ofn1",ofn1,0);
+ debug(F110,"rcvfil backup ofn2",ofn2,0);
+#ifdef CK_LABELED
+#ifdef OS2ONLY
+/*
+ In case this is a FAT file system, we can't change only the FAT name, we
+ also have to change the longname from the extended attributes block.
+ Otherwise, we'll have many files with the same longname and if we copy them
+ to an HPFS volume, only one will survive.
+*/
+ if (os2getlongname(ofn1, &longname) > -1) {
+ if (strlen(longname)) {
+ char tmp[10];
+ extern int ck_znewn;
+ sprintf(tmp,".~%d~",ck_znewn);
+ newlongname =
+ (char *) malloc(strlen(longname) + strlen(tmp) + 1);
+ if (newlongname) {
+ strcpy(newlongname, longname); /* safe (prechecked) */
+ strcat(newlongname, tmp); /* safe (prechecked) */
+ os2setlongname(ofn1, newlongname);
+ free(newlongname);
+ newlongname = NULL;
+ }
+ }
+ } else debug(F100,"rcvfil os2getlongname failed","",0);
+#endif /* OS2ONLY */
+#endif /* CK_LABELED */
+
+#ifdef COMMENT
+ /* Do this later, in opena()... */
+ if (zrename(ofn1,ofn2) < 0) {
+ rf_err = "Can't transform filename";
+ debug(F110,"rcvfil rename fails",ofn1,0);
+ discard = opnerr = 1;
+ return(0);
+ }
+#endif /* COMMENT */
+ break;
+
+ case XYFX_D: /* Discard (refuse new file) */
+ ofperms = "";
+ discard = 1;
+ rejection = 1; /* Horrible hack: reason = name */
+ debug(F101,"rcvfil discard","",discard);
+ tlog(F100," refused: name","",0);
+ break;
+
+ case XYFX_R: /* Rename incoming file */
+ znewn(ofn1,&ofn2); /* Make new name for it */
+#ifdef OS2ONLY
+ if (iattr.longname.len) {
+ char tmp[10];
+ extern int ck_znewn;
+ sprintf(tmp,".~%d~",ck_znewn);
+ newlongname =
+ (char *) malloc(iattr.longname.len + strlen(tmp) + 1);
+ if (newlongname) {
+ strcpy(newlongname, iattr.longname.val); /* safe */
+ strcat(newlongname, tmp); /* safe */
+ debug(F110,
+ "Rename Incoming: newlongname",newlongname,0);
+ if (iattr.longname.len &&
+ iattr.longname.val)
+ free(iattr.longname.val);
+ iattr.longname.len = strlen(newlongname);
+ iattr.longname.val = newlongname;
+ /* free(newlongname) here ? */
+ }
+ }
+#endif /* OS2ONLY */
+ break;
+ case XYFX_X: /* Replace old file */
+ debug(F100,"rcvfil overwrite","",0);
+ if (dirflg) {
+ rf_err = "Can't overwrite existing directory";
+ tlog(F100," error - can't overwrite directory","",0);
+ discard = opnerr = 1;
+#ifdef COMMENT
+ return(0);
+#else
+ break;
+#endif /* COMMENT */
+ }
+ tlog(F110,"overwriting",ofn1,0);
+ break;
+ case XYFX_U: /* Refuse if older */
+ debug(F110,"rcvfil update",ofn1,0);
+ if (dirflg) {
+ rf_err = "File has same name as existing directory";
+ tlog(F110," error - directory exists:",ofn1,0);
+ discard = opnerr = 1;
+#ifdef COMMENT
+ /* Don't send an error packet, just refuse the file */
+ return(0);
+#endif /* COMMENT */
+ }
+ break; /* Not here, we don't have */
+ /* the attribute packet yet. */
+ default:
+ ofperms = "";
+ debug(F101,"rcvfil bad collision action","",fncact);
+ break;
+ }
+ }
+ debug(F110,"rcvfil ofn1",ofn1,0);
+ debug(F110,"rcvfil ofn2",ofn2,0);
+ debug(F110,"rcvfil ofperms",ofperms,0);
+ if (fncact == XYFX_R && ofn1x && ofn2) { /* Renaming incoming file? */
+ xxscreen(SCR_AN,0,0l,ofn2); /* Display renamed name */
+ ckstrncpy(n, ofn2, CKMAXPATH+1); /* Return it */
+ } else { /* No */
+ xxscreen(SCR_AN,0,0l,ofn1); /* Display regular name */
+ ckstrncpy(n, ofn1, CKMAXPATH+1); /* and return it. */
+ }
+
+#ifdef CK_MKDIR
+/* Create directory(s) if necessary. */
+ if (!discard && fnrpath != PATH_OFF) { /* RECEIVE PATHAMES ON? */
+ int x;
+ debug(F110,"rcvfil calling zmkdir",ofn1,0); /* Yes */
+ if ((x = zmkdir(ofn1)) < 0) {
+ debug(F100,"zmkdir fails","",0);
+ tlog(F110," error - directory creation failure:",ofn1,0);
+ rf_err = "Directory creation failure.";
+ discard = 1;
+ return(0);
+ }
+#ifdef TLOG
+ else if (x > 0)
+ tlog(F110," path created:",ofn1,0);
+#endif /* TLOG */
+ }
+#else
+ debug(F110,"rcvfil CK_MKDIR not defined",ofn1,0);
+#endif /* CK_MKDIR */
+
+ if (calibrate)
+ ckstrncpy(fspec,ofn1,CKMAXPATH+1);
+ else
+ zfnqfp(ofn1,fspeclen,fspec);
+ debug(F110,"rcvfil fspec",fspec,0);
+
+#ifdef COMMENT
+ /* See comments with VMS zfnqfp()... */
+#ifdef VMS
+ /* zfnqfp() does not return the version number */
+ if (!calibrate) {
+ int x, v;
+ x = strlen(ofn1);
+ if (x > 0) {
+ if (ofn1[x-1] == ';') {
+ v = getvnum(ofn1);
+ ckstrncpy(&ofn1[x],ckitoa(v),CKMAXPATH-x);
+ }
+ }
+ }
+#endif /* VMS */
+#endif /* COMMENT */
+ fspec[fspeclen] = NUL;
+ makestr(&prfspec,fspec); /* New preliminary filename */
+
+#ifdef PIPESEND
+ rcvfilx:
+#endif /* PIPESEND */
+
+ debug(F110,"rcvfilx: n",n,0);
+ debug(F110,"rcvfilx: ofn1",ofn1,0);
+ ffc = 0L; /* Init per-file counters */
+ cps = oldcps = 0L;
+ rs_len = 0L;
+ rejection = -1;
+ fsecs = gtimer(); /* Time this file started */
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+ debug(F101,"rcvfil fpfsecs","",fpfsecs);
+#endif /* GFTIMER */
+ filcnt++;
+ intmsg(filcnt);
+ return(1); /* Successful return */
+}
+
+
+/* R E O F -- Receive End Of File packet for incoming file */
+
+/*
+ Closes the received file.
+ Returns:
+ 0 on success.
+ -1 if file could not be closed.
+ 2 if disposition was mail, mail was sent, but temp file not deleted.
+ 3 if disposition was print, file was printed, but not deleted.
+ -2 if disposition was mail and mail could not be sent
+ -3 if disposition was print and file could not be printed
+ -4 if MOVE-TO: failed
+ -5 if RENAME-TO: failed
+*/
+int
+reof(f,yy) char *f; struct zattr *yy; {
+ extern char * rcv_move, * rcv_rename;
+ extern int o_isopen;
+ int rc = 0; /* Return code */
+ char *p;
+ char c;
+
+ debug(F111,"reof fncact",f,fncact);
+ debug(F101,"reof discard","",discard);
+ success = 1; /* Assume status is OK */
+ lsstate = 0; /* Cancel locking-shift state */
+ if (discard) { /* Handle attribute refusals, etc. */
+ debug(F101,"reof discarding","",0);
+ success = 0; /* Status = failed. */
+ if (rejection == '#' || /* Unless rejection reason is */
+ rejection == 1 || /* date or name (SET FILE COLLISION */
+ rejection == '?') /* UPDATE or DISCARD) */
+ success = 1;
+ debug(F101,"reof success","",success);
+ filrej++; /* Count this rejection. */
+ discard = 0; /* We never opened the file, */
+ return(0); /* so we don't close it. */
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"reof cxseen","",cxseen);
+ debug(F101,"reof czseen","",czseen);
+ debug(F110,"reof rdatap",rdatap,0);
+ }
+#endif /* DEBUG */
+
+ if (cxseen == 0) /* Got cancel directive? */
+ cxseen = (*rdatap == 'D');
+ if (cxseen || czseen) /* (for hints) */
+ interrupted = 1;
+ success = (cxseen || czseen) ? 0 : 1; /* Set SUCCESS flag appropriately */
+ if (!success) /* "Uncount" this file */
+ filrej++;
+ debug(F101,"reof o_isopen","",o_isopen);
+
+ if (o_isopen) { /* If an output file was open... */
+
+#ifdef CK_CTRLZ
+ if (success) {
+ debug(F101,"reof lastchar","",lastchar);
+ if (!binary && eofmethod == XYEOF_Z && lastchar != 26 &&
+ (!xflg || (xflg && remfile)))
+ pnbyte((char)26,putfil);
+ }
+#endif /* CK_CTRLZ */
+
+ rc = clsof(cxseen || czseen); /* Close the file (resets cxseen) */
+ debug(F101,"reof closf","",rc);
+ if (rc < 0) { /* If failure to close, FAIL */
+ if (success) filrej++;
+ success = 0;
+ }
+ if (!calibrate) {
+ /* Set file modification date and/or permissions */
+ if (success)
+ zstime(f,yy,0);
+#ifdef OS2ONLY
+#ifdef CK_LABELED
+ if (success && yy->longname.len)
+ os2setlongname(f, yy->longname.val);
+#endif /* CK_LABELED */
+#endif /* OS2ONLY */
+ }
+ if (success == 0) { /* And program return code */
+ xitsta |= W_RECV;
+ } else if (success == 1) { /* File rec'd successfully */
+ makestr(&rrfspec,prrfspec); /* Record it for wheremsg */
+ makestr(&rfspec,prfspec);
+ }
+
+/* Handle dispositions from attribute packet... */
+
+ c = NUL;
+#ifndef NOFRILLS
+ if (!calibrate && yy->disp.len != 0) {
+ p = yy->disp.val;
+ c = *p++;
+#ifndef UNIX
+/*
+ See ckcpro.w. In UNIX we don't use temp files any more -- we pipe the
+ stuff right into mail or lpr.
+*/
+ if (c == 'M') { /* Mail to user. */
+ rc = zmail(p,filnam); /* Do the system's mail command */
+ if (rc < 0) success = 0; /* Remember status */
+ tlog(F110,"mailed",filnam,0L);
+ tlog(F110," to",p,0L);
+ zdelet(filnam); /* Delete the file */
+ } else if (c == 'P') { /* Print the file. */
+ rc = zprint(p,filnam); /* Do the system's print command */
+ if (rc < 0) success = 0; /* Remember status */
+ tlog(F110,"printed",filnam,0L);
+ tlog(F110," with options",p,0L);
+#ifndef VMS
+#ifndef STRATUS
+ /* spooler deletes file after print complete in VOS & VMS */
+ if (zdelet(filnam) && rc == 0) rc = 3; /* Delete the file */
+#endif /* STRATUS */
+#endif /* VMS */
+ }
+#endif /* UNIX */
+ }
+#endif /* NOFRILLS */
+
+ if (success &&
+ !pipesend &&
+ !calibrate && c != 'M' && c != 'P') {
+ if (rcv_move) { /* If /MOVE-TO was given... */
+ char * p = rcv_move;
+#ifdef COMMENT
+/* No need for this - it's a directory name */
+ char tmpbuf[CKMAXPATH+1];
+ extern int cmd_quoting; /* for \v(filename) */
+ if (cmd_quoting) { /* But only if cmd_quoting is on */
+ int n;
+ n = CKMAXPATH;
+ p = tmpbuf;
+ debug(F111,"reof /move-to",rcv_move,0);
+ zzstring(rcv_move,&p,&n);
+ p = tmpbuf;
+ }
+#endif /* COMMENT */
+/*
+ Here we could create the directory if it didn't exist (and it was relative)
+ but there would have to be a user-settable option to say whether to do this.
+*/
+ rc = zrename(filnam,p);
+ debug(F111,"reof MOVE zrename",rcv_move,rc);
+ if (rc > -1) {
+ tlog(F110," moving received file to",rcv_move,0);
+ } else {
+ rc = -4;
+ tlog(F110," FAILED to move received file to",rcv_move,0);
+ }
+ } else if (rcv_rename) { /* Or /RENAME-TO: */
+ char *s = rcv_rename; /* This is the renaming string */
+#ifndef NOSPL
+ char tmpnam[CKMAXPATH+16];
+ extern int cmd_quoting; /* for \v(filename) */
+ if (cmd_quoting) { /* But only if cmd_quoting is on */
+ int n; /* Pass it thru the evaluator */
+ n = CKMAXPATH;
+ s = (char *)tmpnam;
+ zzstring(rcv_rename,&s,&n);
+ s = (char *)tmpnam;
+ }
+#endif /* NOSPL */
+ if (s) if (*s) {
+ rc = zrename(filnam,s);
+ debug(F111,"reof RENAME zrename",s,rc);
+ if (rc > -1) {
+ tlog(F110," renaming received file to",s,0);
+ } else {
+ rc = -5;
+ tlog(F110," FAILED to rename received file to",s,0);
+ }
+ }
+ }
+ }
+ }
+ debug(F101,"reof success","",success);
+ debug(F101,"reof returns","",rc);
+
+ filnam[0] = NUL; /* Erase the filename */
+ return(rc);
+}
+
+/* R E O T -- Receive End Of Transaction */
+
+VOID
+reot() {
+ cxseen = czseen = discard = 0; /* Reset interruption flags */
+ tstats(); /* Finalize transfer statistics */
+}
+
+/* S F I L E -- Send File header or teXt header packet */
+
+/*
+ Call with x nonzero for X packet, zero for F packet.
+ If X == 0, filename to send is in filnam[], and if cmarg2 is not null
+ or empty, the file should be sent under this name rather than filnam[].
+ If sndfilter not NULL, it is the name of a send filter.
+ Returns 1 on success, 0 on failure.
+*/
+int
+sfile(x) int x; {
+#ifdef pdp11
+#define PKTNL 64
+#else
+#define PKTNL 256
+#endif /* pdp11 */
+ char pktnam[PKTNL+1]; /* Local copy of name */
+ char *s;
+ int rc;
+ int notafile = 0;
+ extern int filepeek;
+#ifdef PIPESEND
+ extern char * sndfilter;
+
+ if (sndfilter) {
+ pipesend = 1;
+ debug(F110,"sfile send filter ",sndfilter,0);
+ }
+#endif /* PIPESEND */
+
+ notafile = calibrate || sndarray || pipesend || x;
+ debug(F101,"sfile x","",x);
+ debug(F101,"sfile notafile","",notafile);
+
+#ifndef NOCSETS
+ if (tcs_save > -1) { /* Character sets */
+ tcharset = tcs_save;
+ tcs_save = -1;
+ debug(F101,"sfile restored tcharset","",tcharset);
+ }
+ if (fcs_save > -1) {
+ fcharset = fcs_save;
+ fcs_save = -1;
+ debug(F101,"sfile restored fcharset","",fcharset);
+ }
+ setxlatype(tcharset,fcharset); /* Translation type */
+#endif /* NOCSETS */
+
+ /* cmarg2 or filnam (with that precedence) have the file's name */
+
+ lsstate = 0; /* Cancel locking-shift state */
+#ifdef COMMENT
+ /* Do this after making sure we can open the file */
+ if (nxtpkt() < 0) return(0); /* Bump packet number, get buffer */
+#endif /* COMMENT */
+ pktnam[0] = NUL; /* Buffer for name we will send */
+ if (x == 0) { /* F-Packet setup */
+ if (!cmarg2) cmarg2 = "";
+#ifdef DEBUG
+ if (deblog) {
+ debug(F111,"sfile cmarg2",cmarg2,cmarg2);
+ debug(F101,"sfile binary 1","",binary);
+ debug(F101,"sfile wearealike","",wearealike);
+ debug(F101,"sfile xfermode","",xfermode);
+ debug(F101,"sfile filepeek","",filepeek);
+#ifndef NOCSETS
+ debug(F101,"sfile s_cset","",s_cset);
+ debug(F101,"sfile tcharset","",tcharset);
+ debug(F101,"sfile xfrxla","",xfrxla);
+#endif /* NOCSETS */
+ }
+#endif /* DEBUG */
+ if (xfermode == XMODE_A /* TRANSFER MODE AUTOMATIC */
+#ifndef NOMSEND
+ && !addlist /* And not working from a SEND-LIST */
+#endif /* NOMSEND */
+ ) {
+ /* Other Kermit is on a like system and no charset translation */
+ if (wearealike
+#ifndef NOCSETS
+ && (tcharset == TC_TRANSP || xfrxla == 0)
+#endif /* NOCSETS */
+ ) {
+#ifdef VMS
+ if (binary != XYFT_I)
+#endif /* VMS */
+#ifdef CK_LABELED
+ if (binary != XYFT_L)
+#endif /* CK_LABELED */
+ binary = XYFT_B; /* Send all files in binary mode */
+ }
+
+ /* Otherwise select transfer mode based on file info */
+
+ else if (!notafile /* but not if sending from pipe */
+#ifdef CK_LABELED
+ && binary != XYFT_L /* and not if FILE TYPE LABELED */
+#endif /* CK_LABELED */
+#ifdef VMS
+ && binary != XYFT_I /* or FILE TYPE IMAGE */
+#endif /* VMS */
+ ) {
+#ifdef UNICODE
+ fileorder = -1; /* File byte order */
+#endif /* UNICODE */
+ if (filepeek && !notafile) { /* Real file, FILE SCAN is ON */
+ int k, x;
+ k = scanfile(filnam,&x,nscanfile); /* Scan the file */
+ debug(F101,"sfile scanfile","",k);
+ switch (k) {
+ case FT_TEXT: /* Unspecified text */
+ debug(F100,"sfile scanfile text","",0);
+ binary = XYFT_T; /* SET FILE TYPE TEXT */
+ break;
+#ifndef NOCSETS
+ case FT_7BIT: /* 7-bit text */
+ binary = XYFT_T; /* SET FILE TYPE TEXT */
+ /* If SEND CHARSET-SELECTION AUTO */
+ /* and SET TRANSFER TRANSLATION is ON */
+ debug(F100,"sfile scanfile text7","",0);
+ if (s_cset == XMODE_A && xfrxla) {
+ if (fcsinfo[fcharset].size != 128) {
+ fcs_save = fcharset; /* Current FCS not 7bit */
+ fcharset = dcset7; /* Use default 7bit set */
+ debug(F101,"sfile scanfile 7 fcharset",
+ "",fcharset);
+ }
+ /* And also switch to appropriate TCS */
+ if (afcset[fcharset] > -1 &&
+ afcset[fcharset] <= MAXTCSETS) {
+ tcs_save = tcharset;
+ tcharset = afcset[fcharset];
+ debug(F101,"sfile scanfile 7 tcharset","",
+ tcharset);
+ }
+ setxlatype(tcharset,fcharset);
+ }
+ break;
+
+ case FT_8BIT: /* 8-bit text */
+ binary = XYFT_T; /* SET FILE TYPE TEXT */
+ /* If SEND CHARSET-SELEC AUTO */
+ /* and SET TRANSFER TRANSLATION is ON */
+ debug(F100,"sfile scanfile text8","",0);
+ if (s_cset == XMODE_A && xfrxla) {
+ if (fcsinfo[fcharset].size != 256) {
+ fcs_save = fcharset; /* Current FCS not 8bit */
+ fcharset = dcset8; /* Use default 8bit set */
+ debug(F101,"sfile scanfile 8 fcharset",
+ "",fcharset);
+ }
+ /* Switch to corresponding transfer charset */
+ if (afcset[fcharset] > -1 &&
+ afcset[fcharset] <= MAXTCSETS) {
+ tcs_save = tcharset;
+ tcharset = afcset[fcharset];
+ debug(F101,"sfile scanfile 8 tcharset","",
+ tcharset);
+ }
+ setxlatype(tcharset,fcharset);
+ }
+ break;
+#ifdef UNICODE
+ case FT_UTF8: /* UTF-8 text */
+ case FT_UCS2: /* UCS-2 text */
+ debug(F101,"sfile scanfile Unicode","",k);
+ binary = XYFT_T;
+ /* If SEND CHARSET-SELEC AUTO */
+ /* and SET TRANSFER TRANSLATION is ON */
+ if (s_cset == XMODE_A && xfrxla) {
+ fcs_save = fcharset;
+ tcs_save = tcharset;
+ fcharset = (k == FT_UCS2) ? FC_UCS2 : FC_UTF8;
+ if (k == FT_UCS2 && x > -1)
+ fileorder = x;
+ }
+ /* Switch to associated transfer charset if any */
+ if (afcset[fcharset] > -1 &&
+ afcset[fcharset] <= MAXTCSETS)
+ tcharset = afcset[fcharset];
+ if (tcharset == TC_TRANSP) /* If none */
+ tcharset = TC_UTF8; /* use UTF-8 */
+ setxlatype(tcharset,fcharset);
+ debug(F101,"sfile Unicode tcharset","",tcharset);
+ break;
+#endif /* UNICODE */
+#endif /* NOCSETS */
+ case FT_BIN:
+ debug(F101,"sfile scanfile binary","",k);
+ binary = XYFT_B;
+ break;
+ /* Default: Don't change anything */
+ }
+ }
+ }
+ debug(F101,"sfile binary 2","",binary);
+ debug(F101,"sfile sendmode","",sendmode);
+ }
+ if (*cmarg2) { /* If we have a send-as name... */
+ int y; char *s;
+#ifndef NOSPL /* and a script programming language */
+ extern int cmd_quoting;
+ if (cmd_quoting) { /* and it's not turned off */
+ y = PKTNL; /* pass as-name thru the evaluator */
+ s = pktnam;
+ zzstring(cmarg2,&s,&y);
+#ifdef COMMENT
+/* This ruins macros like BSEND */
+ if (!pktnam[0]) /* and make sure result is not empty */
+ sprintf(pktnam,"FILE%02ld",filcnt);
+#endif /* COMMENT */
+ } else
+#endif /* NOSPL */
+ ckstrncpy(pktnam,cmarg2,PKTNL); /* copy it literally, */
+
+ debug(F110,"sfile pktnam",pktnam,0);
+#ifdef COMMENT
+/* We don't do this any more because now we have filename templates */
+ cmarg2 = ""; /* and blank it out for next time. */
+#endif /* COMMENT */
+ }
+ if (!*pktnam) { /* No as-name... */
+#ifdef NZLTOR
+ int xfncnv, xpath;
+ debug(F101,"sfile fnspath","",fnspath);
+ debug(F101,"sfile fncnv","",fncnv);
+ xfncnv = fncnv;
+ xpath = fnspath;
+ if (notafile) { /* If not an actual file */
+ xfncnv = 0; /* Don't convert name */
+ xpath = PATH_OFF; /* Leave path off */
+ } else if (xfncnv &&
+ (!strcmp(whoareu,"U1") || !strcmp(whoareu,"UN"))
+ ) {
+ /* Less-strict conversion if partner is UNIX or Win32 */
+ xfncnv = -1;
+ }
+ debug(F101,"sfile xpath","",xpath);
+ debug(F101,"sfile xfncnv","",xfncnv);
+ nzltor(filnam,pktnam,xfncnv,xpath,PKTNL);
+
+#else /* Not NZLTOR */
+
+ debug(F101,"sfile fnspath","",fnspath);
+ if (fnspath == PATH_OFF /* Stripping path names? */
+ && (!notafile) /* (of actual files) */
+ ) {
+ char *t; /* Yes. */
+ zstrip(filnam,&t); /* Strip off the path. */
+ debug(F110,"sfile zstrip",t,0);
+ if (!t) t = "UNKNOWN"; /* Be cautious... */
+ else if (*t == '\0')
+ t = "UNKNOWN";
+ ckstrncpy(pktnam,t,PKTNL); /* Copy stripped name literally. */
+ } else if (fnspath == PATH_ABS && !notafile) {
+ /* Converting to absolute form */
+ zfnqfp(filnam,PKTNL,pktnam);
+ } else
+ ckstrncpy(pktnam,filnam,PKTNL);
+
+ /* pktnam[] has the packet name, filnam[] has the original name. */
+ /* But we still need to convert pktnam if FILE NAMES CONVERTED. */
+
+ debug(F101,"sfile fncnv","",fncnv);
+ if (fncnv && !notafile) { /* If converting names of files */
+ zltor(pktnam,(char *)srvcmd); /* convert it to common form, */
+ ckstrncpy(pktnam,(char *)srvcmd,PKTNL);
+ *srvcmd = NUL;
+ }
+#endif /* NZLTOR */
+ }
+ if (!*pktnam) /* Failsafe... */
+ sprintf(pktnam,"FILE%02ld",filcnt);
+ debug(F110,"sfile filnam 1",filnam,0);
+ debug(F110,"sfile pktnam 1",pktnam,0);
+#ifdef PIPESEND
+/* If we have a send filter, substitute the current filename into it */
+
+ if (sndfilter) {
+ char * p = NULL, * q;
+ int n = CKMAXPATH;
+#ifndef NOSPL
+ if ((p = (char *) malloc(n + 1))) {
+ q = p;
+ debug(F110,"sfile pipesend filter",sndfilter,0);
+ zzstring(sndfilter,&p,&n);
+ debug(F111,"sfile pipename",q,n);
+ if (n <= 0) {
+ printf(
+ "?Sorry, send filter + filename too long, %d max.\n",
+ CKMAXPATH
+ );
+ free(q);
+ return(0);
+ }
+ ckstrncpy(filnam,q,CKMAXPATH+1);
+ free(q);
+ }
+#endif /* NOSPL */
+ }
+#endif /* PIPESEND */
+
+ debug(F110,"sfile filnam 2",filnam,0); /* Log debugging info */
+ debug(F110,"sfile pktnam 2",pktnam,0);
+ if (openi(filnam) == 0) /* Try to open the input file */
+ return(0);
+
+#ifdef CK_RESEND
+/*
+ The following check is done after openi() is called, since openi() itself
+ can change the transfer mode (as in VMS).
+*/
+ if ((binary == XYFT_T
+#ifdef VMS
+ || binary == XYFT_L
+#endif /* VMS */
+ ) && sendmode == SM_RESEND) {
+ /* Trying to RESEND/REGET a file first sent in TEXT mode. */
+ debug(F111,"sfile error - Recover vs Text",filnam,binary);
+ /* Set appropriate error messages and make log entries here */
+#ifdef VMS
+ if (binary == XYFT_L)
+ ckmakmsg((char *)epktmsg,
+ PKTMSGLEN,
+ "Recovery attempted in LABELED mode: ",
+ filnam,
+ NULL,
+ NULL
+ );
+ else
+#endif /* VMS */
+ ckmakmsg((char *)epktmsg,
+ PKTMSGLEN,
+ "Recovery attempted in TEXT mode: ",
+ filnam,
+ NULL,
+ NULL
+ );
+ return(0);
+ }
+ if (sendmode == SM_PSEND) /* PSENDing? */
+ if (sendstart > 0L) /* Starting position */
+ if (zfseek(sendstart) < 0) /* seek to it... */
+ return(0);
+#endif /* CK_RESEND */
+ s = pktnam; /* Name for packet data field */
+#ifdef OS2
+ /* Never send a disk letter. */
+ if (isalpha(*s) && (*(s+1) == ':'))
+ s += 2;
+#endif /* OS2 */
+
+ } else { /* X-packet setup, not F-packet. */
+ binary = XYFT_T; /* Text always */
+ debug(F110,"sfile X packet",cmdstr,0); /* Log debugging info */
+ s = cmdstr; /* Name for data field */
+ }
+
+ /* Now s points to the string that goes in the packet data field. */
+
+ debug(F101,"sfile binary","",binary); /* Log debugging info */
+ encstr((CHAR *)s); /* Encode the name. */
+ /* Send the F or X packet */
+ /* If the encoded string did not fit into the packet, it was truncated. */
+
+ if (nxtpkt() < 0) return(0); /* Bump packet number, get buffer */
+
+ rc = spack((char) (x ? 'X' : 'F'), pktnum, size, data);
+ if (rc < 0)
+ return(rc);
+
+#ifndef NOCSETS
+ setxlatype(tcharset,fcharset); /* Set up charset translations */
+#endif /* NOCSETS */
+
+ if (x == 0) { /* Display for F packet */
+ if (displa) { /* Screen */
+ xxscreen(SCR_FN,'F',(long)pktnum,filnam);
+ xxscreen(SCR_AN,0,0L,pktnam);
+ xxscreen(SCR_FS,0,calibrate ? calibrate : fsize,"");
+ }
+#ifdef pdp11
+ tlog(F110,"Sending",filnam,0L); /* Transaction log entry */
+ makestr(&psfspec,filnam); /* New filename */
+#else
+#ifndef ZFNQFP
+ tlog(F110,"Sending",filnam,0L);
+ makestr(&psfspec,filnam); /* New filename */
+#else
+ if (notafile) { /* If not a file log simple name */
+ tlog(F110,"Sending", filnam, 0L);
+ } else { /* Log fully qualified filename */
+#ifdef COMMENT
+ /* This section generates bad code in SCO 3.2v5.0.5's cc */
+ char *p = NULL, *q = filnam;
+ debug(F101,"sfile CKMAXPATH","",CKMAXPATH);
+ if ((p = malloc(CKMAXPATH+1))) {
+ debug(F111,"sfile calling zfnqfp",filnam,strlen(filnam));
+ if (zfnqfp(filnam, CKMAXPATH, p)) {
+ debug(F111,"sfile zfnqfp ok",p,strlen(p));
+ q = p;
+ }
+ }
+#else
+ char tmpbuf[CKMAXPATH+1];
+ char *p = tmpbuf, *q = filnam;
+ if (zfnqfp(filnam, CKMAXPATH, p))
+ q = p;
+#endif /* COMMENT */
+ debug(F111,"sfile q",q,strlen(q));
+ tlog(F110,"Sending",q,0L);
+ makestr(&psfspec,q); /* New preliminary filename */
+#ifdef COMMENT
+ if (p) free(p);
+#endif /* COMMENT */
+ }
+#endif /* ZFNQFP */
+#endif /* pdp11 */
+ tlog(F110," as",pktnam,0L);
+ if (binary) { /* Log file mode in transaction log */
+ tlog(F101," mode: binary","",(long) binary);
+ } else { /* If text mode, check character set */
+ tlog(F100," mode: text","",0L);
+#ifndef NOCSETS
+ if (tcharset == TC_TRANSP || xfrxla == 0) {
+ tlog(F110," character set","transparent",0L);
+ } else {
+ tlog(F110," xfer character set",tcsinfo[tcharset].name,0L);
+ tlog(F110," file character set",fcsinfo[fcharset].name,0L);
+ }
+#endif /* NOCSETS */
+ }
+ } else { /* Display for X-packet */
+
+ xxscreen(SCR_XD,'X',(long)pktnum,cmdstr); /* Screen */
+ tlog(F110,"Sending from:",cmdstr,0L); /* Transaction log */
+ }
+ intmsg(++filcnt); /* Count file, give interrupt msg */
+ first = 1; /* Init file character lookahead. */
+ ffc = 0L; /* Init file character counter. */
+ cps = oldcps = 0L; /* Init cps statistics */
+ rejection = -1;
+ fsecs = gtimer(); /* Time this file started */
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+ debug(F101,"SFILE fpfsecs","",fpfsecs);
+#endif /* GFTIMER */
+ debug(F101,"SFILE fsecs","",fsecs);
+ return(1);
+}
+
+/* S D A T A -- Send a data packet */
+
+/*
+ Returns -1 if no data to send (end of file), -2 if connection is broken.
+ If there is data, a data packet is sent, and sdata() returns 1.
+
+ In the streaming case, the window is regarded as infinite and we keep
+ sending data packets until EOF or there appears to be a Kermit packet on the
+ reverse channel. When not streaming and the window size is greater than 1,
+ we keep sending data packets until window is full or characters start to
+ appear from the other Kermit.
+
+ In the windowing or streaming case, when there is no more data left to send
+ (or when sending has been interrupted), sdata() does nothing and returns 0
+ each time it is called until the acknowledgement sequence number catches up
+ to the last data packet that was sent.
+*/
+int
+sdata() {
+ int i, x, len;
+ char * s;
+
+ debug(F101,"sdata entry, first","",first);
+ debug(F101,"sdata drain","",drain);
+/*
+ The "drain" flag is used with window size > 1. It means we have sent
+ our last data packet. If called and drain is not zero, then we return
+ 0 as if we had sent an empty data packet, until all data packets have
+ been ACK'd, then then we can finally return -1 indicating EOF, so that
+ the protocol can switch to seof state. This is a kludge, but at least
+ it's localized...
+*/
+ if (first == 1) drain = 0; /* Start of file, init drain flag. */
+
+ if (drain) { /* If draining... */
+ debug(F101,"sdata draining, winlo","",winlo);
+ if (winlo == pktnum) /* If all data packets are ACK'd */
+ return(-1); /* return EOF indication */
+ else /* otherwise */
+ return(0); /* pretend we sent a data packet. */
+ }
+ debug(F101,"sdata sbufnum","",sbufnum);
+ for (i = sbufnum;
+ i > 0
+#ifdef STREAMING
+ || streaming
+#endif /* STREAMING */
+ ;
+ i--) {
+ if (i < 0)
+ break;
+ debug(F101,"sdata countdown","",i);
+#ifdef STREAMING
+ if (streaming) {
+ pktnum = (pktnum + 1) % 64;
+ winlo = pktnum;
+ debug(F101,"sdata streaming pktnum","",pktnum);
+ } else {
+#endif /* STREAMING */
+ x = nxtpkt(); /* Get next pkt number and buffer */
+ debug(F101,"sdata nxtpkt pktnum","",pktnum);
+ if (x < 0) return(0);
+#ifdef STREAMING
+ }
+#endif /* STREAMING */
+ debug(F101,"sdata packet","",pktnum);
+ if (chkint() < 0) /* Especially important if streaming */
+ return(-9);
+ if (cxseen || czseen) { /* If interrupted, done. */
+ if (wslots > 1) {
+ drain = 1;
+ debug(F100,"sdata cx/zseen windowing","",0);
+ return(0);
+ } else {
+ debug(F100,"sdata cx/zseen nonwindowing","",0);
+ return(-1);
+ }
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"sdata spsiz","",spsiz);
+ debug(F101,"sdata binary","",binary);
+ debug(F101,"sdata parity","",parity);
+ }
+#endif /* DEBUG */
+#ifdef CKTUNING
+ if (binary && !parity && !memstr && !funcstr)
+ len = bgetpkt(spsiz);
+ else
+ len = getpkt(spsiz,1);
+#else
+ len = getpkt(spsiz,1);
+#endif /* CKTUNING */
+ s = (char *)data;
+ if (len == -3) { /* Timed out (e.g.reading from pipe) */
+ s = ""; /* Send an empty data packet. */
+ len = 0;
+ } else if (len == 0) { /* Done if no data. */
+ if (pktnum == winlo)
+ return(-1);
+ drain = 1; /* But can't return -1 until all */
+ debug(F101,"sdata eof, drain","",drain);
+ return(0); /* ACKs are drained. */
+ }
+ debug(F101,"sdata pktnum","",pktnum);
+ debug(F101,"sdata len","",len);
+ debug(F011,"sdata data",data,52);
+
+ x = spack('D',pktnum,len,(CHAR *)s); /* Send the data packet. */
+ debug(F101,"sdata spack","",x);
+ if (x < 0) { /* Error */
+ ttchk(); /* See if connection is still there */
+ return(-2);
+ }
+#ifdef STREAMING
+ if (streaming) /* What an ACK would do. */
+ winlo = pktnum;
+#endif /* STREAMING */
+ x = ttchk(); /* Peek at input buffer. */
+ debug(F101,"sdata ttchk","",x); /* ACKs waiting, maybe? */
+ if (x < 0) /* Or connection broken? */
+ return(-2);
+/*
+ Here we check to see if any ACKs or NAKs have arrived, in which case we
+ break out of the D-packet-sending loop and return to the state switcher
+ to process them. This is what makes our windows slide instead of lurch.
+*/
+ if (
+#ifdef GEMDOS
+/*
+ In the Atari ST version, ttchk() can only return 0 or 1. But note: x will
+ probably always be > 0, since the as-yet-unread packet terminator from the
+ last packet is probably still in the buffer, so sliding windows will
+ probably never happen when the Atari ST is the file sender. The alternative
+ is to say "if (0)", in which case the ST will always send a window full of
+ packets before reading any ACKs or NAKs.
+*/
+ x > 0
+
+#else /* !GEMDOS */
+/*
+ In most other versions, ttchk() returns the actual count.
+ It can't be a Kermit packet if it's less than five bytes long.
+*/
+ x > 4 + bctu
+
+#endif /* GEMDOS */
+ )
+ return(1); /* Yes, stop sending data packets */
+ } /* and go try to read the ACKs. */
+ return(1);
+}
+
+
+/* S E O F -- Send an End-Of-File packet */
+
+/* Call with a string pointer to character to put in the data field, */
+/* or else a null pointer or "" for no data. */
+
+/*
+ There are two "send-eof" functions. seof() is used to send the normal eof
+ packet at the end of a file's data (even if the file has no data), or when
+ a file transfer is interrupted. sxeof() is used to send an EOF packet that
+ occurs because of attribute refusal or interruption prior to entering data
+ state. The difference is purely a matter of buffer allocation and packet
+ sequence number management. Both functions act as "front ends" to the
+ common send-eof function, szeof().
+*/
+
+/* Code common to both seof() and sxeof() */
+
+int
+szeof(s) CHAR *s; {
+ int x;
+ lsstate = 0; /* Cancel locking-shift state */
+ if (!s) s = (CHAR *)"";
+ debug(F111,"szeof",s,pktnum);
+ if (*s) {
+ x = spack('Z',pktnum,1,s);
+ xitsta |= W_SEND;
+#ifdef COMMENT
+ tlog(F100," *** interrupted, sending discard request","",0L);
+#endif /* COMMENT */
+ filrej++;
+ } else {
+ x = spack('Z',pktnum,0,(CHAR *)"");
+ }
+ if (x < 0)
+ return(x);
+
+#ifdef COMMENT
+ /* No, too soon */
+ discard = 0; /* Turn off per-file discard flag */
+#endif /* COMMENT */
+
+/* If we were sending from a pipe, we're not any more... */
+ pipesend = 0;
+ return(0);
+}
+
+int
+seof(x) int x; {
+ char * s;
+/*
+ ckcpro.w, before calling seof(), sets window size back to 1 and then calls
+ window(), which clears out the old buffers. This is OK because the final
+ data packet for the file has been ACK'd. However, sdata() has already
+ called nxtpkt(), which set the new value of pktnum which seof() will use.
+ So all we need to do here is is allocate a new send-buffer.
+*/
+ s = x ? "D" : "";
+ debug(F111,"seof",s,pktnum);
+ if (getsbuf(pktnum) < 0) { /* Get a buffer for packet n */
+ debug(F101,"seof can't get s-buffer","",pktnum);
+ return(-1);
+ }
+ return(szeof((CHAR *)s));
+}
+
+/*
+ Version of seof() to be called when sdata() has not been called before. The
+ difference is that this version calls nxtpkt() to allocate a send-buffer and
+ get the next packet number.
+*/
+int
+sxeof(x) int x; {
+ char * s;
+ s = x ? "D" : "";
+ if (nxtpkt() < 0) /* Get next pkt number and buffer */
+ debug(F101,"sxeof nxtpkt fails","",pktnum);
+ else
+ debug(F101,"sxeof packet","",pktnum);
+ return(szeof((CHAR *)s));
+}
+
+/* S E O T -- Send an End-Of-Transaction packet */
+
+int
+seot() {
+ int x;
+ x = nxtpkt();
+ debug(F101,"seot nxtpkt","",x);
+ if (x < 0) return(-1); /* Bump packet number, get buffer */
+ x = spack('B',pktnum,0,(CHAR *)""); /* Send the EOT packet */
+ if (x < 0)
+ return(x);
+ cxseen = czseen = discard = 0; /* Reset interruption flags */
+ tstats(); /* Log timing info */
+ return(0);
+}
+
+
+/* R P A R -- Fill the data array with my send-init parameters */
+
+int q8flag = 0;
+
+CHAR dada[32]; /* Use this instead of data[]. */
+ /* To avoid some kind of wierd */
+ /* addressing foulup in spack()... */
+ /* (which might be fixed now...) */
+
+CHAR *
+rpar() {
+ char *p;
+ int i, x, max;
+ extern int sprmlen;
+
+ max = maxdata(); /* Biggest data field I can send */
+ debug(F101, "rpar max 1","",max);
+ debug(F101, "rpar sprmlen","",sprmlen);
+ if (sprmlen > 1 && sprmlen < max) /* User override */
+ max = sprmlen;
+ debug(F101, "rpar max 2","",max);
+
+ if (rpsiz > MAXPACK) /* Biggest normal packet I want. */
+ dada[0] = (char) tochar(MAXPACK); /* If > 94, use 94, but specify */
+ else /* extended packet length below... */
+ dada[0] = (char) tochar(rpsiz); /* else use what the user said. */
+ dada[1] = (char) tochar(chktimo(pkttim,0)); /* When to time me out */
+ dada[2] = (char) tochar(mypadn); /* How much padding I need (none) */
+ dada[3] = (char) ctl(mypadc); /* Padding character I want */
+ dada[4] = (char) tochar(eol); /* End-Of-Line character I want */
+ dada[5] = myctlq; /* Control-Quote character I send */
+
+ if (max < 6) { dada[6] = NUL; rqf = 0; ebq = sq = NUL; return(dada); }
+
+ switch (rqf) { /* 8th-bit prefix (single-shift) */
+ case -1: /* I'm opening the bids */
+ case 1: /* Other Kermit already bid 'Y' */
+ if (parity) ebq = sq = MYEBQ; /* So I reply with '&' if parity */
+ break; /* otherwise with 'Y'. */
+ case 2: /* Other Kermit sent a valid prefix */
+ if (q8flag)
+ sq = ebq; /* Fall through on purpose */
+ case 0: /* Other Kermit bid nothing */
+ break; /* So I reply with 'Y'. */
+ }
+ debug(F000,"rpar 8bq sq","",sq);
+ debug(F000,"rpar 8bq ebq","",ebq);
+ if (lscapu == 2) /* LOCKING-SHIFT FORCED */
+ dada[6] = 'N'; /* requires no single-shift */
+ else /* otherwise send prefix or 'Y' */
+ dada[6] = (char) sq;
+ ebqsent = dada[6]; /* And remember what I really sent */
+
+ if (max < 7) { dada[7] = NUL; bctr = 1; return(dada); }
+
+ dada[7] = (char) (bctr == 4) ? 'B' : bctr + '0'; /* Block check type */
+
+ if (max < 8) { dada[8] = NUL; rptflg = 0; return(dada); }
+
+ if (rptena) {
+ if (rptflg) /* Run length encoding */
+ dada[8] = (char) rptq; /* If receiving, agree */
+ else /* by replying with same character. */
+ dada[8] = (char) (rptq = myrptq); /* When sending use this. */
+ } else dada[8] = SP; /* Not enabled, put a space here. */
+
+ /* CAPAS mask */
+
+ if (max < 9) {
+ dada[9] = NUL;
+ atcapr = 0;
+ lpcapr = 0;
+ swcapr = 0;
+ rscapr = 0;
+ return(dada);
+ }
+ dada[9] = (char) tochar((lscapr ? lscapb : 0) | /* Locking shifts */
+ (atcapr ? atcapb : 0) | /* Attribute packets */
+ (lpcapr ? lpcapb : 0) | /* Long packets */
+ (swcapr ? swcapb : 0) | /* Sliding windows */
+ (rscapr ? rscapb : 0)); /* RESEND */
+ if (max < 10) { wslotr = 1; return(dada); }
+ dada[10] = (char) tochar(swcapr ? wslotr : 1); /* CAPAS+1 = Window size */
+
+ if (max < 12) { rpsiz = 80; return(dada); }
+ if (urpsiz > 94)
+ rpsiz = urpsiz - 1; /* Long packets ... */
+ dada[11] = (char) tochar(rpsiz / 95); /* Long packet size, big part */
+ dada[12] = (char) tochar(rpsiz % 95); /* Long packet size, little part */
+
+ if (max < 16) return(dada);
+ dada[13] = '0'; /* CAPAS+4 = WONT CHKPNT */
+ dada[14] = '_'; /* CAPAS+5 = CHKINT (reserved) */
+ dada[15] = '_'; /* CAPAS+6 = CHKINT (reserved) */
+ dada[16] = '_'; /* CAPAS+7 = CHKINT (reserved) */
+ if (max < 17) return(dada);
+#ifndef WHATAMI
+ dada[17] = ' ';
+#else
+ x = 0;
+ if (server) x |= WMI_SERVE; /* Whether I am a server */
+ if (binary) x |= WMI_FMODE; /* My file transfer mode is ... */
+ if (fncnv) x |= WMI_FNAME; /* My filename conversion is ... */
+#ifdef STREAMING
+ if (streamrq == SET_ON)
+ x |= WMI_STREAM;
+ else if (streamrq == SET_AUTO && reliable == SET_ON)
+ x |= WMI_STREAM;
+ /*
+ Always offer to stream when in remote mode and STREAMING is AUTO
+ and RELIABLE is not OFF (i.e. is ON or AUTO).
+ */
+ else if (!local && streamrq == SET_AUTO && reliable != SET_OFF)
+ x |= WMI_STREAM;
+#endif /* STREAMING */
+#ifdef TCPSOCKET
+ if (clearrq == SET_ON)
+ x |= WMI_CLEAR;
+ else if (clearrq == SET_AUTO && /* SET CLEAR-CHANNEL AUTO */
+ ((network && nettype == NET_TCPB /* TCP/IP */
+#ifdef RLOGCODE
+ && ttnproto != NP_RLOGIN/* Rlogin is not clear */
+ && !(ttnproto >= NP_K4LOGIN && ttnproto <= NP_EK5LOGIN)
+#endif /* RLOGCODE */
+ )
+#ifdef SSHBUILTIN
+ || (network && nettype == NET_SSH)
+#endif /* SSHBUILTIN */
+#ifdef IKSD
+ || inserver /* We are IKSD */
+#endif /* IKSD */
+ ))
+ x |= WMI_CLEAR;
+#endif /* TCPSOCKET */
+ x |= WMI_FLAG;
+ dada[17] = (char) tochar(x);
+#endif /* WHATAMI */
+ i = 18; /* Position of next field */
+ p = cksysid; /* WHOAMI (my system ID) */
+ x = strlen(p);
+ if (max - i < x + 1) return(dada);
+ if (x > 0) {
+ dada[i++] = (char) tochar(x);
+ while (*p)
+ dada[i++] = *p++;
+ }
+
+ if (max < i+1) return(dada);
+#ifndef WHATAMI /* WHATAMI2 */
+ dada[i++] = ' ';
+#else
+ debug(F101,"rpar xfermode","",xfermode);
+ x = WMI2_FLAG; /* Is-Valid flag */
+ if (xfermode != XMODE_A) /* If TRANSFER MODE is MANUAL */
+ x |= WMI2_XMODE; /* set the XFERMODE bit */
+ if (recursive > 0) /* If this is a recursive transfer */
+ x |= WMI2_RECU; /* set the RECURSIVE bit */
+ dada[i++] = tochar(x);
+ debug(F101,"rpar whatami2","",x);
+#endif /* WHATAMI */
+
+ dada[i] = '\0'; /* Terminate the init string */
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"rpar",dada,0);
+ rdebu(dada,(int)strlen((char *)dada));
+ }
+#endif /* DEBUG */
+ ckstrncpy((char *)myinit,(char *)dada,MYINITLEN);
+ return(dada); /* Return pointer to string. */
+}
+
+int
+spar(s) CHAR *s; { /* Set parameters */
+ int x, y, lpsiz, biggest;
+ extern int rprmlen, lastspmax;
+ extern struct sysdata sysidlist[];
+
+ whatru = 0;
+ whoareu[0] = NUL;
+#ifdef STREAMING
+ streamok = 0;
+ streaming = 0;
+#endif /* STREAMING */
+ biggest = rln;
+
+ debug(F101, "spar biggest 1","",biggest);
+ debug(F101, "spar rprmlen","",rprmlen);
+ if (rprmlen > 1 && rprmlen < biggest)
+ biggest = rprmlen;
+ debug(F101, "rpar biggest 2","",biggest);
+ debug(F110,"spar packet",s,0);
+
+ s--; /* Line up with field numbers. */
+
+/* Limit on size of outbound packets */
+ x = (biggest >= 1) ? xunchar(s[1]) : 80;
+ lpsiz = spsizr; /* Remember what they SET. */
+ if (spsizf) { /* SET-command override? */
+ if (x < spsizr) spsiz = x; /* Ignore LEN unless smaller */
+ } else { /* otherwise */
+ spsiz = (x < 10) ? 80 : x; /* believe them if reasonable */
+ }
+ spmax = spsiz; /* Remember maximum size */
+
+/* Timeout on inbound packets */
+ if (timef) {
+ timint = rtimo; /* SET SEND TIMEOUT value overrides */
+ } else { /* Otherwise use requested value, */
+ x = (biggest >= 2) ? xunchar(s[2]) : rtimo; /* if it is legal. */
+ timint = (x < 0) ? rtimo : x;
+ }
+ timint = chktimo(timint,timef); /* Adjust if necessary */
+
+/* Outbound Padding */
+ npad = 0; padch = '\0';
+ if (biggest >= 3) {
+ npad = xunchar(s[3]);
+ if (biggest >= 4) padch = (CHAR) ctl(s[4]); else padch = 0;
+ }
+ if (npad) {
+ int i;
+ for (i = 0; i < npad; i++) padbuf[i] = dopar(padch);
+ }
+
+/* Outbound Packet Terminator */
+ seol = (CHAR) (biggest >= 5) ? xunchar(s[5]) : CR;
+ if ((seol < 1) || (seol > 31)) seol = CR;
+
+/* Control prefix that the other Kermit is sending */
+ x = (biggest >= 6) ? s[6] : '#';
+ ctlq = (CHAR) (((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#');
+
+/* 8th-bit prefix */
+/*
+ NOTE: Maybe this could be simplified using rcvtyp.
+ If rcvtyp == 'Y' then we're reading the ACK,
+ otherwise we're reading the other Kermit's initial bid.
+ But his horrendous code has been working OK for years, so...
+*/
+ rq = (biggest >= 7) ? s[7] : 0;
+ if (rq == 'Y') rqf = 1;
+ else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2;
+ else rqf = 0;
+ debug(F000,"spar 8bq rq","",rq);
+ debug(F000,"spar 8bq sq","",sq);
+ debug(F000,"spar 8bq ebq","",ebq);
+ debug(F101,"spar 8bq rqf","",rqf);
+ switch (rqf) {
+ case 0: /* Field is missing from packet. */
+ ebqflg = 0; /* So no 8th-bit prefixing. */
+ break;
+ case 1: /* Other Kermit sent 'Y' = Will Do. */
+ /*
+ When I am the file receiver, ebqsent is 0 because I didn't send a
+ negotiation yet. If my parity is set to anything other than NONE,
+ either because my user SET PARITY or because I detected parity bits
+ on this packet, I reply with '&', otherwise 'Y'.
+
+ When I am the file sender, ebqsent is what I just sent in rpar(),
+ which can be 'Y', 'N', or '&'. If I sent '&', then this 'Y' means
+ the other Kermit agrees to do 8th-bit prefixing.
+
+ If I sent 'Y' or 'N', but then detected parity on the ACK packet
+ that came back, then it's too late: there is no longer any way for
+ me to tell the other Kermit that I want to do 8th-bit prefixing, so
+ I must not do it, and in that case, if there is any 8-bit data in
+ the file to be transferred, the transfer will fail because of block
+ check errors.
+
+ The following clause covers all of these situations:
+ */
+ if (parity && (ebqsent == 0 || ebqsent == '&')) {
+ ebqflg = 1;
+ ebq = MYEBQ;
+ }
+ break;
+ case 2: /* Other Kermit sent a valid prefix */
+ ebqflg = (ebq == sq || sq == 'Y');
+ if (ebqflg) {
+ ebq = rq;
+ debug(F101,"spar setting parity to space","",ebq);
+ if (!parity) parity = ttprty = 's';
+ }
+ }
+ if (lscapu == 2) { /* But no single-shifts if LOCKING-SHIFT FORCED */
+ ebqflg = 0;
+ ebq = 'N';
+ }
+
+/* Block check */
+ x = 1;
+ if (biggest >= 8) {
+ if (s[8] == 'B') x = 4;
+ else x = s[8] - '0';
+ if ((x < 1) || (x > 4)) x = 1;
+ }
+ bctr = x;
+
+/* Repeat prefix */
+
+ rptflg = 0; /* Assume no repeat-counts */
+ if (biggest >= 9) { /* Is there a repeat-count field? */
+ char t; /* Yes. */
+ t = s[9]; /* Get its contents. */
+/*
+ If I'm sending files, then I'm reading these parameters from an ACK, and so
+ this character must agree with what I sent.
+*/
+ if (rptena) { /* If enabled ... */
+ if ((char) rcvtyp == 'Y') { /* Sending files, reading ACK. */
+ if (t == myrptq) rptflg = 1;
+ } else { /* I'm receiving files */
+ if ((t > 32 && t < 63) || (t > 95 && t < 127)) {
+ rptflg = 1;
+ rptq = t;
+ }
+ }
+ } else rptflg = 0;
+ }
+
+/* Capabilities */
+
+ atcapu = lpcapu = swcapu = rscapu = 0; /* Assume none of these. */
+ if (lscapu != 2) lscapu = 0; /* Assume no LS unless forced. */
+ y = 11; /* Position of next field, if any */
+ if (biggest >= 10) {
+ x = xunchar(s[10]);
+ debug(F101,"spar capas","",x);
+ atcapu = (x & atcapb) && atcapr; /* Attributes */
+ lpcapu = (x & lpcapb) && lpcapr; /* Long packets */
+ swcapu = (x & swcapb) && swcapr; /* Sliding windows */
+ rscapu = (x & rscapb) && rscapr; /* RESEND */
+ debug(F101,"spar lscapu","",lscapu);
+ debug(F101,"spar lscapr","",lscapr);
+ debug(F101,"spar ebqflg","",ebqflg);
+ if (lscapu != 2) lscapu = ((x & lscapb) && lscapr && ebqflg) ? 1 : 0;
+ debug(F101,"spar swcapr","",swcapr);
+ debug(F101,"spar swcapu","",swcapu);
+ debug(F101,"spar lscapu","",lscapu);
+ for (y = 10; (xunchar(s[y]) & 1) && (biggest >= y); y++);
+ debug(F101,"spar y","",y);
+ }
+
+/* Long Packets */
+ debug(F101,"spar lpcapu","",lpcapu);
+ if (lpcapu) {
+ if (biggest > y+1) {
+ x = xunchar(s[y+2]) * 95 + xunchar(s[y+3]);
+ debug(F101,"spar lp len","",x);
+ if (spsizf) { /* If overriding negotiations */
+ spsiz = (x < lpsiz) ? x : lpsiz; /* do this, */
+ } else { /* otherwise */
+ spsiz = (x > MAXSP) ? MAXSP : x; /* do this. */
+ }
+ if (spsiz < 10) spsiz = 80; /* Be defensive... */
+ }
+ }
+ /* (PWP) save current send packet size for optimal packet size calcs */
+ spmax = spsiz; /* Maximum negotiated length */
+ lastspmax = spsiz; /* For stats */
+ if (slostart && spsiz > 499) /* Slow start length */
+ spsiz = 244;
+ debug(F101,"spar slow-start spsiz","",spsiz);
+ debug(F101,"spar lp spmax","",spmax);
+ timint = chktimo(timint,timef); /* Recalculate the packet timeout */
+
+/* Sliding Windows... */
+
+ if (swcapr) { /* Only if requested... */
+ if (biggest > y) { /* See what other Kermit says */
+ x = xunchar(s[y+1]);
+ debug(F101,"spar window","",x);
+ wslotn = (x > MAXWS) ? MAXWS : x;
+/*
+ wslotn = negotiated size (from other Kermit's S or I packet).
+ wslotr = requested window size (from this Kermit's SET WINDOW command).
+*/
+ if (wslotn > wslotr) /* Use the smaller of the two */
+ wslotn = wslotr;
+ if (wslotn < 1) /* Watch out for bad negotiation */
+ wslotn = 1;
+ if (wslotn > 1) {
+ swcapu = 1; /* We do windows... */
+ if (wslotn > maxtry) /* Retry limit must be greater */
+ maxtry = wslotn + 1; /* than window size. */
+ }
+ debug(F101,"spar window after adjustment","",x);
+ } else { /* No window size specified. */
+ wslotn = 1; /* We don't do windows... */
+ debug(F101,"spar window","",x);
+ swcapu = 0;
+ debug(F101,"spar no windows","",wslotn);
+ }
+ }
+
+/* Now recalculate packet length based on number of windows. */
+/* The nogotiated number of window slots will be allocated, */
+/* and the maximum packet length will be reduced if necessary, */
+/* so that a windowful of packets can fit in the big buffer. */
+
+ if (wslotn > 1) { /* Shrink to fit... */
+ x = adjpkl(spmax,wslotn,bigsbsiz);
+ if (x < spmax) {
+ spmax = x;
+ lastspmax = spsiz;
+ if (slostart && spsiz > 499) spsiz = 244; /* Slow start again */
+ debug(F101,"spar sending, redefine spmax","",spmax);
+ }
+ }
+#ifdef WHATAMI
+ debug(F101,"spar biggest","",biggest);
+ if (biggest > y+7) { /* Get WHATAMI info if any */
+ whatru = xunchar(s[y+8]);
+ debug(F101,"spar whatru","",whatru);
+ }
+ if (whatru & WMI_FLAG) { /* Only valid if this bit is set */
+#ifdef STREAMING
+ if (whatru & WMI_STREAM) {
+ if (streamrq == SET_ON ||
+ (streamrq == SET_AUTO &&
+ (reliable == SET_ON || (reliable == SET_AUTO && !local)
+#ifdef TN_COMPORT
+ && !istncomport()
+#endif /* TN_COMPORT */
+#ifdef IKSD
+ || inserver
+#endif /* IKSD */
+ ))) {
+ streamok = 1; /* Streaming negotiated */
+ slostart = 0; /* Undo slow-start machinations */
+ spsiz = lastspmax;
+ }
+ }
+ streamed = streamok;
+ debug(F101,"spar streamok","",streamok);
+ debug(F101,"spar clearrq","",clearrq);
+ if (clearrq == SET_ON ||
+ (clearrq == SET_AUTO &&
+ ((network && nettype == NET_TCPB
+#ifdef RLOGCODE
+ && ttnproto != NP_RLOGIN/* Rlogin is not clear */
+ && !(ttnproto >= NP_K4LOGIN && ttnproto <= NP_EK5LOGIN)
+#endif /* RLOGCODE */
+#ifdef TN_COMPORT
+ && !istncomport()
+#endif /* TN_COMPORT */
+ )
+#ifdef SSHBUILTIN
+ || (network && nettype == NET_SSH)
+#endif /* SSHBUILTIN */
+#ifdef IKSD
+ || inserver
+#endif /* IKSD */
+ )))
+ urclear = (whatru & WMI_CLEAR);
+ debug(F101,"spar urclear","",urclear);
+#ifdef CK_SPEED
+ if (urclear)
+ setprefix(PX_NON);
+#endif /* CK_SPEED */
+ cleared = urclear;
+#endif /* STREAMING */
+ }
+#endif /* WHATAMI */
+
+ if (biggest > y+8) { /* Get WHOAREYOU info if any */
+ int x, z;
+ x = xunchar(s[y+9]); /* Length of it */
+ z = y;
+ y += (9 + x);
+ debug(F101,"spar sysindex x","",x);
+ debug(F101,"spar sysindex y","",y);
+ debug(F101,"spar sysindex biggest","",biggest);
+
+ if (x > 0 && x < 16 && biggest >= y) {
+ strncpy(whoareu,(char *)s+z+10,x); /* Other Kermit's system ID */
+ debug(F111,"spar whoareyou",whoareu,whoareu[0]);
+ if (whoareu[0]) { /* Got one? */
+ sysindex = getsysix((char *)whoareu);
+ debug(F101,"spar sysindex",whoareu,sysindex);
+ }
+ }
+ } else
+ goto xspar;
+
+#ifdef WHATAMI
+ y++; /* Advance pointer */
+ if (biggest >= y) {
+ whatru2 = xunchar(s[y]); /* Next field is WHATAMI2 */
+ debug(F101,"spar whatru2","",whatru2);
+ if (whatru2 & WMI2_FLAG) { /* Valid only if this bit is set */
+ if (server) { /* Server obeys client's xfer mode */
+ xfermode = (whatru2 & WMI2_XMODE) ? XMODE_M : XMODE_A;
+ debug(F101,"spar whatru2 xfermode","",xfermode);
+ }
+ if (whatru2 & WMI2_RECU) { /* RECURSIVE transfer */
+ if (fnrpath == PATH_AUTO) { /* If REC PATH AUTO */
+ fnrpath = PATH_REL; /* Set it to RELATIVE */
+ autopath = 1; /* and remember we did this */
+ }
+ }
+ }
+ }
+#endif /* WHATAMI */
+
+ xspar:
+ if (sysindex > -1) {
+ char * p;
+ p = sysidlist[sysindex].sid_name;
+ tlog(F110,"Remote system type: ",p,0L);
+ if (sysindex > 0) { /* If partnet's system type known */
+ whoarewe(); /* see if we are a match. */
+#ifdef CK_SPEED
+/* Never unprefix XON and XOFF when sending to VMS */
+ debug(F111,"proto whoareu",whoareu,sysindex);
+ if (!strcmp((char *)whoareu,"D7")) {
+ debug(F111,"proto special VMS prefixing","",0);
+ ctlp[XON] = ctlp[XOFF] = 1;
+ ctlp[XON+128] = ctlp[XOFF+128] = 1;
+ ctlp[3] = 1; /* Ctrl-C might be dangerous too */
+ ctlp[14] = ctlp[15] = 1; /* And SO/SI */
+ ctlp[24] = ctlp[25] = 1; /* And ^X/^Y */
+ ctlp[141] = 1; /* And CR+128 */
+ }
+#endif /* CK_SPEED */
+ }
+ }
+
+/* Record parameters in debug log */
+#ifdef DEBUG
+ if (deblog) sdebu(biggest);
+#endif /* DEBUG */
+ numerrs = 0; /* Start counting errors here. */
+ return(0);
+}
+
+/* G N F I L E -- Get name of next file to send */
+/*
+ Expects global sndsrc to be:
+ -9: if we are generating a file internally for calibration.
+ -1: next filename to be obtained by calling znext().
+ 0: no next file name
+ 1: (or greater) next filename to be obtained from **cmlist,
+ or if addlist != 0, from the "filehead" linked list,
+ or if filefile pointer not null from that file (which is already open).
+ Returns:
+ 1, with name of next file in filnam.
+ 0, no more files, with filnam set to empty string.
+ -1, file not found
+ -2, file is not readable (but then we just skip to the next one if any)
+ -3, read access denied
+ -4, cancelled
+ -5, too many files match wildcard
+ -6, no files selected
+ NOTE:
+ If gnfile() returns 0, then the global variable gnferror should be checked
+ to find out the most recent gnfile() error, and use that instead of the
+ return code (for reasons to hard to explain).
+*/
+int
+gnfile() {
+ int i = 0, x = 0; long y = 0L;
+ int dodirstoo = 0;
+ int retcode = 0;
+
+ char fullname[CKMAXPATH+1];
+
+ dodirstoo = ((what & (W_FTP|W_SEND)) == (W_FTP|W_SEND)) && recursive;
+
+ debug(F101,"gnfile sndsrc","",sndsrc);
+ debug(F101,"gnfile filcnt","",filcnt);
+ debug(F101,"gnfile what","",what);
+ debug(F101,"gnfile recursive","",recursive);
+ debug(F101,"gnfile dodirstoo","",dodirstoo);
+ gnferror = 0;
+ fsize = -1L; /* Initialize file size */
+ fullname[0] = NUL;
+ if (!(what & W_REMO) && (xfermode == XMODE_A)
+#ifndef NOMSEND
+ && !addlist
+#endif /* NOMSEND */
+ ) {
+ extern int stdinf;
+ if (!stdinf) /* Not if sending from stdin */
+#ifdef WHATAMI
+ /* We don't do this in server mode because it undoes WHATAMI */
+ if (!server || (server && ((whatru & WMI_FLAG) == 0)))
+#endif /* WHATAMI */
+ binary = gnf_binary; /* Restore prevailing transfer mode */
+ debug(F101,"gnfile binary = gnf_binary","",gnf_binary);
+ }
+#ifdef PIPESEND
+ debug(F101,"gnfile pipesend","",pipesend);
+ if (pipesend) { /* First one */
+ if (filcnt == 0) {
+ ckstrncpy(filnam,cmarg,CKMAXPATH+1);
+ return(1);
+ } else { /* There's only one... */
+ *filnam = NUL;
+ pipesend = 0;
+ return(0);
+ }
+ }
+#endif /* PIPESEND */
+
+#ifdef CALIBRATE
+ if (sndsrc == -9) {
+ debug(F100,"gnfile calibrate","",0);
+ ckstrncpy(filnam,"CALIBRATION",CKMAXPATH);
+ nfils = 0;
+ cal_j = 0;
+ fsize = calibrate;
+ sndsrc = 0; /* For next time */
+ nfils = 0;
+ return(1);
+ }
+#endif /* CALIBRATE */
+
+#ifndef NOSPL
+ if (sndarray) { /* Sending from an array */
+ extern char sndxnam[]; /* Pseudo filename */
+ debug(F100,"gnfile array","",0);
+ nfils = 0;
+ fsize = -1L; /* Size unknown */
+ sndsrc = 0;
+ ckstrncpy(filnam,sndxnam,CKMAXPATH);
+ return(1);
+ }
+#endif /* NOSPL */
+
+ if (sndsrc == 0) { /* It's not really a file */
+ if (nfils > 0) { /* It's a pipe, or stdin */
+ ckstrncpy(filnam,*cmlist,CKMAXPATH+1); /* Copy its "name" */
+ nfils = 0; /* There is no next file */
+ return(1); /* OK this time */
+ } else return(0); /* but not next time */
+ }
+
+/* If file group interruption (C-Z) occurred, fail. */
+
+ if (czseen) {
+ tlog(F100,"Transaction cancelled","",0L);
+ debug(F100,"gnfile czseen","",0);
+ return(-4);
+ }
+
+/* Loop through file list till we find a readable, sendable file */
+
+ y = -1L; /* Loop exit (file size) variable */
+ while (y < 0L) { /* Keep trying till we get one... */
+ retcode = 0;
+ if (sndsrc > 0) { /* File list in cmlist or file */
+ if (filefile) { /* Reading list from file... */
+ if (zsinl(ZMFILE,filnam,CKMAXPATH) < 0) { /* Read a line */
+ zclose(ZMFILE); /* Failed */
+ debug(F110,"gnfile filefile EOF",filefile,0);
+ makestr(&filefile,NULL);
+ return(0);
+ }
+ debug(F110,"gnfile filefile filnam",filnam,0);
+ }
+ debug(F101,"gnfile nfils","",nfils);
+ if (nfils-- > 0 || filefile) { /* Still some left? */
+#ifndef NOMSEND
+ if (addlist) {
+ if (filenext && filenext->fl_name) {
+ ckstrncpy(filnam,filenext->fl_name,CKMAXPATH+1);
+ cmarg2 =
+ filenext->fl_alias ?
+ filenext->fl_alias :
+ "";
+ binary = filenext->fl_mode;
+ } else {
+ printf("?Internal error expanding ADD list\n");
+ return(-5);
+ }
+ filenext = filenext->fl_next;
+ debug(F111,"gnfile addlist filnam",filnam,nfils);
+ } else if (sndsrc > 0 && !filefile) {
+#endif /* NOMSEND */
+ ckstrncpy(filnam,*cmlist++,CKMAXPATH+1);
+ debug(F111,"gnfile cmlist filnam",filnam,nfils);
+#ifndef NOMSEND
+ }
+#endif /* NOMSEND */
+ i = 0;
+#ifndef NOSERVER
+ debug(F101,"gnfile ngetpath","",ngetpath);
+#endif /* NOSERVER */
+nextinpath:
+#ifndef NOSERVER
+ fromgetpath = 0;
+ if (server && !isabsolute(filnam) && (ngetpath > i)) {
+ ckstrncpy(fullname,getpath[i],CKMAXPATH+1);
+ strncat(fullname,filnam,CKMAXPATH);
+ debug(F111,"gnfile getpath",fullname,i);
+ fromgetpath = 1;
+ i++;
+ } else {
+ i = ngetpath + 1;
+#else
+ i = 1; /* ? */
+#endif /* NOSERVER */
+ ckstrncpy(fullname,filnam,CKMAXPATH+1);
+ debug(F110,"gnfile absolute",fullname,0);
+#ifndef NOSERVER
+ }
+#endif /* NOSERVER */
+ if (iswild(fullname)
+#ifdef RECURSIVE
+ || recursive > 0 || !strcmp(fullname,".")
+#endif /* RECURSIVE */
+ ) { /* It looks wild... */
+ /* First check if a file with this name exists */
+ debug(F110,"gnfile wild",fullname,0);
+ if (zchki(fullname) > -1) {
+ /*
+ Here we have a file whose name actually
+ contains wildcard characters.
+ */
+ goto gotnam;
+ }
+#ifdef COMMENT
+ nzxopts = ZX_FILONLY; /* (was 0: 25 Jul 2001 fdc) */
+#else
+ nzxopts = recursive ? 0 : ZX_FILONLY; /* 30 Jul 2001 */
+#endif /* COMMENT */
+ if (nolinks) nzxopts |= ZX_NOLINKS; /* (26 Jul 2001 fdc) */
+#ifdef UNIXOROSK
+ if (matchdot) nzxopts |= ZX_MATCHDOT;
+#endif /* UNIXOROSK */
+ if (recursive) nzxopts |= ZX_RECURSE;
+ x = nzxpand(fullname,nzxopts); /* Expand wildcards */
+ debug(F101,"gnfile nzxpand","",x);
+ if (x == 1) {
+ int xx;
+ xx = znext(fullname);
+ debug(F111,"gnfile znext A",fullname,xx);
+ goto gotnam;
+ }
+ if (x == 0) { /* None match */
+#ifndef NOSERVER
+ if (server && ngetpath > i)
+ goto nextinpath;
+#endif /* NOSERVER */
+ retcode = -1;
+ debug(F101,"gnfile gnferror A","",gnferror);
+ gnferror = -1;
+ continue;
+ }
+ if (x < 0) { /* Too many to expand */
+ debug(F101,"gnfile gnferror B","",gnferror);
+ gnferror = -5;
+ return(-5);
+ }
+ sndsrc = -1; /* Change send-source to znext() */
+ }
+ } else { /* We're out of files. */
+ debug(F111,"gnfile done",ckitoa(gnferror),nfils);
+ *filnam = '\0';
+ return(0);
+ }
+ }
+
+/* Otherwise, step to next element of internal wildcard expansion list. */
+
+ if (sndsrc == -1) {
+ int xx = 0;
+ while (1) {
+ debug(F111,"gnfile znext X",filnam,xx);
+ xx = znext(filnam);
+ debug(F111,"gnfile znext B",filnam,xx);
+ if (!filnam[0])
+ break;
+ if (dodirstoo) {
+ debug(F111,"gnfile FTP MPUT /RECURSIVE",filnam,xx);
+ break;
+ }
+ if (!isdir(filnam))
+ break;
+ }
+ debug(F111,"gnfile znext C",filnam,x);
+ if (!filnam[0]) { /* If no more, */
+ sndsrc = 1; /* go back to previous list */
+ debug(F101,"gnfile setting sndsrc back","",sndsrc);
+ continue;
+ } else
+ ckstrncpy(fullname,filnam,CKMAXPATH+1);
+ }
+
+/* Get here with a filename. */
+
+gotnam:
+ debug(F110,"gnfile fullname",fullname,0);
+ if (fullname[0]) {
+#ifdef DTILDE
+ char * dirp = "";
+ if (fullname[0] == '~') {
+ dirp = tilde_expand((char *)fullname);
+ if (*dirp) ckstrncpy(fullname,dirp,CKMAXPATH+1);
+ }
+#endif /* DTILDE */
+ y = zchki(fullname); /* Check if file readable */
+ debug(F111,"gnfile zchki",fullname,y);
+ retcode = (int) y; /* Possible return code */
+ if (y == -2L && dodirstoo) {
+ y = 0L;
+ }
+ if (y < 0L) {
+ gnferror = (int) y;
+ debug(F101,"gnfile gnferror C","",gnferror);
+ }
+ if (y == -1L) { /* If not found */
+ debug(F100,"gnfile -1","",0);
+#ifndef NOSERVER
+ if (server && ngetpath > i)
+ goto nextinpath;
+#endif /* NOSERVER */
+ debug(F110,"gnfile skipping:",fullname,0);
+ tlog(F110,fullname,": open failure - skipped",0);
+ xxscreen(SCR_FN,0,0l,fullname);
+ xxscreen(SCR_ST,ST_SKIP,SKP_ACC,fullname);
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,fullname,fsize,binary,1,"Skipped");
+#endif /* TLOG */
+ continue;
+ } else if (y < 0) {
+ if (y == -3) { /* Exists but not readable */
+ debug(F100,"gnfile -3","",0);
+ filrej++; /* Count this one as not sent */
+ tlog(F110,"Read access denied",fullname,0); /* Log this */
+ xxscreen(SCR_FN,0,0l,fullname);
+ xxscreen(SCR_ST,ST_SKIP,SKP_ACC,fullname); /* Display it */
+#ifdef TLOG
+ if (tralog && !tlogfmt)
+ doxlog(what,fullname,fsize,binary,1,"Skipped");
+#endif /* TLOG */
+ }
+ continue;
+ } else {
+ int xx;
+ fsize = y;
+ xx = fileselect(fullname,
+ sndafter, sndbefore,
+ sndnafter,sndnbefore,
+ sndsmaller,sndlarger,
+ skipbup,
+ NSNDEXCEPT,sndexcept);
+ debug(F111,"gnfile fileselect",fullname,xx);
+ if (!xx) {
+ y = -1L;
+ gnferror = -6;
+ debug(F101,"gnfile gnferror D","",gnferror);
+ continue;
+ }
+ ckstrncpy(filnam,fullname,CKMAXPATH+1);
+ return(1);
+ }
+#ifdef COMMENT
+ /* This can't be right! */
+ } else { /* sndsrc is 0... */
+ if (!fileselect(fullname,
+ sndafter, sndbefore,
+ sndnafter,sndnbefore,
+ sndsmaller,sndlarger,
+ skipbup,
+ NSNDEXCEPT,sndexcept)) {
+ gnferror = -6;
+ debug(F111,"gnfile fileselect",fullname,gnferror);
+ y = -1L;
+ continue;
+ }
+ ckstrncpy(filnam,fullname,CKMAXPATH+1);
+ return(1);
+#endif /* COMMENT */
+ }
+ }
+ debug(F101,"gnfile result","",retcode);
+ *filnam = '\0';
+ return(0);
+}
+
+/*
+ The following bunch of routines feed internally generated data to the server
+ to send to the client in response to REMOTE commands like DIRECTORY, DELETE,
+ and so on. We have to write these lines in the format appropriate to our
+ platform, so they can be converted to generic (CRLF) text format by the
+ packetizer.
+*/
+#ifdef UNIX
+char * endline = "\12";
+#else
+#ifdef datageneral
+char * endline = "\12";
+#else
+#ifdef MAC
+char * endline = "\15";
+#else
+#ifdef OSK
+char * endline = "\15";
+#else
+char * endline = "\15\12";
+#endif /* OSK */
+#endif /* MAC */
+#endif /* datageneral */
+#endif /* UNIX */
+
+#ifdef MAC
+#define FNCBUFL 256
+#else
+#ifdef CKSYMLINK
+#define FNCBUFL (CKMAXPATH + CKMAXPATH + 64)
+#else
+#define FNCBUFL (CKMAXPATH + 64)
+#endif /* CKSYMLINK */
+#endif /* MAC */
+
+/* NB: The minimum FNCBUFL is 255 */
+
+static CHAR funcbuf[FNCBUFL];
+static int funcnxt = 0;
+static int funclen = 0;
+static int nxpnd = -1;
+static long ndirs = 0;
+static long nfiles = 0;
+static long nbytes = 0;
+
+int
+sndstring(p) char * p; {
+#ifndef NOSERVER
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,versio,CMDSTRL); /* Data for X packet. */
+ first = 1; /* Init getchx lookahead */
+ memstr = 1; /* Just set the flag. */
+ memptr = p; /* And the pointer. */
+ binary = XYFT_T; /* Text mode for this. */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+/* S N D H L P -- Routine to send builtin help */
+
+static int srvhlpnum = 0;
+
+#ifdef IKSD
+static char *nmx[] = { "Disabled", "Disabled", "Enabled", "Enabled" };
+#endif /* IKSD */
+
+static char *
+xnm(x) int x; {
+#ifdef IKSD
+ if (inserver)
+ return(nmx[x]);
+ else
+#endif /* IKSD */
+ return(nm[x]);
+}
+
+static int
+nxthlp(
+#ifdef CK_ANSIC
+ void
+#endif /* CK_ANSIC */
+ ) {
+ int x = 0;
+ extern int
+ en_cpy, en_cwd, en_del, en_dir, en_fin, en_get, en_bye, en_mai,
+ en_pri, en_hos, en_ren, en_sen, en_spa, en_set, en_typ, en_who,
+ /* en_ret, */ en_mkd, en_rmd, en_asg, en_que, en_xit, x_login, x_logged,
+ xfinish;
+ extern char * ckxsys;
+
+ if (funcnxt < funclen)
+ return (funcbuf[funcnxt++]);
+
+ switch (srvhlpnum++) {
+ case 0:
+ x = ckstrncpy((char *)funcbuf,
+ "Client Command Status Description\n",
+ FNCBUFL
+ );
+ if (x_login && !x_logged) {
+ x += ckstrncat((char *)funcbuf,
+ " REMOTE LOGIN required\n",
+ FNCBUFL
+ );
+ }
+ if (FNCBUFL - x > 74)
+ sprintf((char *)(funcbuf+x)," GET %-14s%s\n",
+ xnm(en_get),
+ "Transfer file(s) from server to client."
+ );
+ break;
+
+/* NOTE: The minimum funcbuf[] size is 255; all of the following are safe. */
+
+ case 1:
+ sprintf((char *)funcbuf," SEND %-14s%s\n",
+ xnm(en_sen),
+ "Transfer file(s) from client to server."
+ );
+ break;
+
+ case 2:
+ sprintf((char *)funcbuf," MAIL %-14s%s\n",
+ xnm(inserver ? 0 : en_mai),
+ "Send file(s) as e-mail."
+ );
+ break;
+
+ case 3:
+#ifndef NOSPL
+ sprintf((char *)funcbuf," REMOTE ASSIGN %-14s%s\n",
+ xnm(en_asg),
+ "Assign value to server variable or macro."
+ );
+#else
+ sprintf((char *)funcbuf," REMOTE ASSIGN not configured\n");
+#endif /* NOSPL */
+
+ break;
+ case 4:
+ sprintf((char *)funcbuf," REMOTE CD %-14s%s\n",
+ xnm(en_cwd),
+ "Change server's directory."
+ );
+ break;
+
+ case 5:
+#ifdef ZCOPY
+ sprintf((char *)funcbuf," REMOTE COPY %-14s%s\n",
+ xnm(en_cpy),
+ "Copy a file on the server."
+ );
+#else
+ sprintf((char *)funcbuf," REMOTE COPY not configured\n");
+#endif /* ZCOPY */
+
+ break;
+ case 6:
+ sprintf((char *)funcbuf," REMOTE DELETE %-14s%s\n",
+ xnm(en_del),
+ "Delete a file on the server."
+ );
+ break;
+
+ case 7:
+ sprintf((char *)funcbuf," REMOTE DIRECTORY %-14s%s\n",
+ xnm(en_dir),
+ "List files on the server."
+ );
+ break;
+
+ case 8:
+ sprintf((char *)funcbuf," REMOTE EXIT %-14s%s\n",
+ xnm(en_xit),
+ "Exit from Kermit server program."
+ );
+ break;
+
+ case 9:
+ sprintf((char *)funcbuf," REMOTE HOST %-14s%s\n",
+ xnm(inserver ? 0 : en_hos),
+#ifdef datageneral
+ "Execute a CLI command on the server."
+#else
+#ifdef VMS
+ "Execute a DCL command on the server."
+#else
+ "Execute a shell command on the server."
+#endif /* VMS */
+#endif /* datageneral */
+ );
+ break;
+
+ case 10:
+ sprintf((char *)funcbuf," REMOTE PRINT %-14s%s\n",
+ xnm(inserver ? 0 : en_pri),
+ "Send a file to the server for printing."
+ );
+ break;
+
+ case 11:
+#ifndef NOSPL
+ sprintf((char *)funcbuf," REMOTE QUERY %-14s%s\n",
+ xnm(en_que),
+ "Get value of server variable or macro."
+ );
+
+#else
+ sprintf((char *)funcbuf," REMOTE QUERY not configured\n");
+#endif /* NOSPL */
+
+ break;
+ case 12:
+ sprintf((char *)funcbuf," REMOTE MKDIR %-14s%s\n",
+ xnm(en_mkd),
+ "Create a directory on the server."
+ );
+ break;
+
+ case 13:
+ sprintf((char *)funcbuf," REMOTE RMDIR %-14s%s\n",
+ xnm(en_rmd),
+ "Remove a directory on the server."
+ );
+ break;
+
+ case 14:
+ sprintf((char *)funcbuf," REMOTE RENAME %-14s%s\n",
+ xnm(en_ren),
+ "Rename a file on the server."
+ );
+ break;
+
+ case 15:
+ sprintf((char *)funcbuf," REMOTE SET %-14s%s\n",
+ xnm(en_set),
+ "Set a parameter on the server"
+ );
+ break;
+
+ case 16:
+ sprintf((char *)funcbuf," REMOTE SPACE %-14s%s\n",
+ xnm(en_spa),
+ "Inquire about disk space on the server."
+ );
+ break;
+
+ case 17:
+ sprintf((char *)funcbuf," REMOTE TYPE %-14s%s\n",
+ xnm(en_typ),
+ "Display a server file on your screen."
+ );
+ break;
+
+ case 18:
+ sprintf((char *)funcbuf," REMOTE WHO %-14s%s\n",
+ xnm(inserver ? 0 : en_who),
+ "List who is logged in to the server."
+ );
+ break;
+
+ case 19:
+ sprintf((char *)funcbuf," FINISH %-14s%s\n",
+ xnm(en_fin),
+ xfinish ?
+ "Exit from Kermit server program." :
+ "Return the server to its command prompt."
+ );
+ break;
+
+ case 20:
+ sprintf((char *)funcbuf," BYE %-14s%s\n\n",
+ xnm(en_bye),
+ "Log the server out and disconnect."
+ );
+ break;
+
+ default:
+ return(-1);
+ }
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ return(funcbuf[funcnxt++]);
+}
+
+int
+sndhlp() {
+#ifndef NOSERVER
+ extern char * ckxsys;
+
+ first = 1; /* Init getchx lookahead */
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,"REMOTE HELP",CMDSTRL); /* Data for X packet. */
+ sprintf((char *)funcbuf, "C-Kermit %s,%s\n\n", versio, ckxsys);
+ funclen = strlen((char *)funcbuf);
+#ifdef IKSD
+ if (inserver) {
+ sprintf((char *)(funcbuf+funclen),
+ "Internet Kermit Service (EXPERIMENTAL)\n\n");
+ funclen = strlen((char *)funcbuf);
+ }
+#endif /* IKSD */
+ funcnxt = 0;
+ funcptr = nxthlp;
+ funcstr = 1;
+ srvhlpnum = 0;
+ binary = XYFT_T; /* Text mode for this. */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+/*
+ Returns the next available character,
+ -1 if no more data.
+*/
+static int
+nxttype(
+#ifdef CK_ANSIC
+ void
+#endif /* CK_ANSIC */
+ ) {
+ int c;
+ if (zchin(ZIFILE,&c) < 0) {
+ zclose(ZIFILE);
+ return(-1);
+ } else {
+ return((unsigned)c);
+ }
+}
+
+/* S N D T Y P -- TYPE a file to remote client */
+
+int
+sndtype(file) char * file; {
+#ifndef NOSERVER
+ char name[CKMAXPATH+1];
+
+#ifdef OS2
+ char * p = NULL;
+
+ if (*file) {
+ ckstrncpy(name, file, CKMAXPATH+1);
+ /* change / to \. */
+ p = name;
+ while (*p) { /* Change them back to \ */
+ if (*p == '/') *p = '\\';
+ p++;
+ }
+ } else
+ return(0);
+#else
+ ckstrncpy(name, file, CKMAXPATH+1);
+#endif /* OS2 */
+
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ if (zchki(name) == -2) {
+ /* Found a directory */
+ return(0);
+ }
+ if (!zopeni(ZIFILE,name))
+ return(0);
+
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,"type",CMDSTRL); /* Data for X packet. */
+ first = 1; /* Init getchx lookahead */
+ funcstr = 1; /* Just set the flag. */
+ funcptr = nxttype; /* And the pointer. */
+ binary = XYFT_T; /* Text mode for this */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+/*
+ N X T D I R -- Provide data for senddir()
+
+ Returns the next available character or -1 if no more data.
+*/
+#ifndef NOICP
+/* Directory listing parameters set by the user interface, if any. */
+extern int dir_head, dir_dots, dir_back;
+#endif /* NOICP */
+static int sd_hdg, sd_bkp, sd_dot; /* Local listing parameters */
+
+static int
+nxtdir(
+#ifdef CK_ANSIC
+ void
+#endif /* CK_ANSIC */
+ ) {
+ char name[CKMAXPATH+1], dbuf[24], *p = NULL;
+ char *dstr = NULL, * lnk = "";
+ CHAR c, * linebuf = funcbuf;
+#ifdef OSK
+ /* Work around bugs in OSK compiler */
+ char *dirtag = "directories";
+ char *filetag = "files";
+ char *bytetag = "bytes";
+#endif /* OSK */
+ long len = 0;
+ int x, itsadir = 0, gotone = 0;
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"nxtdir funcnxt","",funcnxt);
+ debug(F101,"nxtdir funclen","",funclen);
+ debug(F110,"nxtdir funcbuf",funcbuf+funcnxt,0);
+ }
+#endif /* DEBUG */
+ if (funcnxt < funclen) { /* Return next character from buffer */
+ c = funcbuf[funcnxt++];
+ debug(F000,"nxtdir return 1","",(unsigned)(c & 0xff));
+ return((unsigned)(c & 0xff));
+ }
+ while (nxpnd > 0) { /* Buffer needs refill */
+ nxpnd--;
+ znext(name); /* Get next filename */
+ if (!name[0]) { /* None left - done */
+ nxpnd = 0;
+ return(nxtdir());
+ }
+ if (sd_bkp) { /* Showing backup files? */
+ gotone = 1; /* Yes, no need to check. */
+ break;
+ }
+ x = ckmatch( /* No - see if this is one */
+#ifdef CKREGEX
+ "*.~[0-9]*~" /* Not perfect but close enough. */
+#else
+ "*.~*~" /* Less close. */
+#endif /* CKREGEX */
+ ,name,filecase,1);
+ debug(F111,"nxtdir ckmatch",name,x);
+ if (x) {
+ continue; /* It's a backup file - skip it */
+ } else {
+ gotone = 1; /* It's not, break from loop. */
+ break;
+ }
+ }
+ if (gotone) {
+ len = zgetfs(name); /* Get file size */
+ debug(F111,"nxtdir zgetfs",name,len);
+#ifdef VMSORUNIX
+ itsadir = zgfs_dir; /* See if it's a directory */
+#else
+ itsadir = (len == -2 || isdir(name));
+#endif /* VMSORUNIX */
+ dstr = zfcdat(name);
+ debug(F111,"nxtdir zcfdat",dstr,0);
+ if (!dstr)
+ dstr = "0000-00-00 00:00:00";
+ if (!*dstr) {
+ dstr = "0000-00-00 00:00:00";
+ } else {
+ dbuf[0] = dstr[0];
+ dbuf[1] = dstr[1];
+ dbuf[2] = dstr[2];
+ dbuf[3] = dstr[3];
+ dbuf[4] = '-';
+ dbuf[5] = dstr[4];
+ dbuf[6] = dstr[5];
+ dbuf[7] = '-';
+ dbuf[8] = dstr[6];
+ dbuf[9] = dstr[7];
+ strcpy(dbuf+10,dstr+8);
+ dstr = dbuf;
+ }
+#ifdef CK_PERMS
+#ifdef VMSORUNIX
+ p = ziperm(name); /* Get permissions */
+#else
+ p = zgperm(name);
+#endif /* VMSORUNIX */
+#else
+ p = NULL;
+#endif /* CK_PERMS */
+ debug(F110,"domydir perms",p,0);
+
+#ifdef VMS
+ /* Make name relative */
+ ckstrncpy(name,zrelname(name,zgtdir()),CKMAXPATH+1);
+#endif /* VMS */
+
+ if (itsadir) {
+ ndirs++;
+ } else {
+ nfiles++;
+ nbytes += len;
+ }
+ lnk = "";
+#ifdef UNIX
+#ifdef CKSYMLINK
+ if (zgfs_link) {
+ extern char linkname[];
+ lnk = linkname;
+ }
+ debug(F111,"nxtdir linkname",lnk,zgfs_link);
+#endif /* CKSYMLINK */
+#endif /* UNIX */
+
+/*
+ The following sprintf's are safe; linebuf is a pointer to funcbuf,
+ which is 64 bytes larger than CKMAXPATH (or double CKMAXPATH when
+ symlinks are possible). 64 allows for the fixed-field portions of
+ the file listing line: permissions, size, and date. CKMAXPATH allows
+ for the longest possible pathname.
+*/
+ if (itsadir && len < 0) { /* Directory */
+#ifdef VMS
+ sprintf((char *)linebuf,
+ "%-22s%-10s %s %s\n",p,"<DIR>",dstr,name);
+#else
+ if (p)
+ sprintf((char *)linebuf,
+ "%10s%-10s %s %s\n",p,"<DIR>",dstr,name);
+ else
+ sprintf((char *)linebuf,
+ "%-10s %s %s\n", "<DIR>", dstr, name);
+#endif /* VMS */
+ } else { /* Regular file */
+#ifdef VMS
+ sprintf((char *)linebuf,
+ "%-22s%10ld %s %s\n", p, len, dstr, name);
+#else
+ if (p)
+ sprintf((char *)linebuf,
+ "%10s%10ld %s %s%s%s\n",
+ p, len, dstr, name,
+ *lnk ? " -> " : "",
+ lnk
+ );
+ else
+ sprintf((char *)linebuf,
+ "%10ld %s %s%s%s\n",
+ len, dstr, name,
+ *lnk ? " -> " : "",
+ lnk
+ );
+#endif /* VMS */
+ }
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ } else if (sd_hdg && nxpnd == 0) { /* Done, send summary */
+ char *blankline = ""; /* At beginning of summary */
+/*
+ The idea is to prevent (a) unnecessary multiple blanklines, and (b)
+ prompt-stomping. Preventing (b) is practically impossible, because it
+ depends on the client so for now always include that final CRLF.
+*/
+ if (!ndirs || !nbytes || !nfiles)
+ blankline = endline;
+#ifdef OSK
+/* Workaround bugs in OS-9 compiler... */
+ if (ndirs == 1)
+ dirtag = "directory";
+ if (nfiles == 1)
+ filetag = "file";
+ if (nbytes == 1)
+ bytetag = "byte";
+ sprintf((char *)funcbuf,
+ "%sSummary: %ld %s, %ld %s, %ld %s%s",
+ blankline,
+ ndirs,
+ dirtag,
+ nfiles,
+ filetag,
+ nbytes,
+ bytetag,
+ endline);
+#else
+ sprintf((char *)funcbuf,
+ "%sSummary: %ld director%s, %ld file%s, %ld byte%s%s",
+ blankline,
+ ndirs,
+ (ndirs == 1) ? "y" : "ies",
+ nfiles,
+ (nfiles == 1) ? "" : "s",
+ nbytes,
+ (nbytes == 1) ? "" : "s",
+ endline
+ );
+#endif /* OSK */
+ nxpnd--;
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ } else {
+ funcbuf[0] = '\0';
+ funcnxt = 0;
+ funclen = 0;
+ }
+ debug(F101,"nxtdir funclen","",funclen);
+
+ if (funcnxt < funclen) { /* If we have data to send... */
+ c = funcbuf[funcnxt++];
+ debug(F000,"nxtdir return 2","",(unsigned)(c & 0xff));
+ return((unsigned)(c & 0xff));
+ } else
+ return(-1); /* Nothing left, done. */
+}
+
+/* S N D D I R -- send directory listing */
+
+int
+snddir(spec) char * spec; {
+#ifndef NOSERVER
+ char * p = NULL, name[CKMAXPATH+1];
+ int t = 0, rc = 0;
+ char fnbuf[CKMAXPATH+1];
+
+ debug(F111,"snddir matchdot",spec,matchdot);
+
+#ifndef NOICP
+ debug(F111,"snddir dir_dots",spec,dir_dots);
+ sd_hdg = dir_head > 0; /* Import listing parameters if any */
+ sd_bkp = dir_back > 0;
+ if (dir_dots > -1)
+ sd_dot = dir_dots;
+ else
+ sd_dot = matchdot;
+#else
+ sd_hdg = 1; /* Or use hardwired defaults */
+ sd_bkp = 1;
+ sd_dot = matchdot;
+#endif /* NOICP */
+
+ if (!spec) spec = "";
+ debug(F111,"snddir sd_dot",spec,sd_dot);
+ if (*spec) {
+#ifdef COMMENT
+ zfnqfp(spec,CKMAXPATH,name);
+ debug(F110,"snddir zfnqfp",name,0);
+#else
+ ckstrncpy(name,spec,CKMAXPATH+1);
+ debug(F110,"snddir name",name,0);
+#endif /* COMMENT */
+ } else {
+#ifdef OS2
+ strcpy(name, "*");
+#else
+#ifdef UNIXOROSK
+ strcpy(name, "./*");
+#else
+#ifdef VMS
+ strcpy(name, "*.*");
+#else
+#ifdef datageneral
+ strcpy(name, "+");
+#else
+ debug(F101,"snddir quit (no filespec)","",0);
+ return(0);
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* OS2 */
+ }
+ debug(F110,"snddir name 1",name,0);
+ ndirs = 0L;
+ nfiles = 0L;
+ nbytes = 0L;
+
+ if (zfnqfp(name,CKMAXPATH,fnbuf))
+
+ debug(F110,"snddir name 2",name,0);
+ p = name + strlen(name); /* Move it to end of list */
+
+ /* sprintf safe because funcbuf size >= max path len + 64 */
+
+ if (sd_hdg) {
+ sprintf((char *)funcbuf,"Listing files: %s%s%s",fnbuf,endline,endline);
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ }
+ diractive = 1;
+
+#ifdef OS2
+ if (zchki(name) == -2) { /* Found a directory */
+ p--;
+ if (*p == '\\' || *p == '/')
+ ckstrncat(name, "*", CKMAXPATH);
+ else if (*p == ':')
+ ckstrncat(name, ".", CKMAXPATH);
+ else
+ ckstrncat(name, "\\*", CKMAXPATH);
+ debug(F110,"snddir directory",name,0);
+ }
+#else
+ if (!iswild(name) && isdir(name)) {
+ char * s = name;
+ p--;
+#ifdef UNIXOROSK
+ if (*p == '/') /* So append wildcard to it */
+ ckstrncat(s, "*", CKMAXPATH);
+ else
+ ckstrncat(s, "/*", CKMAXPATH);
+#else
+#ifdef VMS
+ if (*p == ']' || *p == '>' || *p == ':')
+ ckstrncat(s, "*.*", CKMAXPATH);
+#else
+#ifdef datageneral
+ if (*p == ':')
+ ckstrncat(s, "+", CKMAXPATH);
+ else
+ ckstrncat(s, ":+", CKMAXPATH);
+#else
+#ifdef VOS
+ if (*p == '>')
+ ckstrncat(s, "*", CKMAXPATH);
+ else
+ ckstrncat(s, ">*", CKMAXPATH);
+#endif /* VOS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIXOROSK */
+ debug(F110,"snddir directory",name,0);
+ }
+#endif /* OS2 */
+
+ nzxopts = 0;
+#ifdef UNIX
+ {
+ extern char ** mtchs;
+ debug(F111,"snddir sd_dot",spec,sd_dot);
+ if (sd_dot > 0)
+ nzxopts |= ZX_MATCHDOT;
+ if (recursive)
+ nzxopts |= ZX_RECURSE;
+ debug(F111,"snddir nzxopts",spec,nzxopts);
+ nxpnd = nzxpand(name,nzxopts); /* Get the array of names */
+ sh_sort(mtchs,NULL,nxpnd,0,0,1); /* Sort the array */
+ }
+#else
+ if (recursive) nzxopts |= ZX_RECURSE;
+ nxpnd = nzxpand(name,nzxopts);
+#endif /* UNIX */
+
+ debug(F101,"snddir nzxpand nxpnd","",nxpnd);
+ if (nxpnd < 1)
+ return(-1);
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ if ((int)strlen(name) < CMDSTRL - 11) /* Data for X packet. */
+ sprintf(cmdstr,"DIRECTORY %s",name); /* safe */
+ else
+ ckstrncpy(cmdstr,"DIRECTORY",CMDSTRL);
+ first = 1; /* Init getchx lookahead */
+ funcstr = 1; /* Just set the flag. */
+ funcptr = nxtdir; /* And the pointer. */
+ rc = sinit();
+ debug(F111,"snddir","sinit()",rc);
+ return(rc);
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+/* N X T D E L -- provide data for delete */
+
+/* Returns the next available character or -1 if no more data */
+
+static int
+nxtdel(
+#ifdef CK_ANSIC
+ void
+#endif /* CK_ANSIC */
+ ) {
+ char name[257], *p = NULL;
+ int len = 0;
+
+ if (funcnxt < funclen)
+ return ((unsigned)funcbuf[funcnxt++]);
+
+ if (nxpnd > 0) {
+ nxpnd--;
+ znext(name);
+ if (!name[0]) {
+ nxpnd = 0;
+ return(nxtdel());
+ }
+ len = zchki(name);
+
+ /* Find just the name of the file */
+
+ for (p = name + strlen(name); p != name && *p != '/' ; p--) ;
+ if (*p == '/') p++;
+
+ /* sprintf's safe because size of funcbuf >= 64 + maxpathlen */
+
+ if (len > -1L) {
+ if (zdelet(name)) {
+ sprintf((char *)funcbuf," %10s: %s%s","skipping",p,endline);
+ } else {
+ nfiles++;
+ nbytes += len;
+ sprintf((char *)funcbuf," %10s: %s%s","deleted",p,endline);
+ }
+ } else
+ sprintf((char *)funcbuf," directory: %s%s", p, endline);
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ } else
+
+ /* If done processing the expanded entries send a summary statement */
+
+ if (nxpnd == 0) {
+ sprintf((char *)funcbuf,
+ "%s%ld file%s deleted, %ld byte%s freed%s",
+ endline,
+ nfiles,
+ (nfiles == 1) ? "" : "s",
+ nbytes,
+ (nbytes == 1) ? "" : "s",
+ endline
+ );
+ nxpnd--;
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+ } else {
+ funcbuf[0] = '\0';
+ funcnxt = 0;
+ funclen = 0;
+ }
+
+ /* If we have data to send */
+
+ if (funcnxt < funclen)
+ return ((unsigned)funcbuf[funcnxt++]); /* Return a character */
+ else
+ return(-1); /* No more input */
+}
+
+/* S N D D E L -- Send delete message */
+
+int
+snddel(spec) char * spec; {
+#ifndef NOSERVER
+ char name[CKMAXPATH+1];
+#ifdef OS2
+ char * p = NULL;
+#endif /* #ifdef OS2 */
+
+ if (!*spec)
+ return(0);
+
+ ckstrncpy(name, spec, CKMAXPATH+1);
+
+#ifdef OS2
+ /* change / to \. */
+ p = name;
+ while (*p) { /* Change them back to \ */
+ if (*p == '/') *p = '\\';
+ p++;
+ }
+#endif /* OS2 */
+
+ nfiles = nbytes = 0L;
+ sprintf((char *)funcbuf,"Deleting \"%s\"%s",name,endline);
+ funcnxt = 0;
+ funclen = strlen((char *)funcbuf);
+
+ nzxopts = ZX_FILONLY; /* Files only */
+#ifdef UNIXOROSK
+ if (matchdot) nzxopts |= ZX_MATCHDOT;
+#endif /* UNIXOROSK */
+#ifdef COMMENT
+ /* Recursive deleting not supported yet */
+ if (recursive) nzxopts |= ZX_RECURSE;
+#endif /* COMMENT */
+ nxpnd = nzxpand(name,nzxopts);
+ if (nxpnd < 1)
+ return(-1);
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,"REMOTE DELETE",CMDSTRL); /* Data for X packet. */
+ first = 1; /* Init getchx lookahead */
+ funcstr = 1; /* Just set the flag. */
+ funcptr = nxtdel; /* And the pointer. */
+ binary = XYFT_T; /* Use text mode for this, */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+#ifdef OS2
+/* S N D S P A C E -- send disk space message */
+int
+sndspace(drive) int drive; {
+#ifndef NOSERVER
+ static char spctext[64];
+ unsigned long space;
+
+ if (drive) {
+ space = zdskspace(drive - 'A' + 1);
+ if (space > 0 && space < 1024)
+ sprintf(spctext,
+ " Drive %c: unknown%s",
+ drive,
+ endline
+ );
+ else
+ sprintf(spctext,
+ " Drive %c: %ldK free%s",
+ drive,
+ space / 1024L,
+ endline
+ );
+ } else {
+ space = zdskspace(0);
+ if (space > 0 && space < 1024)
+ sprintf(spctext, " Free space: unknown%s", endline);
+ else
+ sprintf(spctext, " Free space: %ldK%s", space / 1024L, endline);
+ }
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,"free space",CMDSTRL); /* Data for X packet. */
+ first = 1; /* Init getchx lookahead */
+ memstr = 1; /* Just set the flag. */
+ memptr = spctext; /* And the pointer. */
+ binary = XYFT_T; /* Text mode for this. */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+
+/* S N D W H O -- send who message */
+int
+sndwho(who) char * who; {
+#ifndef NOSERVER
+ nfils = 0; /* No files, no lists. */
+ xflg = 1; /* Flag we must send X packet. */
+ ckstrncpy(cmdstr,"who",CMDSTRL); /* Data for X packet. */
+ first = 1; /* Init getchx lookahead */
+ memstr = 1; /* Just set the flag. */
+#ifdef NT
+ memptr = "\15\12K95 SERVER\15\12"; /* And the pointer. */
+#else
+ memptr = "\15\12K/2 SERVER\15\12";
+#endif /* NT */
+ binary = XYFT_T; /* Use text mode */
+ return(sinit());
+#else
+ return(0);
+#endif /* NOSERVER */
+}
+#endif /* OS2 */
+
+/* C W D -- Change server's working directory */
+
+/*
+ String passed has first byte as length of directory name, rest of string
+ is name. Returns:
+ 0 on failure.
+ 1 on success after sending short-form response (ACK with name).
+ 2 on success if a CD Message file is to be sent.
+*/
+int
+cwd(vdir) char *vdir; {
+ char *cdd, *dirp;
+
+ vdir[xunchar(*vdir) + 1] = '\0'; /* Terminate string with a null */
+ dirp = vdir+1;
+ tlog(F110,"Directory requested: ",dirp,0L);
+ if (zchdir(dirp)) { /* Try to change */
+ cdd = zgtdir(); /* Get new working directory. */
+ debug(F110,"cwd",cdd,0);
+ if (srvcdmsg) { /* Send orientation file? */
+ int i;
+ for (i = 0; i < 8; i++) {
+ if (zchki(cdmsgfile[i]) > -1) {
+ xxscreen(SCR_CD,0,0l,cdd);
+ tlog(F110,"Changed directory to",cdd,0L);
+ return(2);
+ }
+ }
+ }
+ encstr((CHAR *)cdd); /* Send short-form reply */
+ ack1(data); /* containing directory name. */
+ xxscreen(SCR_CD,0,0l,cdd);
+ tlog(F110,"Changed directory to",cdd,0L);
+ return(1);
+ } else {
+ debug(F110,"cwd failed",dirp,0);
+ tlog(F110,"Failed to change directory to",dirp,0L);
+ return(0);
+ }
+}
+
+
+/* S Y S C M D -- Do a system command */
+
+/* Command string is formed by concatenating the two arguments. */
+
+int
+syscmd(prefix,suffix) char *prefix, *suffix; {
+ extern int i_isopen;
+#ifndef NOPUSH
+ char *cp;
+
+ i_isopen = 0;
+ if (!prefix)
+ return(0);
+ if (!*prefix)
+ return(0);
+ for (cp = cmdstr; *prefix != '\0'; (*cp++ = *prefix++));
+ while ((*cp++ = *suffix++))
+#ifdef OS2
+ /* This takes away more than we gain in convenience
+ if (*(cp-1) == '/') *(cp-1) = '\\' */
+#endif /* OS2 */
+ ; /* Copy suffix */
+
+ debug(F110,"syscmd",cmdstr,0);
+
+ if (zxcmd(ZIFILE,cmdstr) > 0) {
+ debug(F110,"syscmd zxcmd ok",cmdstr,0);
+ nfils = sndsrc = 0; /* Flag that input is from stdin */
+ xflg = hcflg = 1; /* And special flags for pipe */
+ binary = XYFT_T; /* Go to text mode */
+ i_isopen = 1;
+ return (sinit()); /* Send S packet */
+ } else {
+ debug(F100,"syscmd zxcmd failed",cmdstr,0);
+ i_isopen = 0;
+ return(0);
+ }
+#else
+ debug(F100,"syscmd zxcmd NOPUSH",cmdstr,0);
+ i_isopen = 0;
+ return(0);
+#endif /* NOPUSH */
+}
+
+/* R E M S E T -- Remote Set */
+/* Called by server to set variables as commanded in REMOTE SET packets. */
+/* Returns 1 on success, 0 on failure. */
+
+int
+remset(s) char *s; {
+ extern int c_save, en_del;
+ int len, i, x, y;
+ char *p;
+
+ len = xunchar(*s++); /* Length of first field */
+ p = s + len; /* Pointer to second length field */
+ *p++ = '\0'; /* Zero out second length field */
+ x = atoi(s); /* Value of first field */
+ debug(F111,"remset",s,x);
+ debug(F110,"remset",p,0);
+ switch (x) { /* Do the right thing */
+ case 132: /* Attributes (all, in) */
+ atcapr = atoi(p);
+ return(1);
+ case 133: /* File length attributes */
+ case 233: /* IN/OUT combined */
+ case 148: /* Both kinds of lengths */
+ case 248:
+ atleni = atleno = atoi(p);
+ return(1);
+ case 134: /* File Type (text/binary) */
+ case 234:
+ attypi = attypo = atoi(p);
+ return(1);
+ case 135: /* File creation date */
+ case 235:
+ atdati = atdato = atoi(p);
+ return(1);
+ case 139: /* File Blocksize */
+ case 239:
+ atblki = atblko = atoi(p);
+ return(1);
+ case 141: /* Encoding / Character Set */
+ case 241:
+ atenci = atenco = atoi(p);
+ return(1);
+ case 142: /* Disposition */
+ case 242:
+ atdisi = atdiso = atoi(p);
+ return(1);
+ case 145: /* System ID */
+ case 245:
+ atsidi = atsido = atoi(p);
+ return(1);
+ case 147: /* System-Dependent Info */
+ case 247:
+ atsysi = atsyso = atoi(p);
+ return(1);
+ case 232: /* Attributes (all, out) */
+ atcapr = atoi(p);
+ return(1);
+ case 300: /* File type (text, binary) */
+ binary = atoi(p);
+ b_save = binary;
+#ifndef NOICP
+ g_binary = -1;
+#endif /* NOICP */
+ return(1);
+ case 301: /* File name conversion */
+ fncnv = 1 - atoi(p); /* (oops) */
+ f_save = fncnv;
+#ifndef NOICP
+ g_fncnv = -1;
+#endif /* NOICP */
+ return(1);
+ case 302: /* File name collision */
+#ifdef IKSD
+#ifdef CK_LOGIN
+ if (inserver && isguest) /* May not be changed by guest */
+ return(0);
+#endif /* CK_LOGIN */
+#endif /* IKSD */
+ x = atoi(p);
+ if (!ENABLED(en_del) && (x == XYFX_X || x == XYFX_U))
+ return(0);
+ if (x == XYFX_R) ckwarn = 1; /* Rename */
+ if (x == XYFX_X) ckwarn = 0; /* Replace */
+ fncact = x;
+ return(1);
+ case 310: /* Incomplete File Disposition */
+ keep = atoi(p); /* Keep, Discard, Auto */
+ return(1);
+ case 311: /* Blocksize */
+ fblksiz = atoi(p);
+ return(1);
+ case 312: /* Record Length */
+ frecl = atoi(p);
+ return(1);
+ case 313: /* Record format */
+ frecfm = atoi(p);
+ return(1);
+ case 314: /* File organization */
+ forg = atoi(p);
+ return(1);
+ case 315: /* File carriage control */
+ fcctrl = atoi(p);
+ return(1);
+ case 330: /* Match dotfiles */
+#ifndef NOICP
+ dir_dots = -1; /* This undoes DIR /DOT option */
+#endif /* NOICP */
+ matchdot = atoi(p);
+ return(1);
+ case 331: /* Match FIFOs */
+ matchfifo = atoi(p);
+ return(1);
+ case 400: /* Block check */
+ y = atoi(p);
+ if (y < 5 && y > 0) {
+ bctr = y;
+ c_save = -1;
+ return(1);
+ } else if (*p == 'B') {
+ bctr = 4;
+ c_save = -1;
+ return(1);
+ }
+ return(0);
+ case 401: /* Receive packet-length */
+ rpsiz = urpsiz = atoi(p);
+ if (urpsiz > MAXRP) urpsiz = MAXRP; /* Max long-packet length */
+ if (rpsiz > 94) rpsiz = 94; /* Max short-packet length */
+ urpsiz = adjpkl(urpsiz,wslots,bigrbsiz);
+ return(1);
+ case 402: /* Receive timeout */
+ y = atoi(p); /* Client is telling us */
+ if (y > -1 && y < 999) { /* the timeout that it wants */
+ pkttim = chktimo(y,timef); /* us to tell it to use. */
+ return(1);
+ } else return(0);
+ case 403: /* Retry limit */
+ y = atoi(p);
+ if (y > -1 && y < 95) {
+ maxtry = y;
+ return(1);
+ } else return(0);
+ case 404: /* Server timeout */
+ y = atoi(p);
+ if (y < 0) return(0);
+ srvtim = y;
+ return(1);
+
+#ifndef NOCSETS
+ case 405: { /* Transfer character set */
+ extern int s_cset, axcset[];
+ int i;
+ for (i = 0; i < ntcsets; i++) {
+ if (!strcmp(tcsinfo[i].designator,p)) break;
+ }
+ debug(F101,"remset tcharset lookup","",i);
+ if (i == ntcsets) return(0);
+ tcharset = tcsinfo[i].code; /* If known, use it */
+ debug(F101,"remset tcharset","",tcharset);
+ if (s_cset == XMODE_A)
+ if (axcset[tcharset] > -1 && axcset[tcharset] > MAXFCSETS)
+ fcharset = axcset[tcharset]; /* Auto-pick file charset */
+ debug(F101,"remset tcharset fcharset","",fcharset);
+ setxlatype(tcharset,fcharset); /* Set up charset translations */
+ debug(F101,"remset xlatype","",xlatype);
+ debug(F101,"remset tcharset after setxlatype","",tcharset);
+ tcs_save = -1;
+ return(1);
+ }
+ case 320: { /* File character set */
+ extern struct keytab fcstab[];
+ extern int nfilc, s_cset, r_cset;
+ x = lookup(fcstab,p,nfilc,&y);
+ debug(F111,"RSET FILE CHAR name",p,x);
+ if (x < 0)
+ return(0);
+ s_cset = XMODE_M; /* No automatic charset switching */
+ r_cset = XMODE_M;
+ fcharset = x; /* Set file charset */
+ setxlatype(tcharset,fcharset); /* and translation type */
+ fcs_save = -1;
+ return(1);
+ }
+#endif /* NOCSETS */
+
+ case 406: /* Window slots */
+ y = atoi(p);
+ if (y == 0) y = 1;
+ if (y < 1 || y > MAXWS) return(0);
+ wslotr = y;
+ swcapr = 1;
+ urpsiz = adjpkl(urpsiz,wslotr,bigrbsiz);
+ return(1);
+
+ case 410: /* Transfer mode */
+ y = atoi(p); /* 0 = automatic, nonzero = manual */
+ if (y != 0) y = 1;
+ xfermode = y;
+ debug(F101,"REMOTE SET xfermode","",xfermode);
+ return(1);
+
+ case 420: /* SERVER CD-MESSAGE { ON, OFF } */
+ y = atoi(p); /* 0 = automatic, nonzero = manual */
+ srvcdmsg = y;
+ return(1);
+
+ default: /* Anything else... */
+ return(0);
+ }
+}
+
+/* Adjust packet length based on number of window slots and buffer size */
+
+int
+adjpkl(pktlen,slots,bufsiz) int pktlen, slots, bufsiz; {
+ if (protocol != PROTO_K) return(pktlen);
+ debug(F101,"adjpkl len","",pktlen);
+ debug(F101,"adjpkl slots","",slots);
+ debug(F101,"adjpkl bufsiz","",bufsiz);
+ if (((pktlen + 6) * slots) > bufsiz)
+ pktlen = (bufsiz / slots) - 6;
+ debug(F101,"adjpkl new len","",pktlen);
+ return(pktlen);
+}
+
+/* Set transfer mode and file naming based on comparison of system types */
+
+
+VOID
+whoarewe() {
+#ifndef NOICP
+ extern int g_xfermode;
+#endif /* NOICP */
+
+ wearealike = 0;
+
+ debug(F101,"whoarewe xfermode","",xfermode);
+#ifndef NOICP
+ debug(F101,"whoarewe g_xfermode","",g_xfermode);
+#endif /* NOICP */
+ if (whoareu[0]) { /* If we know partner's system type */
+ char * p = (char *)whoareu;
+ debug(F110,"whoarewe remote sysid",whoareu,0);
+ if (!strcmp(p,cksysid)) /* Other system same as us */
+ wearealike = 1;
+
+#ifdef UNIX
+ else if (!strcmp(p,"L3")) /* UNIX is sort of like AmigaDOS */
+ wearealike = 1; /* (same directory separator) */
+ else if (!strcmp(p,"N3")) /* UNIX like Aegis */
+ wearealike = 1;
+#else
+#ifdef AMIGA
+/* Like UNIX, but case distinctions are ignored and can begin with device:. */
+ else if (!strcmp(p,"U1")) /* Amiga is sort of like UNIX */
+ wearealike = 1;
+ else if (!strcmp(p,"N3")) /* Amiga is sort of like Aegis */
+ wearealike = 1;
+#else
+#ifdef OS2 /* (Includes Windows 95/NT) */
+
+ /* DOS, GEMDOS, Windows 3.x, Windows 95, Windows NT */
+ /* All "the same" for FAT partitions but all bets off otherwise */
+ /* so this part needs some refinement ... */
+
+ else if (!strcmp(p,"U8")) /* MS-DOS */
+ wearealike = 1;
+ else if (!strcmp(p,"UO")) /* OS/2 */
+ wearealike = 1;
+ else if (!strcmp(p,"UN")) /* Windows NT or 95 */
+ wearealike = 1;
+ else if (!strcmp(p,"K2")) /* GEMDOS */
+ wearealike = 1;
+#else
+#ifdef GEMDOS
+ else if (!strcmp(p,"U8"))
+ wearealike = 1;
+ else if (!strcmp(p,"UO"))
+ wearealike = 1;
+ else if (!strcmp(p,"UN"))
+ wearealike = 1;
+ else if (!strcmp(p,"K2"))
+ wearealike = 1;
+#endif /* GEMDOS */
+#endif /* OS2 */
+#endif /* AMIGA */
+#endif /* UNIX */
+
+ /* Get here with wearealike == 1 if system types match */
+
+ debug(F101,"whoarewe wearealike","",wearealike);
+ if (!wearealike) /* Not alike */
+ return;
+
+ fncnv = XYFN_L; /* Alike, so literal filenames */
+ debug(F101,"whoarewe setting fncnv","",fncnv);
+
+ if (xfermode == XMODE_A) { /* Current xfer mode is auto */
+#ifdef VMS
+ binary = XYFT_L; /* For VMS-to-VMS, use labeled */
+#else
+#ifdef OS2
+ /* OS/2 but not Windows */
+ if (!strcmp(cksysid,"UO") && !strcmp((char *)whoareu,"UO"))
+ binary = XYFT_L; /* For OS/2-to-OS/2, use labeled */
+#else
+ binary = XYFT_B; /* For all others use binary */
+#endif /* OS2 */
+#endif /* VMS */
+ gnf_binary = binary; /* Prevailing type for gnfile() */
+ debug(F101,"whoarewe setting binary","",binary);
+ }
+ }
+}
+#endif /* NOXFER */
diff --git a/ckermit-8.0.211/ckcftp.c b/ckermit-8.0.211/ckcftp.c
new file mode 100644
index 0000000..d8d9ddd
--- /dev/null
+++ b/ckermit-8.0.211/ckcftp.c
@@ -0,0 +1,17126 @@
+/* C K C F T P -- FTP Client for C-Kermit */
+
+char *ckftpv = "FTP Client, 8.0.226, 7 Jan 2004";
+
+/*
+ Authors:
+ Jeffrey E Altman <jaltman@secure-endpoints.com>
+ Secure Endpoints Inc., New York City
+ Frank da Cruz <fdc@columbia.edu>,
+ The Kermit Project, Columbia University.
+
+ Copyright (C) 2000, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+
+ Portions of conditionally included code Copyright Regents of the
+ University of California and The Stanford SRP Authentication Project;
+ see notices below.
+*/
+
+/*
+ Pending...
+
+ . Implement recursive NLST downloads by trying to CD to each filename.
+ If it works, it's a directory; if not, it's a file -- GET it. But
+ that won't work with servers like wu-ftpd that don't send directory
+ names. Recursion with MLSD is done.
+
+ . Make syslog entries for session? Files?
+
+ . Messages are printed to stdout and stderr in random fashion. We should
+ either print everything to stdout, or else be systematic about when
+ to use stderr.
+
+ . Implement mail (MAIL, MLFL, MSOM, etc) if any servers support it.
+
+ . Adapt to VMS. Big job because of its record-oriented file system.
+ RMS programmer required. There are probably also some VMS TCP/IP
+ product-specific wrinkles, e.g. attribute preservation in VMS-to-VMS
+ transfers using special options for Multinet or other FTP servers
+ (find out about STRU VMS).
+*/
+
+/*
+ Quick FTP command reference:
+
+ RFC765 (1980) and earlier:
+ MODE S(tream), B(lock), C(ompressed)
+ STRU F(ILE), R(ECORD), P(AGE)
+ TYPE A(SCII) <format>, E(BCDIC) <format>, I(MAGE), L(OCAL) <bytesize>
+ PORT - Port
+ PASV - Passive mode
+ USER - User
+ PASS - Password
+ ACCT - Account
+ CWD - Change Working Directory
+ REIN - Logout but not disconnect
+ QUIT - Bye
+ RETR - Retreive
+ STOR - Store
+ APPE - Append
+ ALLO - Allocate
+ REST - Restart
+ RNFR - Rename from
+ RNTO - Rename to
+ ABOR - Cancel
+ DELE - Delete
+ LIST - Directory
+ NLST - Name List
+ SITE - Site parameters or commands
+ STAT - Status
+ HELP - Help
+ NOOP - Noop
+
+ RFC959 (1985):
+ CDUP - Change to Parent Directory
+ SMNT - Structure Mount
+ STOU - Store Unique
+ RMD - Remove Directory
+ MKD - Make Directory
+ PWD - Print Directory
+ SYST - System
+
+ RFC2389 (1998):
+ FEAT - List Features (done)
+ OPTS - Send options (done)
+
+ RFC2640 (1999):
+ LANG - Specify language for messages (not done)
+
+ Pending (Internet Drafts):
+ SIZE - File size (done)
+ MDTM - File modification date-time (done)
+ MLST - File name and attribute list (single file) (not done)
+ MLSD - File list with attributes (multiple files) (done)
+ MAIL, MLFL, MSOM - mail delivery (not done)
+
+ Alphabetical syntax list:
+ ABOR <CRLF>
+ ACCT <SP> <account-information> <CRLF>
+ ALLO <SP> <decimal-integer> [<SP> R <SP> <decimal-integer>] <CRLF>
+ APPE <SP> <pathname> <CRLF>
+ CDUP <CRLF>
+ CWD <SP> <pathname> <CRLF>
+ DELE <SP> <pathname> <CRLF>
+ FEAT <CRLF>
+ HELP [<SP> <string>] <CRLF>
+ LANG [<SP> <language-tag> ] <CRLF>
+ LIST [<SP> <pathname>] <CRLF>
+ MKD <SP> <pathname> <CRLF>
+ MLSD [<SP> <pathname>] <CRLF>
+ MLST [<SP> <pathname>] <CRLF>
+ MODE <SP> <mode-code> <CRLF>
+ NLST [<SP> <pathname-or-wildcard>] <CRLF>
+ NOOP <CRLF>
+ OPTS <SP> <commandname> [ <SP> <command-options> ] <CRLF>
+ PASS <SP> <password> <CRLF>
+ PASV <CRLF>
+ PORT <SP> <host-port> <CRLF>
+ PWD <CRLF>
+ QUIT <CRLF>
+ REIN <CRLF>
+ REST <SP> <marker> <CRLF>
+ RETR <SP> <pathname> <CRLF>
+ RMD <SP> <pathname> <CRLF>
+ RNFR <SP> <pathname> <CRLF>
+ RNTO <SP> <pathname> <CRLF>
+ SITE <SP> <string> <CRLF>
+ SIZE <SP> <pathname> <CRLF>
+ SMNT <SP> <pathname> <CRLF>
+ STAT [<SP> <pathname>] <CRLF>
+ STOR <SP> <pathname> <CRLF>
+ STOU <CRLF>
+ STRU <SP> <structure-code> <CRLF>
+ SYST <CRLF>
+ TYPE <SP> <type-code> <CRLF>
+ USER <SP> <username> <CRLF>
+*/
+#include "ckcsym.h" /* Standard includes */
+#include "ckcdeb.h"
+
+#ifndef NOFTP /* NOFTP = no FTP */
+#ifndef SYSFTP /* SYSFTP = use external ftp client */
+#ifdef TCPSOCKET /* Build only if TCP/IP included */
+#define CKCFTP_C
+
+/* Note: much of the following duplicates what was done in ckcdeb.h */
+/* but let's not mess with it unless it causes trouble. */
+
+#ifdef CK_ANSIC
+#include <stdarg.h>
+#else /* CK_ANSIC */
+#include <varargs.h>
+#endif /* CK_ANSIC */
+#include <signal.h>
+#ifdef OS2
+#ifdef OS2ONLY
+#include <os2.h>
+#endif /* OS2ONLY */
+#include "ckowin.h"
+#include "ckocon.h"
+#endif /* OS2 */
+#ifndef ZILOG
+#ifdef NT
+#include <setjmpex.h>
+#ifdef NTSIG
+extern int TlsIndex;
+#endif /* NTSIG */
+#else /* NT */
+#include <setjmp.h>
+#endif /* NT */
+#else
+#include <setret.h>
+#endif /* ZILOG */
+#include "ckcsig.h"
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#ifndef NOTIMEH
+#include <time.h>
+#endif /* NOTIMEH */
+#ifndef EPIPE
+#define EPIPE 32 /* Broken pipe error */
+#endif /* EPIPE */
+
+/* Kermit includes */
+
+#include "ckcasc.h"
+#include "ckcker.h"
+#include "ckucmd.h"
+#include "ckuusr.h"
+#include "ckcnet.h" /* Includes ckctel.h */
+#include "ckctel.h" /* (then why include it again?) */
+#include "ckcxla.h"
+
+/*
+ How to get the struct timeval definition so we can call select(). The
+ xxTIMEH symbols are defined in ckcdeb.h, overridden in various makefile
+ targets. The problem is: maybe we have already included some header file
+ that defined struct timeval, and maybe we didn't. If we did, we don't want
+ to include another header file that defines it again or the compilation will
+ fail. If we didn't, we have to include the header file where it's defined.
+ But in some cases even that won't work because of strict POSIX constraints
+ or somesuch, or because this introduces other conflicts (e.g. struct tm
+ multiply defined), in which case we have to define it ourselves, but this
+ can work only if we didn't already encounter a definition.
+*/
+#ifndef DCLTIMEVAL
+#ifdef SV68R3V6
+#define DCLTIMEVAL
+#else
+#ifdef SCO234
+#define DCLTIMEVAL
+#endif /* SCO234 */
+#endif /* SV68R3V6 */
+#endif /* DCLTIMEVAL */
+
+#ifdef DCLTIMEVAL
+/* Also maybe in some places the elements must be unsigned... */
+struct timeval {
+ long tv_sec;
+ long tv_usec;
+};
+#ifdef COMMENT
+/* Currently we don't use this... */
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+#endif /* COMMENT */
+#else /* !DCLTIMEVAL */
+#ifndef NOSYSTIMEH
+#ifdef SYSTIMEH
+#include <sys/time.h>
+#endif /* SYSTIMEH */
+#endif /* NOSYSTIMEH */
+#ifndef NOSYSTIMEBH
+#ifdef SYSTIMEBH
+#include <sys/timeb.h>
+#endif /* SYSTIMEBH */
+#endif /* NOSYSTIMEBH */
+#endif /* DCLTIMEVAL */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#ifndef NOSETTIME
+#ifdef COMMENT
+/* This section moved to ckcdeb.h */
+#ifdef POSIX
+#define UTIMEH
+#else
+#ifdef HPUX9
+#define UTIMEH
+#else
+#ifdef OS2
+#define SYSUTIMEH
+#endif /* OS2 */
+#endif /* HPUX9 */
+#endif /* POSIX */
+#endif /* COMMENT */
+
+#ifdef SYSUTIMEH
+#include <sys/utime.h>
+#else
+#ifdef UTIMEH
+#include <utime.h>
+#define SYSUTIMEH
+#endif /* UTIMEH */
+#endif /* SYSUTIMEH */
+#endif /* NOSETTIME */
+
+#ifndef SCO_OSR504
+#ifdef SELECT_H
+#include <sys/select.h>
+#endif /* SELECT_H */
+#endif /* SCO_OSR504 */
+
+/* select() dialects... */
+
+#ifdef UNIX
+#define BSDSELECT /* BSD select() syntax/semantics */
+#else
+#ifdef OS2 /* OS/2 or Win32 */
+#ifdef NT
+#define BSDSELECT
+#else /* NT */
+#define IBMSELECT
+#endif /* NT */
+#endif /* OS2 */
+#endif /* UNIX */
+
+/* Other select() peculiarities */
+
+#ifdef HPUX
+#ifndef HPUX10 /* HP-UX 9.xx and earlier */
+#ifndef HPUX1100
+/* The three interior args to select() are (int *) rather than (fd_set *) */
+#ifndef INTSELECT
+#define INTSELECT
+#endif /* INTSELECT */
+#endif /* HPUX1100 */
+#endif /* HPUX10 */
+#endif /* HPUX */
+
+#ifdef CK_SOCKS /* SOCKS Internet relay package */
+#ifdef CK_SOCKS5 /* SOCKS 5 */
+#define accept SOCKSaccept
+#define bind SOCKSbind
+#define connect SOCKSconnect
+#define getsockname SOCKSgetsockname
+#define listen SOCKSlisten
+#else /* Not SOCKS 5 */
+#define accept Raccept
+#define bind Rbind
+#define connect Rconnect
+#define getsockname Rgetsockname
+#define listen Rlisten
+#endif /* CK_SOCKS5 */
+#endif /* CK_SOCKS */
+
+#ifndef NOHTTP
+extern char * tcp_http_proxy; /* Name[:port] of http proxy server */
+extern int tcp_http_proxy_errno;
+extern char * tcp_http_proxy_user;
+extern char * tcp_http_proxy_pwd;
+extern char * tcp_http_proxy_agent;
+#define HTTPCPYL 1024
+static char proxyhost[HTTPCPYL];
+#endif /* NOHTTP */
+int ssl_ftp_proxy = 0; /* FTP over SSL/TLS Proxy Server */
+
+/* Feature selection */
+
+#ifndef USE_SHUTDOWN
+/*
+ We don't use shutdown() because (a) we always call it just before close()
+ so it's redundant and unnecessary, and (b) it introduces a long pause on
+ some platforms like SV/68 R3.
+*/
+/* #define USE_SHUTDOWN */
+#endif /* USE_SHUTDOWN */
+
+#ifndef NORESEND
+#ifndef NORESTART /* Restart / recover */
+#ifndef FTP_RESTART
+#define FTP_RESTART
+#endif /* FTP_RESTART */
+#endif /* NORESTART */
+#endif /* NORESEND */
+
+#ifndef NOUPDATE /* Update mode */
+#ifndef DOUPDATE
+#define DOUPDATE
+#endif /* DOUPDATE */
+#endif /* NOUPDATE */
+
+#ifndef UNICODE /* Unicode required */
+#ifndef NOCSETS /* for charset translation */
+#define NOCSETS
+#endif /* NOCSETS */
+#endif /* UNICODE */
+
+#ifndef OS2
+#ifndef HAVE_MSECS /* Millisecond timer */
+#ifdef UNIX
+#ifdef GFTIMER
+#define HAVE_MSECS
+#endif /* GFTIMER */
+#endif /* UNIX */
+#endif /* HAVE_MSECS */
+#endif /* OS2 */
+
+#ifdef PIPESEND /* PUT from pipe */
+#ifndef PUTPIPE
+#define PUTPIPE
+#endif /* PUTPIPE */
+#endif /* PIPESEND */
+
+#ifndef NOSPL /* PUT from array */
+#ifndef PUTARRAY
+#define PUTARRAY
+#endif /* PUTARRAY */
+#endif /* NOSPL */
+
+/* Security... */
+
+#ifdef CK_SRP
+#define FTP_SRP
+#endif /* CK_SRP */
+
+#ifdef CK_KERBEROS
+#ifdef KRB4
+/*
+ There is a conflict between the Key Schedule formats used internally
+ within the standalone MIT KRB4 library and that used by Eric Young
+ in OpenSSL and his standalone DES library. Therefore, KRB4 FTP AUTH
+ cannot be supported when either of those two packages are used.
+*/
+#ifdef KRB524
+#define FTP_KRB4
+#else /* KRB524 */
+#ifndef CK_SSL
+#ifndef LIBDES
+#define FTP_KRB4
+#endif /* LIBDES */
+#endif /* CK_SSL */
+#endif /* KRB524 */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifndef HEIMDAL
+#define FTP_GSSAPI
+#endif /* HEIMDAL */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+/* FTP_SECURITY is defined if any of the above is selected */
+#ifndef FTP_SECURITY
+#ifdef FTP_GSSAPI
+#define FTP_SECURITY
+#else
+#ifdef FTP_KRB4
+#define FTP_SECURITY
+#else
+#ifdef FTP_SRP
+#define FTP_SECURITY
+#else
+#ifdef CK_SSL
+#define FTP_SECURITY
+#endif /* CK_SSL */
+#endif /* FTP_SRP */
+#endif /* FTP_KRB4 */
+#endif /* FTP_GSSAPI */
+#endif /* FTP_SECURITY */
+
+#ifdef CK_DES
+#ifdef CK_SSL
+#ifndef LIBDES
+#define LIBDES
+#endif /* LIBDES */
+#endif /* CK_SSL */
+#endif /* CK_DES */
+
+#ifdef CRYPT_DLL
+#ifndef LIBDES
+#define LIBDES
+#endif /* LIBDES */
+#endif /* CRYPT_DLL */
+
+#ifdef FTP_KRB4
+#define des_cblock Block
+#define des_key_schedule Schedule
+#ifdef KRB524
+#ifdef NT
+#define _WINDOWS
+#endif /* NT */
+#include "kerberosIV/krb.h"
+#else /* KRB524 */
+#ifdef SOLARIS
+#ifndef sun
+/* For some reason lost in history the Makefile Solaris targets have -Usun */
+#define sun
+#endif /* sun */
+#endif /* SOLARIS */
+#include "krb.h"
+#define krb_get_err_text_entry krb_get_err_text
+#endif /* KRB524 */
+#endif /* FTP_KRB4 */
+
+#ifdef CK_SSL
+#ifdef FTP_KRB4
+#ifndef HEADER_DES_H
+#define HEADER_DES_H
+#endif /* HEADER_DES_H */
+#endif /* FTP_KRB4 */
+#include "ck_ssl.h"
+#endif /* CK_SSL */
+
+#ifdef FTP_SRP
+#ifdef HAVE_PWD_H
+#include "pwd.h"
+#endif /* HAVE_PWD_H */
+#include "t_pwd.h"
+#include "t_client.h"
+#include "krypto.h"
+#endif /* FTP_SRP */
+
+#ifdef FTP_GSSAPI
+#include <gssapi/gssapi.h>
+/*
+ Need to include the krb5 file, because we're doing manual fallback
+ from the v2 mech to the v1 mech. Once there's real negotiation,
+ we can be generic again.
+*/
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+static gss_ctx_id_t gcontext;
+#endif /* FTP_GSSAPI */
+
+#ifdef OS2
+#ifdef FTP_SRP
+#define MAP_KRYPTO
+#ifdef SRPDLL
+#define MAP_SRP
+#endif /* SRPDLL */
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+#define MAP_KRB4
+#ifdef CK_ENCRYPTION
+#define MAP_DES
+#endif /* CK_ENCRYPTION */
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+#define MAP_GSSAPI
+#define GSS_OIDS
+#endif /* FTP_GSSAPI */
+#include "ckoath.h"
+
+extern int k95stdout, wherex[], wherey[];
+extern unsigned char colorcmd;
+#endif /* OS2 */
+
+#ifdef FTP_KRB4
+static char ftp_realm[REALM_SZ + 1];
+static KTEXT_ST ftp_tkt;
+#ifdef OS2
+static LEASH_CREDENTIALS ftp_cred;
+#else /* OS2 */
+static CREDENTIALS ftp_cred;
+#endif /* OS2 */
+static MSG_DAT ftp_msg_data;
+static des_key_schedule ftp_sched;
+static int foo[4] = {99,99,99,99};
+#endif /* FTP_KRB4 */
+
+/* getreply() function codes */
+
+#define GRF_AUTH 1 /* Reply to AUTH command */
+#define GRF_FEAT 2 /* Reply to FEAT command */
+
+/* Operational definitions */
+
+#define DEF_VBM 0 /* Default verbose mode */
+/* #define SETVBM */ /* (see getreply) */
+
+#define URL_ONEFILE /* GET, not MGET, for FTP URL */
+
+#define FTP_BUFSIZ 10240 /* Max size for FTP cmds & replies */
+#define SRVNAMLEN 32 /* Max length for server type name */
+#define PWDSIZ 256
+#define PASSBUFSIZ 256
+#define PROMPTSIZ 256
+
+#ifndef MGETMAX /* Max operands for MGET command */
+#define MGETMAX 1000
+#endif /* MGETMAX */
+
+#ifdef FTP_SRP
+#define FUDGE_FACTOR 100
+#endif /* FTP_SRP */
+
+/*
+ Amount of growth from cleartext to ciphertext. krb_mk_priv adds this
+ number bytes. Must be defined for each auth type.
+ GSSAPI appears to add 52 bytes, but I'm not sure it is a constant--hartmans
+ 3DES requires 56 bytes. Lets use 96 just to be sure.
+*/
+#ifdef FTP_GSSAPI
+#ifndef FUDGE_FACTOR
+#define FUDGE_FACTOR 96
+#endif /* FUDGE_FACTOR */
+#endif /* FTP_GSSAPI */
+
+#ifdef FTP_KRB4
+#ifndef FUDGE_FACTOR
+#define FUDGE_FACTOR 32
+#endif /* FUDGE_FACTOR */
+#endif /* FTP_KRB4 */
+
+#ifndef FUDGE_FACTOR /* In case no auth types define it */
+#define FUDGE_FACTOR 0
+#endif /* FUDGE_FACTOR */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif /* MAXHOSTNAMELEN */
+#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
+
+/* Fascist compiler toadying */
+
+#ifndef SENDARG2TYPE
+#ifdef COMMENT /* Might be needed here and there */
+#define SENDARG2TYPE const char *
+#else
+#define SENDARG2TYPE char *
+#endif /* COMMENT */
+#endif /* SENDARG2TYPE */
+
+/* Common text messages */
+
+static char *nocx = "?No FTP control connection\n";
+
+static char *fncnam[] = {
+ "rename", "overwrite", "backup", "append", "discard", "ask", "update",
+ "dates-differ", ""
+};
+
+/* Macro definitions */
+
+/* Used to speed up text-mode PUTs */
+#define zzout(fd,c) \
+((fd<0)?(-1):((nout>=ucbufsiz)?(zzsend(fd,c)):(ucbuf[nout++]=c)))
+
+#define CHECKCONN() if(!connected){printf(nocx);return(-9);}
+
+/* Externals */
+
+#ifdef CK_URL
+extern struct urldata g_url;
+#endif /* CK_URL */
+
+#ifdef DYNAMIC
+extern char *zinbuffer, *zoutbuffer; /* Regular Kermit file i/o */
+#else
+extern char zinbuffer[], zoutbuffer[];
+#endif /* DYNAMIC */
+extern char *zinptr, *zoutptr;
+extern int zincnt, zoutcnt, zobufsize, fncact;
+
+#ifdef CK_TMPDIR
+extern int f_tmpdir; /* Directory changed temporarily */
+extern char savdir[]; /* For saving current directory */
+extern char * dldir;
+#endif /* CK_TMPDIR */
+
+extern char * rfspec, * sfspec, * srfspec, * rrfspec; /* For WHERE command */
+
+extern xx_strp xxstring;
+extern struct keytab onoff[], txtbin[], rpathtab[];
+extern int nrpathtab, xfiletype, patterns, gnferror, moving, what, pktnum;
+extern int success, nfils, sndsrc, quiet, nopush, recursive, inserver, binary;
+extern int filepeek, nscanfile, fsecs, xferstat, xfermode, lastxfer, tsecs;
+extern int backgrd, spackets, rpackets, spktl, rpktl, xaskmore, cmd_rows;
+extern int nolinks, msgflg, keep;
+extern long fsize, ffc, tfc, filcnt, xfsecs, tfcps, cps, oldcps;
+#ifdef GFTIMER
+extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
+#else
+extern long xfsecs;
+#endif /* GFTIMER */
+
+extern char filnam[], * filefile, myhost[];
+extern char * snd_move, * rcv_move, * snd_rename, * rcv_rename;
+extern int g_skipbup, skipbup, sendmode;
+extern int g_displa, fdispla, displa;
+
+#ifdef LOCUS
+extern int locus, autolocus;
+#endif /* LOCUS */
+
+#ifndef NOCSETS
+extern int nfilc, dcset7, dcset8, fileorder;
+extern struct csinfo fcsinfo[];
+extern struct keytab fcstab[];
+extern int fcharset;
+#endif /* NOCSETS */
+
+extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */
+extern char sndnbefore[], sndnafter[], *rcvexcept[];
+extern CHAR feol;
+extern long sendstart, sndsmaller, sndlarger, rs_len;
+
+extern char * remdest;
+extern int remfile, remappd, rempipe;
+
+#ifndef NOSPL
+extern int cmd_quoting;
+#ifdef PUTARRAY
+extern int sndxlo, sndxhi, sndxin;
+extern char sndxnam[];
+extern char **a_ptr[]; /* Array pointers */
+extern int a_dim[]; /* Array dimensions */
+#endif /* PUTARRAY */
+#endif /* NOSPL */
+
+#ifndef NOMSEND /* MPUT and ADD SEND-LIST lists */
+extern char *msfiles[];
+extern int filesinlist;
+extern struct filelist * filehead;
+extern struct filelist * filetail;
+extern struct filelist * filenext;
+extern int addlist;
+extern char fspec[]; /* Most recent filespec */
+extern int fspeclen; /* Length of fspec[] buffer */
+#endif /* NOMSEND */
+
+extern int pipesend;
+#ifdef PIPESEND
+extern char * sndfilter, * rcvfilter;
+#endif /* PIPESEND */
+
+#ifdef CKROOT
+extern int ckrooterr;
+#endif /* CKROOT */
+
+#ifdef KRB4
+extern int krb4_autoget;
+_PROTOTYP(char * ck_krb4_realmofhost,(char *));
+#endif /* KRB4 */
+
+#ifdef KRB5
+extern int krb5_autoget;
+extern int krb5_d_no_addresses;
+_PROTOTYP(char * ck_krb5_realmofhost,(char *));
+#endif /* KRB5 */
+
+#ifdef DCMDBUF
+extern char *atmbuf; /* Atom buffer (malloc'd) */
+extern char *cmdbuf; /* Command buffer (malloc'd) */
+extern char *line; /* Big string buffer #1 */
+extern char *tmpbuf; /* Big string buffer #2 */
+#else
+extern char atmbuf[]; /* The same, but static */
+extern char cmdbuf[];
+extern char line[];
+extern char tmpbuf[];
+#endif /* DCMDBUF */
+
+extern char * cmarg, * cmarg2, ** cmlist; /* For setting up file lists */
+
+/* Public variables declared here */
+
+#ifdef NOXFER
+int ftpget = 1; /* GET/PUT/REMOTE orientation FTP */
+#else
+int ftpget = 2; /* GET/PUT/REMOTE orientation AUTO */
+#endif /* NOXFER */
+int ftpcode = -1; /* Last FTP response code */
+int ftp_cmdlin = 0; /* FTP invoked from command line */
+int ftp_fai = 0; /* FTP failure count */
+int ftp_deb = 0; /* FTP debugging */
+int ftp_dis = -1; /* FTP display style */
+int ftp_log = 1; /* FTP Auto-login */
+int sav_log = -1;
+int ftp_action = 0; /* FTP action from command line */
+int ftp_dates = 1; /* Set file dates from server */
+
+char ftp_reply_str[FTP_BUFSIZ] = ""; /* Last line of previous reply */
+char ftp_srvtyp[SRVNAMLEN] = { NUL, NUL }; /* Server's system type */
+char ftp_user_host[MAX_DNS_NAMELEN]= ""; /* FTP hostname specified by user */
+char * ftp_host = NULL; /* FTP hostname */
+char * ftp_logname = NULL; /* FTP username */
+char * ftp_rdir = NULL; /* Remote directory from cmdline */
+char * ftp_apw = NULL; /* Anonymous password */
+
+/* Definitions and typedefs needed for prototypes */
+
+#define sig_t my_sig_t
+#define sigtype SIGTYP
+typedef sigtype (*sig_t)();
+
+/* Static global variables */
+
+static char ftpsndbuf[FTP_BUFSIZ+64];
+
+static char * fts_sto = NULL;
+
+static int ftpsndret = 0;
+static struct _ftpsnd {
+ sig_t oldintr, oldintp;
+ int reply;
+ int incs,
+ outcs;
+ char * cmd, * local, * remote;
+ int bytes;
+ int restart;
+ int xlate;
+ char * lmode;
+} ftpsnd;
+
+/*
+ This is just a first stab -- these strings should match how the
+ corresponding FTP servers identify themselves.
+*/
+#ifdef UNIX
+static char * myostype = "UNIX";
+#else
+#ifdef VMS
+/* not yet... */
+static char * myostype = "VMS";
+#else
+#ifdef OS2
+#ifdef NT
+static char * myostype = "WIN32";
+#else
+static char * myostype = "OS/2";
+#endif /* NT */
+#else
+static char * myostype = "UNSUPPORTED";
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* UNIX */
+
+static int noinit = 0; /* Don't send REST, STRU, MODE */
+static int alike = 0; /* Client/server like platforms */
+static int local = 1; /* Shadows Kermit global 'local' */
+static int dout = -1; /* Data connection file descriptor */
+static int dpyactive = 0; /* Data transfer is active */
+static int globaldin = -1; /* Data connection f.d. */
+static int out2screen = 0; /* GET output is to screen */
+static int forcetype = 0; /* Force text or binary mode */
+static int cancelfile = 0; /* File canceled */
+static int cancelgroup = 0; /* Group canceled */
+static int anonymous = 0; /* Logging in as anonymous */
+static int loggedin = 0; /* Logged in (or not) */
+static int puterror = 0; /* What to do on PUT error */
+static int geterror = 0; /* What to do on GET error */
+static int rfrc = 0; /* remote_files() return code */
+static int okrestart = 0; /* Server understands REST */
+static int printlines = 0; /* getreply()should print data lines */
+static int haveurl = 0; /* Invoked by command-line FTP URL */
+static int mdtmok = 1; /* Server supports MDTM */
+static int sizeok = 1;
+static int featok = 1;
+static int mlstok = 1;
+static int stouarg = 1;
+static int typesent = 0;
+static int havesigint = 0;
+static long havetype = 0;
+static long havesize = -1L;
+static char * havemdtm = NULL;
+static int mgetmethod = 0; /* NLST or MLSD */
+static int mgetforced = 0;
+
+static int i, /* j, k, */ x, y, z; /* Volatile temporaries */
+static int c0, c1; /* Temp variables for characters */
+
+static char putpath[CKMAXPATH+1] = { NUL, NUL };
+static char asnambuf[CKMAXPATH+1] = { NUL, NUL };
+
+#define RFNBUFSIZ 4096 /* Remote filename buffer size */
+
+static unsigned int maxbuf = 0, actualbuf = 0;
+static CHAR *ucbuf = NULL;
+static int ucbufsiz = 0;
+static unsigned int nout = 0; /* Number of chars in ucbuf */
+
+static jmp_buf recvcancel;
+static jmp_buf sendcancel;
+static jmp_buf ptcancel;
+static jmp_buf jcancel;
+static int ptabflg = 0;
+
+/* Protection level symbols */
+
+#define FPL_CLR 1 /* Clear */
+#define FPL_SAF 2 /* Safe */
+#define FPL_PRV 3 /* Private */
+#define FPL_CON 4 /* Confidential */
+
+/* Symbols for file types returned by MLST/MLSD */
+
+#define FTYP_FILE 1 /* Regular file */
+#define FTYP_DIR 2 /* Directory */
+#define FTYP_CDIR 3 /* Current directory */
+#define FTYP_PDIR 4 /* Parent directory */
+
+/* File type symbols keyed to the file-type symbols from ckcker.h */
+
+#define FTT_ASC XYFT_T /* ASCII (text) */
+#define FTT_BIN XYFT_B /* Binary (image) */
+#define FTT_TEN XYFT_X /* TENEX (TOPS-20) */
+
+/* Server feature table - sfttab[0] > 0 means server supports FEAT and OPTS */
+
+static int sfttab[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+#define SFT_AUTH 1 /* FTP server feature codes */
+#define SFT_LANG 2
+#define SFT_MDTM 3
+#define SFT_MLST 4
+#define SFT_PBSZ 5
+#define SFT_PROT 6
+#define SFT_REST 7
+#define SFT_SIZE 8
+#define SFT_TVFS 9
+#define SFT_UTF8 10
+
+#define CNV_AUTO 2 /* FTP filename conversion */
+#define CNV_CNV 1
+#define CNV_LIT 0
+
+/* SET FTP values */
+
+static int /* SET FTP values... */
+ ftp_aut = 1, /* Auto-authentication */
+#ifdef FTP_SECURITY
+ ftp_cry = 1, /* Auto-encryption */
+ ftp_cfw = 0, /* Credential forwarding */
+#endif /* FTP_SECURITY */
+ ftp_cpl = FPL_CLR, /* Command protection level */
+ ftp_dpl = FPL_CLR, /* Data protection level */
+#ifdef FTP_PROXY
+ ftp_prx = 0, /* Use proxy */
+#endif /* FTP_PROXY */
+ sav_psv = -1, /* For saving passive mode */
+ ftp_psv = 1, /* Passive mode */
+ ftp_spc = 1, /* Send port commands */
+ ftp_typ = FTT_ASC, /* Type */
+ get_auto = 1, /* Automatic type switching for GET */
+ tenex = 0, /* Type is Tenex */
+ ftp_usn = 0, /* Unique server names */
+ ftp_prm = 0, /* Permissions */
+ ftp_cnv = CNV_AUTO, /* Filename conversion (2 = auto) */
+ ftp_vbm = DEF_VBM, /* Verbose mode */
+ ftp_vbx = DEF_VBM, /* Sticky version of same */
+ ftp_err = 0, /* Error action */
+ ftp_fnc = -1; /* Filename collision action */
+
+#ifdef CK_SSL
+static int ftp_bug_use_ssl_v2 = 0; /* use SSLv2 for AUTH SSL */
+#endif /* CK_SSL */
+
+static int
+#ifdef NOCSETS
+ ftp_csr = -1, /* Remote (server) character set */
+#else
+ ftp_csr = FC_UTF8,
+#endif /* NOCSETS */
+ ftp_xla = 0; /* Character-set translation on/off */
+int
+ ftp_csx = -1, /* Remote charset currently in use */
+ ftp_csl = -1; /* Local charset currently in use */
+
+static int g_ftp_typ = FTT_ASC; /* For saving and restoring ftp_typ */
+
+char * ftp_nml = NULL; /* /NAMELIST */
+char * ftp_tmp = NULL; /* Temporary string */
+static char * ftp_acc = NULL; /* Account string */
+static char * auth_type = NULL; /* Authentication type */
+static char * srv_renam = NULL; /* Server-rename string */
+FILE * fp_nml = NULL; /* Namelist file pointer */
+
+static int csocket = -1; /* Control socket */
+static int connected = 0; /* Connected to FTP server */
+static short ftp_port = 0; /* FTP port */
+#ifdef FTPHOST
+static int hostcmd = 0; /* Has HOST command been sent */
+#endif /* FTPHOST */
+static int form, mode, stru, bytesize, curtype = FTT_ASC;
+static char bytename[8];
+
+/* For parsing replies to FTP server command */
+static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
+
+#ifdef FTP_PROXY
+static int proxy, unix_proxy
+#endif /* FTP_PROXY */
+
+static char pasv[64]; /* Passive-mode port */
+static int passivemode = 0;
+static int sendport = 0;
+static int servertype = 0; /* FTP server's OS type */
+
+static int testing = 0;
+static char ftpcmdbuf[FTP_BUFSIZ];
+
+/* Macro definitions */
+
+#define UC(b) ckitoa(((int)b)&0xff)
+#define nz(x) ((x) == 0 ? 1 : (x))
+
+/* Command tables and definitions */
+
+#define FTP_ACC 1 /* FTP command keyword codes */
+#define FTP_APP 2
+#define FTP_CWD 3
+#define FTP_CHM 4
+#define FTP_CLS 5
+#define FTP_DEL 6
+#define FTP_DIR 7
+#define FTP_GET 8
+#define FTP_IDL 9
+#define FTP_MDE 10
+#define FTP_MDI 11
+#define FTP_MGE 12
+#define FTP_MKD 13
+#define FTP_MOD 14
+#define FTP_MPU 15
+#define FTP_OPN 16
+#define FTP_PUT 17
+#define FTP_PWD 18
+#define FTP_RGE 19
+#define FTP_REN 20
+#define FTP_RES 21
+#define FTP_HLP 22
+#define FTP_RMD 23
+#define FTP_STA 24
+#define FTP_SIT 25
+#define FTP_SIZ 26
+#define FTP_SYS 27
+#define FTP_UMA 28
+#define FTP_GUP 29
+#define FTP_USR 30
+#define FTP_QUO 31
+#define FTP_TYP 32
+#define FTP_FEA 33
+#define FTP_OPT 34
+#define FTP_CHK 35
+#define FTP_VDI 36
+#define FTP_ENA 37
+#define FTP_DIS 38
+
+struct keytab gprtab[] = { /* GET-PUT-REMOTE keywords */
+ { "auto", 2, 0 },
+ { "ftp", 1, 0 },
+ { "kermit", 0, 0 }
+};
+
+static struct keytab qorp[] = { /* QUIT or PROCEED keywords */
+ { "proceed", 0, 0 }, /* 0 = proceed */
+ { "quit", 1, 0 } /* 1 = quit */
+};
+
+static struct keytab ftpcmdtab[] = { /* FTP command table */
+ { "account", FTP_ACC, 0 },
+ { "append", FTP_APP, 0 },
+ { "bye", FTP_CLS, 0 },
+ { "cd", FTP_CWD, 0 },
+ { "cdup", FTP_GUP, 0 },
+ { "check", FTP_CHK, 0 },
+ { "chmod", FTP_CHM, 0 },
+ { "close", FTP_CLS, 0 },
+ { "cwd", FTP_CWD, CM_INV },
+ { "delete", FTP_MDE, 0 },
+ { "directory", FTP_DIR, 0 },
+ { "disable", FTP_DIS, 0 },
+ { "enable", FTP_ENA, 0 },
+ { "features", FTP_FEA, 0 },
+ { "get", FTP_GET, 0 },
+ { "help", FTP_HLP, 0 },
+ { "idle", FTP_IDL, 0 },
+ { "login", FTP_USR, CM_INV },
+ { "mdelete", FTP_MDE, CM_INV },
+ { "mget", FTP_MGE, 0 },
+ { "mkdir", FTP_MKD, 0 },
+ { "modtime", FTP_MOD, 0 },
+ { "mput", FTP_MPU, 0 },
+ { "open", FTP_OPN, 0 },
+ { "opt", FTP_OPT, CM_INV|CM_ABR },
+ { "opts", FTP_OPT, CM_INV },
+ { "options", FTP_OPT, 0 },
+ { "put", FTP_PUT, 0 },
+ { "pwd", FTP_PWD, 0 },
+ { "quit", FTP_CLS, CM_INV },
+ { "quote", FTP_QUO, 0 },
+ { "reget", FTP_RGE, 0 },
+ { "rename", FTP_REN, 0 },
+ { "reset", FTP_RES, 0 },
+ { "rmdir", FTP_RMD, 0 },
+ { "send", FTP_PUT, CM_INV },
+ { "site", FTP_SIT, 0 },
+ { "size", FTP_SIZ, 0 },
+ { "status", FTP_STA, 0 },
+ { "system", FTP_SYS, 0 },
+ { "type", FTP_TYP, 0 },
+ { "umask", FTP_UMA, 0 },
+ { "up", FTP_GUP, CM_INV },
+ { "user", FTP_USR, 0 },
+ { "vdirectory",FTP_VDI, 0 },
+ { "", 0, 0 }
+};
+static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1;
+
+#define OPN_ANO 1 /* FTP OPEN switch codes */
+#define OPN_PSW 2
+#define OPN_USR 3
+#define OPN_ACC 4
+#define OPN_ACT 5
+#define OPN_PSV 6
+#define OPN_TLS 7
+#define OPN_NIN 8
+#define OPN_NOL 9
+
+#ifdef FTP_SECURITY
+#ifdef CK_SSL
+#define USETLSTAB
+static struct keytab tlstab[] = { /* FTP SSL/TLS switches */
+ { "/ssl", OPN_TLS, 0 },
+ { "/tls", OPN_TLS, 0 },
+ { "", 0, 0 }
+};
+static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1;
+#endif /* CK_SSL */
+#endif /* FTP_SECURITY */
+
+static struct keytab ftpswitab[] = { /* FTP command switches */
+ { "/account", OPN_ACC, CM_ARG },
+ { "/active", OPN_ACT, 0 },
+ { "/anonymous", OPN_ANO, 0 },
+ { "/noinit", OPN_NIN, 0 },
+ { "/nologin", OPN_NOL, 0 },
+ { "/passive", OPN_PSV, 0 },
+ { "/password", OPN_PSW, CM_ARG },
+ { "/user", OPN_USR, CM_ARG },
+ { "", 0, 0 }
+};
+static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1;
+
+/* FTP { ENABLE, DISABLE } items */
+
+#define ENA_FEAT 1
+#define ENA_MDTM 2
+#define ENA_MLST 3
+#define ENA_SIZE 4
+#define ENA_AUTH 5
+
+static struct keytab ftpenatab[] = {
+ { "AUTH", ENA_AUTH, 0 },
+ { "FEAT", ENA_FEAT, 0 },
+ { "MDTM", ENA_MDTM, 0 },
+ { "ML", ENA_MLST, CM_INV|CM_ABR },
+ { "MLS", ENA_MLST, CM_INV|CM_ABR },
+ { "MLSD", ENA_MLST, CM_INV },
+ { "MLST", ENA_MLST, 0 },
+ { "SIZE", ENA_SIZE, 0 },
+ { "", 0, 0 }
+};
+static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1;
+
+/* SET FTP command keyword indices */
+
+#define FTS_AUT 1 /* Autoauthentication */
+#define FTS_CRY 2 /* Encryption */
+#define FTS_LOG 3 /* Autologin */
+#define FTS_CPL 4 /* Command protection level */
+#define FTS_CFW 5 /* Credentials forwarding */
+#define FTS_DPL 6 /* Data protection level */
+#define FTS_DBG 7 /* Debugging */
+#define FTS_PSV 8 /* Passive mode */
+#define FTS_SPC 9 /* Send port commands */
+#define FTS_TYP 10 /* (file) Type */
+#define FTS_USN 11 /* Unique server names (for files) */
+#define FTS_VBM 12 /* Verbose mode */
+#define FTS_ATP 13 /* Authentication type */
+#define FTS_CNV 14 /* Filename conversion */
+#define FTS_TST 15 /* Test (progress) messages */
+#define FTS_PRM 16 /* (file) Permissions */
+#define FTS_XLA 17 /* Charset translation */
+#define FTS_CSR 18 /* Server charset */
+#define FTS_ERR 19 /* Error action */
+#define FTS_FNC 20 /* Collision */
+#define FTS_SRP 21 /* SRP options */
+#define FTS_GFT 22 /* GET automatic file-type switching */
+#define FTS_DAT 23 /* Set file dates */
+#define FTS_STO 24 /* Server time offset */
+#define FTS_APW 25 /* Anonymous password */
+#define FTS_DIS 26 /* File-transfer display style */
+#define FTS_BUG 27 /* Bug(s) */
+
+/* FTP BUGS */
+
+#define FTB_SV2 1 /* use SSLv2 */
+
+static struct keytab ftpbugtab[] = {
+ { "use-ssl-v2", FTB_SV2, 0 }
+};
+static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab));
+
+/* FTP PUT options (mutually exclusive, not a bitmask) */
+
+#define PUT_UPD 1 /* Update */
+#define PUT_RES 2 /* Restart */
+#define PUT_SIM 4 /* Simulation */
+#define PUT_DIF 8 /* Dates Differ */
+
+static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */
+#ifndef MAC
+ { "append", XYFX_A, 0 }, /* append to old file */
+#endif /* MAC */
+#ifdef COMMENT
+ { "ask", XYFX_Q, 0 }, /* ask what to do (not implemented) */
+#endif
+ { "backup", XYFX_B, 0 }, /* rename old file */
+#ifndef MAC
+ { "dates-differ", XYFX_M, 0 }, /* accept if dates differ */
+ { "discard", XYFX_D, 0 }, /* don't accept new file */
+ { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
+#endif /* MAC */
+ { "overwrite", XYFX_X, 0 }, /* overwrite the old file */
+ { "rename", XYFX_R, 0 }, /* rename the incoming file */
+#ifndef MAC /* This crashes Mac Kermit. */
+ { "update", XYFX_U, 0 }, /* replace if newer */
+#endif /* MAC */
+ { "", 0, 0 }
+};
+static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1;
+
+
+#ifdef FTP_SECURITY
+/* FTP authentication options */
+
+#define FTA_AUTO 0 /* Auto */
+#define FTA_SRP 1 /* SRP */
+#define FTA_GK5 2 /* Kerberos 5 */
+#define FTA_K4 3 /* Kerberos 4 */
+#define FTA_SSL 4 /* SSL */
+#define FTA_TLS 5 /* TLS */
+
+/* FTP authentication types */
+
+#define FTPATYPS 8
+static int ftp_auth_type[FTPATYPS] = {
+#ifdef FTP_GSSAPI
+ FTA_GK5, /* GSSAPI Kerberos 5 */
+#endif /* FTP_GK5 */
+#ifdef FTP_SRP
+ FTA_SRP, /* SRP */
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ FTA_K4, /* Kerberos 4 */
+#endif /* FTP_KRB4 */
+#ifdef CK_SSL
+ FTA_TLS, /* TLS */
+ FTA_SSL, /* SSL */
+#endif /* CK_SSL */
+ 0
+};
+
+static struct keytab ftpauth[] = { /* SET FTP AUTHTYPE cmd table */
+ { "automatic", FTA_AUTO, CM_INV },
+#ifdef FTP_GSSAPI
+ { "gssapi-krb5", FTA_GK5, 0 },
+#endif /* FTP_GSSAPI */
+#ifdef FTP_KRB4
+ { "k4", FTA_K4, CM_INV },
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ { "k5", FTA_GK5, CM_INV },
+#endif /* FTP_GSSAPI */
+#ifdef FTP_KRB4
+ { "kerberos4", FTA_K4, 0 },
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ { "kerberos5", FTA_GK5, CM_INV },
+#endif /* FTP_GSSAPI */
+#ifdef FTP_KRB4
+ { "kerberos_iv",FTA_K4, CM_INV },
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ { "kerberos_v", FTA_GK5, CM_INV },
+#endif /* FTP_GSSAPI */
+#ifdef FTP_KRB4
+ { "krb4", FTA_K4, CM_INV },
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ { "krb5", FTA_GK5, CM_INV },
+#endif /* FTP_GSSAPI */
+#ifdef FTP_SRP
+ { "srp", FTA_SRP, 0 },
+#endif /* FTP_SRP */
+#ifdef CK_SSL
+ { "ssl", FTA_SSL, 0 },
+ { "tls", FTA_TLS, 0 },
+#endif /* CK_SSL */
+ { "", 0, 0 }
+};
+static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1;
+
+#ifdef FTP_SRP
+#define SRP_CIPHER 1
+#define SRP_HASH 2
+static struct keytab ftpsrp[] = { /* SET FTP SRP command table */
+ { "cipher", SRP_CIPHER, 0 },
+ { "hash", SRP_HASH, 0 },
+ { "", 0, 0 }
+};
+static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1;
+#endif /* FTP_SRP */
+#endif /* FTP_SECURITY */
+
+static struct keytab ftpset[] = { /* SET FTP commmand table */
+ { "anonymous-password", FTS_APW, 0 },
+#ifdef FTP_SECURITY
+ { "authtype", FTS_ATP, 0 },
+ { "autoauthentication", FTS_AUT, 0 },
+ { "autoencryption", FTS_CRY, 0 },
+#endif /* FTP_SECURITY */
+ { "autologin", FTS_LOG, 0 },
+ { "bug", FTS_BUG, 0 },
+#ifndef NOCSETS
+ { "character-set-translation",FTS_XLA, 0 },
+#endif /* NOCSETS */
+ { "collision", FTS_FNC, 0 },
+#ifdef FTP_SECURITY
+ { "command-protection-level", FTS_CPL, 0 },
+ { "cpl", FTS_CPL, CM_INV },
+ { "credential-forwarding", FTS_CFW, 0 },
+ { "da", FTS_DAT, CM_INV|CM_ABR },
+ { "data-protection-level", FTS_DPL, 0 },
+#endif /* FTP_SECURITY */
+ { "dates", FTS_DAT, 0 },
+ { "debug", FTS_DBG, 0 },
+ { "display", FTS_DIS, 0 },
+#ifdef FTP_SECURITY
+ { "dpl", FTS_DPL, CM_INV },
+#endif /* FTP_SECURITY */
+ { "error-action", FTS_ERR, 0 },
+ { "filenames", FTS_CNV, 0 },
+ { "get-filetype-switching", FTS_GFT, 0 },
+ { "passive-mode", FTS_PSV, 0 },
+ { "pasv", FTS_PSV, CM_INV },
+ { "permissions", FTS_PRM, 0 },
+ { "progress-messages", FTS_TST, 0 },
+ { "send-port-commands", FTS_SPC, 0 },
+#ifndef NOCSETS
+ { "server-character-set", FTS_CSR, 0 },
+#endif /* NOCSETS */
+ { "server-time-offset", FTS_STO, 0 },
+#ifdef FTP_SRP
+ { "srp", FTS_SRP, 0 },
+#else
+ { "srp", FTS_SRP, CM_INV },
+#endif /* FTP_SRP */
+ { "type", FTS_TYP, 0 },
+ { "unique-server-names", FTS_USN, 0 },
+ { "verbose-mode", FTS_VBM, 0 },
+ { "", 0, 0 }
+};
+static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1;
+
+/*
+ GET and PUT switches are approximately the same as Kermit GET and SEND,
+ and use the same SND_xxx definitions, but hijack a couple for FTP use.
+ Don't just make up new ones, since the number of SND_xxx options must be
+ known in advance for the switch-parsing arrays.
+*/
+#define SND_USN SND_PRO /* /UNIQUE instead of /PROTOCOL */
+#define SND_PRM SND_PIP /* /PERMISSIONS instead of /PIPES */
+#define SND_TEN SND_CAL /* /TENEX instead of /CALIBRATE */
+
+static struct keytab putswi[] = { /* FTP PUT switch table */
+ { "/after", SND_AFT, CM_ARG },
+#ifdef PUTARRAY
+ { "/array", SND_ARR, CM_ARG },
+#endif /* PUTARRAY */
+ { "/as", SND_ASN, CM_ARG|CM_INV|CM_ABR },
+ { "/as-name", SND_ASN, CM_ARG },
+ { "/ascii", SND_TXT, CM_INV },
+ { "/b", SND_BIN, CM_INV|CM_ABR },
+ { "/before", SND_BEF, CM_ARG },
+ { "/binary", SND_BIN, 0 },
+#ifdef PUTPIPE
+ { "/command", SND_CMD, CM_PSH },
+#endif /* PUTPIPE */
+#ifdef COMMENT
+/* This works but it's dangerous */
+#ifdef DOUPDATE
+ { "/dates-differ", SND_DIF, CM_INV },
+#endif /* DOUPDATE */
+#endif /* COMMENT */
+ { "/delete", SND_DEL, 0 },
+#ifdef UNIXOROSK
+ { "/dotfiles", SND_DOT, 0 },
+#endif /* UNIXOROSK */
+ { "/error-action", SND_ERR, CM_ARG },
+ { "/except", SND_EXC, CM_ARG },
+ { "/filenames", SND_NAM, CM_ARG },
+#ifdef PIPESEND
+#ifndef NOSPL
+ { "/filter", SND_FLT, CM_ARG|CM_PSH },
+#endif /* NOSPL */
+#endif /* PIPESEND */
+#ifdef CKSYMLINK
+ { "/followlinks", SND_LNK, 0 },
+#endif /* CKSYMLINK */
+#ifdef VMS
+ { "/image", SND_IMG, 0 },
+#else
+ { "/image", SND_BIN, CM_INV },
+#endif /* VMS */
+ { "/larger-than", SND_LAR, CM_ARG },
+ { "/listfile", SND_FIL, CM_ARG },
+#ifndef NOCSETS
+ { "/local-character-set", SND_CSL, CM_ARG },
+#endif /* NOCSETS */
+#ifdef CK_TMPDIR
+ { "/move-to", SND_MOV, CM_ARG },
+#endif /* CK_TMPDIR */
+ { "/nobackupfiles", SND_NOB, 0 },
+#ifdef UNIXOROSK
+ { "/nodotfiles", SND_NOD, 0 },
+#endif /* UNIXOROSK */
+#ifdef CKSYMLINK
+ { "/nofollowlinks", SND_NLK, 0 },
+#endif /* CKSYMLINK */
+
+ { "/not-after", SND_NAF, CM_ARG },
+ { "/not-before", SND_NBE, CM_ARG },
+#ifdef UNIX
+ { "/permissions", SND_PRM, CM_ARG },
+#else
+ { "/permissions", SND_PRM, CM_ARG|CM_INV },
+#endif /* UNIX */
+ { "/quiet", SND_SHH, 0 },
+#ifdef FTP_RESTART
+ { "/recover", SND_RES, 0 },
+#endif /* FTP_RESTART */
+#ifdef RECURSIVE
+ { "/recursive", SND_REC, 0 },
+#endif /* RECURSIVE */
+ { "/rename-to", SND_REN, CM_ARG },
+#ifdef FTP_RESTART
+ { "/restart", SND_RES, CM_INV },
+#endif /* FTP_RESTART */
+#ifndef NOCSETS
+ { "/server-character-set", SND_CSR, CM_ARG },
+#endif /* NOCSETS */
+ { "/server-rename-to", SND_SRN, CM_ARG },
+ { "/simulate", SND_SIM, 0 },
+ { "/since", SND_AFT, CM_INV|CM_ARG },
+ { "/smaller-than", SND_SMA, CM_ARG },
+#ifdef COMMENT
+ { "/starting-at", SND_STA, CM_ARG },
+#endif /* COMMENT */
+#ifdef RECURSIVE
+ { "/subdirectories", SND_REC, CM_INV },
+#endif /* RECURSIVE */
+ { "/tenex", SND_TEN, 0 },
+ { "/text", SND_TXT, 0 },
+#ifndef NOCSETS
+ { "/transparent", SND_XPA, 0 },
+#endif /* NOCSETS */
+ { "/type", SND_TYP, CM_ARG },
+#ifdef DOUPDATE
+ { "/update", SND_UPD, 0 },
+#endif /* DOUPDATE */
+ { "/unique-server-names", SND_USN, 0 },
+ { "", 0, 0 }
+};
+static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1;
+
+static struct keytab getswi[] = { /* FTP [M]GET switch table */
+ { "/after", SND_AFT, CM_INV },
+ { "/as", SND_ASN, CM_ARG|CM_INV|CM_ABR },
+ { "/as-name", SND_ASN, CM_ARG },
+ { "/ascii", SND_TXT, CM_INV },
+ { "/before", SND_BEF, CM_INV },
+ { "/binary", SND_BIN, 0 },
+ { "/collision", SND_COL, CM_ARG },
+#ifdef PUTPIPE
+ { "/command", SND_CMD, CM_PSH },
+#endif /* PUTPIPE */
+ { "/delete", SND_DEL, 0 },
+ { "/error-action", SND_ERR, CM_ARG },
+ { "/except", SND_EXC, CM_ARG },
+ { "/filenames", SND_NAM, CM_ARG },
+#ifdef PIPESEND
+#ifndef NOSPL
+ { "/filter", SND_FLT, CM_ARG|CM_PSH },
+#endif /* NOSPL */
+#endif /* PIPESEND */
+#ifdef VMS
+ { "/image", SND_IMG, 0 },
+#else
+ { "/image", SND_BIN, CM_INV },
+#endif /* VMS */
+ { "/larger-than", SND_LAR, CM_ARG },
+ { "/listfile", SND_FIL, CM_ARG },
+#ifndef NOCSETS
+ { "/local-character-set", SND_CSL, CM_ARG },
+#endif /* NOCSETS */
+ { "/match", SND_PAT, CM_ARG },
+ { "/ml", SND_MLS, CM_INV|CM_ABR },
+ { "/mls", SND_MLS, CM_INV|CM_ABR },
+ { "/mlsd", SND_MLS, 0 },
+ { "/mlst", SND_MLS, CM_INV },
+#ifdef CK_TMPDIR
+ { "/move-to", SND_MOV, CM_ARG },
+#endif /* CK_TMPDIR */
+ { "/namelist", SND_NML, CM_ARG },
+ { "/nlst", SND_NLS, 0 },
+ { "/nobackupfiles", SND_NOB, 0 },
+ { "/nodotfiles", SND_NOD, 0 },
+#ifdef DOUPDATE
+ { "/dates-differ", SND_DIF, CM_INV },
+#endif /* DOUPDATE */
+ { "/not-after", SND_NAF, CM_INV },
+ { "/not-before", SND_NBE, CM_INV },
+ { "/permissions", SND_PRM, CM_INV },
+ { "/quiet", SND_SHH, 0 },
+#ifdef FTP_RESTART
+ { "/recover", SND_RES, 0 },
+#endif /* FTP_RESTART */
+#ifdef RECURSIVE
+ { "/recursive", SND_REC, 0 },
+#endif /* RECURSIVE */
+ { "/rename-to", SND_REN, CM_ARG },
+#ifdef FTP_RESTART
+ { "/restart", SND_RES, CM_INV },
+#endif /* FTP_RESTART */
+#ifndef NOCSETS
+ { "/server-character-set", SND_CSR, CM_ARG },
+#endif /* NOCSETS */
+ { "/server-rename-to", SND_SRN, CM_ARG },
+ { "/smaller-than", SND_SMA, CM_ARG },
+#ifdef RECURSIVE
+ { "/subdirectories", SND_REC, CM_INV },
+#endif /* RECURSIVE */
+ { "/text", SND_TXT, 0 },
+ { "/tenex", SND_TEN, 0 },
+#ifndef NOCSETS
+ { "/transparent", SND_XPA, 0 },
+#endif /* NOCSETS */
+ { "/to-screen", SND_MAI, 0 },
+#ifdef DOUPDATE
+ { "/update", SND_UPD, CM_INV },
+#endif /* DOUPDATE */
+ { "", 0, 0 }
+};
+static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1;
+
+static struct keytab delswi[] = { /* FTP [M]DELETE switch table */
+ { "/error-action", SND_ERR, CM_ARG },
+ { "/except", SND_EXC, CM_ARG },
+ { "/filenames", SND_NAM, CM_ARG },
+ { "/larger-than", SND_LAR, CM_ARG },
+ { "/nobackupfiles", SND_NOB, 0 },
+#ifdef UNIXOROSK
+ { "/nodotfiles", SND_NOD, 0 },
+#endif /* UNIXOROSK */
+ { "/quiet", SND_SHH, 0 },
+#ifdef RECURSIVE
+ { "/recursive", SND_REC, 0 },
+#endif /* RECURSIVE */
+ { "/smaller-than", SND_SMA, CM_ARG },
+#ifdef RECURSIVE
+ { "/subdirectories", SND_REC, CM_INV },
+#endif /* RECURSIVE */
+ { "", 0, 0 }
+};
+static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1;
+
+static struct keytab fntab[] = { /* Filename conversion keyword table */
+ { "automatic", 2, CNV_AUTO },
+ { "converted", 1, CNV_CNV },
+ { "literal", 0, CNV_LIT }
+};
+static int nfntab = (sizeof(fntab) / sizeof(struct keytab));
+
+static struct keytab ftptyp[] = { /* SET FTP TYPE table */
+ { "ascii", FTT_ASC, 0 },
+ { "binary", FTT_BIN, 0 },
+ { "tenex", FTT_TEN, 0 },
+ { "text", FTT_ASC, CM_INV },
+ { "", 0, 0 }
+};
+static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1;
+
+#ifdef FTP_SECURITY
+static struct keytab ftppro[] = { /* SET FTP PROTECTION-LEVEL table */
+ { "clear", FPL_CLR, 0 },
+ { "confidential", FPL_CON, 0 },
+ { "private", FPL_PRV, 0 },
+ { "safe", FPL_SAF, 0 },
+ { "", 0, 0 }
+};
+static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1;
+#endif /* FTP_SECURITY */
+
+/* Definitions for FTP from RFC765. */
+
+/* Reply codes */
+
+#define REPLY_PRELIM 1 /* Positive preliminary */
+#define REPLY_COMPLETE 2 /* Positive completion */
+#define REPLY_CONTINUE 3 /* Positive intermediate */
+#define REPLY_TRANSIENT 4 /* Transient negative completion */
+#define REPLY_ERROR 5 /* Permanent negative completion */
+#define REPLY_SECURE 6 /* Security encoded message */
+
+/* Form codes and names */
+
+#define FORM_N 1 /* Non-print */
+#define FORM_T 2 /* Telnet format effectors */
+#define FORM_C 3 /* Carriage control (ASA) */
+
+/* Structure codes and names */
+
+#define STRU_F 1 /* File (no record structure) */
+#define STRU_R 2 /* Record structure */
+#define STRU_P 3 /* Page structure */
+
+/* Mode types and names */
+
+#define MODE_S 1 /* Stream */
+#define MODE_B 2 /* Block */
+#define MODE_C 3 /* Compressed */
+
+/* Protection levels and names */
+
+#define PROT_C 1 /* Clear */
+#define PROT_S 2 /* Safe */
+#define PROT_P 3 /* Private */
+#define PROT_E 4 /* Confidential */
+
+#ifdef COMMENT /* Not used */
+#ifdef FTP_NAMES
+char *strunames[] = {"0", "File", "Record", "Page" };
+char *formnames[] = {"0", "Nonprint", "Telnet", "Carriage-control" };
+char *modenames[] = {"0", "Stream", "Block", "Compressed" };
+char *levelnames[] = {"0", "Clear", "Safe", "Private", "Confidential" };
+#endif /* FTP_NAMES */
+
+/* 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_REPLY_ERRORS 0x20 /* Block might have errors */
+#define BLK_RESTART 0x10 /* Block is Restart Marker */
+#define BLK_BYTECOUNT 2 /* Bytes in this block */
+#endif /* COMMENT */
+
+#define RADIX_ENCODE 0 /* radix_encode() function codes */
+#define RADIX_DECODE 1
+
+/*
+ The default setpbsz() value in the Unix FTP client is 1<<20 (1MB). This
+ results in a serious performance degradation due to the increased number
+ of page faults and the inability to overlap encrypt/decrypt, file i/o, and
+ network i/o. So instead we set the value to 1<<13 (8K), about half the size
+ of the typical TCP window. Maybe we should add a command to allow the value
+ to be changed.
+*/
+#define DEFAULT_PBSZ 1<<13
+
+/* Prototypes */
+
+_PROTOTYP(int remtxt, (char **) );
+_PROTOTYP(char * gskreason, (int) );
+_PROTOTYP(static int ftpclose,(void));
+_PROTOTYP(static int zzsend, (int, CHAR));
+_PROTOTYP(static int getreply,(int,int,int,int,int));
+_PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int));
+_PROTOTYP(static int setpbsz,(unsigned int));
+_PROTOTYP(static int recvrequest,(char *,char *,char *,char *,
+ int,int,char *,int,int,int));
+_PROTOTYP(static int ftpcmd,(char *,char *,int,int,int));
+_PROTOTYP(static int fts_cpl,(int));
+_PROTOTYP(static int fts_dpl,(int));
+#ifdef FTP_SECURITY
+_PROTOTYP(static int ftp_auth, (void));
+#endif /* FTP_SECURITY */
+_PROTOTYP(static int ftp_user, (char *, char *, char *));
+_PROTOTYP(static int ftp_login, (char *));
+_PROTOTYP(static int ftp_reset, (void));
+_PROTOTYP(static int ftp_rename, (char *, char *));
+_PROTOTYP(static int ftp_umask, (char *));
+_PROTOTYP(static int secure_flush, (int));
+#ifdef COMMENT
+_PROTOTYP(static int secure_putc, (char, int));
+#endif /* COMMENT */
+_PROTOTYP(static int secure_write, (int, CHAR *, unsigned int));
+_PROTOTYP(static int scommand, (char *));
+_PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int));
+_PROTOTYP(static int secure_getc, (int, int));
+_PROTOTYP(static int secure_getbyte, (int, int));
+_PROTOTYP(static int secure_read, (int, char *, int));
+_PROTOTYP(static int initconn, (void));
+_PROTOTYP(static int dataconn, (char *));
+_PROTOTYP(static int setprotbuf,(unsigned int));
+_PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int));
+
+_PROTOTYP(static char * radix_error,(int));
+_PROTOTYP(static char * ftp_hookup,(char *, int, int));
+_PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int));
+
+_PROTOTYP(static VOID mlsreset, (void));
+_PROTOTYP(static VOID secure_error, (char *fmt, ...));
+_PROTOTYP(static VOID lostpeer, (void));
+_PROTOTYP(static VOID cancel_remote, (int));
+_PROTOTYP(static VOID changetype, (int, int));
+
+_PROTOTYP(static sigtype cmdcancel, (int));
+
+#ifdef FTP_SRP
+_PROTOTYP(static int srp_reset, ());
+_PROTOTYP(static int srp_ftp_auth, (char *,char *,char *));
+_PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *));
+_PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *));
+_PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int));
+_PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int));
+_PROTOTYP(static int srp_selcipher, (char *));
+_PROTOTYP(static int srp_selhash, (char *));
+#endif /* FTP_SRP */
+
+#ifdef FTP_GSSAPI
+_PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *));
+#endif /* FTP_GSSAPI */
+
+/* D O F T P A R G -- Do an FTP command-line argument. */
+
+#ifdef FTP_SECURITY
+#ifndef NOICP
+#define FT_NOGSS 1
+#define FT_NOK4 2
+#define FT_NOSRP 3
+#define FT_NOSSL 4
+#define FT_NOTLS 5
+#define FT_CERTFI 6
+#define FT_OKCERT 7
+#define FT_DEBUG 8
+#define FT_KEY 9
+#define FT_SECURE 10
+#define FT_VERIFY 11
+
+static struct keytab ftpztab[] = {
+ { "!gss", FT_NOGSS, 0 },
+ { "!krb4", FT_NOK4, 0 },
+ { "!srp", FT_NOSRP, 0 },
+ { "!ssl", FT_NOSSL, 0 },
+ { "!tls", FT_NOTLS, 0 },
+ { "cert", FT_CERTFI, CM_ARG },
+ { "certsok", FT_OKCERT, 0 },
+ { "debug", FT_DEBUG, 0 },
+ { "key", FT_KEY, CM_ARG },
+ { "nogss", FT_NOGSS, 0 },
+ { "nokrb4", FT_NOK4, 0 },
+ { "nosrp", FT_NOSRP, 0 },
+ { "nossl", FT_NOSSL, 0 },
+ { "notls", FT_NOTLS, 0 },
+#ifdef COMMENT
+ { "secure", FT_SECURE, 0 },
+#endif /* COMMENT */
+ { "verify", FT_VERIFY, CM_ARG },
+ { "", 0, 0 }
+};
+static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1;
+
+/*
+ The following cipher and hash tables should be replaced with
+ dynamicly created versions based upon the linked library.
+*/
+#define SRP_BLOWFISH_ECB 1
+#define SRP_BLOWFISH_CBC 2
+#define SRP_BLOWFISH_CFB64 3
+#define SRP_BLOWFISH_OFB64 4
+#define SRP_CAST5_ECB 5
+#define SRP_CAST5_CBC 6
+#define SRP_CAST5_CFB64 7
+#define SRP_CAST5_OFB64 8
+#define SRP_DES_ECB 9
+#define SRP_DES_CBC 10
+#define SRP_DES_CFB64 11
+#define SRP_DES_OFB64 12
+#define SRP_DES3_ECB 13
+#define SRP_DES3_CBC 14
+#define SRP_DES3_CFB64 15
+#define SRP_DES3_OFB64 16
+
+static struct keytab ciphertab[] = {
+ { "blowfish_ecb", SRP_BLOWFISH_ECB, 0 },
+ { "blowfish_cbc", SRP_BLOWFISH_CBC, 0 },
+ { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 },
+ { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 },
+ { "cast5_ecb", SRP_CAST5_ECB, 0 },
+ { "cast5_cbc", SRP_CAST5_CBC, 0 },
+ { "cast5_cfb64", SRP_CAST5_CFB64, 0 },
+ { "cast5_ofb64", SRP_CAST5_OFB64, 0 },
+ { "des_ecb", SRP_DES_ECB, 0 },
+ { "des_cbc", SRP_DES_CBC, 0 },
+ { "des_cfb64", SRP_DES_CFB64, 0 },
+ { "des_ofb64", SRP_DES_OFB64, 0 },
+ { "des3_ecb", SRP_DES3_ECB, 0 },
+ { "des3_cbc", SRP_DES3_CBC, 0 },
+ { "des3_cfb64", SRP_DES3_CFB64, 0 },
+ { "des3_ofb64", SRP_DES3_OFB64, 0 },
+ { "none", 0, 0 },
+ { "", 0, 0 }
+};
+static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1;
+
+#define SRP_MD5 1
+#define SRP_SHA 2
+static struct keytab hashtab[] = {
+ { "md5", SRP_MD5, 0 },
+ { "none", 0, 0 },
+ { "sha", SRP_SHA, 0 },
+ { "", 0, 0 }
+};
+static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1;
+#endif /* NOICP */
+#endif /* FTP_SECURITY */
+
+static char *
+strval(s1,s2) char * s1, * s2; {
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ return(*s1 ? s1 : (*s2 ? s2 : "(none)"));
+}
+
+#ifndef NOCSETS
+static char * rfnptr = NULL;
+static int rfnlen = 0;
+static char rfnbuf[RFNBUFSIZ]; /* Remote filename translate buffer */
+static char * xgnbp = NULL;
+
+static int
+strgetc() { /* Helper function for xgnbyte() */
+ int c;
+ if (!xgnbp)
+ return(-1);
+ if (!*xgnbp)
+ return(-1);
+ c = (unsigned) *xgnbp++;
+ return(((unsigned) c) & 0xff);
+}
+
+static int /* Helper function for xpnbyte() */
+#ifdef CK_ANSIC
+strputc(char c)
+#else
+strputc(c) char c;
+#endif /* CK_ANSIC */
+{
+ rfnlen = rfnptr - rfnbuf;
+ if (rfnlen >= (RFNBUFSIZ - 1))
+ return(-1);
+ *rfnptr++ = c;
+ *rfnptr = NUL;
+ return(0);
+}
+
+static int
+#ifdef CK_ANSIC
+xprintc(char c)
+#else
+xprintc(c) char c;
+#endif /* CK_ANSIC */
+{
+ printf("%c",c);
+ return(0);
+}
+
+static VOID
+bytswap(c0,c1) int * c0, * c1; {
+ int t;
+ t = *c0;
+ *c0 = *c1;
+ *c1 = t;
+}
+#endif /* NOCSETS */
+
+#ifdef CKLOGDIAL
+char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */
+int ftplogactive = 0;
+long ftplogprev = 0L;
+
+VOID
+ftplogend() {
+ extern int dialog;
+ extern char diafil[];
+ long d1, d2, t1, t2;
+ char buf[32], * p;
+
+ debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive);
+ debug(F110,"ftp cx log buf",ftplogbuf,0);
+
+ if (!ftplogactive || !ftplogbuf[0]) /* No active record */
+ return;
+
+ ftplogactive = 0; /* Record is not active */
+
+ d1 = mjd((char *)ftplogbuf); /* Get start date of this session */
+ ckstrncpy(buf,ckdate(),31); /* Get current date */
+ d2 = mjd(buf); /* Convert them to mjds */
+ p = ftplogbuf; /* Get start time */
+ p[11] = NUL;
+ p[14] = NUL; /* Convert to seconds */
+ t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
+ p[11] = ':';
+ p[14] = ':';
+ p = buf; /* Get end time */
+ p[11] = NUL;
+ p[14] = NUL;
+ t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
+ t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */
+ if (t2 > -1L) {
+ ftplogprev = t2;
+ p = hhmmss(t2);
+ strncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */
+ strncat(ftplogbuf,p,CXLOGBUFL);
+ } else
+ ftplogprev = 0L;
+ debug(F101,"ftp cx log dialog","",dialog);
+ if (dialog) { /* If logging */
+ int x;
+ x = diaopn(diafil,1,1); /* Open log in append mode */
+ if (x > 0) {
+ debug(F101,"ftp cx log open","",x);
+ x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */
+ debug(F101,"ftp cx log write","",x);
+ x = zclose(ZDIFIL); /* Close the log */
+ debug(F101,"ftp cx log close","",x);
+ }
+ }
+}
+
+VOID
+dologftp() {
+ ftplogend(); /* Previous session not closed out? */
+ ftplogprev = 0L;
+ ftplogactive = 1; /* Record is active */
+
+ ckmakxmsg(ftplogbuf,CXLOGBUFL,
+ ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(),
+ " T=FTP N=", strval(ftp_host,NULL)," H=",myhost," ",NULL,NULL);
+ debug(F110,"ftp cx log begin",ftplogbuf,0);
+}
+#endif /* CKLOGDIAL */
+
+static char * dummy[2] = { NULL, NULL };
+
+static struct keytab modetab[] = {
+ { "active", 0, 0 },
+ { "passive", 1, 0 }
+};
+
+#ifndef NOCMDL
+int /* Called from ckuusy.c */
+#ifdef CK_ANSIC
+doftparg(char c)
+#else
+doftparg(c) char c;
+#endif /* CK_ANSIC */
+/* doftparg */ {
+ int x, z;
+ char *xp;
+ extern char **xargv, *xarg0;
+ extern int xargc, stayflg, haveftpuid;
+ extern char uidbuf[];
+
+ xp = *xargv+1; /* Pointer for bundled args */
+ while (c) {
+ if (ckstrchr("MuDPkcHzm",c)) { /* Options that take arguments */
+ if (*(xp+1)) {
+ fatal("?Invalid argument bundling");
+ }
+ xargv++, xargc--;
+ if ((xargc < 1) || (**xargv == '-')) {
+ fatal("?Required argument missing");
+ }
+ }
+ switch (c) { /* Big switch on arg */
+ case 'h': /* help */
+ printf("C-Kermit's FTP client command-line personality. Usage:\n");
+ printf(" %s [ options ] host [ port ] [-pg files ]\n\n",xarg0);
+ printf("Options:\n");
+ printf(" -h = help (this message)\n");
+ printf(" -m mode = \"passive\" (default) or \"active\"\n");
+ printf(" -u name = username for autologin (or -M)\n");
+ printf(" -P password = password for autologin (RISKY)\n");
+ printf(" -A = autologin anonymously\n");
+ printf(" -D directory = cd after autologin\n");
+ printf(" -b = force binary mode\n");
+ printf(" -a = force text (\"ascii\") mode (or -T)\n");
+ printf(" -d = debug (double to add timestamps)\n");
+ printf(" -n = no autologin\n");
+ printf(" -v = verbose (default)\n");
+ printf(" -q = quiet\n");
+ printf(" -S = Stay (issue command prompt when done)\n");
+ printf(" -Y = do not execute Kermit init file\n");
+ printf(" -p files = files to put after autologin (or -s)\n");
+ printf(" -g files = files to get after autologin\n");
+ printf(" -R = recursive (for use with -p)\n");
+
+#ifdef FTP_SECURITY
+ printf("\nSecurity options:\n");
+ printf(" -k realm = Kerberos 4 realm\n");
+ printf(" -f = Kerboros 5 credentials forwarding\n");
+ printf(" -x = autoencryption mode\n");
+ printf(" -c cipher = SRP cipher type\n");
+ printf(" -H hash = SRP encryption hash\n");
+ printf(" -z option = Security options\n");
+#endif /* FTP_SECURITY */
+
+ printf("\n-p or -g, if given, should be last. Example:\n");
+ printf(" ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n");
+
+ doexit(GOOD_EXIT,-1);
+ break;
+
+ case 'R': /* Recursive */
+ recursive = 1;
+ break;
+
+ case 'd': /* Debug */
+#ifdef DEBUG
+ if (deblog) {
+ extern int debtim;
+ debtim = 1;
+ } else {
+ deblog = debopn("debug.log",0);
+ debok = 1;
+ }
+#endif /* DEBUG */
+ /* fall thru on purpose */
+
+ case 't': /* Trace */
+ ftp_deb++;
+ break;
+
+ case 'n': /* No autologin */
+ ftp_log = 0;
+ break;
+
+ case 'i': /* No prompt */
+ case 'v': /* Verbose */
+ break; /* (ignored) */
+
+ case 'q': /* Quiet */
+ quiet = 1;
+ break;
+
+ case 'S': /* Stay */
+ stayflg = 1;
+ break;
+
+ case 'M':
+ case 'u': /* My User Name */
+ if ((int)strlen(*xargv) > 63) {
+ fatal("username too long");
+ }
+ ckstrncpy(uidbuf,*xargv,UIDBUFLEN);
+ haveftpuid = 1;
+ break;
+
+ case 'A':
+ ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
+ haveftpuid = 1;
+ break;
+
+ case 'T': /* Text */
+ case 'a': /* "ascii" */
+ case 'b': /* Binary */
+ binary = (c == 'b') ? FTT_BIN : FTT_ASC;
+ xfermode = XMODE_M;
+ filepeek = 0;
+ patterns = 0;
+ break;
+
+ case 'g': /* Get */
+ case 'p': /* Put */
+ case 's': { /* Send (= Put) */
+ int havefiles, rc;
+ if (ftp_action) {
+ fatal("Only one FTP action at a time please");
+ }
+ if (*(xp+1)) {
+ fatal("invalid argument bundling after -s");
+ }
+ nfils = 0; /* Initialize file counter */
+ havefiles = 0; /* Assume nothing to send */
+ cmlist = xargv + 1; /* Remember this pointer */
+
+ while (++xargv, --xargc > 0) { /* Traverse the list */
+ if (c == 'g') {
+ havefiles++;
+ nfils++;
+ continue;
+ }
+#ifdef RECURSIVE
+ if (!strcmp(*xargv,".")) {
+ havefiles = 1;
+ nfils++;
+ recursive = 1;
+ } else
+#endif /* RECURSIVE */
+ if ((rc = zchki(*xargv)) > -1 || (rc == -2)) {
+ if (rc != -2)
+ havefiles = 1;
+ nfils++;
+ } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) {
+ havefiles = 1;
+ nfils++;
+ }
+ }
+ xargc++, xargv--; /* Adjust argv/argc */
+ if (!havefiles) {
+ if (c == 'g') {
+ fatal("No files to put");
+ } else {
+ fatal("No files to get");
+ }
+ }
+ ftp_action = c;
+ break;
+ }
+ case 'D': /* Directory */
+ makestr(&ftp_rdir,*xargv);
+ break;
+
+ case 'm': /* Mode (Active/Passive */
+ ftp_psv = lookup(modetab,*xargv,2,NULL);
+ if (ftp_psv < 0) fatal("Invalid mode");
+ break;
+
+ case 'P':
+ makestr(&ftp_tmp,*xargv); /* You-Know-What */
+ break;
+
+ case 'Y': /* No initialization file */
+ break; /* (already done in prescan) */
+
+#ifdef CK_URL
+ case 'U': { /* URL */
+ /* These are set by urlparse() - any not set are NULL */
+ if (g_url.hos) {
+/*
+ Kermit has accepted host:port notation since many years before URLs were
+ invented. Unfortunately, URLs conflict with this notation. Thus "ftp
+ host:449" looks like a URL and results in service = host and host = 449.
+ Here we try to catch this situation transparently to the user.
+*/
+ if (ckstrcmp(g_url.svc,"ftp",-1,0)
+#ifdef CK_SSL
+ && ckstrcmp(g_url.svc,"ftps",-1,0)
+#endif /* CK_SSL */
+ ) {
+ if (!g_url.usr &&
+ !g_url.psw &&
+ !g_url.por &&
+ !g_url.pth) {
+ g_url.por = g_url.hos;
+ g_url.hos = g_url.svc;
+ g_url.svc = "ftp";
+ } else {
+ ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=",
+ g_url.svc," host=",g_url.hos);
+ fatal(tmpbuf);
+ }
+ }
+ makestr(&ftp_host,g_url.hos);
+ if (g_url.usr) {
+ haveftpuid = 1;
+ ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
+ makestr(&ftp_logname,uidbuf);
+ }
+ if (g_url.psw) {
+ makestr(&ftp_tmp,g_url.psw);
+ }
+ if (g_url.pth) {
+ if (!g_url.usr) {
+ haveftpuid = 1;
+ ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
+ makestr(&ftp_logname,uidbuf);
+ }
+ if (ftp_action) {
+ fatal("Only one FTP action at a time please");
+ }
+ if (!stayflg)
+ quiet = 1;
+ nfils = 1;
+ dummy[0] = g_url.pth;
+ cmlist = dummy;
+ ftp_action = 'g';
+ }
+ xp = NULL;
+ haveurl = 1;
+ }
+ break;
+ }
+#endif /* CK_URL */
+
+#ifdef FTP_SECURITY
+ case 'k': { /* K4 Realm */
+#ifdef FTP_KRB4
+ ckstrncpy(ftp_realm,*xargv, REALM_SZ);
+#endif /* FTP_KRB4 */
+ if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv);
+ break;
+ }
+ case 'f': {
+#ifdef FTP_GSSAPI
+ ftp_cfw = 1;
+ if (ftp_deb) printf("K5 Credentials Forwarding\n");
+#else /* FTP_GSSAPI */
+ printf("K5 Credentials Forwarding not supported\n");
+#endif /* FTP_GSSAPI */
+ break;
+ }
+ case 'x': {
+ ftp_cry = 1;
+ if (ftp_deb) printf("Autoencryption\n");
+ break;
+ }
+ case 'c': { /* Cipher */
+#ifdef FTP_SRP
+ if (!srp_selcipher(*xargv)) {
+ if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv);
+ } else
+ printf("?Invalid SRP cipher type: \"%s\"\n",*xargv);
+#else /* FTP_SRP */
+ printf("?SRP not supported\n");
+#endif /* FTP_SRP */
+ break;
+ }
+ case 'H': {
+#ifdef FTP_SRP
+ if (!srp_selhash(*xargv)) {
+ if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv);
+ } else
+ printf("?Invalid SRP hash type: \"%s\"\n",*xargv);
+#else /* FTP_SRP */
+ printf("?SRP not supported\n");
+#endif /* FTP_SRP */
+ break;
+ }
+ case 'z': {
+ /* *xargv contains a value of the form tag=value */
+ /* we need to lookup the tag and save the value */
+ char * p = NULL, * q = NULL;
+ makestr(&p,*xargv);
+ y = ckindex("=",p,0,0,1);
+ if (y > 0)
+ p[y-1] = '\0';
+ x = lookup(ftpztab,p,nftpztab,&z);
+ if (x < 0) {
+ printf("?Invalid security option: \"%s\"\n",p);
+ } else {
+ if (ftp_deb)
+ printf("Security option: \"%s",p);
+ if (ftpztab[z].flgs & CM_ARG) {
+ if (y <= 0)
+ fatal("?Missing required value");
+ q = &p[y];
+ if (!*q)
+ fatal("?Missing required value");
+ if (ftp_deb)
+ printf("=%s\"",q);
+ }
+ switch (ftpztab[z].kwval) { /* -z options w/args */
+ case FT_NOGSS:
+#ifdef FTP_GSSAPI
+ for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
+ if (ftp_auth_type[z] == FTA_GK5) {
+ for (y = z;
+ y < (FTPATYPS-1) && ftp_auth_type[y];
+ y++
+ )
+ ftp_auth_type[y] = ftp_auth_type[y+1];
+ ftp_auth_type[FTPATYPS-1] = 0;
+ break;
+ }
+ }
+#endif /* FTP_GSSAPI */
+ break;
+ case FT_NOK4:
+#ifdef FTP_KRB4
+ for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
+ if (ftp_auth_type[z] == FTA_K4) {
+ for (y = z;
+ y < (FTPATYPS-1) && ftp_auth_type[y];
+ y++
+ )
+ ftp_auth_type[y] = ftp_auth_type[y+1];
+ ftp_auth_type[FTPATYPS-1] = 0;
+ break;
+ }
+ }
+#endif /* FTP_KRB4 */
+ break;
+ case FT_NOSRP:
+#ifdef FTP_SRP
+ for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
+ if (ftp_auth_type[z] == FTA_SRP) {
+ for (y = z;
+ y < (FTPATYPS-1) && ftp_auth_type[y];
+ y++
+ )
+ ftp_auth_type[y] = ftp_auth_type[y+1];
+ ftp_auth_type[FTPATYPS-1] = 0;
+ break;
+ }
+ }
+#endif /* FTP_SRP */
+ break;
+ case FT_NOSSL:
+#ifdef CK_SSL
+ for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
+ if (ftp_auth_type[z] == FTA_SSL) {
+ for (y = z;
+ y < (FTPATYPS-1) && ftp_auth_type[y];
+ y++
+ )
+ ftp_auth_type[y] = ftp_auth_type[y+1];
+ ftp_auth_type[FTPATYPS-1] = 0;
+ break;
+ }
+ }
+#endif /* CK_SSL */
+ break;
+ case FT_NOTLS:
+#ifdef CK_SSL
+ for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
+ if (ftp_auth_type[z] == FTA_TLS) {
+ for (y = z;
+ y < (FTPATYPS-1) && ftp_auth_type[y];
+ y++
+ )
+ ftp_auth_type[y] = ftp_auth_type[y+1];
+ ftp_auth_type[FTPATYPS-1] = 0;
+ break;
+ }
+ }
+#endif /* CK_SSL */
+ break;
+ case FT_CERTFI:
+#ifdef CK_SSL
+ makestr(&ssl_rsa_cert_file,q);
+#endif /* CK_SSL */
+ break;
+ case FT_OKCERT:
+#ifdef CK_SSL
+ ssl_certsok_flag = 1;
+#endif /* CK_SSL */
+ break;
+ case FT_DEBUG:
+#ifdef DEBUG
+ if (deblog) {
+ extern int debtim;
+ debtim = 1;
+ } else {
+ deblog = debopn("debug.log",0);
+ }
+#endif /* DEBUG */
+ break;
+ case FT_KEY:
+#ifdef CK_SSL
+ makestr(&ssl_rsa_key_file,q);
+#endif /* CK_SSL */
+ break;
+ case FT_SECURE:
+ /* no equivalent */
+ break;
+ case FT_VERIFY:
+#ifdef CK_SSL
+ if (!rdigits(q))
+ printf("?Bad number: %s\n",q);
+ ssl_verify_flag = atoi(q);
+#endif /* CK_SSL */
+ break;
+ }
+ }
+ if (ftp_deb) printf("\"\n");
+ free(p);
+ break;
+ }
+#endif /* FTP_SECURITY */
+
+ default:
+ fatal2(*xargv,
+ "unknown command-line option, type \"ftp -h\" for help"
+ );
+ }
+ if (!xp) break;
+ c = *++xp; /* See if options are bundled */
+ }
+ return(0);
+}
+#endif /* NOCMDL */
+
+int
+ftpisconnected() {
+ return(connected);
+}
+
+int
+ftpisloggedin() {
+ return(connected ? loggedin : 0);
+}
+
+int
+ftpissecure() {
+ return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1);
+}
+
+static VOID
+ftscreen(n, c, z, s) int n; char c; long z; char * s; {
+ if (displa && fdispla && !backgrd && !quiet && !out2screen) {
+ if (!dpyactive) {
+ ckscreen(SCR_PT,'S',0L,"");
+ dpyactive = 1;
+ }
+ ckscreen(n,c,z,s);
+ }
+}
+
+#ifndef OS2
+/* g m s t i m e r -- Millisecond timer */
+
+long
+gmstimer() {
+#ifdef HAVE_MSECS
+ /* For those versions of ztime() that also set global ztmsec. */
+ char *p = NULL;
+ long z;
+ ztime(&p);
+ if (!p) return(0L);
+ if (!*p) return(0L);
+ z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
+ return(z * 1000 + ztmsec);
+#else
+ return((long)time(NULL) * 1000L);
+#endif /* HAVE_MSECS */
+}
+#endif /* OS2 */
+
+/* d o s e t f t p -- The SET FTP command */
+
+int
+dosetftp() {
+ int cx;
+ if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */
+ return(cx);
+ switch (cx) {
+
+ case FTS_FNC: /* Filename collision action */
+ if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ ftp_fnc = x;
+ return(1);
+
+ case FTS_CNV: /* Filename conversion */
+ if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ ftp_cnv = x;
+ return(1);
+
+ case FTS_DBG: /* Debug messages */
+ return(seton(&ftp_deb));
+
+ case FTS_LOG: /* Auto-login */
+ return(seton(&ftp_log));
+
+ case FTS_PSV: /* Passive mode */
+ return(dosetftppsv());
+
+ case FTS_SPC: /* Send port commands */
+ x = seton(&ftp_spc);
+ if (x > 0) sendport = ftp_spc;
+ return(x);
+
+ case FTS_TYP: /* Type */
+ if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0) return(y);
+ ftp_typ = x;
+ g_ftp_typ = x;
+ tenex = (ftp_typ == FTT_TEN);
+ return(1);
+
+ case FTS_USN: /* Unique server names */
+ return(seton(&ftp_usn));
+
+ case FTS_VBM: /* Verbose mode */
+ if ((x = seton(&ftp_vbm)) < 0) /* Per-command copy */
+ return(x);
+ ftp_vbx = ftp_vbm; /* Global sticky copy */
+ return(x);
+
+ case FTS_TST: /* "if (testing)" messages */
+ return(seton(&testing));
+
+ case FTS_PRM: /* Send permissions */
+ return(setonaut(&ftp_prm));
+
+ case FTS_AUT: /* Auto-authentication */
+ return(seton(&ftp_aut));
+
+ case FTS_ERR: /* Error action */
+ if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ ftp_err = x;
+ return(success = 1);
+
+#ifndef NOCSETS
+ case FTS_XLA: /* Translation */
+ return(seton(&ftp_xla));
+
+ case FTS_CSR: /* Server charset */
+ if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ ftp_csr = x;
+ ftp_xla = 1; /* Also enable translation */
+ return(success = 1);
+#endif /* NOCSETS */
+
+ case FTS_GFT:
+ return(seton(&get_auto)); /* GET-filetype-switching */
+
+ case FTS_DAT:
+ return(seton(&ftp_dates)); /* Set file dates */
+
+ case FTS_STO: { /* Server time offset */
+ char * s, * p = NULL;
+ int k;
+ if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0)
+ return(x);
+ if (!strcmp(s,"+0")) {
+ s = NULL;
+ } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */
+ printf("?Invalid time offset\n");
+ return(-9);
+ }
+ makestr(&p,s); /* Make a safe copy the string */
+ if ((x = cmcfm()) < 0) { /* Get confirmation */
+ if (p)
+ makestr(&p,NULL);
+ return(x);
+ }
+ fts_sto = p; /* Confirmed - set the string. */
+ return(success = 1);
+ }
+ case FTS_APW: {
+ char * s;
+ if ((x = cmtxt("Text", "", &s, xxstring)) < 0)
+ return(x);
+ makestr(&ftp_apw, *s ? s : NULL);
+ return(success = 1);
+ }
+
+ case FTS_BUG: {
+ if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0)
+ return(x);
+ switch (x) {
+#ifdef CK_SSL
+ case FTB_SV2:
+ return seton(&ftp_bug_use_ssl_v2);
+#endif /* CK_SSL */
+ default:
+ return(-2);
+ }
+ }
+
+#ifdef FTP_SECURITY
+ case FTS_CRY: /* Auto-encryption */
+ return(seton(&ftp_cry));
+
+ case FTS_CFW: /* Credential-forwarding */
+ return(seton(&ftp_cfw));
+
+ case FTS_CPL: /* Command protection level */
+ if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
+ if ((y = cmcfm()) < 0) return(y);
+ success = fts_cpl(x);
+ return(success);
+
+ case FTS_DPL: /* Data protection level */
+ if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
+ if ((y = cmcfm()) < 0) return(y);
+ success = fts_dpl(x);
+ return(success);
+
+ case FTS_ATP: { /* FTP Auth Type */
+ int i, j, atypes[8];
+
+ for (i = 0; i < 8; i++) {
+ if ((y = cmkey(ftpauth,nftpauth,"",
+ (i == 0) ? "automatic" : "",
+ xxstring)) < 0) {
+ if (y == -3)
+ break;
+ return(y);
+ }
+ if (i > 0 && (y == FTA_AUTO)) {
+ printf("?Choice may only be used in first position.\r\n");
+ return(-9);
+ }
+ for (j = 0; j < i; j++) {
+ if (atypes[j] == y) {
+ printf("\r\n?Choice has already been used.\r\n");
+ return(-9);
+ }
+ }
+ atypes[i] = y;
+ if (y == FTA_AUTO) {
+ i++;
+ break;
+ }
+ }
+ if (i < 8)
+ atypes[i] = 0;
+ if ((z = cmcfm()) < 0)
+ return(z);
+ if (atypes[0] == FTA_AUTO) {
+ i = 0;
+#ifdef FTP_GSSAPI
+ ftp_auth_type[i++] = FTA_GK5;
+#endif /* FTP_GSSAPI */
+#ifdef FTP_SRP
+ ftp_auth_type[i++] = FTA_SRP;
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ ftp_auth_type[i++] = FTA_K4;
+#endif /* FTP_KRB4 */
+#ifdef CK_SSL
+ ftp_auth_type[i++] = FTA_TLS;
+ ftp_auth_type[i++] = FTA_SSL;
+#endif /* CK_SSL */
+ ftp_auth_type[i] = 0;
+ } else {
+ for (i = 0; i < 8; i++)
+ ftp_auth_type[i] = atypes[i];
+ }
+ return(success = 1);
+ }
+
+ case FTS_SRP:
+#ifdef FTP_SRP
+ if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0)
+ return(x);
+ switch (x) {
+ case SRP_CIPHER:
+ if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0)
+ return(x);
+ if ((z = cmcfm()) < 0)
+ return(z);
+ success = !srp_selcipher(ciphertab[x].kwd);
+ return(success);
+ case SRP_HASH:
+ if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0)
+ return(x);
+ if ((z = cmcfm()) < 0)
+ return(z);
+ success = !srp_selhash(hashtab[x].kwd);
+ return(success = 1);
+ default:
+ if ((z = cmcfm()) < 0)
+ return(z);
+ return(-2);
+ }
+#else /* FTP_SRP */
+ if ((z = cmcfm()) < 0)
+ return(z);
+ return(-2);
+#endif /* FTP_SRP */
+#endif /* FTP_SECURITY */
+
+ case FTS_DIS:
+ doxdis(2); /* 2 == ftp */
+ return(success = 1);
+
+ default:
+ return(-2);
+ }
+}
+
+int
+ftpbye() {
+ int x;
+ if (!connected)
+ return(1);
+ if (testing)
+ printf(" ftp closing %s...\n",ftp_host);
+ x = ftpclose();
+ return((x > -1) ? 1 : 0);
+}
+
+/* o p e n f t p -- Parse FTP hostname & port and open */
+
+static int
+openftp(s,opn_tls) char * s; int opn_tls; {
+ char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL;
+ int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0;
+ int haveuser = 0;
+ struct FDB sw, fl, cm;
+ extern int nnetdir; /* Network services directory */
+ extern int nhcount; /* Lookup result */
+ extern char *nh_p[]; /* Network directory entry pointers */
+ extern char *nh_p2[]; /* Network directory entry nettype */
+
+ if (!s) return(-2);
+ if (!*s) return(-2);
+
+ makestr(&hostname,s);
+ hostsave = hostname;
+ makestr(&ftp_logname,NULL);
+ anonymous = 0;
+ noinit = 0;
+
+ debug(F110,"ftp open",hostname,0);
+
+ if (sav_psv > -1) { /* Restore prevailing active/passive */
+ ftp_psv = sav_psv; /* selection in case it was */
+ sav_psv = -1; /* temporarily overriden by a switch */
+ }
+ if (sav_log > -1) { /* Ditto for autologin */
+ ftp_log = sav_log;
+ sav_log = -1;
+ }
+ cmfdbi(&sw, /* Switches */
+ _CMKEY,
+ "Service name or port;\n or switch",
+ "", /* default */
+ "", /* addtl string data */
+ nftpswi, /* addtl numeric data 1: tbl size */
+ 4, /* addtl numeric data 2: none */
+ xxstring, /* Processing function */
+ ftpswitab, /* Keyword table */
+ &fl /* Pointer to next FDB */
+ );
+ cmfdbi(&fl, /* A host name or address */
+ _CMFLD, /* fcode */
+ "", /* help */
+ "xYzBoo", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ &cm
+ );
+ cmfdbi(&cm, /* Command confirmation */
+ _CMCFM,
+ "",
+ "",
+ "",
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ for (n = 0;; n++) {
+ rc = cmfdb(&sw); /* Parse a service name or a switch */
+ if (rc < 0)
+ goto xopenftp;
+
+ if (cmresult.fcode == _CMCFM) { /* Done? */
+ break;
+ } else if (cmresult.fcode == _CMFLD) { /* Port */
+ if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1))
+ makestr(&service,cmresult.sresult);
+ else
+ makestr(&service,opn_tls?"ftps":"ftp");
+ } else if (cmresult.fcode == _CMKEY) { /* Have a switch */
+ c = cmgbrk(); /* get break character */
+ getval = (c == ':' || c == '=');
+ rc = -9;
+ if (getval && !(cmresult.kflags & CM_ARG)) {
+ printf("?This switch does not take arguments\n");
+ goto xopenftp;
+ }
+ if (!getval && (cmresult.kflags & CM_ARG)) {
+ printf("?This switch requires an argument\n");
+ goto xopenftp;
+ }
+ switch (cmresult.nresult) { /* Switch */
+ case OPN_ANO: /* /ANONYMOUS */
+ anonymous++;
+ nologin = 0;
+ break;
+ case OPN_NIN: /* /NOINIT */
+ noinit++;
+ break;
+ case OPN_NOL: /* /NOLOGIN */
+ nologin++;
+ anonymous = 0;
+ makestr(&ftp_logname,NULL);
+ break;
+ case OPN_PSW: /* /PASSWORD */
+ if (!anonymous) /* Don't log real passwords */
+ debok = 0;
+ rc = cmfld("Password for FTP server","",&p,xxstring);
+ if (rc == -3) {
+ makestr(&ftp_tmp,NULL);
+ } else if (rc < 0) {
+ goto xopenftp;
+ } else {
+ makestr(&ftp_tmp,brstrip(p));
+ nologin = 0;
+ }
+ break;
+ case OPN_USR: /* /USER */
+ rc = cmfld("Username for FTP server","",&p,xxstring);
+ if (rc == -3) {
+ makestr(&ftp_logname,NULL);
+ } else if (rc < 0) {
+ goto xopenftp;
+ } else {
+ nologin = 0;
+ anonymous = 0;
+ haveuser = 1;
+ makestr(&ftp_logname,brstrip(p));
+ }
+ break;
+ case OPN_ACC:
+ rc = cmfld("Account for FTP server","",&p,xxstring);
+ if (rc == -3) {
+ makestr(&ftp_acc,NULL);
+ } else if (rc < 0) {
+ goto xopenftp;
+ } else {
+ makestr(&ftp_acc,brstrip(p));
+ }
+ break;
+ case OPN_ACT:
+ opn_psv = 0;
+ break;
+ case OPN_PSV:
+ opn_psv = 1;
+ break;
+ case OPN_TLS:
+ opn_tls = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (n == 0) { /* After first time through */
+ cmfdbi(&sw, /* accept only switches */
+ _CMKEY,
+ "\nCarriage return to confirm to command, or switch",
+ "",
+ "",
+ nftpswi,
+ 4,
+ xxstring,
+ ftpswitab,
+ &cm
+ );
+ }
+ }
+#ifdef COMMENT
+ debug(F100,"ftp openftp while exit","",0);
+ rc = cmcfm();
+ debug(F101,"ftp openftp cmcfm rc","",rc);
+ if (rc < 0)
+ goto xopenftp;
+#endif /* COMMENT */
+
+ if (opn_psv > -1) { /* /PASSIVE or /ACTIVE switch given */
+ sav_psv = ftp_psv;
+ ftp_psv = opn_psv;
+ }
+ if (nologin || haveuser) { /* /NOLOGIN or /USER switch given */
+ sav_log = ftp_log;
+ ftp_log = haveuser ? 1 : 0;
+ }
+ if (*hostname == '=') { /* Bypass directory lookup */
+ hostname++; /* if hostname starts with '=' */
+ havehost++;
+ } else if (isdigit(*hostname)) { /* or if it starts with a digit */
+ havehost++;
+ }
+ if (!service)
+ makestr(&service,opn_tls?"ftps":"ftp");
+
+#ifndef NODIAL
+ if (!havehost && nnetdir > 0) { /* If there is a networks directory */
+ lunet(hostname); /* Look up the name */
+ debug(F111,"ftp openftp lunet",hostname,nhcount);
+ if (nhcount == 0) {
+ if (testing)
+ printf(" ftp open trying \"%s %s\"...\n",hostname,service);
+ success = ftpopen(hostname,service,opn_tls);
+ debug(F101,"ftp openftp A ftpopen success","",success);
+ rc = success;
+ } else {
+ int found = 0;
+ for (i = 0; i < nhcount; i++) {
+ if (nh_p2[i]) /* If network type specified */
+ if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0))
+ continue;
+ found++;
+ makestr(&hostname,nh_p[i]);
+ debug(F111,"ftpopen lunet substitution",hostname,i);
+ if (testing)
+ printf(" ftp open trying \"%s %s\"...\n",hostname,service);
+ success = ftpopen(hostname,service,opn_tls);
+ debug(F101,"ftp openftp B ftpopen success","",success);
+ rc = success;
+ if (success)
+ break;
+ }
+ if (!found) { /* E.g. if no network types match */
+ if (testing)
+ printf(" ftp open trying \"%s %s\"...\n",hostname,service);
+ success = ftpopen(hostname,service,opn_tls);
+ debug(F101,"ftp openftp C ftpopen success","",success);
+ rc = success;
+ }
+ }
+ } else {
+#endif /* NODIAL */
+ if (testing)
+ printf(" ftp open trying \"%s %s\"...\n",hostname,service);
+ success = ftpopen(hostname,service,opn_tls);
+ debug(F111,"ftp openftp D ftpopen success",hostname,success);
+ debug(F111,"ftp openftp D ftpopen connected",hostname,connected);
+ rc = success;
+#ifndef NODIAL
+ }
+#endif /* NODIAL */
+
+ xopenftp:
+ debug(F101,"ftp openftp xopenftp rc","",rc);
+ if (hostsave) free(hostsave);
+ if (service) free(service);
+ if (rc < 0 && ftp_logname) {
+ free(ftp_logname);
+ ftp_logname = NULL;
+ }
+ if (ftp_tmp) {
+ free(ftp_tmp);
+ ftp_tmp = NULL;
+ }
+ return(rc);
+}
+
+int
+doftpacct() {
+ int x;
+ char * s;
+ if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ makestr(&ftp_acc,brstrip(s));
+ if (testing)
+ printf(" ftp account: \"%s\"\n",ftp_acc);
+ success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
+ return(success);
+}
+
+int
+doftpusr() { /* Log in as USER */
+ int x;
+ char *s, * acct = "";
+
+ debok = 0; /* Don't log */
+ if ((x = cmfld("Remote username or ID","",&s,xxstring)) < 0)
+ return(x);
+ ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */
+ if ((x = cmfld("Remote password","",&s,xxstring)) < 0)
+ if (x != -3)
+ return(x);
+ ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
+ if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command",
+ "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ if (*s) {
+ x = strlen(tmpbuf);
+ if (x > 0) {
+ acct = &tmpbuf[x+2];
+ ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2);
+ }
+ }
+ if (testing)
+ printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf);
+ success = ftp_user(line,tmpbuf,acct);
+#ifdef CKLOGDIAL
+ dologftp();
+#endif /* CKLOGDIAL */
+ return(success);
+}
+
+/* DO (various FTP commands)... */
+
+int
+doftptyp(type) int type; { /* TYPE */
+ CHECKCONN();
+ ftp_typ = type;
+ changetype(ftp_typ,ftp_vbm);
+ return(1);
+}
+
+static int
+doftpxmkd(s,vbm) char * s; int vbm; { /* MKDIR action */
+ int lcs = -1, rcs = -1;
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+ debug(F110,"ftp doftpmkd",s,0);
+ if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
+ return(success = 1);
+ if (ftpcode == 500 || ftpcode == 502) {
+ if (!quiet)
+ printf("MKD command not recognized, trying XMKD\n");
+ if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
+ return(success = 1);
+ }
+ return(success = 0);
+}
+
+static int
+doftpmkd() { /* MKDIR parse */
+ int x;
+ char * s;
+ if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp mkdir \"%s\"...\n",line);
+ return(success = doftpxmkd(line,-1));
+}
+
+static int
+doftprmd() { /* RMDIR */
+ int x, lcs = -1, rcs = -1;
+ char * s;
+ if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp rmdir \"%s\"...\n",line);
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+ if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE)
+ return(success = 1);
+ if (ftpcode == 500 || ftpcode == 502) {
+ if (!quiet)
+ printf("RMD command not recognized, trying XMKD\n");
+ success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
+ } else
+ success = 0;
+ return(success);
+}
+
+static int
+doftpren() { /* RENAME */
+ int x;
+ char * s;
+ if ((x = cmfld("Remote filename","",&s,xxstring)) < 0)
+ return(x);
+ ckstrncpy(line,s,LINBUFSIZ);
+ if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0)
+ return(x);
+ ckstrncpy(tmpbuf,s,TMPBUFSIZ);
+ if ((x = cmcfm()) < 0)
+ return(x);
+ CHECKCONN();
+ if (testing)
+ printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf);
+ success = ftp_rename(line,tmpbuf);
+ return(success);
+}
+
+int
+doftpres() { /* RESET (log out without close) */
+ int x;
+ if ((x = cmcfm()) < 0)
+ return(x);
+ CHECKCONN();
+ if (testing)
+ printf(" ftp reset...\n");
+ return(success = ftp_reset());
+}
+
+static int
+doftpxhlp() { /* HELP */
+ int x;
+ char * s;
+ if ((x = cmtxt("Command name", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp help \"%s\"...\n",line);
+ /* No need to translate -- all FTP commands are ASCII */
+ return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE));
+}
+
+static int
+doftpdir(cx) int cx; { /* [V]DIRECTORY */
+ int x, lcs = 0, rcs = 0, xlate = 0;
+ char * p, * s, * m = "";
+ if (cx == FTP_VDI) {
+ switch (servertype) {
+ case SYS_VMS:
+ case SYS_DOS:
+ case SYS_TOPS10:
+ case SYS_TOPS20:
+ m = "*.*";
+ break;
+ default:
+ m = "*";
+ }
+ }
+ if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0)
+ return(x);
+ if ((x = remtxt(&s)) < 0)
+ return(x);
+#ifdef NOCSETS
+ xlate = 0;
+#else
+ xlate = ftp_xla;
+#endif /* NOCSETS */
+ line[0] = NUL;
+ ckstrncpy(line,s,LINBUFSIZ);
+ s = line;
+ CHECKCONN();
+
+#ifndef NOCSETS
+ if (xlate) { /* SET FTP CHARACTER-SET-TRANSLATION */
+ lcs = ftp_csl; /* Local charset */
+ if (lcs < 0) lcs = fcharset;
+ if (lcs < 0) xlate = 0;
+ }
+ if (xlate) { /* Still ON? */
+ rcs = ftp_csx; /* Remote (Server) charset */
+ if (rcs < 0) rcs = ftp_csr;
+ if (rcs < 0) xlate = 0;
+ }
+#endif /* NOCSETS */
+
+ if (testing) {
+ p = s;
+ if (!p) p = "";
+ if (*p)
+ printf("Directory of files %s at %s:\n", line, ftp_host);
+ else
+ printf("Directory of files at %s:\n", ftp_host);
+ }
+ debug(F111,"doftpdir",s,cx);
+
+ if (cx == FTP_DIR) {
+ /* Translation of line[] is done inside recvrequest() */
+ /* when it calls ftpcmd(). */
+ return(success =
+ (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0));
+ }
+ success = 1; /* VDIR - one file at a time... */
+ p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */
+ cancelgroup = 0;
+ if (!ftp_vbm && !quiet)
+ printlines = 1;
+ while (p && !cancelfile && !cancelgroup) { /* STAT one file */
+ if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) {
+ success = 0;
+ break;
+ }
+ p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */
+ debug(F110,"ftp vdir file",s,0);
+ }
+ return(success);
+}
+
+static int
+doftppwd() { /* PWD */
+ int x, lcs = -1, rcs = -1;
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+ if ((x = cmcfm()) < 0)
+ return(x);
+ CHECKCONN();
+ if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) {
+ success = 1;
+ } else if (ftpcode == 500 || ftpcode == 502) {
+ if (ftp_deb)
+ printf("PWD command not recognized, trying XPWD\n");
+ success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE);
+ }
+ return(success);
+}
+
+static int
+doftpcwd(s,vbm) char * s; int vbm; { /* CD (CWD) */
+ int lcs = -1, rcs = -1;
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+
+ debug(F110,"ftp doftpcwd",s,0);
+ if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
+ return(success = 1);
+ if (ftpcode == 500 || ftpcode == 502) {
+ if (!quiet)
+ printf("CWD command not recognized, trying XCWD\n");
+ if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
+ return(success = 1);
+ }
+ return(success = 0);
+}
+
+static int
+doftpcdup() { /* CDUP */
+ debug(F100,"ftp doftpcdup","",0);
+ if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE)
+ return(success = 1);
+ if (ftpcode == 500 || ftpcode == 502) {
+ if (!quiet)
+ printf("CDUP command not recognized, trying XCUP\n");
+ if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE)
+ return(success = 1);
+ }
+ return(success = 0);
+}
+
+/* s y n c d i r -- Synchronizes client & server directories */
+
+/* Used with recursive PUTs; Returns 0 on failure, 1 on success */
+
+static int cdlevel = 0, cdsimlvl = 0;
+
+static int
+syncdir(local,sim) char * local; int sim; {
+ char buf[CKMAXPATH+1];
+ char tmp[CKMAXPATH+1];
+ char msgbuf[CKMAXPATH+64];
+ char c, * p = local, * s = buf, * q = buf;
+ int i, k = 0, done = 0, itsadir = 0, saveq;
+
+ debug(F110,"ftp syncdir local (new)",local,0);
+ debug(F110,"ftp syncdir putpath (old)",putpath,0);
+
+ itsadir = isdir(local);
+ saveq = quiet;
+
+ while ((*s = *p)) { /* Copy the argument filename */
+ if (++k == CKMAXPATH) /* so we can poke it. */
+ return(-1);
+ if (*s == '/') /* Pointer to rightmost dirsep */
+ q = s;
+ s++;
+ p++;
+ }
+ if (!itsadir)
+ *q = NUL; /* Keep just the path part */
+
+ debug(F110,"ftp syncdir buf",buf,0);
+ if (!strcmp(buf,putpath)) { /* Same as for previous file? */
+ if (itsadir) { /* It's a directory? */
+ if (doftpcwd(local,0)) { /* Try to CD to it */
+ doftpcdup(); /* Worked - CD back up */
+ } else if (sim) { /* Simulating... */
+ if (fdispla == XYFD_B) {
+ printf("WOULD CREATE DIRECTORY %s\n",local);
+ } else if (fdispla) {
+ ckmakmsg(msgbuf,CKMAXPATH,
+ "WOULD CREATE DIRECTORY",local,NULL,NULL);
+ ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
+ }
+ /* See note above */
+ return(0);
+ } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */
+ return(0);
+ } else {
+ if (fdispla == XYFD_B) {
+ printf("CREATED DIRECTORY %s\n",local);
+ } else if (fdispla) {
+ ckmakmsg(msgbuf,CKMAXPATH+64,
+ "CREATED DIRECTORY ",local,NULL,NULL);
+ ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
+ }
+ }
+ }
+ debug(F110,"ftp syncdir no change",buf,0);
+ return(1); /* Yes, done. */
+ }
+ ckstrncpy(tmp,buf,CKMAXPATH+1); /* Make a safe (pre-poked) copy */
+ debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */
+
+ p = buf; /* New */
+ s = putpath; /* Old */
+
+ debug(F110,"ftp syncdir A p",p,0);
+ debug(F110,"ftp syncdir A s",s,0);
+
+ while (*p != NUL && *s != NUL && *p == *s) p++,s++;
+
+ if (*s == '/' && !*p) s++; /* Don't count initial slash */
+
+ debug(F110,"ftp syncdir B p",p,0);
+ debug(F110,"ftp syncdir B s",s,0);
+
+ /* p and s now point to the leftmost spot where they differ */
+
+ if (*s) { /* We have to back up */
+ k = 1; /* How many levels */
+ while ((c = *s++)) { /* Count dirseps */
+ if (c == '/' && *s)
+ k++;
+ }
+ for (i = 0; i < k; i++) { /* Do that many CDUPs */
+ debug(F111,"ftp syncdir up",p,i+1);
+ if (sim && cdsimlvl) {
+ cdsimlvl--;
+ } else {
+ if (!doftpcdup()) {
+ quiet = saveq;
+ return(0);
+ }
+ }
+ cdlevel--;
+ }
+ if (!*p) /* If we don't have to go down */
+ goto xcwd; /* we're done. */
+ }
+ while (p > buf && *p && *p != '/') /* If in middle of segment */
+ p--; /* back up to beginning */
+ if (*p == '/') /* and terminate there */
+ p++;
+
+ s = p; /* Point to start of new down path. */
+ while (1) { /* Loop through characters. */
+ if (*s == '/' || !*s) { /* Have a segment. */
+ if (!*s) /* If end of string, */
+ done++; /* after this segment we're done. */
+ else
+ *s = NUL; /* NUL out the separator. */
+ if (*p) { /* If segment is not empty */
+ debug(F110,"ftp syncdir down segment",p,0);
+ if (!doftpcwd(p,0)) { /* Try to CD to it */
+ if (sim) {
+ if (fdispla == XYFD_B) {
+ printf("WOULD CREATE DIRECTORY %s\n",local);
+ } else if (fdispla) {
+ ckmakmsg(msgbuf,CKMAXPATH,"WOULD CREATE DIRECTORY",
+ local,NULL,NULL);
+ ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
+ }
+ cdsimlvl++;
+ } else {
+ if (!doftpxmkd(p,0)) { /* Can't CD - try to create */
+/*
+ Suppose we are executing SEND /RECURSIVE. Locally we have a directory
+ FOO but the remote has a regular file with the same name. We can't CD
+ to it, can't MKDIR it either. There's no way out but to fail and let
+ the user handle the problem.
+*/
+ quiet = saveq;
+ return(0);
+ }
+ if (fdispla == XYFD_B) {
+ printf("CREATED DIRECTORY %s\n",p);
+ } else if (fdispla) {
+ ckmakmsg(msgbuf,CKMAXPATH,
+ "CREATED DIRECTORY ",p,NULL,NULL);
+ ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
+ }
+ if (!doftpcwd(p,0)) { /* Try again to CD */
+ quiet = saveq;
+ return(0);
+ }
+ }
+ }
+ cdlevel++;
+ }
+ if (done) /* Quit if no next segment */
+ break;
+ p = s+1; /* Point to next segment */
+ }
+ s++; /* Point to next source char */
+ }
+
+ xcwd:
+ ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */
+ quiet = saveq;
+ return(1);
+}
+
+#ifdef DOUPDATE
+#ifdef DEBUG
+static VOID
+dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */
+ if (deblog) {
+ debug(F111,"ftp year ",s,xx->tm_year);
+ debug(F111,"ftp month",s,xx->tm_mon);
+ debug(F111,"ftp day ",s,xx->tm_mday);
+ debug(F111,"ftp hour ",s,xx->tm_hour);
+ debug(F111,"ftp min ",s,xx->tm_min);
+ debug(F111,"ftp sec ",s,xx->tm_sec);
+ }
+}
+#endif /* DEBUG */
+
+/* t m c o m p a r e -- Compare two struct tm's */
+
+/* Like strcmp() but for struct tm's */
+/* Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */
+
+static int
+tmcompare(xx,yy) struct tm * xx, * yy; {
+
+ if (xx->tm_year < yy->tm_year) /* First year less than second */
+ return(-1);
+ if (xx->tm_year > yy->tm_year) /* First year greater than second */
+ return(1);
+
+ /* Years are equal so compare months */
+
+ if (xx->tm_mon < yy->tm_mon) /* And so on... */
+ return(-1);
+ if (xx->tm_mon > yy->tm_mon)
+ return(1);
+
+ if (xx->tm_mday < yy->tm_mday)
+ return(-1);
+ if (xx->tm_mday > yy->tm_mday)
+ return(1);
+
+ if (xx->tm_hour < yy->tm_hour)
+ return(-1);
+ if (xx->tm_hour > yy->tm_hour)
+ return(1);
+
+ if (xx->tm_min < yy->tm_min)
+ return(-1);
+ if (xx->tm_min > yy->tm_min)
+ return(1);
+
+ if (xx->tm_sec < yy->tm_sec)
+ return(-1);
+ if (xx->tm_sec > yy->tm_sec)
+ return(1);
+
+ return(0);
+}
+#endif /* DOUPDATE */
+
+#ifndef HAVE_TIMEGM /* For platforms that do not have timegm() */
+static CONST int MONTHDAYS[] = { /* Number of days in each month. */
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/* Macro for whether a given year is a leap year. */
+#define ISLEAP(year) \
+(((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
+#endif /* HAVE_TIMEGM */
+
+/* m k u t i m e -- Like mktime() but argument is already UTC */
+
+static time_t
+#ifdef CK_ANSIC
+mkutime(struct tm * tm)
+#else
+mkutime(tm) struct tm * tm;
+#endif /* CK_ANSIC */
+/* mkutime */ {
+#ifdef HAVE_TIMEGM
+ return(timegm(tm)); /* Have system service, use it. */
+#else
+/*
+ Contributed by Russ Allbery (rra@stanford.edu), used by permission.
+ Given a struct tm representing a calendar time in UTC, convert it to
+ seconds since epoch. Returns (time_t) -1 if the time is not
+ convertable. Note that this function does not canonicalize the provided
+ struct tm, nor does it allow out-of-range values or years before 1970.
+ Result should be identical with timegm().
+*/
+ time_t result = 0;
+ int i;
+ /*
+ We do allow some ill-formed dates, but we don't do anything special
+ with them and our callers really shouldn't pass them to us. Do
+ explicitly disallow the ones that would cause invalid array accesses
+ or other algorithm problems.
+ */
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"mkutime tm_mon","",tm->tm_mon);
+ debug(F101,"mkutime tm_year","",tm->tm_year);
+ }
+#endif /* DEBUG */
+ if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70)
+ return((time_t) -1);
+
+ /* Convert to time_t. */
+ for (i = 1970; i < tm->tm_year + 1900; i++)
+ result += 365 + ISLEAP(i);
+ for (i = 0; i < tm->tm_mon; i++)
+ result += MONTHDAYS[i];
+ if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900))
+ result++;
+ result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
+ result = 60 * result + tm->tm_min;
+ result = 60 * result + tm->tm_sec;
+ debug(F101,"mkutime result","",result);
+ return(result);
+#endif /* HAVE_TIMEGM */
+}
+
+
+/*
+ s e t m o d t i m e -- Set file modification time.
+
+ f = char * filename;
+ t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local)
+
+ UNIX-specific; isolates mainline code from hideous #ifdefs.
+ Returns:
+ 0 on success,
+ -1 on error.
+
+*/
+static int
+#ifdef CK_ANSIC
+setmodtime(char * f, time_t t)
+#else
+setmodtime(f,t) char * f; time_t t;
+#endif /* CK_ANSIC */
+/* setmodtime */ {
+#ifdef NT
+ struct _stat sb;
+#else /* NT */
+ struct stat sb;
+#endif /* NT */
+ int x, rc = 0;
+#ifdef BSD44
+ struct timeval tp[2];
+#else
+#ifdef V7
+ struct utimbuf {
+ time_t timep[2];
+ } tp;
+#else
+#ifdef SYSUTIMEH
+#ifdef NT
+ struct _utimbuf tp;
+#else /* NT */
+ struct utimbuf tp;
+#endif /* NT */
+#else
+ struct utimbuf {
+ time_t atime;
+ time_t mtime;
+ } tp;
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+ if (stat(f,&sb) < 0) {
+ debug(F111,"setmodtime stat failure",f,errno);
+ return(-1);
+ }
+#ifdef BSD44
+ tp[0].tv_sec = sb.st_atime; /* Access time first */
+ tp[1].tv_sec = t; /* Update time second */
+ debug(F111,"setmodtime BSD44",f,t);
+#else
+#ifdef V7
+ tp.timep[0] = t; /* Set modif. time to creation date */
+ tp.timep[1] = sb.st_atime; /* Don't change the access time */
+ debug(F111,"setmodtime V7",f,t);
+#else
+#ifdef SYSUTIMEH
+ tp.modtime = t; /* Set modif. time to creation date */
+ tp.actime = sb.st_atime; /* Don't change the access time */
+ debug(F111,"setmodtime SYSUTIMEH",f,t);
+#else
+ tp.mtime = t; /* Set modif. time to creation date */
+ tp.atime = sb.st_atime; /* Don't change the access time */
+ debug(F111,"setmodtime (other)",f,t);
+#endif /* SYSUTIMEH */
+#endif /* V7 */
+#endif /* BSD44 */
+
+ /* Try to set the file date */
+
+#ifdef BSD44
+ x = utimes(f,tp);
+ debug(F111,"setmodtime utimes()","BSD44",x);
+#else
+#ifdef IRIX65
+ {
+ /*
+ The following produces the nonsensical warning:
+ Argument of type "const struct utimbuf *" is incompatible with
+ parameter of type "const struct utimbuf *". If you can make it
+ go away, be my guest.
+ */
+ const struct utimbuf * t2 = &tp;
+ x = utime(f,t2);
+ }
+#else
+ x = utime(f,&tp);
+ debug(F111,"setmodtime utime()","other",x);
+#endif /* IRIX65 */
+#endif /* BSD44 */
+ if (x)
+ rc = -1;
+
+ debug(F101,"setmodtime result","",rc);
+ return(rc);
+}
+
+
+/*
+ c h k m o d t i m e -- Check/Set file modification time.
+
+ fc = function code:
+ 0 = Check; returns:
+ -1 on error,
+ 0 if local older than remote,
+ 1 if modtimes are equal,
+ 2 if local newer than remote.
+ 1 = Set (local file's modtime from remote's); returns:
+ -1 on error,
+ 0 on success.
+*/
+static int
+chkmodtime(local,remote,fc) char * local, * remote; int fc; {
+#ifdef NT
+ struct _stat statbuf;
+#else /* NT */
+ struct stat statbuf;
+#endif /* NT */
+ struct tm * tmlocal = NULL;
+ struct tm tmremote;
+ int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0;
+ char * s, timebuf[64];
+
+ debug(F111,"chkmodtime",local,mdtmok);
+ if (!mdtmok) /* Server supports MDTM? */
+ return(-1); /* No don't bother. */
+
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+
+ if (fc == 0) {
+ rc = stat(local,&statbuf);
+ if (rc == 0) { /* Get local file's mod time */
+ tmlocal = gmtime(&statbuf.st_mtime); /* Convert to struct tm */
+#ifdef DEBUG
+ if (tmlocal) {
+ dbtime(local,tmlocal);
+ }
+#endif /* DEBUG */
+ }
+ }
+ /* Get remote file's mod time as yyyymmddhhmmss */
+
+ if (havemdtm) { /* Already got it from MLSD? */
+ s = havemdtm;
+ flag++;
+ } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) {
+ char c;
+ bzero((char *)&tmremote, sizeof(struct tm));
+ s = ftp_reply_str;
+ while ((c = *s++)) { /* Skip past response code */
+ if (c == SP) {
+ flag++;
+ break;
+ }
+ }
+ }
+ if (flag) {
+ debug(F111,"ftp chkmodtime string",s,flag);
+ if (fts_sto) { /* User gave server time offset? */
+ char * p;
+ debug(F110,"ftp chkmodtime offset",fts_sto,0);
+ ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */
+ if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */
+ ckstrncpy(timebuf,p,64); /* Convert to MDTM format */
+ timebuf[8] = timebuf[9]; /* h */
+ timebuf[9] = timebuf[10]; /* h */
+ timebuf[10] = timebuf[12]; /* m */
+ timebuf[11] = timebuf[13]; /* m */
+ timebuf[12] = timebuf[12]; /* s */
+ timebuf[13] = timebuf[13]; /* s */
+ timebuf[14] = NUL;
+ s = timebuf;
+ debug(F110,"ftp chkmodtime adjust",s,0);
+ }
+ }
+ if (flag) { /* Convert to struct tm */
+ char * pat;
+ int y2kbug = 0; /* Seen in Kerberos 4 FTP servers */
+ if (!ckstrcmp(s,"191",3,0)) {
+ pat = "%05d%02d%02d%02d%02d%02d";
+ y2kbug++;
+ debug(F110,"ftp chkmodtime Y2K BUG detected",s,0);
+ } else {
+ pat = "%04d%02d%02d%02d%02d%02d";
+ }
+ if (sscanf(s, /* Parse into struct tm */
+ pat,
+ &(tmremote.tm_year),
+ &(tmremote.tm_mon),
+ &(tmremote.tm_mday),
+ &(tmremote.tm_hour),
+ &(tmremote.tm_min),
+ &(tmremote.tm_sec)
+ ) == 6) {
+ tmremote.tm_year -= (y2kbug ? 19000 : 1900);
+ debug(F101,"ftp chkmodtime year","",tmremote.tm_year);
+ tmremote.tm_mon--;
+
+#ifdef DEBUG
+ debug(F100,"SERVER TIME FOLLOWS:","",0);
+ dbtime(remote,&tmremote);
+#endif /* DEBUG */
+
+ if (havedate > -1)
+ havedate = 1;
+ }
+ }
+ } else { /* Failed */
+ debug(F101,"ftp chkmodtime ftpcode","",ftpcode);
+ if (ftpcode == 500 || /* Command unrecognized */
+ ftpcode == 502 || /* Command not implemented */
+ ftpcode == 202) /* Command superfluous */
+ mdtmok = 0; /* Don't ask this server again */
+ return(-1);
+ }
+ if (fc == 0) { /* Compare */
+ if (havedate == 1) { /* Only if we have both file dates */
+ /*
+ Compare with local file's time. We don't use
+ clock time (time_t) here in case of signed/unsigned
+ confusion, etc.
+ */
+ int xx;
+#ifdef COMMENT
+#ifdef DEBUG
+ if (deblog) {
+ dbtime("LOCAL",tmlocal);
+ dbtime("REMOT",&tmremote);
+ }
+#endif /* DEBUG */
+#endif /* COMMENT */
+ xx = tmcompare(tmlocal,&tmremote);
+ debug(F101,"chkmodtime tmcompare","",xx);
+ return(xx + 1);
+ }
+ } else if (ftp_dates) { /* Set */
+ /*
+ Here we must convert struct tm to time_t
+ without applying timezone conversion, for which
+ there is no portable API. The method is hidden
+ in mkutime(), defined above.
+ */
+ time_t utc;
+ utc = mkutime(&tmremote);
+ debug(F111,"ftp chkmodtime mkutime",remote,utc);
+ if (utc != (time_t)-1)
+ return(setmodtime(local,utc));
+ }
+ return(-1);
+}
+
+/* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */
+
+static int
+getfile(remote,local,recover,append,pipename,xlate,fcs,rcs)
+ char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs;
+/* getfile */ {
+ int rc = -1;
+ ULONG t0, t1;
+
+#ifdef GFTIMER
+ CKFLOAT sec;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+ char fullname[CKMAXPATH+1];
+
+ debug(F110,"ftp getfile remote A",remote,0);
+ debug(F110,"ftp getfile local A",local,0);
+ debug(F110,"ftp getfile pipename",pipename,0);
+ if (!remote) remote = "";
+
+#ifdef PATTERNS
+ /* Automatic type switching? */
+ if (xfermode == XMODE_A && patterns && get_auto && !forcetype) {
+ int x;
+ x = matchname(remote,0,servertype);
+ debug(F111,"ftp getfile matchname",remote,x);
+ switch (x) {
+ case 0: ftp_typ = FTT_ASC; break;
+ case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break;
+ default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ;
+ }
+ changetype(ftp_typ,ftp_vbm);
+ binary = ftp_typ; /* For file-transfer display */
+ }
+#endif /* PATTERNS */
+
+#ifndef NOCSETS
+ ftp_csx = -1; /* For file-transfer display */
+ ftp_csl = -1; /* ... */
+
+ if (rcs > -1) /* -1 means no translation */
+ if (ftp_typ == FTT_ASC) /* File type is "ascii"? */
+ if (fcs < 0) /* File charset not forced? */
+ fcs = fcharset; /* use prevailing FILE CHARACTER-SET */
+ if (fcs > -1 && rcs > -1) { /* Set up translation functions */
+ debug(F110,"ftp getfile","initxlate",0);
+ initxlate(rcs,fcs); /* NB: opposite order of PUT */
+ ftp_csx = rcs;
+ ftp_csl = fcs;
+ } else
+ xlate = 0;
+#endif /* NOCSETS */
+
+ if (!pipename && (!local || !local[0]))
+ local = remote;
+
+ out2screen = !strcmp(local,"-");
+
+ fullname[0] = NUL;
+ if (pipename) {
+ ckstrncpy(fullname,pipename,CKMAXPATH+1);
+ } else {
+ zfnqfp(local,CKMAXPATH,fullname);
+ if (!fullname[0])
+ ckstrncpy(fullname,local,CKMAXPATH+1);
+ }
+ if (!out2screen && displa && fdispla) { /* Screen */
+ ftscreen(SCR_FN,'F',(long)pktnum,remote);
+ ftscreen(SCR_AN,0,0L,fullname);
+ ftscreen(SCR_FS,0,fsize,"");
+ }
+ tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0);
+ tlog(F110," as",fullname,0);
+ debug(F111,"ftp getfile size",remote,fsize);
+ debug(F111,"ftp getfile local",local,out2screen);
+
+ ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH);
+
+ t0 = gmstimer(); /* Start time */
+ debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */
+ rc = recvrequest("RETR",
+ local,
+ remote,
+ append ? "ab" : "wb",
+ 0,
+ recover,
+ pipename,
+ xlate,
+ fcs,
+ rcs
+ );
+ t1 = gmstimer(); /* End time */
+ debug(F111,"ftp getfile t1",remote,t1);
+ debug(F111,"ftp getfile sec",remote,(t1-t0)/1000);
+#ifdef GFTIMER
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ fpxfsecs = sec; /* (for doxlog()) */
+#else
+ sec = (t1 - t0) / 1000;
+ xfsecs = (int)sec;
+#endif /* GFTIMER */
+ debug(F111,"ftp recvrequest rc",remote,rc);
+ if (cancelfile || cancelgroup) {
+ debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup);
+ ftscreen(SCR_ST,ST_INT,0l,"");
+ } else if (rc > 0) {
+ debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup);
+ ftscreen(SCR_ST,ST_SKIP,0l,cmarg);
+ } else if (rc < 0) {
+ switch (ftpcode) {
+ case -4: /* Network error */
+ case -2: /* File error */
+ ftscreen(SCR_ST,ST_MSG,0l,ck_errstr());
+ break;
+ case -3:
+ ftscreen(SCR_ST,ST_MSG,0l,"Failure to make data connection");
+ break;
+ case -1:
+ ftscreen(SCR_ST,ST_INT,0l,""); /* (should be covered above) */
+ break;
+ default:
+ ftscreen(SCR_ST,ST_MSG,0l,&ftp_reply_str[4]);
+ }
+ } else { /* Tudo bem */
+ ftscreen(SCR_PT,'Z',0L,"");
+ if (rc == 0) {
+ ftscreen(SCR_ST,ST_OK,0L,""); /* For screen */
+ makestr(&rrfspec,remote); /* For WHERE command */
+ makestr(&rfspec,fullname);
+ }
+ }
+ if (ftp_dates) /* If FTP DATES ON... */
+ if (!pipename && !out2screen) /* and it's a real file */
+ if (rc < 1 && rc != -3) /* and it wasn't skipped */
+ if (connected) /* and we still have a connection */
+ if (zchki(local) > -1) { /* and the file wasn't discarded */
+ chkmodtime(local,remote,1); /* set local file date */
+ debug(F110,"ftp get set date",local,0);
+ }
+ filcnt++; /* Used by \v(filenum) */
+#ifdef TLOG
+ if (tralog) {
+ if (rc > 0) {
+ tlog(F100," recovery skipped","",0);
+ } else if (rc == 0) {
+ tlog(F101," complete, size", "", fsize);
+ } else if (cancelfile) {
+ tlog(F100," canceled by user","",0);
+ } else {
+ tlog(F110," failed:",ftp_reply_str,0);
+ }
+ if (!tlogfmt)
+ doxlog(what,local,fsize,ftp_typ,rc,"");
+ }
+#endif /* TLOG */
+ return(rc);
+}
+
+/* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */
+/* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */
+
+static int
+putfile(cx,
+ local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg)
+ char * local, * remote, * mvto, *rnto, *srvrn;
+ int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg;
+
+/* putfile */ {
+
+ char asname[CKMAXPATH+1];
+ char fullname[CKMAXPATH+1];
+ int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0;
+ int xlate = 0, restart = 0, mt = -1;
+ char * s = NULL, * cmd = NULL;
+ ULONG t0 = 0, t1 = 0; /* Times for stats */
+ int ofcs = 0, orcs = 0;
+
+#ifdef GFTIMER
+ CKFLOAT sec = 0.0;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+ debug(F111,"ftp putfile flg",local,flg);
+ debug(F110,"ftp putfile srv_renam",srvrn,0);
+ debug(F101,"ftp putfile fcs","",fcs);
+ debug(F101,"ftp putfile rcs","",rcs);
+
+ ofcs = fcs; /* Save charset args */
+ orcs = rcs;
+
+ sendstart = 0L;
+ restart = flg & PUT_RES;
+ if (!remote)
+ remote = "";
+
+ /* FTP protocol command to send to server */
+ cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR");
+
+ if (x_cnv == SET_AUTO) { /* Name conversion is auto */
+ if (alike) { /* If server & client are alike */
+ nc = 0; /* no conversion */
+ } else { /* If they are different */
+ if (servertype == SYS_UNIX || servertype == SYS_WIN32)
+ nc = -1; /* only minimal conversions needed */
+ else /* otherwise */
+ nc = 1; /* full conversion */
+ }
+ } else /* Not auto - do what user said */
+ nc = x_cnv;
+
+ /* If Transfer Mode is Automatic, determine file type */
+ if (xfermode == XMODE_A && filepeek && !pipesend) {
+ if (isdir(local)) { /* If it's a directory */
+ k = FT_BIN; /* skip the file scan */
+ } else {
+ debug(F110,"FTP PUT calling scanfile",local,0);
+ k = scanfile(local,&o,nscanfile); /* Scan the file */
+ }
+ debug(F111,"FTP PUT scanfile",local,k);
+ if (k > -1 && !forcetype) {
+ ftp_typ = (k == FT_BIN) ? 1 : 0;
+ if (xft > -1 && ftp_typ != xft) {
+ if (flg & PUT_SIM)
+ tlog(F110,"ftp put SKIP (Type):", local, 0);
+ return(SKP_TYP);
+ }
+ if (ftp_typ == 1 && tenex) /* User said TENEX? */
+ ftp_typ = FTT_TEN;
+ }
+ }
+#ifndef NOCSETS
+ ftp_csx = -1; /* For file-transfer display */
+ ftp_csl = -1; /* ... */
+
+ if (rcs > -1) { /* -1 means no translation */
+ if (ftp_typ == 0) { /* File type is "ascii"? */
+ if (fcs < 0) { /* File charset not forced? */
+ if (k < 0) { /* If we didn't scan */
+ fcs = fcharset; /* use prevailing FILE CHARACTER-SET */
+ } else { /* If we did scan, use scan result */
+ switch (k) {
+ case FT_TEXT: /* Unknown text */
+ fcs = fcharset;
+ break;
+ case FT_7BIT: /* 7-bit text */
+ fcs = dcset7;
+ break;
+ case FT_8BIT: /* 8-bit text */
+ fcs = dcset8;
+ break;
+ case FT_UTF8: /* UTF-8 */
+ fcs = FC_UTF8;
+ break;
+ case FT_UCS2: /* UCS-2 */
+ fcs = FC_UCS2;
+ if (o > -1) /* Input file byte order */
+ fileorder = o;
+ break;
+ default:
+ rcs = -1;
+ }
+ }
+ }
+ }
+ }
+ if (fcs > -1 && rcs > -1) { /* Set up translation functions */
+ debug(F110,"ftp putfile","initxlate",0);
+ initxlate(fcs,rcs);
+ debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs);
+ xlate = 1;
+ ftp_csx = rcs;
+ ftp_csl = fcs;
+ }
+#endif /* NOCSETS */
+
+ binary = ftp_typ; /* For file-transfer display */
+ asname[0] = NUL;
+
+ if (recursive) { /* If sending recursively, */
+ if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */
+ return(-1); /* Don't PUT if it fails. */
+ else if (isdir(local)) /* It's a directory */
+ return(0); /* Don't send it! */
+ }
+ if (*remote) { /* If an as-name template was given */
+#ifndef NOSPL
+ if (cmd_quoting) { /* and COMMAND QUOTING is ON */
+ y = CKMAXPATH; /* evaluate it for this file */
+ s = asname;
+ zzstring(remote,&s,&y);
+ } else
+#endif /* NOSPL */
+ ckstrncpy(asname,remote,CKMAXPATH); /* (or take it literally) */
+ } else { /* No as-name */
+ nzltor(local,asname,nc,0,CKMAXPATH); /* use local name strip path */
+ debug(F110,"FTP PUT nzltor",asname,0);
+ }
+ /* Preliminary messages and log entries */
+
+ fullname[0] = NUL;
+ zfnqfp(local,CKMAXPATH,fullname);
+ if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1);
+ fullname[CKMAXPATH] = NUL;
+
+ if (displa && fdispla) { /* Screen */
+ ftscreen(SCR_FN,'F',(long)pktnum,local);
+ ftscreen(SCR_AN,0,0L,asname);
+ ftscreen(SCR_FS,0,fsize,"");
+ }
+#ifdef DOUPDATE
+ if (flg & (PUT_UPD|PUT_DIF)) { /* Date-checking modes... */
+ mt = chkmodtime(fullname,asname,0);
+ debug(F111,"ftp putfile chkmodtime",asname,mt);
+ if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */
+ tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0);
+ ftscreen(SCR_ST,ST_SKIP,SKP_DAT,fullname); /* Skip this one */
+ filcnt++;
+ return(SKP_DAT);
+ } else if (mt == 1) { /* Times are equal */
+ tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0);
+ ftscreen(SCR_ST,ST_SKIP,SKP_EQU,fullname); /* Skip it */
+ filcnt++;
+ return(SKP_DAT);
+ }
+ /* Local file is newer */
+ tlog(F110,ftp_typ ? "ftp put /update BINARY:" :
+ "ftp put /update TEXT:", fullname, 0);
+ } else if (flg & PUT_RES) {
+ tlog(F110,ftp_typ ? "ftp put /recover BINARY:" :
+ "ftp put /recover TEXT:", fullname, 0);
+ } else {
+ tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
+ }
+#else
+ tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
+#endif /* DOUPDATE */
+ tlog(F110," as",asname,0);
+
+#ifndef NOCSETS
+ if (xlate) {
+ debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs);
+ tlog(F110," file character set:",fcsinfo[fcs].keyword,0);
+ tlog(F110," server character set:",fcsinfo[rcs].keyword,0);
+ } else if (!ftp_typ) {
+ tlog(F110," character sets:","no conversion",0);
+ fcs = ofcs; /* Binary file but we still must */
+ rcs = orcs; /* translate its name */
+ }
+#endif /* NOCSETS */
+
+ /* PUT THE FILE */
+
+ t0 = gmstimer(); /* Start time */
+ if (flg & PUT_SIM) { /* rc > 0 is a skip reason code */
+ if (flg & (PUT_UPD|PUT_DIF)) { /* (see SKP_xxx in ckcker.h) */
+ rc = (mt < 0) ? /* Update mode... */
+ SKP_XNX : /* Remote file doesn't exist */
+ SKP_XUP; /* Remote file is older */
+ } else {
+ rc = SKP_SIM; /* "Would be sent", period. */
+ }
+ } else {
+ rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart);
+ }
+ t1 = gmstimer(); /* End time */
+ filcnt++; /* File number */
+
+#ifdef GFTIMER
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ fpxfsecs = sec; /* (for doxlog()) */
+#else
+ sec = (t1 - t0) / 1000;
+ xfsecs = (int)sec;
+#endif /* GFTIMER */
+
+ debug(F111,"ftp sendrequest rc",local,rc);
+
+ if (cancelfile || cancelgroup) {
+ debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup);
+ ftscreen(SCR_ST,ST_INT,0l,"");
+ } else if (rc > 0) {
+ debug(F101,"ftp put skipped",local,rc);
+ ftscreen(SCR_ST,ST_SKIP,rc,fullname);
+ } else if (rc < 0) {
+ debug(F111,"ftp put error",local,ftpcode);
+ ftscreen(SCR_ST,ST_MSG,0L,&ftp_reply_str[4]);
+ } else {
+ debug(F111,"ftp put not canceled",ckitoa(displa),fdispla);
+ ftscreen(SCR_PT,'Z',0L,"");
+ debug(F111,"ftp put ST_OK",local,rc);
+ ftscreen(SCR_ST,ST_OK,0L,"");
+ debug(F110,"ftp put old sfspec",sfspec,0);
+ makestr(&sfspec,fullname); /* For WHERE command */
+ debug(F110,"ftp put new sfspec",sfspec,0);
+ debug(F110,"ftp put old srfspec",srfspec,0);
+ makestr(&srfspec,asname);
+ debug(F110,"ftp put new srfspec",srfspec,0);
+ }
+
+ /* Final log entries */
+
+#ifdef TLOG
+ if (tralog) {
+ if (rc > 0) {
+ if (rc == SKP_XNX)
+ tlog(F100," /simulate: WOULD BE SENT:","no remote file",0);
+ else if (rc == SKP_XUP)
+ tlog(F100," /simulate: WOULD BE SENT:","remote file older",0);
+ else if (rc == SKP_SIM)
+ tlog(F100," /simulate: WOULD BE SENT","",0);
+ else
+ tlog(F110," skipped:",gskreason(rc),0);
+ } else if (rc == 0) {
+ tlog(F101," complete, size", "", fsize);
+ } else if (cancelfile) {
+ tlog(F100," canceled by user","",0);
+ } else {
+ tlog(F110," failed:",ftp_reply_str,0);
+ }
+ if (!tlogfmt)
+ doxlog(what,local,fsize,ftp_typ,rc,"");
+ }
+#endif /* TLOG */
+
+ if (rc < 0) /* PUT did not succeed */
+ return(-1); /* so done. */
+
+ if (flg & PUT_SIM) /* Simulating, skip the rest. */
+ return(SKP_SIM);
+
+#ifdef UNIX
+ /* Set permissions too? */
+
+ if (prm) { /* Change permissions? */
+ s = zgperm(local); /* Get perms of local file */
+ if (!s) s = "";
+ x = strlen(s);
+ if (x > 3) s += (x - 3);
+ if (rdigits(s)) {
+ ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL);
+ x =
+ ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE;
+ tlog(F110, x ? " chmod" : " chmod failed",
+ s,
+ 0
+ );
+ if (!x)
+ return(-1);
+ }
+ }
+#endif /* UNIX */
+
+ /* Disposition of source file */
+
+ if (moving) {
+ x = zdelet(local);
+ tlog(F110, (x > -1) ?
+ " deleted" : " failed to delete",
+ local,
+ 0
+ );
+ if (x < 0)
+ return(-1);
+ } else if (mvto) {
+ x = zrename(local,mvto);
+ tlog(F110, (x > -1) ?
+ " moved source to" : " failed to move source to",
+ mvto,
+ 0
+ );
+ if (x < 0)
+ return(-1);
+ /* ftscreen(SCR_ST,ST_MSG,0L,mvto); */
+
+ } else if (rnto) {
+ char * s = rnto;
+#ifndef NOSPL
+ int y; /* Pass it thru the evaluator */
+ extern int cmd_quoting; /* for \v(filename) */
+ if (cmd_quoting) { /* But only if cmd_quoting is on */
+ y = CKMAXPATH;
+ s = (char *)asname;
+ zzstring(rnto,&s,&y);
+ s = (char *)asname;
+ }
+#endif /* NOSPL */
+ if (s) if (*s) {
+ int x;
+ x = zrename(local,s);
+ tlog(F110, (x > -1) ?
+ " renamed source file to" :
+ " failed to rename source file to",
+ s,
+ 0
+ );
+ if (x < 0)
+ return(-1);
+ /* ftscreen(SCR_ST,ST_MSG,0L,s); */
+ }
+ }
+
+ /* Disposition of destination file */
+
+ if (srvrn) { /* /SERVER-RENAME: */
+ char * s = srvrn;
+#ifndef NOSPL
+ int y; /* Pass it thru the evaluator */
+ extern int cmd_quoting; /* for \v(filename) */
+ debug(F111,"ftp putfile srvrn",s,1);
+
+ if (cmd_quoting) { /* But only if cmd_quoting is on */
+ y = CKMAXPATH;
+ s = (char *)fullname; /* We can recycle this buffer now */
+ zzstring(srvrn,&s,&y);
+ s = (char *)fullname;
+ }
+#endif /* NOSPL */
+ debug(F111,"ftp putfile srvrn",s,2);
+ if (s) if (*s) {
+ int x;
+ x = ftp_rename(asname,s);
+ debug(F111,"ftp putfile ftp_rename",asname,x);
+ tlog(F110, (x > 0) ?
+ " renamed destination file to" :
+ " failed to rename destination file to",
+ s,
+ 0
+ );
+ if (x < 1)
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/* xxout must only be used for ASCII transfers */
+static int
+#ifdef CK_ANSIC
+xxout(char c)
+#else
+xxout(c) char c;
+#endif /* CK_ANSIC */
+{
+#ifndef OS2
+#ifndef VMS
+#ifndef MAC
+#ifndef OSK
+ /* For Unix, DG, Stratus, Amiga, Gemdos, other */
+ if (c == '\012') {
+ if (zzout(dout,(CHAR)'\015') < 0)
+ return(-1);
+ ftpsnd.bytes++;
+ }
+#else /* OSK */
+ if (c == '\015') {
+ c = '\012';
+ if (zzout(dout,(CHAR)'\015') < 0)
+ return(-1);
+ ftpsnd.bytes++;
+ }
+#endif /* OSK */
+#else /* MAC */
+ if (c == '\015') {
+ c = '\012';
+ if (zzout(dout,(CHAR)'\015') < 0)
+ return(-1);
+ ftpsnd.bytes++;
+ }
+#endif /* MAC */
+#endif /* VMS */
+#endif /* OS2 */
+ if (zzout(dout,(CHAR)c) < 0)
+ return(-1);
+ ftpsnd.bytes++;
+ return(0);
+}
+
+static int
+#ifdef CK_ANSIC
+scrnout(char c)
+#else
+scrnout(c) char c;
+#endif /* CK_ANSIC */
+{
+ return(putchar(c));
+}
+
+static int
+#ifdef CK_ANSIC
+pipeout(char c)
+#else
+pipeout(c) char c;
+#endif /* CK_ANSIC */
+{
+ return(zmchout(c));
+}
+
+static int
+ispathsep(c) int c; {
+ switch (servertype) {
+ case SYS_VMS:
+ case SYS_TOPS10:
+ case SYS_TOPS20:
+ return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0);
+ case SYS_OS2:
+ case SYS_WIN32:
+ case SYS_DOS:
+ return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0);
+ case SYS_VOS:
+ return((c == '>') ? 1 : 0);
+ default:
+ return((c == '/') ? 1 : 0);
+ }
+}
+
+static int
+iscanceled() {
+#ifdef CK_CURSES
+ extern int ck_repaint();
+#endif /* CK_CURSES */
+ int x, rc = 0;
+ char c = 0;
+ if (cancelfile)
+ return(1);
+ x = conchk(); /* Any chars waiting at console? */
+ if (x-- > 0) { /* Yes... */
+ c = coninc(5); /* Get one */
+ switch (c) {
+ case 032: /* Ctrl-X or X */
+ case 'z':
+ case 'Z': cancelgroup++; /* fall thru on purpose */
+ case 030: /* Ctrl-Z or Z */
+ case 'x':
+ case 'X': cancelfile++; rc++; break;
+#ifdef CK_CURSES
+ case 'L':
+ case 'l':
+ case 014: /* Ctrl-L or L or Ctrl-W */
+ case 027:
+ ck_repaint(); /* Refresh screen */
+#endif /* CK_CURSES */
+ }
+ }
+ while (x-- > 0) /* Soak up any rest */
+ c = coninc(1);
+ return(rc);
+}
+
+/* zzsend - used by buffered output macros. */
+
+static int
+#ifdef CK_ANSIC
+zzsend(int fd, CHAR c)
+#else
+zzsend(fd,c) int fd; CHAR c;
+#endif /* CK_ANSIC */
+{
+ int rc;
+
+ debug(F101,"zzsend ucbufsiz","",ucbufsiz);
+ debug(F101,"zzsend nout","",nout);
+ debug(F111,"zzsend","secure?",ftpissecure());
+
+ if (iscanceled()) /* Check for cancellation */
+ return(-9);
+ rc = (!ftpissecure()) ?
+ send(fd, (SENDARG2TYPE)ucbuf, nout, 0) :
+ secure_putbuf(fd, ucbuf, nout);
+ ucbuf[nout] = NUL;
+ nout = 0;
+ ucbuf[nout++] = c;
+ spackets++;
+ pktnum++;
+ if (rc > -1 && fdispla != XYFD_B) {
+ spktl = nout;
+ ftscreen(SCR_PT,'D',spackets,NULL);
+ }
+ return(rc);
+}
+
+/* c m d l i n p u t -- Command-line PUT */
+
+int
+cmdlinput(stay) int stay; {
+ int x, rc = 0, done = 0, good = 0, status = 0;
+ ULONG t0, t1; /* Times for stats */
+#ifdef GFTIMER
+ CKFLOAT sec;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+
+ if (quiet) { /* -q really means quiet */
+ displa = 0;
+ fdispla = 0;
+ } else {
+ displa = 1;
+ fdispla = XYFD_B;
+ }
+ testing = 0;
+ out2screen = 0;
+ dpyactive = 0;
+ what = W_FTP|W_SEND;
+
+#ifndef NOSPL
+ cmd_quoting = 0;
+#endif /* NOSPL */
+ sndsrc = nfils;
+
+ t0 = gmstimer(); /* Record starting time */
+
+ while (!done && !cancelgroup) { /* Loop for all files */
+
+ cancelfile = 0;
+ x = gnfile(); /* Get next file from list(s) */
+ if (x == 0) /* (see gnfile() comments...) */
+ x = gnferror;
+
+ switch (x) {
+ case 1: /* File to send */
+ rc = putfile(FTP_PUT, /* Function (PUT, APPEND) */
+ filnam, /* Local file to send */
+ filnam, /* Remote name for file */
+ forcetype, /* Text/binary mode forced */
+ 0, /* Not moving */
+ NULL, /* No move-to */
+ NULL, /* No rename-to */
+ NULL, /* No server-rename */
+ ftp_cnv, /* Filename conversion */
+ 0, /* Unique-server-names */
+ -1, /* All file types */
+ 0, /* No permissions */
+ -1, /* No character sets */
+ -1, /* No character sets */
+ 0 /* No update or restart */
+ );
+ if (rc > -1) {
+ good++;
+ status = 1;
+ }
+ if (cancelfile) {
+ continue; /* Or break? */
+ }
+ if (rc < 0) {
+ ftp_fai++;
+ }
+ continue; /* Or break? */
+
+ case 0: /* No more files, done */
+ done++;
+ continue;
+
+ case -2:
+ case -1:
+ printf("?%s: file not found - \"%s\"\n",
+ puterror ? "Fatal" : "Warning",
+ filnam
+ );
+ continue; /* or break? */
+ case -3:
+ printf("?Warning access denied - \"%s\"\n", filnam);
+ continue; /* or break? */
+ case -5:
+ printf("?Too many files match\n");
+ done++;
+ break;
+ case -6:
+ if (good < 1)
+ printf("?No files selected\n");
+ done++;
+ break;
+ default:
+ printf("?getnextfile() - unknown failure\n");
+ done++;
+ }
+ }
+ if (status > 0) {
+ if (cancelgroup)
+ status = 0;
+ else if (cancelfile && good < 1)
+ status = 0;
+ }
+ success = status;
+ x = success;
+ if (x > -1) {
+ lastxfer = W_FTP|W_SEND;
+ xferstat = success;
+ }
+ t1 = gmstimer(); /* End time */
+#ifdef GFTIMER
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ if (!sec) sec = 0.001;
+ fptsecs = sec;
+#else
+ sec = (t1 - t0) / 1000;
+ if (!sec) sec = 1;
+#endif /* GFTIMER */
+ tfcps = (long) (tfc / sec);
+ tsecs = (int)sec;
+ lastxfer = W_FTP|W_SEND;
+ xferstat = success;
+ if (dpyactive)
+ ftscreen(SCR_TC,0,0L,"");
+
+ if (!stay)
+ doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
+ return(success);
+}
+
+
+/* d o f t p p u t -- Parse and execute PUT, MPUT, and APPEND */
+
+int
+#ifdef CK_ANSIC
+doftpput(int cx, int who) /* who == 1 for ftp, 0 for kermit */
+#else
+doftpput(cx,who) int cx, who;
+#endif /* CK_ANSIC */
+{
+ struct FDB sf, fl, sw, cm;
+ int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0;
+ int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0;
+ char * s, * s2;
+
+ int x_csl, x_csr = -1; /* Local and remote charsets */
+ int x_xla = 0;
+ int x_recurse = 0;
+ char c, * p; /* Workers */
+#ifdef PUTARRAY
+ int range[2]; /* Array range */
+ char ** ap = NULL; /* Array pointer */
+ int arrayx = -1; /* Array index */
+#endif /* PUTARRAY */
+ ULONG t0 = 0L, t1 = 0L; /* Times for stats */
+#ifdef GFTIMER
+ CKFLOAT sec;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+
+ struct stringint { /* Temporary array for switch values */
+ char * sval;
+ int ival;
+ } pv[SND_MAX+1];
+
+ success = 0; /* Assume failure */
+ forcetype = 0; /* No /TEXT or /BINARY given yet */
+ out2screen = 0; /* Not outputting file to screen */
+ putflags = 0; /* PUT options */
+ x_cnv = ftp_cnv; /* Filename conversion */
+ x_usn = ftp_usn; /* Unique server names */
+ x_prm = ftp_prm; /* Permissions */
+ if (x_prm == SET_AUTO) /* Permissions AUTO */
+ x_prm = alike;
+
+#ifndef NOCSETS
+ x_csr = ftp_csr; /* Inherit global server charset */
+ x_csl = ftp_csl;
+ if (x_csl < 0)
+ x_csl = fcharset;
+ x_xla = ftp_xla;
+#endif /* NOCSETS */
+
+ makestr(&filefile,NULL); /* No filename list file yet. */
+ makestr(&srv_renam,NULL); /* Clear /SERVER-RENAME: */
+ makestr(&snd_rename,NULL); /* PUT /RENAME */
+ makestr(&snd_move,NULL); /* PUT /MOVE */
+ putpath[0] = NUL; /* Initialize for syncdir(). */
+ puterror = ftp_err; /* Inherit global error action. */
+ what = W_SEND|W_FTP; /* What we're doing (sending w/FTP) */
+ asnambuf[0] = NUL; /* Clear as-name buffer */
+
+ if (g_ftp_typ > -1) { /* Restore TYPE if saved */
+ ftp_typ = g_ftp_typ;
+ /* g_ftp_typ = -1; */
+ }
+ for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */
+ pv[i].sval = NULL; /* to null pointers */
+ pv[i].ival = -1; /* and -1 int values */
+ }
+ if (who == 0) { /* Called with unprefixed command */
+ switch (cx) {
+ case XXRSEN: pv[SND_RES].ival = 1; break;
+ case XXCSEN: pv[SND_CMD].ival = 1; break;
+ case XXMOVE: pv[SND_DEL].ival = 1; break;
+ case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */
+ case XXMSE: mput++; break;
+ }
+ } else {
+ if (cx == FTP_MPU)
+ mput++;
+ }
+ cmfdbi(&sw, /* First FDB - command switches */
+ _CMKEY, /* fcode */
+ "Filename, or switch", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ nputswi, /* addtl numeric data 1: tbl size */
+ 4, /* addtl numeric data 2: 4 = cmswi */
+ xxstring, /* Processing function */
+ putswi, /* Keyword table */
+ &sf /* Pointer to next FDB */
+ );
+ cmfdbi(&fl, /* 3rd FDB - local filespec */
+ _CMFLD, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ &cm
+ );
+ cmfdbi(&cm, /* 4th FDB - Confirmation */
+ _CMCFM, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ NULL,
+ NULL,
+ NULL
+ );
+
+ again:
+ cmfdbi(&sf, /* 2nd FDB - file to send */
+ _CMIFI, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */
+ nolinks | x_recurse, /* addtl numeric data 1 */
+ 0, /* dirflg 0 means "not dirs only" */
+ xxstring,
+ NULL,
+#ifdef COMMENT
+ mput ? &cm : &fl
+#else
+ &fl
+#endif /* COMMENT */
+ );
+
+ while (1) { /* Parse zero or more switches */
+ x = cmfdb(&sw); /* Parse something */
+ debug(F101,"ftp put cmfdb A","",x);
+ debug(F101,"ftp put fcode A","",cmresult.fcode);
+ if (x < 0) /* Error */
+ goto xputx; /* or reparse needed */
+ if (cmresult.fcode != _CMKEY) /* Break out of loop if not a switch */
+ break;
+ c = cmgbrk(); /* Get break character */
+ getval = (c == ':' || c == '='); /* to see how they ended the switch */
+ if (getval && !(cmresult.kflags & CM_ARG)) {
+ printf("?This switch does not take arguments\n");
+ x = -9;
+ goto xputx;
+ }
+ if (!getval && (cmgkwflgs() & CM_ARG)) {
+ printf("?This switch requires an argument\n");
+ x = -9;
+ goto xputx;
+ }
+ n = cmresult.nresult; /* Numeric result = switch value */
+ debug(F101,"ftp put switch","",n);
+
+ switch (n) { /* Process the switch */
+ case SND_AFT: /* Send /AFTER:date-time */
+ case SND_BEF: /* Send /BEFORE:date-time */
+ case SND_NAF: /* Send /NOT-AFTER:date-time */
+ case SND_NBE: /* Send /NOT-BEFORE:date-time */
+ if (!getval) break;
+ if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) {
+ if (x == -3) {
+ printf("?Date-time required\n");
+ x = -9;
+ }
+ goto xputx;
+ }
+ pv[n].ival = 1;
+ makestr(&(pv[n].sval),s);
+ break;
+
+ case SND_ASN: /* /AS-NAME: */
+ debug(F101,"ftp put /as-name getval","",getval);
+ if (!getval) break;
+ if ((x = cmfld("Name to send under","",&s,NULL)) < 0) {
+ if (x == -3) {
+ printf("?name required\n");
+ x = -9;
+ }
+ goto xputx;
+ }
+ makestr(&(pv[n].sval),brstrip(s));
+ debug(F110,"ftp put /as-name 1",pv[n].sval,0);
+ if (pv[n].sval) pv[n].ival = 1;
+ break;
+
+#ifdef PUTARRAY
+ case SND_ARR: /* /ARRAY */
+ if (!getval) break;
+ ap = NULL;
+ if ((x = cmfld("Array name (a single letter will do)",
+ "",
+ &s,
+ NULL
+ )) < 0) {
+ if (x == -3)
+ break;
+ else
+ return(x);
+ }
+ if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) {
+ printf("?Bad array: %s\n",s);
+ return(-9);
+ }
+ if (!(ap = a_ptr[x])) {
+ printf("?No such array: %s\n",s);
+ return(-9);
+ }
+ pv[n].ival = 1;
+ pv[SND_CMD].ival = 0; /* Undo any conflicting ones... */
+ pv[SND_RES].ival = 0;
+ pv[SND_FIL].ival = 0;
+ arrayx = x;
+ break;
+#endif /* PUTARRAY */
+
+ case SND_BIN: /* /BINARY */
+ case SND_TXT: /* /TEXT or /ASCII */
+ case SND_TEN: /* /TENEX */
+ pv[SND_BIN].ival = 0;
+ pv[SND_TXT].ival = 0;
+ pv[SND_TEN].ival = 0;
+ pv[n].ival = 1;
+ break;
+
+#ifdef PUTPIPE
+ case SND_CMD: /* These take no args */
+ if (nopush) {
+ printf("?Sorry, system command access is disabled\n");
+ x = -9;
+ goto xputx;
+ }
+#ifdef PIPESEND
+ else if (sndfilter) {
+ printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
+ x = -9;
+ goto xputx;
+ }
+#endif /* PIPESEND */
+ sw.hlpmsg = "Command, or switch"; /* Change help message */
+ pv[n].ival = 1; /* Just set the flag */
+ pv[SND_ARR].ival = 0;
+ break;
+#endif /* PUTPIPE */
+
+#ifdef CKSYMLINK
+ case SND_LNK:
+ nolinks = 0;
+ goto again; /* Because CMIFI params changed... */
+ case SND_NLK:
+ nolinks = 2;
+ goto again;
+#endif /* CKSYMLINK */
+
+#ifdef FTP_RESTART
+ case SND_RES: /* /RECOVER (resend) */
+ pv[SND_ARR].ival = 0; /* fall thru on purpose... */
+#endif /* FTP_RESTART */
+
+ case SND_NOB:
+ case SND_DEL: /* /DELETE */
+ case SND_SHH: /* /QUIET */
+ case SND_UPD: /* /UPDATE */
+ case SND_SIM: /* /UPDATE */
+ case SND_USN: /* /UNIQUE */
+ pv[n].ival = 1; /* Just set the flag */
+ break;
+
+ case SND_REC: /* /RECURSIVE */
+ recursive = 2; /* Must be set before cmifi() */
+ x_recurse = 1;
+ goto again; /* Because CMIFI params changed... */
+ break;
+
+#ifdef UNIXOROSK
+ case SND_DOT: /* /DOTFILES */
+ matchdot = 1;
+ break;
+ case SND_NOD: /* /NODOTFILES */
+ matchdot = 0;
+ break;
+#endif /* UNIXOROSK */
+
+ case SND_ERR: /* /ERROR-ACTION */
+ if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
+ goto xputx;
+ pv[n].ival = x;
+ break;
+
+ case SND_EXC: /* Excludes */
+ if (!getval) break;
+ if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
+ if (x == -3) {
+ printf("?Pattern required\n");
+ x = -9;
+ }
+ goto xputx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ if (pv[n].sval)
+ pv[n].ival = 1;
+ break;
+
+ case SND_PRM: /* /PERMISSIONS */
+ if (!getval)
+ x = 1;
+ else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
+ goto xputx;
+ pv[SND_PRM].ival = x;
+ break;
+
+#ifdef PIPESEND
+ case SND_FLT: /* /FILTER */
+ debug(F101,"ftp put /filter getval","",getval);
+ if (!getval) break;
+ if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
+ if (x == -3)
+ s = "";
+ else
+ goto xputx;
+ }
+ if (*s) s = brstrip(s);
+ y = strlen(s);
+ for (x = 0; x < y; x++) { /* Make sure they included "\v(...)" */
+ if (s[x] != '\\') continue;
+ if (s[x+1] == 'v') break;
+ }
+ if (x == y) {
+ printf(
+ "?Filter must contain a replacement variable for filename.\n"
+ );
+ x = -9;
+ goto xputx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ if (pv[n].sval)
+ pv[n].ival = 1;
+ break;
+#endif /* PIPESEND */
+
+ case SND_NAM: /* /FILENAMES */
+ if (!getval) break;
+ if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
+ goto xputx;
+ debug(F101,"ftp put /filenames","",x);
+ pv[n].ival = x;
+ break;
+
+ case SND_SMA: /* Smaller / larger than */
+ case SND_LAR:
+ if (!getval) break;
+ if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0)
+ goto xputx;
+ pv[n].ival = y;
+ break;
+
+ case SND_FIL: /* Name of file containing filenames */
+ if (!getval) break;
+ if ((x = cmifi("Name of file containing list of filenames",
+ "",&s,&y,xxstring)) < 0) {
+ if (x == -3) {
+ printf("?Filename required\n");
+ x = -9;
+ }
+ goto xputx;
+ } else if (y && iswild(s)) {
+ printf("?Wildcards not allowed\n");
+ x = -9;
+ goto xputx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ if (pv[n].sval) {
+ pv[n].ival = 1;
+ pv[SND_ARR].ival = 0;
+ } else {
+ pv[n].ival = 0;
+ }
+ mput = 0;
+ break;
+
+ case SND_MOV: /* MOVE after */
+ case SND_REN: /* RENAME after */
+ case SND_SRN: { /* SERVER-RENAME after */
+ char * m = "";
+ switch (n) {
+ case SND_MOV:
+ m = "device and/or directory for source file after sending";
+ break;
+ case SND_REN:
+ m = "new name for source file after sending";
+ break;
+ case SND_SRN:
+ m = "new name for destination file after sending";
+ break;
+ }
+ if (!getval) break;
+ if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
+ if (x == -3) {
+ printf("%s\n", n == SND_MOV ?
+ "?Destination required" :
+ "?New name required"
+ );
+ x = -9;
+ }
+ goto xputx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s ? brstrip(s) : NULL);
+ pv[n].ival = (pv[n].sval) ? 1 : 0;
+ break;
+ }
+ case SND_STA: /* Starting position (= PSEND) */
+ if (!getval) break;
+ if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0)
+ goto xputx;
+ pv[n].ival = y;
+ break;
+
+ case SND_TYP: /* /TYPE */
+ if (!getval) break;
+ if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0)
+ goto xputx;
+ pv[n].ival = (x == 2) ? -1 : x;
+ break;
+
+#ifndef NOCSETS
+ case SND_CSL: /* Local character set */
+ case SND_CSR: /* Remote (server) charset */
+ if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) {
+ return((x == -3) ? -2 : x);
+ }
+ if (n == SND_CSL)
+ x_csl = x;
+ else
+ x_csr = x;
+ x_xla = 1; /* Overrides global OFF setting */
+ break;
+
+ case SND_XPA: /* Transparent */
+ x_xla = 0;
+ x_csr = -1;
+ x_csl = -1;
+ break;
+#endif /* NOCSETS */
+ }
+ }
+#ifdef PIPESEND
+ if (pv[SND_RES].ival > 0) { /* /RECOVER */
+ if (sndfilter || pv[SND_FLT].ival > 0) {
+ printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n");
+ x = -9;
+ goto xputx;
+ }
+ if (sfttab[0] > 0 && sfttab[SFT_REST] == 0)
+ printf("WARNING: Server says it doesn't support REST.\n");
+ }
+#endif /* PIPESEND */
+
+ cmarg = "";
+ cmarg2 = asnambuf;
+ line[0] = NUL;
+ s = line;
+ wild = 0;
+
+ switch (cmresult.fcode) { /* How did we get out of switch loop */
+ case _CMIFI: /* Input filename */
+ if (pv[SND_FIL].ival > 0) {
+ printf("?You may not give a PUT filespec and a /LISTFILE\n");
+ x = -9;
+ goto xputx;
+ }
+ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */
+ if (pv[SND_ARR].ival > 0)
+ ckstrncpy(asnambuf,line,CKMAXPATH);
+ else
+ wild = cmresult.nresult; /* Wild flag */
+ debug(F111,"ftp put wild",line,wild);
+ if (!wild && !recursive && !mput)
+ nolinks = 0;
+ break;
+ case _CMFLD: /* Field */
+ /* Only allowed with /COMMAND and /ARRAY */
+ if (pv[SND_FIL].ival > 0) {
+ printf("?You may not give a PUT filespec and a /LISTFILE\n");
+ x = -9;
+ goto xputx;
+ }
+ /* For MPUT it's OK to have filespecs that don't match any files */
+ if (mput)
+ break;
+ if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) {
+#ifdef CKROOT
+ if (ckrooterr)
+ printf("?Off limits: %s\n",cmresult.sresult);
+ else
+#endif /* CKROOT */
+ printf("?%s - \"%s\"\n",
+ iswild(cmresult.sresult) ?
+ "No files match" : "File not found",
+ cmresult.sresult
+ );
+ x = -9;
+ goto xputx;
+ }
+ ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
+ if (pv[SND_ARR].ival > 0)
+ ckstrncpy(asnambuf,line,CKMAXPATH);
+ break;
+ case _CMCFM: /* Confirmation */
+ confirmed = 1;
+ break;
+ default:
+ printf("?Unexpected function code: %d\n",cmresult.fcode);
+ x = -9;
+ goto xputx;
+ }
+ debug(F110,"ftp put string",s,0);
+ debug(F101,"ftp put confirmed","",confirmed);
+
+ /* Save and change protocol and transfer mode */
+ /* Global values are restored in main parse loop */
+
+ g_displa = fdispla;
+ if (ftp_dis > -1)
+ fdispla = ftp_dis;
+ g_skipbup = skipbup;
+
+ if (pv[SND_NOB].ival > -1) { /* /NOBACKUP (skip backup file) */
+ g_skipbup = skipbup;
+ skipbup = 1;
+ }
+ if (pv[SND_TYP].ival > -1) { /* /TYPE */
+ xfiletype = pv[SND_TYP].ival;
+ if (xfiletype == 2)
+ xfiletype = -1;
+ }
+ if (pv[SND_BIN].ival > 0) { /* /BINARY really means binary... */
+ forcetype = 1; /* So skip file scan */
+ ftp_typ = FTT_BIN; /* Set binary */
+ } else if (pv[SND_TXT].ival > 0) { /* Similarly for /TEXT... */
+ forcetype = 1;
+ ftp_typ = FTT_ASC;
+ } else if (pv[SND_TEN].ival > 0) { /* and /TENEX*/
+ forcetype = 1;
+ ftp_typ = FTT_TEN;
+ } else if (ftp_cmdlin && xfermode == XMODE_M) {
+ forcetype = 1;
+ ftp_typ = binary;
+ g_ftp_typ = binary;
+ }
+
+#ifdef PIPESEND
+ if (pv[SND_CMD].ival > 0) { /* /COMMAND - strip any braces */
+ debug(F110,"PUT /COMMAND before stripping",s,0);
+ s = brstrip(s);
+ debug(F110,"PUT /COMMAND after stripping",s,0);
+ if (!*s) {
+ printf("?Sorry, a command to send from is required\n");
+ x = -9;
+ goto xputx;
+ }
+ cmarg = s;
+ }
+#endif /* PIPESEND */
+
+/* Set up /MOVE and /RENAME */
+
+ if (pv[SND_DEL].ival > 0 &&
+ (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
+ printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
+ x = -9;
+ goto xputx;
+ }
+#ifdef CK_TMPDIR
+ if (pv[SND_MOV].ival > 0) {
+ int len;
+ char * p = pv[SND_MOV].sval;
+ len = strlen(p);
+ if (!isdir(p)) { /* Check directory */
+#ifdef CK_MKDIR
+ char * s = NULL;
+ s = (char *)malloc(len + 4);
+ if (s) {
+ strcpy(s,p); /* safe */
+#ifdef datageneral
+ if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
+#else
+ if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
+#endif /* datageneral */
+ s[len++] = 'X';
+ s[len] = NUL;
+#ifdef NOMKDIR
+ x = -1;
+#else
+ x = zmkdir(s);
+#endif /* NOMKDIR */
+ free(s);
+ if (x < 0) {
+ printf("?Can't create \"%s\"\n",p);
+ x = -9;
+ goto xputx;
+ }
+ }
+#else
+ printf("?Directory \"%s\" not found\n",p);
+ x = -9;
+ goto xputx;
+#endif /* CK_MKDIR */
+ }
+ makestr(&snd_move,p);
+ }
+#endif /* CK_TMPDIR */
+
+ if (pv[SND_REN].ival > 0) { /* /RENAME */
+ char * p = pv[SND_REN].sval;
+ if (!p) p = "";
+ if (!*p) {
+ printf("?New name required for /RENAME\n");
+ x = -9;
+ goto xputx;
+ }
+ p = brstrip(p);
+#ifndef NOSPL
+ /* If name given is wild, rename string must contain variables */
+ if (wild) {
+ char * s = tmpbuf;
+ x = TMPBUFSIZ;
+ zzstring(p,&s,&x);
+ if (!strcmp(tmpbuf,p)) {
+ printf(
+ "?/RENAME for file group must contain variables such as \\v(filename)\n"
+ );
+ x = -9;
+ goto xputx;
+ }
+ }
+#endif /* NOSPL */
+ makestr(&snd_rename,p);
+ debug(F110,"FTP snd_rename",snd_rename,0);
+ }
+ if (pv[SND_SRN].ival > 0) { /* /SERVER-RENAME */
+ char * p = pv[SND_SRN].sval;
+ if (!p) p = "";
+ if (!*p) {
+ printf("?New name required for /SERVER-RENAME\n");
+ x = -9;
+ goto xputx;
+ }
+ p = brstrip(p);
+#ifndef NOSPL
+ if (wild) {
+ char * s = tmpbuf;
+ x = TMPBUFSIZ;
+ zzstring(p,&s,&x);
+ if (!strcmp(tmpbuf,p)) {
+ printf(
+"?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n"
+ );
+ x = -9;
+ goto xputx;
+ }
+ }
+#endif /* NOSPL */
+ makestr(&srv_renam,p);
+ debug(F110,"ftp put srv_renam",srv_renam,0);
+ }
+ if (!confirmed) { /* CR not typed yet, get more fields */
+ char * lp;
+ if (mput) { /* MPUT or MMOVE */
+ nfils = 0; /* We already have the first one */
+#ifndef NOMSEND
+ if (cmresult.fcode == _CMIFI) {
+ /* First filespec is valid */
+ msfiles[nfils++] = line; /* Store pointer */
+ lp = line + (int)strlen(line) + 1; /* Point past it */
+ debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1);
+ } else {
+ /* First filespec matches no files */
+ debug(F110,"ftp put mput skipping first filespec",
+ cmresult.sresult,
+ 0
+ );
+ lp = line;
+ }
+ /* Parse a filespec, a "field", or confirmation */
+
+ cmfdbi(&sf, /* 1st FDB - file to send */
+ _CMIFI, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ nolinks | x_recurse, /* addtl numeric data 1 */
+ 0, /* dirflg 0 means "not dirs only" */
+ xxstring,
+ NULL,
+ &fl
+ );
+ cmfdbi(&fl, /* 2nd FDB - local filespec */
+ _CMFLD, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ &cm
+ );
+ cmfdbi(&cm, /* 3rd FDB - Confirmation */
+ _CMCFM, /* fcode */
+ "",
+ "",
+ "",
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ while (!confirmed) { /* Get more filenames */
+ x = cmfdb(&sf); /* Parse something */
+ debug(F101,"ftp put cmfdb B","",x);
+ debug(F101,"ftp put fcode B","",cmresult.fcode);
+ if (x < 0) /* Error */
+ goto xputx; /* or reparse needed */
+ switch (cmresult.fcode) {
+ case _CMCFM: /* End of command */
+ confirmed++;
+ if (nfils < 1) {
+ debug(F100,"ftp put mput no files match","",0);
+ printf("?No files match MPUT list\n");
+ x = -9;
+ goto xputx;
+ }
+ break;
+ case _CMFLD: /* No match */
+ debug(F110,"ftp put mput skipping",cmresult.sresult,0);
+ continue;
+ case _CMIFI: /* Good match */
+ s = cmresult.sresult;
+ msfiles[nfils++] = lp; /* Got one, count, point to it, */
+ p = lp; /* remember pointer, */
+ while ((*lp++ = *s++)) /* and copy it into buffer */
+ if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */
+ printf("?MPUT list too long\n");
+ line[0] = NUL;
+ x = -9;
+ goto xputx;
+ }
+ debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1);
+ if (nfils == 1) /* Take care of \v(filespec) */
+ fspec[0] = NUL;
+#ifdef ZFNQFP
+ zfnqfp(p,TMPBUFSIZ,tmpbuf);
+ p = tmpbuf;
+#endif /* ZFNQFP */
+ if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) {
+ strcat(fspec,p); /* safe */
+ strcat(fspec," "); /* safe */
+ } else {
+#ifdef COMMENT
+ printf("WARNING - \\v(filespec) buffer overflow\n");
+#else
+ debug(F101,"doxput filespec buffer overflow","",0);
+#endif /* COMMENT */
+ }
+ }
+ }
+#endif /* NOMSEND */
+ } else { /* Regular PUT */
+ nfils = -1;
+ if ((x = cmtxt(wild ?
+"\nOptional as-name template containing replacement variables \
+like \\v(filename)" :
+ "Optional name to send it with",
+ "",&p,NULL)) < 0)
+ goto xputx;
+
+ if (p) if (!*p) p = NULL;
+ p = brstrip(p);
+
+ if (p && *p) {
+ makestr(&(pv[SND_ASN].sval),p);
+ if (pv[SND_ASN].sval)
+ pv[SND_ASN].ival = 1;
+ debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0);
+ }
+ }
+ }
+ /* Set cmarg2 from as-name, however we got it. */
+
+ CHECKCONN();
+ if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
+ char * p;
+ p = brstrip(pv[SND_ASN].sval);
+ ckstrncpy(asnambuf,p,CKMAXPATH+1);
+ }
+ debug(F110,"ftp put asnambuf",asnambuf,0);
+
+ if (pv[SND_FIL].ival > 0) {
+ if (confirmed) {
+ if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
+ debug(F110,"ftp put can't open",pv[SND_FIL].sval,0);
+ printf("?Failure to open %s\n",pv[SND_FIL].sval);
+ x = -9;
+ goto xputx;
+ }
+ makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */
+ debug(F110,"ftp PUT /LISTFILE opened",filefile,0);
+ wild = 1;
+ }
+ }
+ if (confirmed && !line[0] && !filefile) {
+#ifndef NOMSEND
+ if (filehead) { /* OK if we have a SEND-LIST */
+ nfils = filesinlist;
+ sndsrc = nfils; /* Like MSEND */
+ addlist = 1; /* But using a different list... */
+ filenext = filehead;
+ goto doput;
+ }
+#endif /* NOMSEND */
+ printf("?Filename required but not given\n");
+ x = -9;
+ goto xputx;
+ }
+#ifndef NOMSEND
+ addlist = 0; /* Don't use SEND-LIST. */
+#endif /* NOMSEND */
+
+ if (mput) { /* MPUT (rather than PUT) */
+#ifndef NOMSEND
+ cmlist = msfiles; /* List of filespecs */
+ sndsrc = nfils; /* rather filespec and as-name */
+#endif /* NOMSEND */
+ pipesend = 0;
+ } else if (filefile) { /* File contains list of filenames */
+ s = "";
+ cmarg = "";
+ line[0] = NUL;
+ nfils = 1;
+ sndsrc = 1;
+
+ } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) {
+
+ /* Not MSEND, MMOVE, /LIST, or /ARRAY */
+ nfils = sndsrc = -1;
+ if (!wild) {
+ y = zchki(s);
+ if (y < 0) {
+ printf("?Read access denied - \"%s\"\n", s);
+ x = -9;
+ goto xputx;
+ }
+ }
+ if (s != line) /* We might already have done this. */
+ ckstrncpy(line,s,LINBUFSIZ); /* Copy of string just parsed. */
+#ifdef DEBUG
+ else
+ debug(F110,"doxput line=s",line,0);
+#endif /* DEBUG */
+ cmarg = line; /* File to send */
+ }
+#ifndef NOMSEND
+ zfnqfp(cmarg,fspeclen,fspec); /* Get full name */
+#endif /* NOMSEND */
+
+ if (!mput) { /* For all but MPUT... */
+#ifdef PIPESEND
+ if (pv[SND_CMD].ival > 0) /* /COMMAND sets pipesend flag */
+ pipesend = 1;
+ debug(F101,"ftp put /COMMAND pipesend","",pipesend);
+ if (pipesend && filefile) {
+ printf("?Invalid switch combination\n");
+ x = -9;
+ goto xputx;
+ }
+#endif /* PIPESEND */
+
+#ifndef NOSPL
+ /* If as-name given and filespec is wild, as-name must contain variables */
+ if ((wild || mput) && asnambuf[0]) {
+ char * s = tmpbuf;
+ x = TMPBUFSIZ;
+ zzstring(asnambuf,&s,&x);
+ if (!strcmp(tmpbuf,asnambuf)) {
+ printf(
+ "?As-name for file group must contain variables such as \\v(filename)\n"
+ );
+ x = -9;
+ goto xputx;
+ }
+ }
+#endif /* NOSPL */
+ }
+
+ doput:
+
+ if (pv[SND_SHH].ival > 0) { /* SEND /QUIET... */
+ fdispla = 0;
+ debug(F101,"ftp put display","",fdispla);
+ } else {
+ displa = 1;
+ if (ftp_deb)
+ fdispla = XYFD_B;
+ }
+
+#ifdef PUTARRAY /* SEND /ARRAY... */
+ if (pv[SND_ARR].ival > 0) {
+ if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */
+ if (range[0] == -1) /* If low end of range not specified */
+ range[0] = 1; /* default to 1 */
+ if (range[1] == -1) /* If high not specified */
+ range[1] = a_dim[arrayx]; /* default to size of array */
+ if ((range[0] < 0) || /* Check range */
+ (range[0] > a_dim[arrayx]) ||
+ (range[1] < range[0]) ||
+ (range[1] > a_dim[arrayx])) {
+ printf("?Bad array range - [%d:%d]\n",range[0],range[1]);
+ x = -9;
+ goto xputx;
+ }
+ sndarray = ap; /* Array pointer */
+ sndxin = arrayx; /* Array index */
+ sndxlo = range[0]; /* Array range */
+ sndxhi = range[1];
+ sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE);
+ if (!asnambuf[0])
+ ckstrncpy(asnambuf,sndxnam,CKMAXPATH);
+ cmarg = "";
+ }
+#endif /* PUTARRAY */
+
+ moving = 0;
+
+ if (pv[SND_ARR].ival < 1) { /* File selection & disposition... */
+ if (pv[SND_DEL].ival > 0) /* /DELETE was specified */
+ moving = 1;
+ if (pv[SND_AFT].ival > 0) /* Copy SEND criteria */
+ ckstrncpy(sndafter,pv[SND_AFT].sval,19);
+ if (pv[SND_BEF].ival > 0)
+ ckstrncpy(sndbefore,pv[SND_BEF].sval,19);
+ if (pv[SND_NAF].ival > 0)
+ ckstrncpy(sndnafter,pv[SND_NAF].sval,19);
+ if (pv[SND_NBE].ival > 0)
+ ckstrncpy(sndnbefore,pv[SND_NBE].sval,19);
+ if (pv[SND_EXC].ival > 0)
+ makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT);
+ if (pv[SND_SMA].ival > -1)
+ sndsmaller = pv[SND_SMA].ival;
+ if (pv[SND_LAR].ival > -1)
+ sndlarger = pv[SND_LAR].ival;
+ if (pv[SND_NAM].ival > -1)
+ x_cnv = pv[SND_NAM].ival;
+ if (pv[SND_USN].ival > -1)
+ x_usn = pv[SND_USN].ival;
+ if (pv[SND_ERR].ival > -1)
+ puterror = pv[SND_ERR].ival;
+
+#ifdef DOUPDATE
+ if (pv[SND_UPD].ival > 0) {
+ if (x_usn) {
+ printf("?Conflicting switches: /UPDATE /UNIQUE\n");
+ x = -9;
+ goto xputx;
+ }
+ putflags |= PUT_UPD;
+ ftp_dates |= 2;
+ }
+#ifdef COMMENT
+ /* This works but it's useless, maybe dangerous */
+ if (pv[SND_DIF].ival > 0) {
+ if (x_usn) {
+ printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n");
+ x = -9;
+ goto xputx;
+ }
+ putflags |= PUT_DIF;
+ ftp_dates |= 2;
+ }
+#endif /* COMMENT */
+#endif /* DOUPDATE */
+
+ if (pv[SND_SIM].ival > 0)
+ putflags |= PUT_SIM;
+
+ if (pv[SND_PRM].ival > -1) {
+#ifdef UNIX
+ if (x_usn) {
+ printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n");
+ x = -9;
+ goto xputx;
+ }
+ x_prm = pv[SND_PRM].ival;
+#else /* UNIX */
+ printf("?/PERMISSIONS switch is not supported\n");
+#endif /* UNIX */
+ }
+#ifdef FTP_RESTART
+ if (pv[SND_RES].ival > 0) {
+ if (!sizeok) {
+ printf("?PUT /RESTART can't be used because SIZE disabled.\n");
+ x = -9;
+ goto xputx;
+ }
+ if (x_usn || putflags) {
+ printf("?Conflicting switches: /RECOVER %s\n",
+ x_usn && putflags ? "/UNIQUE /UPDATE" :
+ (x_usn ? "/UNIQUE" : "/UPDATE")
+ );
+ x = -9;
+ goto xputx;
+ }
+#ifndef NOCSETS
+ if (x_xla &&
+ (x_csl == FC_UCS2 ||
+ x_csl == FC_UTF8 ||
+ x_csr == FC_UCS2 ||
+ x_csr == FC_UTF8)) {
+ printf("?/RECOVER can not be used with Unicode translation\n");
+ x = -9;
+ goto xputx;
+ }
+#endif /* NOCSETS */
+ putflags = PUT_RES;
+ }
+#endif /* FTP_RESTART */
+ }
+ debug(F101,"ftp PUT restart","",putflags & PUT_RES);
+ debug(F101,"ftp PUT update","",putflags & PUT_UPD);
+
+#ifdef PIPESEND
+ if (pv[SND_FLT].ival > 0) { /* Have SEND FILTER? */
+ if (!pv[SND_FLT].sval) {
+ sndfilter = NULL;
+ } else {
+ sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1);
+ if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */
+ }
+ debug(F110,"ftp put /FILTER", sndfilter, 0);
+ }
+ if (sndfilter || pipesend) /* No /UPDATE or /RESTART */
+ if (putflags) /* with pipes or filters */
+ putflags = 0;
+#endif /* PIPESEND */
+
+ tfc = 0L; /* Initialize stats and counters */
+ filcnt = 0;
+ pktnum = 0;
+ spackets = 0L;
+
+ if (wild) /* (is this necessary?) */
+ cx = FTP_MPU;
+
+ t0 = gmstimer(); /* Record starting time */
+
+ done = 0; /* Loop control */
+ cancelgroup = 0;
+
+ cdlevel = 0;
+ cdsimlvl = 0;
+ while (!done && !cancelgroup) { /* Loop for all files */
+ /* or until canceled. */
+#ifdef FTP_PROXY
+ /*
+ If we are using a proxy, we don't use the local file list;
+ instead we use the list on the remote machine which we want
+ sent to someone else, and we use remglob() to get the names.
+ But in that case we shouldn't even be executing this routine;
+ see ftp_mput().
+ */
+#endif /* FTP_PROXY */
+
+ cancelfile = 0;
+ x = gnfile(); /* Get next file from list(s) */
+
+ if (x == 0) /* (see gnfile() comments...) */
+ x = gnferror;
+ debug(F111,"FTP PUT gnfile",filnam,x);
+
+ switch (x) {
+ case 1: /* File to send */
+ s2 = asnambuf;
+#ifndef NOSPL
+ if (asnambuf[0]) { /* As-name */
+ int n; char *p; /* to be evaluated... */
+ n = TMPBUFSIZ;
+ p = tmpbuf;
+ zzstring(asnambuf,&p,&n);
+ s2 = tmpbuf;
+ debug(F110,"ftp put asname",s2,0);
+ }
+#endif /* NOSPL */
+ rc = putfile(cx, /* Function (PUT, APPEND) */
+ filnam, s2, /* Name to send, as-name */
+ forcetype, moving, /* Parameters from switches... */
+ snd_move, snd_rename, srv_renam,
+ x_cnv, x_usn, xfiletype, x_prm,
+#ifndef NOCSETS
+ x_csl, (!x_xla ? -1 : x_csr),
+#else
+ -1, -1,
+#endif /* NOCSETS */
+ putflags
+ );
+ debug(F111,"ftp put putfile rc",filnam,rc);
+ debug(F111,"ftp put putfile cancelfile",filnam,cancelfile);
+ debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup);
+ if (rc > -1) {
+ good++;
+ status = 1;
+ }
+ if (cancelfile)
+ continue;
+ if (rc < 0) {
+ ftp_fai++;
+ if (puterror) {
+ status = 0;
+ printf("?Fatal upload error: %s\n",filnam);
+ done++;
+ }
+ }
+ continue;
+ case 0: /* No more files, done */
+ done++;
+ continue;
+ case -1:
+ printf("?%s: file not found - \"%s\"\n",
+ puterror ? "Fatal" : "Warning",
+ filnam
+ );
+ if (puterror) {
+ status = 0;
+ done++;
+ break;
+ }
+ continue;
+ case -2:
+ if (puterror) {
+ printf("?Fatal: file not found - \"%s\"\n", filnam);
+ status = 0;
+ done++;
+ break;
+ }
+ continue; /* Not readable, keep going */
+ case -3:
+ if (puterror) {
+ printf("?Fatal: Read access denied - \"%s\"\n", filnam);
+ status = 0;
+ done++;
+ break;
+ }
+ printf("?Warning access denied - \"%s\"\n", filnam);
+ continue;
+#ifdef COMMENT
+ case -4: /* Canceled */
+ done++;
+ break;
+#endif /* COMMENT */
+ case -5:
+ printf("?Too many files match\n");
+ done++;
+ break;
+ case -6:
+ if (good < 1)
+ printf("?No files selected\n");
+ done++;
+ break;
+ default:
+ printf("?getnextfile() - unknown failure\n");
+ done++;
+ }
+ }
+ if (cdlevel > 0) {
+ while (cdlevel--) {
+ if (cdsimlvl) {
+ cdsimlvl--;
+ } else if (!doftpcdup())
+ break;
+ }
+ }
+ if (status > 0) {
+ if (cancelgroup)
+ status = 0;
+ else if (cancelfile && good < 1)
+ status = 0;
+ }
+ success = status;
+ x = success;
+
+ xputx:
+ if (x > -1) {
+#ifdef GFTIMER
+ t1 = gmstimer(); /* End time */
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ if (!sec) sec = 0.001;
+ fptsecs = sec;
+#else
+ sec = (t1 - t0) / 1000;
+ if (!sec) sec = 1;
+#endif /* GFTIMER */
+ tfcps = (long) (tfc / sec);
+ tsecs = (int)sec;
+ lastxfer = W_FTP|W_SEND;
+ xferstat = success;
+ if (dpyactive)
+ ftscreen(SCR_TC,0,0L,"");
+ }
+ for (i = 0; i <= SND_MAX; i++) { /* Free malloc'd memory */
+ if (pv[i].sval)
+ free(pv[i].sval);
+ }
+ ftreset(); /* Undo switch effects */
+ dpyactive = 0;
+ return(x);
+}
+
+
+static char ** mgetlist = NULL; /* For MGET */
+static int mgetn = 0, mgetx = 0;
+static char xtmpbuf[4096];
+
+/*
+ c m d l i n g e t
+
+ Get files specified by -g command-line option.
+ File list is set up in cmlist[] by ckuusy.c; nfils is length of list.
+*/
+int
+cmdlinget(stay) int stay; {
+ int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0;
+ int lcs = -1, rcs = -1, xlate = 0;
+ int first = 1;
+ int mget = 1;
+ int nc;
+ char * s, * s2, * s3;
+ ULONG t0, t1; /* Times for stats */
+#ifdef GFTIMER
+ CKFLOAT sec;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+
+ if (quiet) { /* -q really means quiet */
+ displa = 0;
+ fdispla = 0;
+ } else {
+ displa = 1;
+ fdispla = XYFD_B;
+ }
+ testing = 0;
+ dpyactive = 0;
+ out2screen = 0;
+ what = W_FTP|W_RECV;
+ mgetmethod = 0;
+ mgetforced = 0;
+
+ havetype = 0;
+ havesize = -1L;
+ makestr(&havemdtm,NULL);
+
+ if (ftp_fnc < 0)
+ ftp_fnc = fncact;
+
+#ifndef NOSPL
+ cmd_quoting = 0;
+#endif /* NOSPL */
+ debug(F101,"ftp cmdlinget nfils","",nfils);
+
+ if (ftp_cnv == CNV_AUTO) { /* Name conversion is auto */
+ if (alike) { /* If server & client are alike */
+ nc = 0; /* no conversion */
+ } else { /* If they are different */
+ if (servertype == SYS_UNIX || servertype == SYS_WIN32)
+ nc = -1; /* only minimal conversions needed */
+ else /* otherwise */
+ nc = 1; /* full conversion */
+ }
+ } else /* Not auto - do what user said */
+ nc = ftp_cnv;
+
+ if (nfils < 1)
+ doexit(BAD_EXIT,-1);
+
+ t0 = gmstimer(); /* Starting time for this batch */
+
+#ifndef NOCSETS
+ if (xlate) { /* SET FTP CHARACTER-SET-TRANSLATION */
+ lcs = ftp_csl; /* Local charset */
+ if (lcs < 0) lcs = fcharset;
+ if (lcs < 0) xlate = 0;
+ }
+ if (xlate) { /* Still ON? */
+ rcs = ftp_csx; /* Remote (Server) charset */
+ if (rcs < 0) rcs = ftp_csr;
+ if (rcs < 0) xlate = 0;
+ }
+#endif /* NOCSETS */
+ /*
+ If we have only one file and it is a directory, then we ask for a
+ listing of its contents, rather than retrieving the directory file
+ itself. This is what (e.g.) Netscape does.
+ */
+ if (nfils == 1) {
+ if (doftpcwd((char *)cmlist[mgetx],-1)) {
+ /* If we can CD to it, it must be a directory */
+ if (recursive) {
+ cmlist[mgetx] = "*";
+ } else {
+ status =
+ (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0);
+ done = 1;
+ }
+ }
+ }
+/*
+ The following is to work around UNIX servers which, when given a command
+ like "NLST path/blah" (not wild) returns the basename without the path.
+*/
+ if (!done && servertype == SYS_UNIX && nfils == 1) {
+ mget = iswild(cmlist[mgetx]);
+ }
+ if (!mget && !done) { /* Invoked by command-line FTP URL */
+ if (ftp_deb)
+ printf("DOING GET...\n");
+ done++;
+ cancelfile = 0; /* This file not canceled yet */
+ s = cmlist[mgetx];
+ rc = 0; /* Initial return code */
+ fsize = -1L;
+ if (sizeok) {
+ x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */
+ if (x == REPLY_COMPLETE)
+ fsize = atol(&ftp_reply_str[4]);
+ }
+ ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */
+ debug(F111,"ftp cmdlinget filnam",filnam,fsize);
+
+ nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
+ s2 = tmpbuf;
+
+ /* If local file already exists, take collision action */
+
+ x = zchki(s2);
+ if (x > -1) {
+ switch (ftp_fnc) {
+ case XYFX_A: /* Append */
+ append = 1;
+ break;
+ case XYFX_R: /* Rename */
+ case XYFX_B: { /* Backup */
+ char * p = NULL;
+ int x = -1;
+ znewn(s2,&p); /* Make unique name */
+ debug(F110,"ftp cmdlinget znewn",p,0);
+ if (ftp_fnc == XYFX_B) { /* Backup existing file */
+ x = zrename(s2,p);
+ debug(F111,"ftp cmdlinget backup zrename",p,x);
+ } else { /* Rename incoming file */
+ x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
+ s2 = tmpbuf;
+ debug(F111,"ftp cmdlinget rename incoming",p,x);
+ }
+ if (x < 0) {
+ printf("?Backup/Rename failed\n");
+ return(success = 0);
+ }
+ break;
+ }
+ case XYFX_D: /* Discard */
+ ftscreen(SCR_FN,'F',0L,s);
+ ftscreen(SCR_ST,ST_SKIP,SKP_NAM,s);
+ tlog(F100," refused: name","",0);
+ debug(F110,"ftp cmdlinget skip name",s2,0);
+ goto xclget;
+
+ case XYFX_X: /* Overwrite */
+ case XYFX_U: /* Update (already handled above) */
+ case XYFX_M: /* ditto */
+ break;
+ }
+ }
+ rc = getfile(s, /* Remote name */
+ s2, /* Local name */
+ 0, /* Recover/Restart */
+ append, /* Append */
+ NULL, /* Pipename */
+ 0, /* Translate charsets */
+ -1, /* File charset (none) */
+ -1 /* Server charset (none) */
+ );
+ debug(F111,"ftp cmdlinget rc",s,rc);
+ debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
+ debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
+
+ if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */
+ rc = getfile(&s[1], /* Remote name without leading '/' */
+ s2, /* Local name */
+ 0, /* Recover/Restart */
+ append, /* Append */
+ NULL, /* Pipename */
+ 0, /* Translate charsets */
+ -1, /* File charset (none) */
+ -1 /* Server charset (none) */
+ );
+ if (rc > -1) {
+ good++;
+ status = 1;
+ }
+ if (cancelfile)
+ goto xclget;
+ if (rc < 0) {
+ ftp_fai++;
+ if (geterror) {
+ status = 0;
+ done++;
+ }
+ }
+ }
+ if (ftp_deb && !done)
+ printf("DOING MGET...\n");
+ while (!done && !cancelgroup) {
+ cancelfile = 0; /* This file not canceled yet */
+ s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
+ if (!s) s = "";
+ if (!*s) {
+ first = 1;
+ mgetx++;
+ if (mgetx < nfils)
+ s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
+ else
+ s = NULL;
+ debug(F111,"ftp cmdlinget remote_files B",s,0);
+ if (!s) {
+ done = 1;
+ break;
+ }
+ }
+ /*
+ The semantics of NLST are ill-defined. Suppose we have just sent
+ NLST /path/[a-z]*. Most servers send back names like /path/foo,
+ /path/bar, etc. But some send back only foo and bar, and subsequent
+ RETR commands based on the pathless names are not going to work.
+ */
+ if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
+ if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) {
+ int len, left = 4096;
+ char * tmp = xtmpbuf;
+ len = s3 - cmlist[mgetx] + 1;
+ ckstrncpy(tmp,cmlist[mgetx],left);
+ tmp += len;
+ left -= len;
+ ckstrncpy(tmp,s,left);
+ s = xtmpbuf;
+ debug(F111,"ftp cmdlinget remote_files X",s,0);
+ }
+ }
+ first = 0; /* Not first any more */
+
+ debug(F111,"ftp cmdlinget havetype",s,havetype);
+ if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */
+ debug(F110,"ftp cmdlinget not-a-file",s,0);
+ continue;
+ }
+ rc = 0; /* Initial return code */
+ if (havesize > -1L) { /* Already have file size? */
+ fsize = havesize;
+ } else { /* No - must ask server */
+ /*
+ Prior to sending the NLST command we necessarily put the
+ server into ASCII mode. We must now put it back into the
+ the requested mode so the upcoming SIZE command returns
+ right kind of size; this is especially important for
+ GET /RECOVER; otherwise the server returns the "ASCII" size
+ of the file, rather than its true size.
+ */
+ changetype(ftp_typ,0); /* Change to requested type */
+ fsize = -1L;
+ if (sizeok) {
+ x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm);
+ if (x == REPLY_COMPLETE)
+ fsize = atol(&ftp_reply_str[4]);
+ }
+ }
+ ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */
+ debug(F111,"ftp cmdlinget filnam",filnam,fsize);
+
+ nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
+ s2 = tmpbuf;
+
+ /* If local file already exists, take collision action */
+
+ x = zchki(s2);
+ if (x > -1) {
+ switch (ftp_fnc) {
+ case XYFX_A: /* Append */
+ append = 1;
+ break;
+ case XYFX_R: /* Rename */
+ case XYFX_B: { /* Backup */
+ char * p = NULL;
+ int x = -1;
+ znewn(s2,&p); /* Make unique name */
+ debug(F110,"ftp cmdlinget znewn",p,0);
+ if (ftp_fnc == XYFX_B) { /* Backup existing file */
+ x = zrename(s2,p);
+ debug(F111,"ftp cmdlinget backup zrename",p,x);
+ } else { /* Rename incoming file */
+ x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
+ s2 = tmpbuf;
+ debug(F111,"ftp cmdlinget rename incoming",p,x);
+ }
+ if (x < 0) {
+ printf("?Backup/Rename failed\n");
+ return(success = 0);
+ }
+ break;
+ }
+ case XYFX_D: /* Discard */
+ ftscreen(SCR_FN,'F',0L,s);
+ ftscreen(SCR_ST,ST_SKIP,SKP_NAM,s);
+ tlog(F100," refused: name","",0);
+ debug(F110,"ftp cmdlinget skip name",s2,0);
+ continue;
+ case XYFX_X: /* Overwrite */
+ case XYFX_U: /* Update (already handled above) */
+ case XYFX_M: /* ditto */
+ break;
+ }
+ }
+ /* ^^^ ADD CHARSET STUFF HERE ^^^ */
+ rc = getfile(s, /* Remote name */
+ s2, /* Local name */
+ 0, /* Recover/Restart */
+ append, /* Append */
+ NULL, /* Pipename */
+ 0, /* Translate charsets */
+ -1, /* File charset (none) */
+ -1 /* Server charset (none) */
+ );
+ debug(F111,"ftp cmdlinget rc",s,rc);
+ debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
+ debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
+
+ if (rc > -1) {
+ good++;
+ status = 1;
+ }
+ if (cancelfile)
+ continue;
+ if (rc < 0) {
+ ftp_fai++;
+ if (geterror) {
+ status = 0;
+ done++;
+ }
+ }
+ }
+
+ xclget:
+ if (cancelgroup)
+ mlsreset();
+ if (status > 0) {
+ if (cancelgroup)
+ status = 0;
+ else if (cancelfile && good < 1)
+ status = 0;
+ }
+ success = status;
+
+#ifdef GFTIMER
+ t1 = gmstimer(); /* End time */
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ if (!sec) sec = 0.001;
+ fptsecs = sec;
+#else
+ sec = (t1 - t0) / 1000;
+ if (!sec) sec = 1;
+#endif /* GFTIMER */
+
+ tfcps = (long) (tfc / sec);
+ tsecs = (int)sec;
+ lastxfer = W_FTP|W_RECV;
+ xferstat = success;
+ if (dpyactive)
+ ftscreen(SCR_TC,0,0L,"");
+ if (!stay)
+ doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
+ return(success);
+}
+
+/* d o f t p g e t -- Parse and execute GET, MGET, MDELETE, ... */
+
+/*
+ Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use
+ zstrdat() to convert to UTC-based time_t. But it doesn't make sense from
+ the user-interface perspective, since the server's directory listings show
+ its own local times and since we don't know what timezone it's in, there's
+ no way to reconcile our local times with the server's.
+*/
+int
+doftpget(cx,who) int cx, who; { /* who == 1 for ftp, 0 for kermit */
+ struct FDB fl, sw, cm;
+ int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0;
+ int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0;
+ int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0;
+ int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0;
+ int moving = 0, deleting = 0, toscreen = 0, haspath = 0;
+ int gotsize = 0;
+ int matchdot = 0;
+ long getlarger = -1, getsmaller = -1;
+ char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL;
+ char * src = "", * local = "";
+ char * pat = "";
+
+ int x_csl = -1, x_csr = -1; /* Local and remote charsets */
+ int x_xla = 0;
+ char c; /* Worker char */
+ ULONG t0 = 0L, t1; /* Times for stats */
+#ifdef GFTIMER
+ CKFLOAT sec;
+#else
+ int sec = 0;
+#endif /* GFTIMER */
+
+ struct stringint { /* Temporary array for switch values */
+ char * sval;
+ int ival;
+ } pv[SND_MAX+1];
+
+ success = 0; /* Assume failure */
+ forcetype = 0; /* No /TEXT or /BINARY given yet */
+ restart = 0; /* No restart yet */
+ out2screen = 0; /* No TO-SCREEN switch given yet */
+ mgetmethod = 0; /* No NLST or MLSD switch yet */
+ mgetforced = 0;
+
+ g_displa = fdispla;
+ if (ftp_dis > -1)
+ fdispla = ftp_dis;
+
+ x_cnv = ftp_cnv; /* Filename conversion */
+ if (x_cnv == CNV_AUTO) { /* Name conversion is auto */
+ if (alike) { /* If server & client are alike */
+ x_cnv = 0; /* no conversion */
+ } else { /* If they are different */
+ if (servertype == SYS_UNIX || servertype == SYS_WIN32)
+ x_cnv = -1; /* only minimal conversions needed */
+ else /* otherwise */
+ x_cnv = 1; /* full conversion */
+ }
+ } else /* Not auto - do what user said */
+ x_cnv = ftp_cnv;
+
+ x_prm = ftp_prm; /* Permissions */
+ if (x_prm == SET_AUTO) /* Permissions AUTO */
+ x_prm = alike;
+
+#ifndef NOCSETS
+ x_csr = ftp_csr; /* Inherit global server charset */
+ x_csl = ftp_csl; /* Inherit global local charset */
+ if (x_csl < 0) /* If none, use current */
+ x_csl = fcharset; /* file character-set. */
+ x_xla = ftp_xla; /* Translation On/Off */
+#endif /* NOCSETS */
+
+ geterror = ftp_err; /* Inherit global error action. */
+ asnambuf[0] = NUL; /* No as-name yet. */
+ pipesave = pipesend;
+ pipesend = 0;
+
+ havetype = 0;
+ havesize = -1L;
+ makestr(&havemdtm,NULL);
+
+ if (g_ftp_typ > -1) { /* Restore TYPE if saved */
+ ftp_typ = g_ftp_typ;
+ /* g_ftp_typ = -1; */
+ }
+ for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */
+ pv[i].sval = NULL; /* to null pointers */
+ pv[i].ival = -1; /* and -1 int values */
+ }
+ zclose(ZMFILE); /* In case it was left open */
+
+ x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */
+
+ if (fp_nml) { /* Reset /NAMELIST */
+ if (fp_nml != stdout)
+ fclose(fp_nml);
+ fp_nml = NULL;
+ }
+ makestr(&ftp_nml,NULL);
+
+ /* Initialize list of remote filespecs */
+
+ if (!mgetlist) {
+ mgetlist = (char **)malloc(MGETMAX * sizeof(char *));
+ if (!mgetlist) {
+ printf("?Memory allocation failure - MGET list\n");
+ return(-9);
+ }
+ for (i = 0; i < MGETMAX; i++)
+ mgetlist[i] = NULL;
+ }
+ mgetn = 0; /* Number of mget arguments */
+ mgetx = 0; /* Current arg */
+
+ if (who == 0) { /* Called with unprefixed command */
+ if (cx == XXGET || cx == XXREGET || cx == XXRETR)
+ getone++;
+ switch (cx) {
+ case XXREGET: pv[SND_RES].ival = 1; break;
+ case XXRETR: pv[SND_DEL].ival = 1; break;
+ case XXGET:
+ case XXMGET: mget++; break;
+ }
+ } else { /* FTP command */
+ if (cx == FTP_GET || cx == FTP_RGE)
+ getone++;
+ switch (cx) {
+ case FTP_DEL: /* (fall thru on purpose) */
+ case FTP_MDE: mdel++; /* (ditto) */
+ case FTP_GET: /* (ditto) */
+ case FTP_MGE: mget++; break;
+ case FTP_RGE: pv[SND_RES].ival = 1; break;
+ }
+ }
+ cmfdbi(&sw, /* First FDB - command switches */
+ _CMKEY, /* fcode */
+ "Remote filename;\n or switch", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ mdel ? ndelswi : ngetswi, /* addtl numeric data 1: tbl size */
+ 4, /* addtl numeric data 2: 4 = cmswi */
+ xxstring, /* Processing function */
+ mdel ? delswi : getswi, /* Keyword table */
+ &fl /* Pointer to next FDB */
+ );
+ cmfdbi(&fl, /* 2nd FDB - remote filename */
+ _CMFLD, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ &cm
+ );
+ cmfdbi(&cm, /* 3rd FDB - Confirmation */
+ _CMCFM, /* fcode */
+ "", /* hlpmsg */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ NULL,
+ NULL,
+ NULL
+ );
+
+ while (1) { /* Parse 0 or more switches */
+ x = cmfdb(&sw); /* Parse something */
+ debug(F101,"ftp get cmfdb","",x);
+ if (x < 0) /* Error */
+ goto xgetx; /* or reparse needed */
+ if (cmresult.fcode != _CMKEY) /* Break out of loop if not a switch */
+ break;
+ c = cmgbrk(); /* Get break character */
+ getval = (c == ':' || c == '='); /* to see how they ended the switch */
+ if (getval && !(cmresult.kflags & CM_ARG)) {
+ printf("?This switch does not take arguments\n");
+ x = -9;
+ goto xgetx;
+ }
+ n = cmresult.nresult; /* Numeric result = switch value */
+ debug(F101,"ftp get switch","",n);
+
+ if (!getval && (cmgkwflgs() & CM_ARG)) {
+ printf("?This switch requires an argument\n");
+ x = -9;
+ goto xgetx;
+ }
+ switch (n) { /* Process the switch */
+ case SND_ASN: /* /AS-NAME: */
+ debug(F101,"ftp get /as-name getval","",getval);
+ if (!getval) break;
+ if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) {
+ if (x == -3) {
+ printf("?name required\n");
+ x = -9;
+ }
+ goto xgetx;
+ }
+ s = brstrip(s);
+ if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ pv[n].ival = 1;
+ break;
+
+ case SND_BIN: /* /BINARY */
+ case SND_TXT: /* /TEXT or /ASCII */
+ case SND_TEN: /* /TENEX */
+ pv[SND_BIN].ival = 0;
+ pv[SND_TXT].ival = 0;
+ pv[SND_TEN].ival = 0;
+ pv[n].ival = 1;
+ break;
+
+#ifdef PUTPIPE
+ case SND_CMD: /* These take no args */
+ if (nopush) {
+ printf("?Sorry, system command access is disabled\n");
+ x = -9;
+ goto xgetx;
+ }
+#ifdef PIPESEND
+ else if (rcvfilter) {
+ printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
+ x = -9;
+ goto xgetx;
+ }
+#endif /* PIPESEND */
+ sw.hlpmsg = "Command, or switch"; /* Change help message */
+ pv[n].ival = 1; /* Just set the flag */
+ pv[SND_ARR].ival = 0;
+ break;
+#endif /* PUTPIPE */
+
+ case SND_SHH: /* /QUIET */
+ case SND_RES: /* /RECOVER (reget) */
+ case SND_NOB: /* /NOBACKUPFILES */
+ case SND_DEL: /* /DELETE */
+ case SND_UPD: /* /UPDATE */
+ case SND_USN: /* /UNIQUE */
+ case SND_NOD: /* /NODOTFILES */
+ case SND_REC: /* /RECOVER */
+ case SND_MAI: /* /TO-SCREEN */
+ pv[n].ival = 1; /* Just set the flag */
+ break;
+
+ case SND_DIF: /* /DATES-DIFFER */
+ pv[SND_COL].ival = XYFX_M; /* Now it's a collision option */
+ pv[n].ival = 1;
+ break;
+
+ case SND_COL: /* /COLLISION: */
+ if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
+ goto xgetx;
+ if (x == XYFX_M)
+ pv[SND_DIF].ival = 1; /* (phase this out) */
+ pv[n].ival = x; /* this should be sufficient */
+ break;
+
+ case SND_ERR: /* /ERROR-ACTION */
+ if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
+ goto xgetx;
+ pv[n].ival = x;
+ break;
+
+ case SND_EXC: /* Exception list */
+ if (!getval) break;
+ if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
+ if (x == -3) {
+ printf("?Pattern required\n");
+ x = -9;
+ }
+ goto xgetx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ if (pv[n].sval)
+ pv[n].ival = 1;
+ break;
+
+#ifdef PIPESEND
+ case SND_FLT:
+ debug(F101,"ftp get /filter getval","",getval);
+ if (!getval) break;
+ if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
+ if (x == -3)
+ s = "";
+ else
+ goto xgetx;
+ }
+ s = brstrip(s);
+ if (pv[SND_MAI].ival < 1) {
+ y = strlen(s);
+ /* Make sure they included "\v(...)" */
+ for (x = 0; x < y; x++) {
+ if (s[x] != '\\') continue;
+ if (s[x+1] == 'v') break;
+ }
+ if (x == y) {
+ printf(
+ "?Filter must contain a replacement variable for filename.\n"
+ );
+ x = -9;
+ goto xgetx;
+ }
+ }
+ if (*s) {
+ pv[n].ival = 1;
+ makestr(&(pv[n].sval),s);
+ } else {
+ pv[n].ival = 0;
+ makestr(&(pv[n].sval),NULL);
+ }
+ break;
+#endif /* PIPESEND */
+
+ case SND_NAM:
+ if (!getval) break;
+ if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
+ goto xgetx;
+ debug(F101,"ftp get /filenames","",x);
+ pv[n].ival = x;
+ break;
+
+ case SND_SMA: /* Smaller / larger than */
+ case SND_LAR:
+ if (!getval) break;
+ if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0)
+ goto xgetx;
+ pv[n].ival = y;
+ break;
+
+ case SND_FIL: /* Name of file containing filnames */
+ if (!getval) break;
+ if ((x = cmifi("Name of file containing list of filenames",
+ "",&s,&y,xxstring)) < 0) {
+ if (x == -3) {
+ printf("?Filename required\n");
+ x = -9;
+ }
+ goto xgetx;
+ } else if (y && iswild(s)) {
+ printf("?Wildcards not allowed BBB\n");
+ x = -9;
+ goto xgetx;
+ }
+ if (s) if (!*s) s = NULL;
+ makestr(&(pv[n].sval),s);
+ if (pv[n].sval)
+ pv[n].ival = 1;
+ break;
+
+ case SND_MOV: /* MOVE after */
+ case SND_REN: /* RENAME after */
+ case SND_SRN: { /* SERVER-RENAME */
+ char * m = "";
+ switch (n) {
+ case SND_MOV:
+ m =
+ "Device and/or directory for incoming file after reception";
+ break;
+ case SND_REN:
+ m = "New name for incoming file after reception";
+ break;
+ case SND_SRN:
+ m = "New name for source file on server after reception";
+ break;
+ }
+ if (!getval) break;
+ if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
+ if (x == -3) {
+ printf("%s\n", n == SND_MOV ?
+ "?Destination required" :
+ "?New name required"
+ );
+ x = -9;
+ }
+ goto xgetx;
+ }
+ makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
+ pv[n].ival = (pv[n].sval) ? 1 : 0;
+ break;
+ }
+#ifndef NOCSETS
+ case SND_CSL: /* Local character set */
+ case SND_CSR: /* Remote (server) charset */
+ if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0)
+ return((x == -3) ? -2 : x);
+ if (n == SND_CSL)
+ x_csl = x;
+ else
+ x_csr = x;
+ x_xla = 1; /* Overrides global OFF setting */
+ break;
+
+ case SND_XPA: /* Transparent */
+ x_xla = 0;
+ x_csr = -1;
+ x_csl = -1;
+ break;
+#endif /* NOCSETS */
+
+ case SND_NML:
+ if ((x = cmofi("Local filename","-",&s,xxstring)) < 0)
+ goto xgetx;
+ makestr(&ftp_nml,s);
+ break;
+
+ case SND_PAT: /* /PATTERN: */
+ if (!getval) break;
+ if ((x = cmfld("Pattern","*", &s, xxstring)) < 0)
+ goto xgetx;
+ makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
+ pv[n].ival = (pv[n].sval) ? 1 : 0;
+ break;
+
+ case SND_NLS: /* /NLST */
+ pv[n].ival = 1; /* Use NLST */
+ pv[SND_MLS].ival = 0; /* Don't use MLSD */
+ break;
+
+ case SND_MLS: /* /MLSD */
+ pv[n].ival = 1; /* Use MLSD */
+ pv[SND_NLS].ival = 0; /* Don't use NLST */
+ break;
+
+ default: /* /AFTER, /PERMISSIONS, etc... */
+ printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf);
+ x = -9;
+ goto xgetx;
+ }
+ }
+ line[0] = NUL;
+ cmarg = line;
+ cmarg2 = asnambuf;
+ s = line;
+/*
+ For GET, we want to parse an optional as-name, like with PUT.
+ For MGET, we must parse a list of names, and then send NLST or MLSD
+ commands for each name separately.
+*/
+ switch (cmresult.fcode) { /* How did we get out of switch loop */
+ case _CMFLD: /* Field */
+ if (!getone) {
+ s = brstrip(cmresult.sresult);
+ makestr(&(mgetlist[mgetn++]),s);
+ while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) {
+ if (x < 0)
+ goto xgetx;
+ makestr(&(mgetlist[mgetn++]),brstrip(s));
+ if (mgetn >= MGETMAX) {
+ printf("?Too many items in MGET list\n");
+ goto xgetx;
+ }
+ }
+ if ((x = cmcfm()) < 0)
+ goto xgetx;
+ } else {
+ s = brstrip(cmresult.sresult);
+ ckstrncpy(line,s,LINBUFSIZ);
+ if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0)
+ if (x != -3)
+ goto xgetx;
+ s = brstrip(s);
+ ckstrncpy(asnambuf,s,CKMAXPATH+1);
+ if ((x = cmcfm()) < 0)
+ goto xgetx;
+ }
+ break;
+ case _CMCFM: /* Confirmation */
+ break;
+ default:
+ printf("?Unexpected function code: %d\n",cmresult.fcode);
+ x = -9;
+ goto xgetx;
+ }
+ if (pv[SND_REC].ival > 0) /* /RECURSIVE */
+ recursive = 2;
+
+ if (pv[SND_BIN].ival > 0) { /* /BINARY really means binary... */
+ forcetype = 1; /* So skip the name-pattern match */
+ ftp_typ = XYFT_B; /* Set binary */
+ } else if (pv[SND_TXT].ival > 0) { /* Similarly for /TEXT... */
+ forcetype = 1;
+ ftp_typ = XYFT_T;
+ } else if (pv[SND_TEN].ival > 0) { /* and /TENEX*/
+ forcetype = 1;
+ ftp_typ = FTT_TEN;
+ } else if (ftp_cmdlin && xfermode == XMODE_M) {
+ forcetype = 1;
+ ftp_typ = binary;
+ g_ftp_typ = binary;
+ }
+ if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
+ char * p;
+ p = brstrip(pv[SND_ASN].sval); /* As-name */
+ ckstrncpy(asnambuf,p,CKMAXPATH+1);
+ }
+ debug(F110,"ftp get asnambuf",asnambuf,0);
+
+#ifdef PIPESEND
+ if (pv[SND_CMD].ival > 0) { /* /COMMAND - strip any braces */
+ char * p;
+ p = asnambuf;
+ debug(F110,"GET /COMMAND before stripping",p,0);
+ p = brstrip(p);
+ debug(F110,"GET /COMMAND after stripping",p,0);
+ if (!*p) {
+ printf("?Sorry, a command to write to is required\n");
+ x = -9;
+ goto xgetx;
+ }
+ pipename = p;
+ pipesend = 1;
+ }
+#endif /* PIPESEND */
+
+/* Set up /MOVE and /RENAME */
+
+ if (pv[SND_DEL].ival > 0 &&
+ (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
+ printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
+ x = -9;
+ goto xgetx;
+ }
+#ifdef CK_TMPDIR
+ if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) {
+ int len;
+ char * p = pv[SND_MOV].sval;
+ len = strlen(p);
+ if (!isdir(p)) { /* Check directory */
+#ifdef CK_MKDIR
+ char * s = NULL;
+ s = (char *)malloc(len + 4);
+ if (s) {
+ strcpy(s,p); /* safe */
+#ifdef datageneral
+ if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
+#else
+ if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
+#endif /* datageneral */
+ s[len++] = 'X';
+ s[len] = NUL;
+#ifdef NOMKDIR
+ x = -1;
+#else
+ x = zmkdir(s);
+#endif /* NOMKDIR */
+ free(s);
+ if (x < 0) {
+ printf("?Can't create \"%s\"\n",p);
+ x = -9;
+ goto xgetx;
+ }
+ }
+#else
+ printf("?Directory \"%s\" not found\n",p);
+ x = -9;
+ goto xgetx;
+#endif /* CK_MKDIR */
+ }
+ makestr(&rcv_move,p);
+ moving = 1;
+ }
+#endif /* CK_TMPDIR */
+
+ if (pv[SND_REN].ival > 0) { /* /RENAME */
+ char * p = pv[SND_REN].sval;
+ if (!p) p = "";
+ if (!*p) {
+ printf("?New name required for /RENAME\n");
+ x = -9;
+ goto xgetx;
+ }
+ p = brstrip(p);
+#ifndef NOSPL
+ /* If name given is wild, rename string must contain variables */
+ if (mget && !getone) {
+ char * s = tmpbuf;
+ x = TMPBUFSIZ;
+ zzstring(p,&s,&x);
+ if (!strcmp(tmpbuf,p)) {
+ printf(
+ "?/RENAME for file group must contain variables such as \\v(filename)\n"
+ );
+ x = -9;
+ goto xgetx;
+ }
+ }
+#endif /* NOSPL */
+ renaming = 1;
+ makestr(&rcv_rename,p);
+ debug(F110,"FTP rcv_rename",rcv_rename,0);
+ }
+ if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) {
+ printf("?Filename required but not given\n");
+ x = -9;
+ goto xgetx;
+ } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) {
+ printf("?You can't give both /LISTFILE and a remote filename\n");
+ x = -9;
+ goto xgetx;
+ }
+ CHECKCONN(); /* Check connection */
+
+ if (pv[SND_COL].ival > -1)
+ x_fnc = pv[SND_COL].ival;
+
+#ifndef NOSPL
+ /* If as-name given for MGET, as-name must contain variables */
+ if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) {
+ char * s = tmpbuf;
+ x = TMPBUFSIZ;
+ zzstring(asnambuf,&s,&x);
+ if (!strcmp(tmpbuf,asnambuf)) {
+ printf(
+ "?As-name for MGET must contain variables such as \\v(filename)\n"
+ );
+ x = -9;
+ goto xgetx;
+ }
+ }
+#endif /* NOSPL */
+
+/* doget: */
+
+ if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */
+ fdispla = 0;
+ } else {
+ displa = 1;
+ if (mdel || ftp_deb)
+ fdispla = XYFD_B;
+ }
+ deleting = 0;
+ if (pv[SND_DEL].ival > 0) /* /DELETE was specified */
+ deleting = 1;
+ if (pv[SND_EXC].ival > 0)
+ makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
+ if (pv[SND_SMA].ival > -1)
+ getsmaller = pv[SND_SMA].ival;
+ if (pv[SND_LAR].ival > -1)
+ getlarger = pv[SND_LAR].ival;
+ if (pv[SND_NAM].ival > -1)
+ x_cnv = pv[SND_NAM].ival;
+ if (pv[SND_ERR].ival > -1)
+ geterror = pv[SND_ERR].ival;
+ if (pv[SND_MAI].ival > -1)
+ toscreen = 1;
+
+ if (pv[SND_NLS].ival > 0) { /* Force NLST or MLSD? */
+ mgetmethod = SND_NLS;
+ mgetforced = 1;
+ } else if (pv[SND_MLS].ival > 0) {
+ mgetmethod = SND_MLS;
+ mgetforced = 1;
+ }
+
+#ifdef FTP_RESTART
+ if (pv[SND_RES].ival > 0) {
+ if (!ftp_typ) {
+ printf("?Sorry, GET /RECOVER requires binary mode\n");
+ x = -9;
+ goto xgetx;
+#ifdef COMMENT
+ /* Not true - the fact that the initial REST fails does not mean */
+ /* it will fail here. */
+ } else if (!okrestart) {
+ printf("WARNING: Server might not support restart...\n");
+#endif /* COMMENT */
+ }
+ restart = 1;
+ }
+#endif /* FTP_RESTART */
+
+#ifdef PIPESEND
+ if (pv[SND_FLT].ival > 0) { /* Have SEND FILTER? */
+ if (pipesend) {
+ printf("?Switch conflict: /FILTER and /COMMAND\n");
+ x = -9;
+ goto xgetx;
+ }
+ makestr(&rcvfilter,pv[SND_FLT].sval);
+ debug(F110,"ftp get /FILTER", rcvfilter, 0);
+ }
+ if (rcvfilter || pipesend) { /* /RESTART */
+#ifdef FTP_RESTART
+ if (restart) { /* with pipes or filters */
+ printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n");
+ x = -9;
+ goto xgetx;
+ }
+#endif /* FTP_RESTART */
+ if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) {
+ printf(
+ "?Switch conflict: /FILTER or /COMMAND and Date Checking\n");
+ x = -9;
+ goto xgetx;
+ }
+ }
+#endif /* PIPESEND */
+
+ tfc = 0L; /* Initialize stats and counters */
+ filcnt = 0;
+ pktnum = 0;
+ rpackets = 0L;
+
+ if (pv[SND_FIL].ival > 0) {
+ if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
+ debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno);
+ printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval);
+ x = -9;
+ goto xgetx;
+ }
+ if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */
+ zclose(ZMFILE); /* Failed */
+ debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0);
+ printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval);
+ x = -9;
+ goto xgetx;
+ }
+ listfile = 1;
+ debug(F110,"ftp get listfile first",tmpbuf,0);
+ makestr(&(mgetlist[0]),tmpbuf);
+ }
+ t0 = gmstimer(); /* Record starting time */
+
+ updating = 0; /* Checking dates? */
+ if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U))
+ updating = 1;
+ if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M)
+ updating = 2;
+ if (updating) /* These switches force FTP DATES ON */
+ ftp_dates |= 2;
+
+ what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */
+
+ cancelgroup = 0; /* Group not canceled yet */
+ if (!(xfermode == XMODE_A && patterns && get_auto && !forcetype))
+ changetype(ftp_typ,0); /* Change to requested type */
+ binary = ftp_typ; /* For file-transfer display */
+ first = 1; /* For MGET list */
+ done = 0; /* Loop control */
+
+#ifdef CK_TMPDIR
+ if (dldir && !f_tmpdir) { /* If they have a download directory */
+ if ((s = zgtdir())) { /* Get current directory */
+ if (zchdir(dldir)) { /* Change to download directory */
+ ckstrncpy(savdir,s,TMPDIRLEN);
+ f_tmpdir = 1; /* Remember that we did this */
+ }
+ }
+ }
+#endif /* CK_TMPDIR */
+
+ if (ftp_nml) { /* /NAMELIST */
+ debug(F110,"ftp GET ftp_nml",ftp_nml,0);
+ if (ftp_nml[0] == '-' && ftp_nml[1] == 0)
+ fp_nml = stdout;
+ else
+ fp_nml = fopen(ftp_nml, "wb");
+ if (!fp_nml) {
+ printf("?%s: %s\n",ftp_nml,ck_errstr());
+ goto xgetx;
+ }
+ }
+ while (!done && !cancelgroup) { /* Loop for all files */
+ /* or until canceled. */
+#ifdef FTP_PROXY
+ /* do something here if proxy */
+#endif /* FTP_PROXY */
+
+ rs_len = 0L; /* REGET position */
+ cancelfile = 0; /* This file not canceled yet */
+ haspath = 0; /* Recalculate this each time thru */
+
+ if (getone) { /* GET */
+ char * p;
+ s = line;
+ src = line; /* Server name */
+ done = 1;
+ debug(F111,"ftp get file",s,0);
+ } else if (mget) { /* MGET */
+ src = mgetlist[mgetx];
+ debug(F111,"ftp mget remote_files A",src,first);
+ s = (char *)remote_files(first,
+ (CHAR *)mgetlist[mgetx],
+ (CHAR *)pv[SND_PAT].sval,
+ 0
+ );
+ debug(F110,"ftp mget remote_files B",s,0);
+ if (!s) s = "";
+ if (!*s) {
+ first = 1;
+ if (listfile) { /* Names from listfile */
+ again:
+ tmpbuf[0] = NUL;
+ while (!tmpbuf[0]) {
+ if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) {
+ zclose(ZMFILE);
+ debug(F110,"ftp get listfile EOF",
+ pv[SND_FIL].sval,0);
+ makestr(&(mgetlist[0]),NULL);
+ s = NULL;
+ done = 1;
+ break;
+ }
+ }
+ if (done)
+ continue;
+
+ makestr(&(mgetlist[0]),tmpbuf);
+ debug(F110,"ftp get listfile next",tmpbuf,0);
+ s = (char *)remote_files(first,
+ (CHAR *)mgetlist[0],
+ (CHAR *)pv[SND_PAT].sval,
+ 0
+ );
+ debug(F110,"ftp mget remote_files C",s,0);
+ if (!s) {
+ ftscreen(SCR_FN,'F',0L,s);
+ ftscreen(SCR_ST,ST_MSG,0L,"File not found");
+ tlog(F110,"ftp get file not found:",s,0);
+ goto again;
+ }
+ } else { /* Names from command line */
+ mgetx++;
+ if (mgetx < mgetn)
+ s = (char *)remote_files(first,
+ (CHAR *)mgetlist[mgetx],
+ (CHAR *)pv[SND_PAT].sval,
+ 0
+ );
+ else
+ s = NULL;
+ if (!s) mgetx++;
+ debug(F111,"ftp mget remote_files D",s,mgetx);
+ }
+ if (!s) {
+ if (!first || mgetx >= mgetn) {
+ done = 1;
+ break;
+ } else if (geterror) {
+ status = 0;
+ done = 1;
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+ debug(F111,"ftp mget remote_files E",s,0);
+ /*
+ The semantics of NLST are ill-defined. Suppose we have just sent
+ NLST /path/[a-z]*. Most servers send back names like /path/foo,
+ /path/bar, etc. But some send back only foo and bar, and subsequent
+ RETR commands based on the pathless names are not going to work.
+ */
+ if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
+ char * s3;
+ if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) {
+ int len, left = 4096;
+ char * tmp = xtmpbuf;
+ len = s3 - mgetlist[mgetx] + 1;
+ ckstrncpy(tmp,mgetlist[mgetx],left);
+ tmp += len;
+ left -= len;
+ ckstrncpy(tmp,s,left);
+ s = xtmpbuf;
+ debug(F111,"ftp mget remote_files F",s,0);
+ }
+ }
+ first = 0;
+ skipthis = 0; /* File selection... */
+ msg = "";
+ nam = s; /* Filename (without path) */
+ rc = 0; /* Initial return code */
+ s2 = "";
+
+ if (!getone && !skipthis) { /* For MGET and MDELETE... */
+ char c, * p = s;
+ int srvpath = 0;
+ int usrpath = 0;
+ int i, k = 0;
+
+ debug(F111,"ftp mget havetype",s,havetype);
+ if (havetype > 0 && havetype != FTYP_FILE) {
+ /* Server says it's not file... */
+ debug(F110,"ftp mget not-a-file",s,0);
+ continue;
+ }
+/*
+ Explanation: Some ftp servers (such as wu-ftpd) return a recursive list.
+ But if the client did not ask for a recursive list, we have to ignore any
+ server files that include a pathname that extends beyond any path that
+ was included in the user's request.
+
+ User's filespec is blah or path/blah (or other non-UNIX syntax). We need to
+ get the user's path segment. Then, for each incoming file, if it begins
+ with the same path segment, we must strip it (point past it).
+*/
+ src = mgetlist[mgetx]; /* In case it moved! */
+ if (src) {
+ for (i = 0; src[i]; i++) { /* Find rightmost path separator */
+ if (ispathsep(src[i])) /* in user's pathname */
+ k = i + 1;
+ }
+ } else {
+ src = "";
+ }
+ usrpath = k; /* User path segment length */
+ debug(F111,"ftp get usrpath",src,usrpath);
+
+ p = s; /* Server filename */
+ while ((c = *p++)) { /* Look for path in server filename */
+ if (ispathsep(c)) {
+ /* haspath++; */
+ nam = p; /* Pathless name (for ckmatch) */
+ srvpath = p - s; /* Server path segment length */
+ }
+ }
+ debug(F111,"ftp get srvpath",s,srvpath);
+
+ if (usrpath == 0) {
+/*
+ Here we handle the case where the user said "mget foo" where foo is a
+ directory name, and the server is sending back names like "foo/file1",
+ "foo/file2", etc. This is a nasty trick but it's necessary because the
+ user can't compensate by typing "mget foo/" because then the server is
+ likely to send back "foo//file1, foo//file2" etc, and we still won't
+ get a match...
+*/
+ int srclen = 0, srvlen = 0;
+ if (src) srclen = strlen(src);
+ if (s) srvlen = strlen(s);
+ if (src && (srvlen > srclen)) {
+ if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) {
+ char * tmpsrc = NULL;
+ tmpsrc = (char *)malloc(srclen + 2);
+ strncpy(tmpsrc,src,srclen);
+ tmpsrc[srclen] = s[srclen];
+ tmpsrc[srclen+1] = NUL;
+ free(mgetlist[mgetx]);
+ mgetlist[mgetx] = tmpsrc;
+ tmpsrc = NULL;
+ src = mgetlist[mgetx];
+ usrpath = srclen+1;
+ }
+ }
+ }
+/*
+ If as-name not given and server filename includes path that matches
+ the pathname from the user's file specification, we must trim the common
+ path prefix from the server's name when constructing the local name.
+*/
+ if (src && /* Wed Sep 25 17:27:48 2002 */
+ !asnambuf[0] &&
+ !recursive && /* Thu Sep 19 16:11:59 2002 */
+ (srvpath > 0) &&
+ !strncmp(src,s,usrpath)) {
+ s2 = s + usrpath; /* Local name skips past remote path */
+ }
+#ifdef COMMENT
+ /* This doesn't work if the path prefix contains wildcards! */
+ haspath = (srvpath > usrpath);
+#else
+ { /* Count path segments instead */
+ int x1 = 0, x2 = 0;
+ char *p;
+ for (p = s; *p; p++)
+ if (ispathsep(*p)) x1++;
+ for (p = src; *p; p++) {
+ if (ispathsep(*p)) x2++;
+ }
+ haspath = recursive ? x1 || x2 : x1 > x2;
+ debug(F111,"ftp get server path segments",s,x1);
+ debug(F111,"ftp get user path segments",src,x2);
+ }
+
+#endif /* COMMENT */
+ debug(F111,"ftp get haspath",s+usrpath,haspath);
+
+ if (haspath) { /* Server file has path segments? */
+ if (!recursive) { /* [M]GET /RECURSIVE? */
+/*
+ We did not ask for a recursive listing, but the server is sending us one
+ anyway (as wu-ftpd is wont to do). We get here if the current filename
+ includes a path segment beyond any path segment we asked for in our
+ non-recursive [M]GET command. We MUST skip this file.
+*/
+ debug(F111,"ftp get skipping because of path",s,0);
+ continue;
+ }
+ }
+ } else if (getone && !skipthis) { /* GET (not MGET) */
+ char * p = nam;
+ while ((c = *p++)) { /* Handle path in local name */
+ if (ispathsep(c)) {
+ if (recursive) { /* If recursive, keep it */
+ haspath = 1;
+ break;
+ } else { /* Otherwise lose it. */
+ nam = p;
+ }
+ }
+ }
+ s2 = nam;
+ }
+ if (!*nam) /* Name without path */
+ nam = s;
+
+ if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */
+ if (nam[0] == '.')
+ continue;
+ }
+ if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */
+ int xx;
+ for (i = 0; i < NSNDEXCEPT; i++) {
+ if (!rcvexcept[i]) {
+ break;
+ }
+ xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1);
+ debug(F111,"ftp mget /except match",rcvexcept[i],xx);
+ if (xx) {
+ tlog(F100," refused: exception list","",0);
+ msg = "Refused: Exception List";
+ skipthis++;
+ break;
+ }
+ }
+ }
+ if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */
+ if (ckmatch(
+#ifdef CKREGEX
+ "*.~[0-9]*~"
+#else
+ "*.~*~"
+#endif /* CKREGEX */
+ ,nam,0,1) > 0)
+ continue;
+ }
+ if (!x_xla) { /* If translation is off */
+ x_csl = -2; /* unset the charsets */
+ x_csr = -2;
+ }
+ ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */
+ if (!*s2) /* Local name */
+ s2 = asnambuf; /* As-name */
+
+ if (!*s2) /* Sat Nov 16 19:19:39 2002 */
+ s2 = recursive ? s : nam; /* Fri Jan 10 13:15:19 2003 */
+
+ debug(F110,"ftp get filnam ",s,0);
+ debug(F110,"ftp get asname A",s2,0);
+
+ /* Receiving to real file */
+ if (!pipesend &&
+#ifdef PIPESEND
+ !rcvfilter &&
+#endif /* PIPESEND */
+ !toscreen) {
+#ifndef NOSPL
+ /* Do this here so we can decide whether to skip */
+ if (cmd_quoting && !skipthis && asnambuf[0]) {
+ int n; char *p;
+ n = TMPBUFSIZ;
+ p = tmpbuf;
+ zzstring(asnambuf,&p,&n);
+ s2 = tmpbuf;
+ debug(F111,"ftp get asname B",s2,updating);
+ }
+#endif /* NOSPL */
+
+ local = *s2 ? s2 : s;
+
+ if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */
+ int x;
+ x = zchki(local);
+ debug(F111,"ftp get DISCARD zchki",local,x);
+ if (x > -1) {
+ skipthis++;
+ debug(F110,"ftp get skip name",local,0);
+ tlog(F100," refused: name","",0);
+ msg = "Refused: Name";
+ }
+ }
+
+#ifdef DOUPDATE
+ if (!skipthis && updating) { /* If updating and not yet skipping */
+ if (zchki(local) > -1) {
+ x = chkmodtime(local,s,0);
+#ifdef DEBUG
+ if (deblog) {
+ if (updating == 2)
+ debug(F111,"ftp get /dates-diff chkmodtime",local,x);
+ else
+ debug(F111,"ftp get /update chkmodtime",local,x);
+ }
+#endif /* DEBUG */
+ if ((updating == 1 && x > 0) || /* /UPDATE */
+ (updating == 2 && x == 1)) { /* /DATES-DIFFER */
+ skipthis++;
+ tlog(F100," refused: date","",0);
+ msg = "Refused: Date";
+ debug(F110,"ftp get skip date",local,0);
+ }
+ }
+ }
+#endif /* DOUPDATE */
+ }
+ /* Initialize file size to -1 in case server doesn't understand */
+ /* SIZE command, so xxscreen() will know we don't know the size */
+
+ fsize = -1L;
+
+ /* Ask for size now only if we need it for selection */
+ /* because if you're going thru a list 100,000 files to select */
+ /* a small subset, 100,000 SIZE commands can take hours... */
+
+ gotsize = 0;
+ if (!mdel && !skipthis && /* Don't need size for DELE... */
+ (getsmaller > -1L || getlarger > -1L)) {
+ if (havesize > -1L) { /* Already have file size? */
+ fsize = havesize;
+ gotsize = 1;
+ } else { /* No - must ask server */
+ /*
+ Prior to sending the NLST command we necessarily put the
+ server into ASCII mode. We must now put it back into the
+ the requested mode so the upcoming SIZE command returns
+ right kind of size; this is especially important for
+ GET /RECOVER; otherwise the server returns the "ASCII" size
+ of the file, rather than its true size.
+ */
+ changetype(ftp_typ,0); /* Change to requested type */
+ fsize = -1L;
+ if (sizeok) {
+ x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
+ if (x == REPLY_COMPLETE) {
+ fsize = atol(&ftp_reply_str[4]);
+ gotsize = 1;
+ }
+ }
+ }
+ if (gotsize) {
+ if (getsmaller > -1L && fsize >= getsmaller)
+ skipthis++;
+ if (getlarger > -1L && fsize <= getlarger)
+ skipthis++;
+ if (skipthis) {
+ debug(F111,"ftp get skip size",s,fsize);
+ tlog(F100," refused: size","",0);
+ msg = "Refused: Size";
+ }
+#ifdef COMMENT
+ } else if (getone) {
+ /* SIZE can fail for many reasons. Does the file exist? */
+ x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm);
+ if (x != REPLY_COMPLETE) {
+ printf(">>> FILE NOT FOUND: %s\n",s);
+ break;
+ }
+#endif /* COMMENT */
+ }
+ }
+ if (skipthis) { /* Skipping this file? */
+ ftscreen(SCR_FN,'F',0L,s);
+ if (msg)
+ ftscreen(SCR_ST,ST_ERR,0L,msg);
+ else
+ ftscreen(SCR_ST,ST_SKIP,0L,s);
+ continue;
+ }
+ if (fp_nml) { /* /NAMELIST only - no transfer */
+ fprintf(fp_nml,"%s\n",s);
+ continue;
+ }
+ if (recursive && haspath && !pipesend
+#ifdef PIPESEND
+ && !rcvfilter
+#endif /* PIPESEND */
+ ) {
+ int x;
+
+#ifdef NOMKDIR
+ x = -1;
+#else
+ x = zmkdir(s); /* Try to make the directory */
+#endif /* NOMKDIR */
+
+ if (x < 0) {
+ rc = -1; /* Failure is fatal */
+ if (geterror) {
+ status = 0;
+ ftscreen(SCR_EM,0,0L,"Directory creation failure");
+ break;
+ }
+ }
+ }
+
+ /* Not skipping */
+
+ selected++; /* Count this file as selected */
+ pn = NULL;
+
+ if (!gotsize && !mdel) { /* Didn't get size yet */
+ if (havesize > -1L) { /* Already have file size? */
+ fsize = havesize;
+ gotsize = 1;
+ } else { /* No - must ask server */
+ fsize = -1L;
+ if (sizeok) {
+ x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
+ if (x == REPLY_COMPLETE) {
+ fsize = atol(&ftp_reply_str[4]);
+ gotsize = 1;
+ }
+ }
+ }
+ }
+ if (mdel) { /* [M]DELETE */
+ if (displa && !ftp_vbm)
+ printf(" %s...",s);
+ rc =
+ (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1;
+ if (rc > -1) {
+ tlog(F110,"ftp mdelete",s,0);
+ if (displa && !ftp_vbm)
+ printf("OK\n");
+ } else {
+ tlog(F110,"ftp mdelete failed:",s,0);
+ if (displa)
+ printf("Failed\n");
+ }
+#ifndef NOSPL
+#ifdef PIPESEND
+ } else if (rcvfilter) { /* [M]GET with filter */
+ int n; char * p;
+ n = CKMAXPATH;
+ p = tmpbuf; /* Safe - no asname with filter */
+ zzstring(rcvfilter,&p,&n);
+ if (n > -1)
+ pn = tmpbuf;
+ debug(F111,"ftp get rcvfilter",pn,n);
+#endif /* PIPESEND */
+#endif /* NOSPL */
+ if (toscreen) s2 = "-";
+ } else if (pipesend) { /* [M]GET /COMMAND */
+ int n; char * p;
+ n = CKMAXPATH;
+ p = tmpbuf; /* Safe - no asname with filter */
+ zzstring(pipename,&p,&n);
+ if (n > -1)
+ pn = tmpbuf;
+ debug(F111,"ftp get pipename",pipename,n);
+ if (toscreen) s2 = "-";
+ } else { /* [M]GET with no pipes or filters */
+ debug(F111,"ftp get s2 A",s2,x_cnv);
+ if (toscreen) {
+ s2 = "-"; /* (hokey convention for stdout) */
+ } else if (!*s2) { /* No asname? */
+ if (x_cnv) { /* If converting */
+ nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */
+ s2 = tmpbuf;
+ debug(F110,"ftp get nzrtol",s2,0);
+ } else /* otherwise */
+ s2 = s; /* use incoming file's name */
+ }
+ debug(F110,"ftp get s2 B",s2,0);
+
+ /* If local file already exists, take collision action */
+
+ if (!pipesend &&
+#ifdef PIPESEND
+ !rcvfilter &&
+#endif /* PIPESEND */
+ !toscreen) {
+ x = zchki(s2);
+ debug(F111,"ftp get zchki",s2,x);
+ debug(F111,"ftp get x_fnc",s2,x_fnc);
+
+ if (x > -1 && !restart) {
+ int x = -1;
+ char * newname = NULL;
+
+ switch (x_fnc) {
+ case XYFX_A: /* Append */
+ append = 1;
+ break;
+ case XYFX_R: /* Rename */
+ case XYFX_B: /* Backup */
+ znewn(s2,&newname); /* Make unique name */
+ debug(F110,"ftp get znewn",newname,0);
+ if (x_fnc == XYFX_B) { /* Backup existing file */
+ x = zrename(s2,newname);
+ debug(F111,"ftp get backup zrename",newname,x);
+ } else { /* Rename incoming file */
+ x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1);
+ s2 = tmpbuf;
+ debug(F111,"ftp get rename incoming",newname,x);
+ }
+ if (x < 0) {
+ ftscreen(SCR_EM,0,0L,"Backup/Rename failed");
+ x = 0;
+ goto xgetx;
+ }
+ break;
+ case XYFX_D: /* Discard (already handled above) */
+ case XYFX_U: /* Update (ditto) */
+ case XYFX_M: /* Update (ditto) */
+ case XYFX_X: /* Overwrite */
+ break;
+ }
+ }
+ }
+ }
+ if (!mdel) {
+#ifdef PIPESEND
+ debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0);
+#endif /* PIPESEND */
+ if (pipesend && !toscreen)
+ s2 = NULL;
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"ftp get x_xla","",x_xla);
+ debug(F101,"ftp get x_csl","",x_csl);
+ debug(F101,"ftp get x_csr","",x_csr);
+ debug(F101,"ftp get append","",append);
+ }
+#endif /* DEBUG */
+
+ rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr);
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F111,"ftp get rc",s,rc);
+ debug(F111,"ftp get cancelfile",s,cancelfile);
+ debug(F111,"ftp get cancelgroup",s,cancelgroup);
+ debug(F111,"ftp get renaming",s,renaming);
+ }
+#endif /* DEBUG */
+ }
+ if (rc > -1) {
+ good++;
+ status = 1;
+ if (!cancelfile) {
+ if (deleting) { /* GET /DELETE (source file) */
+ rc =
+ (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE)
+ ? 1 : -1;
+ tlog(F110, (rc > -1) ?
+ " deleted" : " failed to delete", s, 0);
+ } else if (renaming && rcv_rename && !toscreen) {
+ char *p; /* Rename downloaded file */
+#ifndef NOSPL
+ char tmpbuf[CKMAXPATH+1];
+ int n;
+ n = CKMAXPATH;
+ p = tmpbuf;
+ debug(F111,"ftp get /rename",rcv_rename,0);
+ zzstring(rcv_rename,&p,&n);
+ debug(F111,"ftp get /rename",rcv_rename,0);
+ p = tmpbuf;
+#else
+ p = rcv_rename;
+#endif /* NOSPL */
+ rc = (zrename(s2,p) < 0) ? -1 : 1;
+ debug(F111,"doftpget /RENAME zrename",p,rc);
+ tlog(F110, (rc > -1) ?
+ " renamed to" :
+ " failed to rename to",
+ p,
+ 0
+ );
+ } else if (moving && rcv_move && !toscreen) {
+ char *p; /* Move downloaded file */
+#ifndef NOSPL
+ char tmpbuf[CKMAXPATH+1];
+ int n;
+ n = TMPBUFSIZ;
+ p = tmpbuf;
+ debug(F111,"ftp get /move-to",rcv_move,0);
+ zzstring(rcv_move,&p,&n);
+ p = tmpbuf;
+#else
+ p = rcv_move;
+#endif /* NOSPL */
+ debug(F111,"ftp get /move-to",p,0);
+ rc = (zrename(s2,p) < 0) ? -1 : 1;
+ debug(F111,"doftpget /MOVE zrename",p,rc);
+ tlog(F110, (rc > -1) ?
+ " moved to" : " failed to move to", p, 0);
+ }
+ if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) {
+ char * s = pv[SND_SRN].sval;
+ char * srvrn = pv[SND_SRN].sval;
+ char tmpbuf[CKMAXPATH+1];
+#ifndef NOSPL
+ int y; /* Pass it thru the evaluator */
+ extern int cmd_quoting; /* for \v(filename) */
+ debug(F111,"ftp get srv_renam",s,1);
+
+ if (cmd_quoting) {
+ y = CKMAXPATH;
+ s = (char *)tmpbuf;
+ zzstring(srvrn,&s,&y);
+ s = (char *)tmpbuf;
+ }
+#endif /* NOSPL */
+ debug(F111,"ftp get srv_renam",s,1);
+ if (s) if (*s) {
+ int x;
+ x = ftp_rename(s2,s);
+ debug(F111,"ftp get ftp_rename",s2,x);
+ tlog(F110, (x > 0) ?
+ " renamed source file to" :
+ " failed to rename source file to",
+ s,
+ 0
+ );
+ if (x < 1)
+ return(-1);
+ }
+ }
+ }
+ }
+ if (cancelfile)
+ continue;
+ if (rc < 0) {
+ ftp_fai++;
+ if (geterror) {
+ status = 0;
+ ftscreen(SCR_EM,0,0L,"Fatal download error");
+ done++;
+ }
+ }
+ }
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"ftp get status","",status);
+ debug(F101,"ftp get cancelgroup","",cancelgroup);
+ debug(F101,"ftp get cancelfile","",cancelfile);
+ debug(F101,"ftp get selected","",selected);
+ debug(F101,"ftp get good","",good);
+ }
+#endif /* DEBUG */
+
+ if (selected == 0) { /* No files met selection criteria */
+ status = 1; /* which is a kind of success. */
+ } else if (status > 0) { /* Some files were selected */
+ if (cancelgroup) /* but MGET was canceled */
+ status = 0; /* so MGET failed */
+ else if (cancelfile && good < 1) /* If file was canceled */
+ status = 0; /* MGET failed if it got no files */
+ }
+ success = status;
+ x = success;
+ debug(F101,"ftp get success","",success);
+
+ xgetx:
+ pipesend = pipesave; /* Restore global pipe selection */
+ if (fp_nml) { /* Close /NAMELIST */
+ if (fp_nml != stdout)
+ fclose(fp_nml);
+ fp_nml = NULL;
+ }
+ if (x > -1) { /* Download successful */
+#ifdef GFTIMER
+ t1 = gmstimer(); /* End time */
+ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
+ if (!sec) sec = 0.001;
+ fptsecs = sec;
+#else
+ sec = (t1 - t0) / 1000;
+ if (!sec) sec = 1;
+#endif /* GFTIMER */
+ tfcps = (long) (tfc / sec);
+ tsecs = (int)sec;
+ lastxfer = W_FTP|W_RECV;
+ xferstat = success;
+ }
+ if (dpyactive)
+ ftscreen(SCR_TC,0,0L,"");
+#ifdef CK_TMPDIR
+ if (f_tmpdir) { /* If we changed to download dir */
+ zchdir((char *) savdir); /* Go back where we came from */
+ f_tmpdir = 0;
+ }
+#endif /* CK_TMPDIR */
+
+ for (i = 0; i <= SND_MAX; i++) { /* Free malloc'd memory */
+ if (pv[i].sval)
+ free(pv[i].sval);
+ }
+ for (i = 0; i < mgetn; i++) /* MGET list too */
+ makestr(&(mgetlist[i]),NULL);
+
+ if (cancelgroup) /* Clear temp-file stack */
+ mlsreset();
+
+ ftreset(); /* Undo switch effects */
+ dpyactive = 0;
+ return(x);
+}
+
+static struct keytab ftprmt[] = {
+ { "cd", XZCWD, 0 },
+ { "cdup", XZCDU, 0 },
+ { "cwd", XZCWD, CM_INV },
+ { "delete", XZDEL, 0 },
+ { "directory", XZDIR, 0 },
+ { "exit", XZXIT, 0 },
+ { "help", XZHLP, 0 },
+ { "login", XZLGI, 0 },
+ { "logout", XZLGO, 0 },
+ { "mkdir", XZMKD, 0 },
+ { "pwd", XZPWD, 0 },
+ { "rename", XZREN, 0 },
+ { "rmdir", XZRMD, 0 },
+ { "type", XZTYP, 0 },
+ { "", 0, 0 }
+};
+static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1;
+
+int
+doftpsite() { /* Send a SITE command */
+ int reply;
+ char * s;
+ int lcs = -1, rcs = -1;
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+ if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing) printf(" ftp site \"%s\"...\n",line);
+ if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) {
+ do {
+ reply = getreply(0,lcs,rcs,ftp_vbm,0);
+ } while (reply == REPLY_PRELIM);
+ }
+ return(success = (reply == REPLY_COMPLETE));
+}
+
+
+int
+dosetftppsv() { /* Passive mode */
+ x = seton(&ftp_psv);
+ if (x > 0) passivemode = ftp_psv;
+ return(x);
+}
+
+/* d o f t p r m t -- Parse and execute REMOTE commands */
+
+int
+doftprmt(cx,who) int cx, who; { /* who == 1 for ftp, 0 for kermit */
+ /* cx == 0 means REMOTE */
+ /* cx != 0 is a XZxxx value */
+ char * s;
+
+ if (who != 0)
+ return(0);
+
+ if (cx == 0) {
+ if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0)
+ return(x);
+ cx = x;
+ }
+ switch (cx) {
+ case XZCDU: /* CDUP */
+ if ((x = cmcfm()) < 0) return(x);
+ return(doftpcdup());
+
+ case XZCWD: /* RCD */
+ if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
+ return(x);
+ ckstrncpy(line,s,LINBUFSIZ);
+ return(doftpcwd((char *)line,1));
+ case XZPWD: /* RPWD */
+ return(doftppwd());
+ case XZDEL: /* RDEL */
+ return(doftpget(FTP_MDE,1));
+ case XZDIR: /* RDIR */
+ return(doftpdir(FTP_DIR));
+ case XZHLP: /* RHELP */
+ return(doftpxhlp());
+ case XZMKD: /* RMKDIR */
+ return(doftpmkd());
+ case XZREN: /* RRENAME */
+ return(doftpren());
+ case XZRMD: /* RRMDIR */
+ return(doftprmd());
+ case XZLGO: /* LOGOUT */
+ return(doftpres());
+ case XZXIT: /* EXIT */
+ return(ftpbye());
+ }
+ printf("?Not usable with FTP - \"%s\"\n", atmbuf);
+ return(-9);
+}
+
+int
+doxftp() { /* Command parser for built-in FTP */
+ int cx, n;
+ struct FDB kw, fl;
+ char * s;
+ int usetls = 0;
+ int lcs = -1, rcs = -1;
+
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+
+ if (inserver) /* FTP not allowed in IKSD. */
+ return(-2);
+
+ if (g_ftp_typ > -1) { /* Restore TYPE if saved */
+ ftp_typ = g_ftp_typ;
+ /* g_ftp_typ = -1; */
+ }
+#ifdef COMMENT
+/*
+ We'll set the collision action locally in doftpget() based on whether
+ ftp_fnc was ever set to a value. if not, we'll use the fncact value.
+*/
+ if (ftp_fnc < 0) /* Inherit global collision action */
+ ftp_fnc = fncact; /* if none specified for FTP */
+#endif /* COMMENT */
+
+ /* Restore global verbose mode */
+ if (ftp_deb)
+ ftp_vbm = 1;
+ else if (quiet)
+ ftp_vbm = 0;
+ else
+ ftp_vbm = ftp_vbx;
+
+ ftp_dates &= 1; /* Undo any previous /UPDATE switch */
+
+ dpyactive = 0; /* Reset global transfer-active flag */
+ printlines = 0; /* Reset printlines */
+
+ if (fp_nml) { /* Reset /NAMELIST */
+ if (fp_nml != stdout)
+ fclose(fp_nml);
+ fp_nml = NULL;
+ }
+ makestr(&ftp_nml,NULL);
+
+ cmfdbi(&kw, /* First FDB - commands */
+ _CMKEY, /* fcode */
+ "Hostname; or FTP command", /* help */
+ "", /* default */
+ "", /* addtl string data */
+ nftpcmd, /* addtl numeric data 1: tbl size */
+ 0, /* addtl numeric data 2: none */
+ xxstring, /* Processing function */
+ ftpcmdtab, /* Keyword table */
+ &fl /* Pointer to next FDB */
+ );
+ cmfdbi(&fl, /* A host name or address */
+ _CMFLD, /* fcode */
+ "Hostname or address", /* help */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ NULL
+ );
+ x = cmfdb(&kw); /* Parse a hostname or a keyword */
+ if (x == -3) {
+ printf("?ftp what? \"help ftp\" for hints\n");
+ return(-9);
+ }
+ if (x < 0)
+ return(x);
+ if (cmresult.fcode == _CMFLD) { /* If hostname */
+ return(openftp(cmresult.sresult,0)); /* go open the connection */
+ } else {
+ cx = cmresult.nresult;
+ }
+ switch (cx) {
+ case FTP_ACC: /* ACCOUNT */
+ if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ makestr(&ftp_acc,s);
+ if (testing)
+ printf(" ftp account: \"%s\"\n",ftp_acc);
+ success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_GUP: /* Go UP */
+ if ((x = cmcfm()) < 0) return(x);
+ CHECKCONN();
+ if (testing) printf(" ftp cd: \"(up)\"\n");
+ return(success = doftpcdup());
+
+ case FTP_CWD: /* CD */
+ if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp cd: \"%s\"\n", line);
+ return(success = doftpcwd(line,1));
+
+ case FTP_CHM: /* CHMOD */
+ if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0)
+ return(x);
+ ckstrncpy(tmpbuf,s,TMPBUFSIZ);
+ if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL);
+ if (testing)
+ printf(" ftp chmod: %s\n",ftpcmdbuf);
+ success =
+ (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_CLS: /* CLOSE FTP connection */
+ if ((y = cmcfm()) < 0)
+ return(y);
+ CHECKCONN();
+ if (testing)
+ printf(" ftp closing...\n");
+ ftpclose();
+ return(success = 1);
+
+ case FTP_DIR: /* DIRECTORY of remote files */
+ case FTP_VDI:
+ return(doftpdir(cx));
+
+ case FTP_GET: /* GET a remote file */
+ case FTP_RGE: /* REGET */
+ case FTP_MGE: /* MGET */
+ case FTP_MDE: /* MDELETE */
+ return(doftpget(cx,1));
+
+ case FTP_IDL: /* IDLE */
+ if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ CHECKCONN();
+ if (z < 0) { /* Display idle timeout */
+ if (testing)
+ printf(" ftp query idle timeout...\n");
+ success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE);
+ } else { /* Set idle timeout */
+ if (testing)
+ printf(" ftp idle timeout set: %d...\n",z);
+ success =
+ (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE);
+ }
+ return(success);
+
+ case FTP_MKD: /* MKDIR */
+ return(doftpmkd());
+
+ case FTP_MOD: /* MODTIME */
+ if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp modtime \"%s\"...\n",line);
+ success = 0;
+ if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) {
+ success = 1;
+ mdtmok = 1;
+ if (!quiet) {
+ int flag = 0;
+ char c, * s;
+ struct tm tmremote;
+
+ bzero((char *)&tmremote, sizeof(struct tm));
+ s = ftp_reply_str;
+ while ((c = *s++)) {
+ if (c == SP) {
+ flag++;
+ break;
+ }
+ }
+ if (flag) {
+ if (sscanf(s, "%04d%02d%02d%02d%02d%02d",
+ &tmremote.tm_year,
+ &tmremote.tm_mon,
+ &tmremote.tm_mday,
+ &tmremote.tm_hour,
+ &tmremote.tm_min,
+ &tmremote.tm_sec
+ ) == 6) {
+ printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n",
+ line,
+ tmremote.tm_year,
+ tmremote.tm_mon,
+ tmremote.tm_mday,
+ tmremote.tm_hour,
+ tmremote.tm_min,
+ tmremote.tm_sec
+ );
+ } else {
+ success = 0;
+ }
+ }
+ }
+ }
+ return(success);
+
+ case FTP_OPN: /* OPEN connection */
+#ifdef COMMENT
+ x = cmfld("IP hostname or address","",&s,xxstring);
+ if (x < 0) {
+ success = 0;
+ return(x);
+ }
+ ckstrncpy(line,s,LINBUFSIZ);
+ s = line;
+ return(openftp(s,0));
+#else
+ { /* OPEN connection */
+ char name[TTNAMLEN+1], *p;
+ extern int network;
+ extern char ttname[];
+ if (network) /* If we have a current connection */
+ ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */
+ else
+ *name = '\0'; /* as default host */
+ for (p = name; *p; p++) /* Remove ":service" from end. */
+ if (*p == ':') { *p = '\0'; break; }
+#ifndef USETLSTAB
+ x = cmfld("IP hostname or address",name,&s,xxstring);
+#else
+ cmfdbi(&kw, /* First FDB - commands */
+ _CMKEY, /* fcode */
+ "Hostname or switch", /* help */
+ "", /* default */
+ "", /* addtl string data */
+ ntlstab, /* addtl numeric data 1: tbl size */
+ 0, /* addtl numeric data 2: none */
+ xxstring, /* Processing function */
+ tlstab, /* Keyword table */
+ &fl /* Pointer to next FDB */
+ );
+ cmfdbi(&fl, /* A host name or address */
+ _CMFLD, /* fcode */
+ "Hostname or address", /* help */
+ "", /* default */
+ "", /* addtl string data */
+ 0, /* addtl numeric data 1 */
+ 0, /* addtl numeric data 2 */
+ xxstring,
+ NULL,
+ NULL
+ );
+
+ for (n = 0;; n++) {
+ x = cmfdb(&kw); /* Parse a hostname or a keyword */
+ if (x == -3) {
+ printf("?ftp open what? \"help ftp\" for hints\n");
+ return(-9);
+ }
+ if (x < 0)
+ break;
+ if (cmresult.fcode == _CMFLD) { /* Hostname */
+ s = cmresult.sresult;
+ break;
+ } else if (cmresult.nresult == OPN_TLS) {
+ usetls = 1;
+ }
+ }
+#endif /* USETLSTAB */
+ if (x < 0) {
+ success = 0;
+ return(x);
+ }
+ ckstrncpy(line,s,LINBUFSIZ);
+ s = line;
+ return(openftp(s,usetls));
+ }
+#endif /* COMMENT */
+
+ case FTP_PUT: /* PUT */
+ case FTP_MPU: /* MPUT */
+ case FTP_APP: /* APPEND */
+ return(doftpput(cx,1));
+
+ case FTP_PWD: /* PWD */
+ x = doftppwd();
+ if (x > -1) success = x;
+ return(x);
+
+ case FTP_REN: /* RENAME */
+ return(doftpren());
+
+ case FTP_RES: /* RESET */
+ return(doftpres());
+
+ case FTP_HLP: /* (remote) HELP */
+ return(doftpxhlp());
+
+ case FTP_RMD: /* RMDIR */
+ return(doftprmd());
+
+ case FTP_STA: /* STATUS */
+ if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing) printf(" ftp status \"%s\"...\n",line);
+ success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_SIT: { /* SITE */
+ return(doftpsite());
+ }
+
+ case FTP_SIZ: /* (ask for) SIZE */
+ if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if (testing)
+ printf(" ftp size \"%s\"...\n",line);
+ success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE);
+ if (success)
+ sizeok = 1;
+ return(success);
+
+ case FTP_SYS: /* Ask for server's SYSTEM type */
+ if ((x = cmcfm()) < 0) return(x);
+ CHECKCONN();
+ if (testing)
+ printf(" ftp system...\n");
+ success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_UMA: /* Set/query UMASK */
+ if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0)
+ if (x != -3)
+ return(x);
+ ckstrncpy(tmpbuf,s,TMPBUFSIZ);
+ if ((x = cmcfm()) < 0) return(x);
+ CHECKCONN();
+ if (testing) {
+ if (tmpbuf[0])
+ printf(" ftp umask \"%s\"...\n",tmpbuf);
+ else
+ printf(" ftp query umask...\n");
+ }
+ success = ftp_umask(tmpbuf);
+ return(success);
+
+ case FTP_USR:
+ return(doftpusr());
+
+ case FTP_QUO:
+ if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_TYP: /* Type */
+ if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0) return(y);
+ CHECKCONN();
+ ftp_typ = x;
+ g_ftp_typ = x;
+ tenex = (ftp_typ == FTT_TEN);
+ changetype(ftp_typ,ftp_vbm);
+ return(1);
+
+ case FTP_CHK: /* Check if remote file(s) exist(s) */
+ if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ success = remote_files(1,(CHAR *)s,NULL,0) ? 1 : 0;
+ return(success);
+
+ case FTP_FEA: /* RFC2389 */
+ if ((y = cmcfm()) < 0)
+ return(y);
+ CHECKCONN();
+ success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE);
+ if (success) {
+ if (sfttab[0] > 0) {
+ ftp_aut = sfttab[SFT_AUTH];
+ sizeok = sfttab[SFT_SIZE];
+ mdtmok = sfttab[SFT_MDTM];
+ mlstok = sfttab[SFT_MLST];
+ }
+ }
+ return(success);
+
+ case FTP_OPT: /* RFC2389 */
+ /* Perhaps this should be a keyword list... */
+ if ((x = cmfld("FTP command","",&s,xxstring)) < 0)
+ return(x);
+ CHECKCONN();
+ ckstrncpy(line,s,LINBUFSIZ);
+ if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0)
+ return(x);
+ success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
+ return(success);
+
+ case FTP_ENA: /* FTP ENABLE */
+ case FTP_DIS: /* FTP DISABLE */
+ if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0) return(y);
+ switch (x) {
+ case ENA_AUTH: /* OK to use autoauthentication */
+ ftp_aut = (cx == FTP_ENA) ? 1 : 0;
+ sfttab[SFT_AUTH] = ftp_aut;
+ break;
+ case ENA_FEAT: /* OK to send FEAT command */
+ featok = (cx == FTP_ENA) ? 1 : 0;
+ break;
+ case ENA_MLST: /* OK to use MLST/MLSD */
+ mlstok = (cx == FTP_ENA) ? 1 : 0;
+ sfttab[SFT_MLST] = mlstok;
+ break;
+ case ENA_MDTM: /* OK to use MDTM */
+ mdtmok = (cx == FTP_ENA) ? 1 : 0;
+ sfttab[SFT_MDTM] = mdtmok;
+ break;
+ case ENA_SIZE: /* OK to use SIZE */
+ sizeok = (cx == FTP_ENA) ? 1 : 0;
+ sfttab[SFT_SIZE] = sizeok;
+ break;
+ }
+ return(success = 1);
+ }
+ return(-2);
+}
+
+#ifndef NOSHOW
+static char *
+shopl(x) int x; {
+ switch (x) {
+ case FPL_CLR: return("clear");
+ case FPL_PRV: return("private");
+ case FPL_SAF: return("safe");
+ case 0: return("(not set)");
+ default: return("(unknown)");
+ }
+}
+
+int
+shoftp(brief) {
+ char * s = "?";
+ int n, x;
+
+ if (g_ftp_typ > -1) { /* Restore TYPE if saved */
+ ftp_typ = g_ftp_typ;
+ /* g_ftp_typ = -1; */
+ }
+ printf("\n");
+ printf("FTP connection: %s\n",connected ?
+ ftp_host :
+ "(none)"
+ );
+ n = 2;
+ if (connected) {
+ n++;
+ printf("FTP server type: %s\n",
+ ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)");
+ }
+ if (loggedin)
+ printf("Logged in as: %s\n",
+ strval(ftp_logname,"(unknown)"));
+ else
+ printf("Not logged in\n");
+ n++;
+ if (brief) return(0);
+
+ printf("\nSET FTP values:\n\n");
+ n += 3;
+
+ printf(" ftp anonymous-password: %s\n",
+ ftp_apw ? ftp_apw : "(default)"
+ );
+ printf(" ftp auto-login: %s\n",showoff(ftp_log));
+ printf(" ftp auto-authentication: %s\n",showoff(ftp_aut));
+ switch (ftp_typ) {
+ case FTT_ASC: s = "text"; break;
+ case FTT_BIN: s = "binary"; break;
+ case FTT_TEN: s = "tenex"; break;
+ }
+ printf(" ftp type: %s\n",s);
+ printf(" ftp get-filetype-switching: %s\n",showoff(get_auto));
+ printf(" ftp dates: %s\n",showoff(ftp_dates));
+ printf(" ftp error-action: %s\n",ftp_err ? "quit":"proceed");
+ printf(" ftp filenames: %s\n",
+ ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal")
+ );
+ printf(" ftp debug %s\n",showoff(ftp_deb));
+
+ printf(" ftp passive-mode: %s\n",showoff(ftp_psv));
+ printf(" ftp permissions: %s\n",showooa(ftp_prm));
+ printf(" ftp verbose-mode: %s\n",showoff(ftp_vbx));
+ printf(" ftp send-port-commands: %s\n",showoff(ftp_psv));
+ printf(" ftp unique-server-names: %s\n",showoff(ftp_usn));
+#ifdef COMMENT
+ /* See note in doxftp() */
+ if (ftp_fnc < 0)
+ ftp_fnc = fncact;
+#endif /* COMMENT */
+ printf(" ftp collision: %s\n",
+ fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]);
+ printf(" ftp server-time-offset: %s\n",
+ fts_sto ? fts_sto : "(none)");
+ n += 15;
+
+#ifndef NOCSETS
+ printf(" ftp character-set-translation: %s\n",showoff(ftp_xla));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+
+ printf(" ftp server-character-set: %s\n",fcsinfo[ftp_csr].keyword);
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+
+ printf(" file character-set: %s\n",fcsinfo[fcharset].keyword);
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+#endif /* NOCSETS */
+
+ x = ftp_dis;
+ if (x < 0)
+ x = fdispla;
+ switch (x) {
+ case XYFD_N: s = "none"; break;
+ case XYFD_R: s = "serial"; break;
+ case XYFD_C: s = "fullscreen"; break;
+ case XYFD_S: s = "crt"; break;
+ case XYFD_B: s = "brief"; break;
+ }
+ printf(" ftp display: %s\n",s);
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+
+ if (mlstok || featok || mdtmok || sizeok || ftp_aut) {
+ printf(" enabled: ");
+ if (ftp_aut) printf(" AUTH");
+ if (featok) printf(" FEAT");
+ if (mdtmok) printf(" MDTM");
+ if (mlstok) printf(" MLST");
+ if (sizeok) printf(" SIZE");
+ printf("\n");
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ }
+ if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) {
+ printf(" disabled: ");
+ if (!ftp_aut) printf(" AUTH");
+ if (!featok) printf(" FEAT");
+ if (!mdtmok) printf(" MDTM");
+ if (!mlstok) printf(" MLST");
+ if (!sizeok) printf(" SIZE");
+ printf("\n");
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ }
+ switch (ftpget) {
+ case 0: s = "kermit"; break;
+ case 1: s = "ftp"; break;
+ case 2: s = "auto"; break;
+ default: s = "?";
+ }
+ printf(" get-put-remote: %s\n",s);
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+
+ printf("\n");
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+
+#ifdef FTP_SECURITY
+ printf("Available security methods: ");
+#ifdef FTP_GSSAPI
+ printf("GSSAPI ");
+#endif /* FTP_GSSAPI */
+#ifdef FTP_KRB4
+ printf("Kerberos4 ");
+#endif /* FTP_KRB4 */
+#ifdef FTP_SRP
+ printf("SRP ");
+#endif /* FTP_SRP */
+#ifdef FTP_SSL
+ printf("SSL ");
+#endif /* FTP_SSL */
+
+ n++;
+ printf("\n\n");
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp authtype: %s\n",strval(auth_type,NULL));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp auto-encryption: %s\n",showoff(ftp_cry));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp credential-forwarding: %s\n",showoff(ftp_cfw));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp command-protection-level: %s\n",shopl(ftp_cpl));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp data-protection-level: %s\n",shopl(ftp_dpl));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+ printf(" ftp secure proxy: %s\n",shopl(ssl_ftp_proxy));
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+#else
+ printf("Available security methods: (none)\n");
+ if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
+#endif /* FTP_SECURITY */
+
+ if (n <= cmd_rows - 3)
+ printf("\n");
+ return(0);
+}
+#endif /* NOSHOW */
+
+#ifndef NOHELP
+/* FTP HELP text strings */
+
+static char * fhs_ftp[] = {
+ "Syntax: FTP subcommand [ operands ]",
+ " Makes an FTP connection, or sends a command to the FTP server.",
+ " To see a list of available FTP subcommands, type \"ftp ?\".",
+ " and then use HELP FTP xxx to get help about subcommand xxx.",
+ " Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.",
+ ""
+};
+
+static char * fhs_acc[] = { /* ACCOUNT */
+ "Syntax: FTP ACCOUNT text",
+ " Sends an account designator to an FTP server that needs one.",
+ " Most FTP servers do not use accounts; some use them for other",
+ " other purposes, such as disk-access passwords.",
+ ""
+};
+static char * fhs_app[] = { /* APPEND */
+ "Syntax: FTP APPEND filname",
+ " Equivalent to [ FTP ] PUT /APPEND. See HELP FTP PUT.",
+ ""
+};
+static char * fhs_cls[] = { /* BYE, CLOSE */
+ "Syntax: [ FTP ] BYE",
+ " Logs out from the FTP server and closes the FTP connection.",
+ " Also see HELP SET GET-PUT-REMOTE. Synonym: [ FTP ] CLOSE.",
+ ""
+};
+static char * fhs_cwd[] = { /* CD, CWD */
+ "Syntax: [ FTP ] CD directory",
+ " Asks the FTP server to change to the given directory.",
+ " Also see HELP SET GET-PUT-REMOTE. Synonyms: [ FTP ] CWD, RCD, RCWD.",
+ ""
+};
+static char * fhs_gup[] = { /* CDUP, UP */
+ "Syntax: FTP CDUP",
+ " Asks the FTP server to change to the parent directory of its current",
+ " directory. Also see HELP SET GET-PUT-REMOTE. Synonym: FTP UP.",
+ ""
+};
+static char * fhs_chm[] = { /* CHMOD */
+ "Syntax: FTP CHMOD filename permissions",
+ " Asks the FTP server to change the permissions, protection, or mode of",
+ " the given file. The given permissions must be in the syntax of the",
+ " the server's file system, e.g. an octal number for UNIX. Also see",
+ " FTP PUT /PERMISSIONS",
+ ""
+};
+static char * fhs_mde[] = { /* DELETE */
+ "Syntax: FTP DELETE [ switches ] filespec",
+ " Asks the FTP server to delete the given file or files.",
+ " Synonym: MDELETE (Kermit makes no distinction between single and",
+ " multiple file deletion). Optional switches:",
+ " ",
+ " /ERROR-ACTION:{PROCEED,QUIT}",
+ " /EXCEPT:pattern",
+ " /FILENAMES:{AUTO,CONVERTED,LITERAL}",
+ " /LARGER-THAN:number",
+#ifdef UNIXOROSK
+ " /NODOTFILES",
+#endif /* UNIXOROSK */
+ " /QUIET",
+#ifdef RECURSIVE
+ " /RECURSIVE (depends on server)",
+ " /SUBDIRECTORIES",
+#endif /* RECURSIVE */
+ " /SMALLER-THAN:number",
+ ""
+};
+static char * fhs_dir[] = { /* DIRECTORY */
+ "Syntax: FTP DIRECTORY [ filespec ]",
+ " Asks the server to send a directory listing of the files that match",
+ " the given filespec, or if none is given, all the files in its current",
+ " directory. The filespec, including any wildcards, must be in the",
+ " syntax of the server's file system. Also see HELP SET GET-PUT-REMOTE.",
+ " Synonym: RDIRECTORY.",
+ ""
+};
+static char * fhs_vdi[] = { /* VDIRECTORY */
+ "Syntax: FTP VDIRECTORY [ filespec ]",
+ " Asks the server to send a directory listing of the files that match",
+ " the given filespec, or if none is given, all the files in its current",
+ " directory. VDIRECTORY is needed for getting verbose directory",
+ " listings from certain FTP servers, such as on TOPS-20. Try it if",
+ " FTP DIRECTORY lists only filenames without details.",
+ ""
+};
+static char * fhs_fea[] = { /* FEATURES */
+ "Syntax: FTP FEATURES",
+ " Asks the FTP server to list its special features. Most FTP servers",
+ " do not recognize this command.",
+ ""
+};
+static char * fhs_mge[] = { /* MGET */
+ "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]",
+ " Download a single file or multiple files. Asks the FTP server to send",
+ " the given file or files. Also see FTP GET. Optional switches:",
+ " ",
+ " /AS-NAME:text",
+ " Name under which to store incoming file.",
+ " Pattern required for for multiple files.",
+ " /BINARY", /* /IMAGE */
+ " Force binary mode. Synonym: /IMAGE.",
+ " /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}",
+ " What to do if an incoming file has the same name as an existing file.",
+
+#ifdef PUTPIPE
+ " /COMMAND",
+ " Specifies that the as-name is a command to which the incoming file",
+ " is to be piped as standard input.",
+#endif /* PUTPIPE */
+
+#ifdef DOUPDATE
+ " /DATES-DIFFER",
+ " Download only those files whose modification date-times differ from",
+ " those of the corresponding local files, or that do not already",
+ " exist on the local computer.",
+#endif /* DOUPDATE */
+
+ " /DELETE",
+ " Specifies that each file is to be deleted from the server after,",
+ " and only if, it is successfully downloaded.",
+ " /ERROR-ACTION:{PROCEED,QUIT}",
+ " When downloading a group of files, what to do upon failure to",
+ " transfer a file: quit or proceed to the next one.",
+ " /EXCEPT:pattern",
+ " Exception list: don't download any files that match this pattern.",
+ " See HELP WILDCARD for pattern syntax.",
+ " /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
+ " Whether to convert incoming filenames to local syntax.",
+#ifdef PIPESEND
+#ifndef NOSPL
+ " /FILTER:command",
+ " Pass incoming files through the given command.",
+#endif /* NOSPL */
+#endif /* PIPESEND */
+ " /LARGER-THAN:number",
+ " Only download files that are larger than the given number of bytes.",
+ " /LISTFILE:filename",
+ " Obtain the list of files to download from the given file.",
+#ifndef NOCSETS
+ " /LOCAL-CHARACTER-SET:name",
+ " When downloading in text mode and character-set conversion is",
+ " desired, this specifies the target set.",
+#endif /* NOCSETS */
+ " /MATCH:pattern",
+ " Specifies a pattern to be used to select filenames locally from the",
+ " server's list.",
+ " /MLSD",
+ " Forces sending of MLSD (rather than NLST) to get the file list.",
+#ifdef CK_TMPDIR
+ " /MOVE-TO:directory",
+ " Each file that is downloaded is to be moved to the given local",
+ " directory immediately after, and only if, it has been received",
+ " successfully.",
+#endif /* CK_TMPDIR */
+ " /NAMELIST:filename",
+ " Instead of downloading the files, stores the list of files that",
+ " would be downloaded in the given local file, one filename per line.",
+ " /NLST",
+ " Forces sending of NLST (rather than MLSD) to get the file list.",
+ " /NOBACKUPFILES",
+ " Don't download any files whose names end with .~<number>~.",
+ " /NODOTFILES",
+ " Don't download any files whose names begin with period (.).",
+ " /QUIET",
+ " Suppress the file-transfer display.",
+#ifdef FTP_RESTART
+ " /RECOVER", /* /RESTART */
+ " Resume a download that was previously interrupted from the point of",
+ " failure. Works only in binary mode. Not supported by all servers.",
+ " Synonym: /RESTART.",
+#endif /* FTP_RESTART */
+#ifdef RECURSIVE
+ " /RECURSIVE", /* /SUBDIRECTORIES */
+ " Create subdirectories automatically if the server sends files",
+ " recursively and includes pathnames (most don't).",
+#endif /* RECURSIVE */
+ " /RENAME-TO:text",
+ " Each file that is downloaded is to be renamed as indicated just,",
+ " after, and only if, it has arrived successfully.",
+#ifndef NOCSETS
+ " /SERVER-CHARACTER-SET:name",
+ " When downloading in text mode and character-set conversion is desired"
+, " this specifies the original file's character set on the server.",
+#endif /* NOCSETS */
+ " /SERVER-RENAME:text",
+ " Each server source file is to be renamed on the server as indicated",
+ " immediately after, but only if, it has arrived succesfully.",
+ " /SMALLER-THAN:number",
+ " Download only those files smaller than the given number of bytes.",
+ " /TEXT", /* /ASCII */
+ " Force text mode. Synonym: /ASCII.",
+ " /TENEX",
+ " Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
+#ifndef NOCSETS
+ " /TRANSPARENT",
+ " When downloading in text mode, do not convert chracter-sets.",
+#endif /* NOCSETS */
+ " /TO-SCREEN",
+ " The downloaded file is to be displayed on the screen.",
+#ifdef DOUPDATE
+ " /UPDATE",
+ " Equivalent to /COLLISION:UPDATE. Download only those files that are",
+ " newer than than their local counterparts, or that do not exist on",
+ " the local computer.",
+#endif /* DOUPDATE */
+ ""
+};
+static char * fhs_hlp[] = { /* HELP */
+ "Syntax: FTP HELP [ command [ subcommand... ] ]",
+ " Asks the FTP server for help about the given command. First use",
+ " FTP HELP by itself to get a list of commands, then use HELP FTP xxx",
+ " to get help for command \"xxx\". Synonyms: REMOTE HELP, RHELP.",
+ ""
+};
+static char * fhs_idl[] = { /* IDLE */
+ "Syntax: FTP IDLE [ number ]",
+ " If given without a number, this asks the FTP server to tell its",
+ " current idle-time limit. If given with a number, it asks the server",
+ " to change its idle-time limit to the given number of seconds.",
+ ""
+};
+static char * fhs_usr[] = { /* USER, LOGIN */
+ "Syntax: FTP USER username [ password [ account ] ]",
+ " Log in to the FTP server. To be used when connected but not yet",
+ " logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.",
+ " If you omit the password, and one is required by the server, you are",
+ " prompted for it. If you omit the account, no account is sent.",
+ " Synonym: FTP LOGIN.",
+ ""
+};
+static char * fhs_get[] = { /* GET */
+ "Syntax: [ FTP ] GET [ options ] filename [ as-name ]",
+ " Download a single file. Asks the FTP server to send the given file.",
+ " The optional as-name is the name to store it under when it arrives;",
+ " if omitted, the file is stored with the name it arrived with, as",
+ " modified according to the FTP FILENAMES setting or /FILENAMES: switch",
+ " value. Aside from the file list and as-name, syntax and options are",
+ " the same as for FTP MGET, which is used for downloading multiple files."
+, ""
+};
+static char * fhs_mkd[] = { /* MKDIR */
+ "Syntax: FTP MKDIR directory",
+ " Asks the FTP server to create a directory with the given name,",
+ " which must be in the syntax of the server's file system. Synonyms:",
+ " REMOTE MKDIR, RMKDIR.",
+ ""
+};
+static char * fhs_mod[] = { /* MODTIME */
+ "Syntax: FTP MODTIME filename",
+ " Asks the FTP server to send the modification time of the given file,",
+ " to be displayed on the screen. The date-time format is all numeric:",
+ " yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating",
+ " fractions of seconds).",
+ ""
+};
+static char * fhs_mpu[] = { /* MPUT */
+ "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]",
+ " Uploads files. Sends the given file or files to the FTP server.",
+ " Also see FTP PUT. Optional switches are:",
+ " ",
+ " /AFTER:date-time",
+ " Uploads only those files newer than the given date-time.",
+ " HELP DATE for info about date-time formats. Synonym: /SINCE.",
+#ifdef PUTARRAY
+ " /ARRAY:array-designator",
+ " Tells Kermit to upload the contents of the given array, rather than",
+ " a file.",
+#endif /* PUTARRAY */
+ " /AS-NAME:text",
+ " Name under which to send files.",
+ " Pattern required for for multiple files.",
+ " /BEFORE:date-time",
+ " Upload only those files older than the given date-time.",
+ " /BINARY",
+ " Force binary mode. Synonym: /IMAGE.",
+#ifdef PUTPIPE
+ " /COMMAND",
+ " Specifies that the filespec is a command whose standard output is",
+ " to be sent.",
+#endif /* PUTPIPE */
+
+#ifdef COMMENT
+#ifdef DOUPDATE
+ " /DATES-DIFFER",
+ " Upload only those files whose modification date-times differ from",
+ " those on the server, or that don't exist on the server at all.",
+#endif /* DOUPDATE */
+#endif /* COMMENT */
+
+ " /DELETE",
+ " Specifies that each source file is to be deleted after, and only if,",
+ " it is successfully uploaded.",
+ " /DOTFILES",
+ " Include files whose names begin with period (.).",
+ " /ERROR-ACTION:{PROCEED,QUIT}",
+ " When uploading a group of files, what to do upon failure to",
+ " transfer a file: quit or proceed to the next one.",
+ " /EXCEPT:pattern",
+ " Exception list: don't upload any files that match this pattern.",
+ " See HELP WILDCARD for pattern syntax.",
+ " /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
+ " Whether to convert outbound filenames to common syntax.",
+#ifdef PIPESEND
+#ifndef NOSPL
+ " /FILTER:command",
+ " Pass outbound files through the given command.",
+#endif /* NOSPL */
+#endif /* PIPESEND */
+#ifdef CKSYMLINK
+ " /FOLLOWINKS",
+ " Send files that are pointed to by symbolic links.",
+ " /NOFOLLOWINKS",
+ " Skip over symbolic links (default).",
+#endif /* CKSYMLINK */
+ " /LARGER-THAN:number",
+ " Only upload files that are larger than the given number of bytes.",
+ " /LISTFILE:filename",
+ " Obtain the list of files to upload from the given file.",
+#ifndef NOCSETS
+ " /LOCAL-CHARACTER-SET:name",
+ " When uploading in text mode and character-set conversion is",
+ " desired, this specifies the source-file character set.",
+#endif /* NOCSETS */
+#ifdef CK_TMPDIR
+ " /MOVE-TO:directory",
+ " Each source file that is uploaded is to be moved to the given local",
+ " directory when, and only if, the transfer is successful.",
+#endif /* CK_TMPDIR */
+ " /NOBACKUPFILES",
+ " Don't upload any files whose names end with .~<number>~.",
+#ifdef UNIXOROSK
+ " /NODOTFILES",
+ " Don't upload any files whose names begin with period (.).",
+#endif /* UNIXOROSK */
+ " /NOT-AFTER:date-time",
+ " Upload only files that are not newer than the given date-time",
+ " /NOT-BEFORE:date-time",
+ " Upload only files that are not older than the given date-time",
+#ifdef UNIX
+ " /PERMISSIONS",
+ " Ask the server to set the permissions of each file it receives",
+ " according to the source file's permissions.",
+#endif /* UNIX */
+ " /QUIET",
+ " Suppress the file-transfer display.",
+#ifdef FTP_RESTART
+ " /RECOVER",
+ " Resume an upload that was previously interrupted from the point of",
+ " failure. Synonym: /RESTART.",
+#endif /* FTP_RESTART */
+#ifdef RECURSIVE
+ " /RECURSIVE",
+ " Send files from the given directory and all the directories beneath",
+ " it. Synonym: /SUBDIRECTORIES.",
+#endif /* RECURSIVE */
+ " /RENAME-TO:text",
+ " Each source file that is uploaded is to be renamed on the local",
+ " local computer as indicated when and only if, the transfer completes",
+ " successfully.",
+#ifndef NOCSETS
+ " /SERVER-CHARACTER-SET:name",
+ " When uploading in text mode and character-set conversion is desired,",
+ " this specifies the character set to which the file should be",
+ " converted for storage on the server.",
+#endif /* NOCSETS */
+ " /SERVER-RENAME:text",
+ " Each file that is uploaded is to be renamed as indicated on the",
+ " server after, and only if, if arrives successfully.",
+ " /SIMULATE",
+ " Show which files would be sent without actually sending them.",
+ " /SMALLER-THAN:number",
+ " Upload only those files smaller than the given number of bytes.",
+ " /TEXT",
+ " Force text mode. Synonym: /ASCII.",
+ " /TENEX",
+ " Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
+#ifndef NOCSETS
+ " /TRANSPARENT",
+ " When uploading in text mode, do not convert chracter-sets.",
+#endif /* NOCSETS */
+ " /TYPE:{TEXT,BINARY}",
+ " Upload only files of the given type.",
+#ifdef DOUPDATE
+ " /UPDATE",
+ " If a file of the same name exists on the server, upload only if",
+ " the local file is newer.",
+#endif /* DOUPDATE */
+ " /UNIQUE-SERVER-NAMES",
+ " Ask the server to compute new names for any incoming file that has",
+ " the same name as an existing file.",
+ ""
+};
+static char * fhs_opn[] = { /* OPEN */
+#ifdef CK_SSL
+ "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]",
+ " Opens a connection to the FTP server on the given host. The default",
+ " TCP port is 21 (990 if SSL/TLS is used), but a different port number",
+ " can be supplied if necessary. Optional switches are:",
+#else /* CK_SSL */
+ "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]",
+ " Opens a connection to the FTP server on the given host. The default",
+ " TCP port is 21, but a different port number can be supplied if",
+ " necessary. Optional switches are:",
+#endif /* CK_SSL */
+ " ",
+ " /ANONYMOUS",
+ " Logs you in anonymously.",
+ " /USER:text",
+ " Supplies the given text as your username.",
+ " /PASSWORD:text",
+ " Supplies the given text as your password. If you include a username",
+ " but omit this switch and the server requires a password, you are",
+ " prompted for it.",
+ " /ACCOUNT:text",
+ " Supplies the given text as your account, if required by the server.",
+ " /ACTIVE",
+ " Forces an active (rather than passive) connection.",
+ " /PASSIVE",
+ " Forces a passive (rather than active) connection.",
+ " /NOINIT",
+ " Inhibits sending initial REST, STRU, and MODE commands, which are",
+ " well-known standard commands, but to which some servers react badly.",
+ " /NOLOGIN",
+ " Inhibits autologin for this connection only.",
+ ""
+};
+static char * fhs_opt[] = { /* OPTS, OPTIONS */
+ "Syntax: FTP OPTIONS",
+ " Asks the FTP server to list its current options. Advanced, new,",
+ " not supported by most FTP servers.",
+ ""
+};
+static char * fhs_put[] = { /* PUT, SEND */
+ "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]",
+ " Like FTP MPUT, but only one filespec is allowed, and if it is followed",
+ " by an additional field, this is interpreted as the name under which",
+ " to send the file or files. See HELP FTP MPUT.",
+ ""
+};
+static char * fhs_pwd[] = { /* PWD */
+ "Syntax: FTP PWD",
+ " Asks the FTP server to reveal its current working directory.",
+ " Synonyms: REMOTE PWD, RPWD.",
+ ""
+};
+static char * fhs_quo[] = { /* QUOTE */
+ "Syntax: FTP QUOTE text",
+ " Sends an FTP protocol command to the FTP server. Use this command",
+ " for sending commands that Kermit might not support.",
+ ""
+};
+static char * fhs_rge[] = { /* REGET */
+ "Syntax: FTP REGET",
+ " Synonym for FTP GET /RECOVER.",
+ ""
+};
+static char * fhs_ren[] = { /* RENAME */
+ "Syntax: FTP RENAME name1 name1",
+ " Asks the FTP server to change the name of the file whose name is name1",
+ " and which resides in the FTP server's file system, to name2. Works",
+ " only for single files; wildcards are not accepted.",
+ ""
+};
+static char * fhs_res[] = { /* RESET */
+ "Syntax: FTP RESET",
+ " Asks the server to log out your session, terminating your access",
+ " rights, without closing the connection.",
+ ""
+};
+static char * fhs_rmd[] = { /* RMDIR */
+ "Syntax: FTP RMDIR directory",
+ " Asks the FTP server to remove the directory whose name is given.",
+ " This usually requires the directory to be empty. Synonyms: REMOTE",
+ " RMDIR, RRMDIR.",
+ ""
+};
+static char * fhs_sit[] = { /* SITE */
+ "Syntax: FTP SITE text",
+ " Sends a site-specific command to the FTP server.",
+ ""
+};
+static char * fhs_siz[] = { /* SIZE */
+ "Syntax: FTP SIZE filename",
+ " Asks the FTP server to send a numeric string representing the size",
+ " of the given file.",
+ ""
+};
+static char * fhs_sta[] = { /* STATUS */
+ "Syntax: FTP STATUS [ filename ]",
+ " Asks the FTP server to report its status. If a filename is given,",
+ " the FTP server should report details about the file.",
+ ""
+};
+static char * fhs_sys[] = { /* SYSTEM */
+ "Syntax: FTP SYSTEM",
+ " Asks the FTP server to report its operating system type.",
+ ""
+};
+static char * fhs_typ[] = { /* TYPE */
+ "Syntax: FTP TYPE { TEXT, BINARY, TENEX }",
+ " Puts the client and server in the indicated transfer mode.",
+ " ASCII is a synonym for TEXT. TENEX is used only for uploading 8-bit",
+ " binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or",
+ " downloading files from TENEX or TOPS-20 that have been uploaded in",
+ " TENEX mode.",
+ ""
+};
+static char * fhs_uma[] = { /* UMASK */
+ "Syntax: FTP UMASK number",
+ " Asks the FTP server to set its file creation mode mask. Applies",
+ " only (or mainly) to UNIX-based FTP servers.",
+ ""
+};
+static char * fhs_chk[] = { /* CHECK */
+ "Syntax: FTP CHECK remote-filespec",
+ " Asks the FTP server if the given file or files exist. If the",
+ " remote-filespec contains wildcards, this command fails if no server",
+ " files match, and succeeds if at least one file matches. If the",
+ " remote-filespec does not contain wildcards, this command succeeds if",
+ " the given file exists and fails if it does not.",
+ ""
+};
+static char * fhs_ena[] = { /* ENABLE */
+ "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
+ " Enables the use of the given FTP protocol command in case it has been",
+ " disabled (but this is no guarantee that the FTP server understands it)."
+,
+ " Use SHOW FTP to see which of these commands is enabled and disabled.",
+ " Also see FTP DISABLE.",
+ ""
+};
+static char * fhs_dis[] = { /* DISABLE */
+ "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
+ " Disables the use of the given FTP protocol command.",
+ " Also see FTP ENABLE.",
+ ""
+};
+
+#endif /* NOHELP */
+
+int
+doftphlp() {
+ int cx;
+ if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0)
+ if (cx != -3)
+ return(cx);
+ if ((x = cmcfm()) < 0)
+ return(x);
+
+#ifdef NOHELP
+ printf("Sorry, no help available\n");
+#else
+ switch (cx) {
+ case -3:
+ return(hmsga(fhs_ftp));
+ case FTP_ACC: /* ACCOUNT */
+ return(hmsga(fhs_acc));
+ case FTP_APP: /* APPEND */
+ return(hmsga(fhs_app));
+ case FTP_CLS: /* BYE, CLOSE */
+ return(hmsga(fhs_cls));
+ case FTP_CWD: /* CD, CWD */
+ return(hmsga(fhs_cwd));
+ case FTP_GUP: /* CDUP, UP */
+ return(hmsga(fhs_gup));
+ case FTP_CHM: /* CHMOD */
+ return(hmsga(fhs_chm));
+ case FTP_MDE: /* DELETE, MDELETE */
+ return(hmsga(fhs_mde));
+ case FTP_DIR: /* DIRECTORY */
+ return(hmsga(fhs_dir));
+ case FTP_VDI: /* VDIRECTORY */
+ return(hmsga(fhs_vdi));
+ case FTP_FEA: /* FEATURES */
+ return(hmsga(fhs_fea));
+ case FTP_GET: /* GET */
+ return(hmsga(fhs_get));
+ case FTP_HLP: /* HELP */
+ return(hmsga(fhs_hlp));
+ case FTP_IDL: /* IDLE */
+ return(hmsga(fhs_idl));
+ case FTP_USR: /* USER, LOGIN */
+ return(hmsga(fhs_usr));
+ case FTP_MGE: /* MGET */
+ return(hmsga(fhs_mge));
+ case FTP_MKD: /* MKDIR */
+ return(hmsga(fhs_mkd));
+ case FTP_MOD: /* MODTIME */
+ return(hmsga(fhs_mod));
+ case FTP_MPU: /* MPUT */
+ return(hmsga(fhs_mpu));
+ case FTP_OPN: /* OPEN */
+ return(hmsga(fhs_opn));
+ case FTP_OPT: /* OPTS, OPTIONS */
+ return(hmsga(fhs_opt));
+ case FTP_PUT: /* PUT, SEND */
+ return(hmsga(fhs_put));
+ case FTP_PWD: /* PWD */
+ return(hmsga(fhs_pwd));
+ case FTP_QUO: /* QUOTE */
+ return(hmsga(fhs_quo));
+ case FTP_RGE: /* REGET */
+ return(hmsga(fhs_rge));
+ case FTP_REN: /* RENAME */
+ return(hmsga(fhs_ren));
+ case FTP_RES: /* RESET */
+ return(hmsga(fhs_res));
+ case FTP_RMD: /* RMDIR */
+ return(hmsga(fhs_rmd));
+ case FTP_SIT: /* SITE */
+ return(hmsga(fhs_sit));
+ case FTP_SIZ: /* SIZE */
+ return(hmsga(fhs_siz));
+ case FTP_STA: /* STATUS */
+ return(hmsga(fhs_sta));
+ case FTP_SYS: /* SYSTEM */
+ return(hmsga(fhs_sys));
+ case FTP_TYP: /* TYPE */
+ return(hmsga(fhs_typ));
+ case FTP_UMA: /* UMASK */
+ return(hmsga(fhs_uma));
+ case FTP_CHK: /* CHECK */
+ return(hmsga(fhs_chk));
+ case FTP_ENA:
+ return(hmsga(fhs_ena));
+ case FTP_DIS:
+ return(hmsga(fhs_dis));
+ default:
+ printf("Sorry, help available for this command.\n");
+ break;
+ }
+#endif /* NOHELP */
+ return(success = 0);
+}
+
+int
+dosetftphlp() {
+ int cx;
+ if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0)
+ if (cx != -3)
+ return(cx);
+ if (cx != -3)
+ ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ);
+ if ((x = cmcfm()) < 0)
+ return(x);
+
+#ifdef NOHELP
+ printf("Sorry, no help available\n");
+#else
+ switch (cx) {
+ case -3:
+ printf("\nSyntax: SET FTP parameter value\n");
+ printf(" Type \"help set ftp ?\" for a list of parameters.\n");
+ printf(" Type \"help set ftp xxx\" for information about setting\n");
+ printf(" parameter xxx. Type \"show ftp\" for current values.\n\n");
+ return(0);
+
+ case FTS_BUG:
+ printf("\nSyntax: SET FTP BUG <name> {ON, OFF}\n");
+ printf(
+ " Activates a workaround for the named bug in the FTP server.\n");
+ printf(" Type SET FTP BUG ? for a list of names.\n");
+ printf(" For each bug, the default is OFF\n\n");
+ return(0);
+
+#ifdef FTP_SECURITY
+ case FTS_ATP: /* "authtype" */
+ printf("\nSyntax: SET FTP AUTHTYPE list\n");
+ printf(" Specifies an ordered list of authentication methods to be\n"
+ );
+ printf(" when FTP AUTOAUTHENTICATION is ON. The default list is:\n");
+ printf(" GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n");
+ return(0);
+
+ case FTS_AUT: /* "autoauthentication" */
+ printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n");
+ printf(" Tells whether authentication should be negotiated by the\n");
+ printf(" FTP OPEN command. Default is ON.\n\n");
+ break;
+
+ case FTS_CRY: /* "autoencryption" */
+ printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n");
+ printf(" Tells whether encryption (privacy) should be negotiated\n");
+ printf(" by the FTP OPEN command. Default is ON.\n\n");
+ break;
+#endif /* FTP_SECURITY */
+
+ case FTS_LOG: /* "autologin" */
+ printf("\nSET FTP AUTOLOGIN { ON, OFF }\n");
+ printf(" Tells Kermit whether to try to log you in automatically\n");
+ printf(" as part of the connection process.\n\n");
+ break;
+
+ case FTS_DIS:
+ printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n");
+ printf(" Chooses the file-transfer display style for FTP.\n");
+ printf(" Like SET TRANSFER DISPLAY but applies only to FTP.\n\n");
+ break;
+
+#ifndef NOCSETS
+ case FTS_XLA: /* "character-set-translation" */
+ printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n");
+ printf(" Whether to translate character sets when transferring\n");
+ printf(" text files with FTP. OFF by default.\n\n");
+ break;
+
+#endif /* NOCSETS */
+ case FTS_FNC: /* "collision" */
+ printf("\n");
+ printf(
+"Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n"
+ );
+ printf(" Tells what do when an incoming file has the same name as\n");
+ printf(" an existing file when downloading with FTP.\n\n");
+ break;
+
+#ifdef FTP_SECURITY
+ case FTS_CPL: /* "command-protection-level" */
+ printf("\n");
+ printf(
+"Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
+ );
+ printf("\n");
+ printf(
+" Tells what level of protection is applied to the FTP command channel.\n\n");
+ break;
+ case FTS_CFW: /* "credential-forwarding" */
+ printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n");
+ printf(" Tells whether end-user credentials are to be forwarded\n");
+ printf(" to the server if supported by the authentication method\n");
+ printf(" (GSSAPI-KRB5 only).\n\n");
+ break;
+ case FTS_DPL: /* "data-protection-level" */
+ printf("\n");
+ printf(
+"Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
+ );
+ printf("\n");
+ printf(
+" Tells what level of protection is applied to the FTP data channel.\n\n");
+ break;
+#endif /* FTP_SECURITY */
+
+ case FTS_DBG: /* "debug" */
+ printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n");
+ printf(" Whether to print FTP protocol messages.\n\n");
+ return(0);
+
+ case FTS_ERR: /* "error-action" */
+ printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n");
+ printf(" What to do when an error occurs when transferring a group\n")
+ ;
+ printf(" of files: quit and fail, or proceed to the next file.\n\n");
+ return(0);
+
+ case FTS_CNV: /* "filenames" */
+ printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n");
+ printf(" What to do with filenames: convert them, take and use them\n"
+ );
+ printf(" literally; or choose what to do automatically based on the\n"
+ );
+ printf(" OS type of the server. The default is AUTO.\n\n");
+ return(0);
+
+ case FTS_PSV: /* "passive-mode" */
+ printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n");
+ printf(" Whether to use passive mode, which helps to get through\n");
+ printf(" firewalls. ON by default.\n\n");
+ return(0);
+
+ case FTS_PRM: /* "permissions" */
+ printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n");
+ printf(" Whether to try to send file permissions when uploading.\n");
+ printf(" OFF by default. AUTO means only if client and server\n");
+ printf(" have the same OS type.\n\n");
+ return(0);
+
+ case FTS_TST: /* "progress-messages" */
+ printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n");
+ printf(" Whether Kermit should print locally-generated feedback\n");
+ printf(" messages for each non-file-transfer command.");
+ printf(" ON by default.\n\n");
+ return(0);
+
+ case FTS_SPC: /* "send-port-commands" */
+ printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n");
+ printf(" Whether Kermit should send a new PORT command for each");
+ printf(" task.\n\n");
+ return(0);
+
+#ifndef NOCSETS
+ case FTS_CSR: /* "server-character-set" */
+ printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n");
+ printf(" The name of the character set used for text files on the\n");
+ printf(" server. Enter a name of '?' for a menu.\n\n");
+ return(0);
+#endif /* NOCSETS */
+
+ case FTS_STO: /* "server-time-offset */
+ printf(
+"\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n");
+ printf(
+" Specifies an offset to apply to the server's file timestamps.\n");
+ printf(
+" Use this to correct for misconfigured server time or timezone.\n");
+ printf(
+" Format: must begin with + or - sign. Hours must be given; minutes\n");
+ printf(
+" and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n");
+ return(0);
+
+ case FTS_TYP: /* "type" */
+ printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n");
+ printf(" Establishes the default transfer mode.\n");
+ printf(" TENEX is used for uploading 8-bit binary files to 36-bit\n");
+ printf(" platforms such as TENEX and TOPS-20 and for downloading\n");
+ printf(" them again.\n\n");
+ return(0);
+
+#ifdef PATTERNS
+ case FTS_GFT:
+ printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n");
+ printf(" Tells whether GET and MGET should automatically switch\n");
+ printf(" the appropriate file type, TEXT, BINARY, or TENEX, by\n");
+ printf(" matching the name of each incoming file with its list of\n");
+ printf(" FILE TEXT-PATTERNS and FILE BINARY-PATTERNS. ON by\n");
+ printf(" default. SHOW PATTERNS displays the current pattern\n");
+ printf(" list. HELP SET FILE to see how to change it.\n");
+ break;
+#endif /* PATTERNS */
+
+ case FTS_USN: /* "unique-server-names" */
+ printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n");
+ printf(" Tells whether to ask the server to create unique names\n");
+ printf(" for any uploaded file that has the same name as an\n");
+ printf(" existing file. Default is OFF.\n\n");
+ return(0);
+
+ case FTS_VBM: /* "verbose-mode" */
+ printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n");
+ printf(" Whether to display all responses from the FTP server.\n");
+ printf(" OFF by default.\n\n");
+ return(0);
+
+ case FTS_DAT:
+ printf("\nSyntax: SET FTP DATES { ON, OFF }\n");
+ printf(" Whether to set date of incoming files from the file date\n");
+ printf(" on the server. ON by default. Note: there is no way to\n")
+ ;
+ printf(" set the date on files uploaded to the server. Also note\n");
+ printf(" that not all servers support this feature.\n\n");
+ return(0);
+
+ case FTS_APW:
+ printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n");
+ printf(" Password to supply automatically on anonymous FTP\n");
+ printf(" connections instead of the default user@host.\n");
+ printf(" Omit optional text to restore default.\n\n");
+ return(0);
+
+ default:
+ printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf);
+ }
+#endif /* NOHELP */
+ return(0);
+}
+
+#ifndef L_SET
+#define L_SET 0
+#endif /* L_SET */
+#ifndef L_INCR
+#define L_INCR 1
+#endif /* L_INCR */
+
+#ifdef FTP_SRP
+char srp_user[BUFSIZ]; /* where is BUFSIZ defined? */
+char *srp_pass;
+char *srp_acct;
+#endif /* FTP_SRP */
+
+static int kerror; /* Needed for all auth types */
+
+static struct sockaddr_in hisctladdr;
+static struct sockaddr_in hisdataaddr;
+static struct sockaddr_in data_addr;
+static int data = -1;
+static int ptflag = 0;
+static struct sockaddr_in myctladdr;
+
+#ifdef COMMENT
+#ifndef OS2
+UID_T getuid();
+#endif /* OS2 */
+#endif /* COMMENT */
+
+
+static int cpend = 0; /* No pending replies */
+
+#ifdef CK_SSL
+extern SSL *ssl_ftp_con;
+extern SSL_CTX *ssl_ftp_ctx;
+extern SSL *ssl_ftp_data_con;
+extern int ssl_ftp_active_flag;
+extern int ssl_ftp_data_active_flag;
+#endif /* CK_SSL */
+
+/* f t p c m d -- Send a command to the FTP server */
+/*
+ Call with:
+ char * cmd: The command to send.
+ char * arg: The argument (e.g. a filename).
+ int lcs: The local character set index.
+ int rcs: The remote (server) character set index.
+ int vbm: Verbose mode:
+ 0 = force verbosity off
+ >0 = force verbosity on
+
+ If arg is given (not NULL or empty) and lcs != rcs and both are > -1,
+ and neither lcs or rcs is UCS-2, the arg is translated from the local
+ character set to the remote one before sending the result to the server.
+
+ Returns:
+ 0 on failure with ftpcode = -1
+ >= 0 on success (getreply() result) with ftpcode = 0.
+*/
+static char xcmdbuf[RFNBUFSIZ];
+
+static int
+ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; {
+ char * s = NULL;
+ int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1;
+ sig_t oldintr;
+
+ if (ftp_deb) /* DEBUG */
+ vbm = 1;
+ else if (quiet || dpyactive) /* QUIET or File Transfer Active */
+ vbm = 0;
+ else if (vbm < 0) /* VERBOSE */
+ vbm = ftp_vbm;
+
+ cancelfile = 0;
+ if (!cmd) cmd = "";
+ if (!arg) arg = "";
+ cmdlen = (int)strlen(cmd);
+ len = cmdlen + (int)strlen(arg) + 1;
+
+ if (ftp_deb /* && !dpyactive */ ) {
+#ifdef FTP_PROXY
+ if (ftp_prx) printf("%s ", ftp_host);
+#endif /* FTP_PROXY */
+ printf("---> ");
+ if (!anonymous && strcmp("PASS",cmd) == 0)
+ printf("PASS XXXX");
+ else
+ printf("%s %s",cmd,arg);
+ printf("\n");
+ }
+ /* bzero(xcmdbuf,RFNBUFSIZ); */
+ ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL);
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"ftpcmd cmd",cmd,0);
+ debug(F110,"ftpcmd arg",arg,0);
+ debug(F101,"ftpcmd lcs","",lcs);
+ debug(F101,"ftpcmd rcs","",rcs);
+ }
+#endif /* DEBUG */
+
+ if (csocket == -1) {
+ perror("No control connection for command");
+ ftpcode = -1;
+ return(0);
+ }
+ havesigint = 0;
+ oldintr = signal(SIGINT, cmdcancel);
+
+#ifndef NOCSETS
+ if (*arg && /* If an arg was given */
+ lcs > -1 && /* and a local charset */
+ rcs > -1 && /* and a remote charset */
+ lcs != rcs && /* and the two are not the same */
+ lcs != FC_UCS2 && /* and neither one is UCS-2 */
+ rcs != FC_UCS2 /* ... */
+ ) {
+ initxlate(lcs,rcs); /* Translate arg from lcs to rcs */
+ xgnbp = arg; /* Global pointer to input string */
+ rfnptr = rfnbuf; /* Global pointer to output buffer */
+
+ while (1) {
+ if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break;
+ if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break;
+ }
+ /*
+ We have to copy here instead of translating directly into
+ xcmdbuf[] so strputc() can check length. Alternatively we could
+ write yet another xpnbyte() output function.
+ */
+ if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) {
+ printf("?FTP command too long: %s + arg\n",cmd);
+ ftpcode = -1;
+ return(0);
+ }
+ x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1));
+ }
+#endif /* NOCSETS */
+
+ s = xcmdbuf; /* Command to send to server */
+
+#ifdef DEBUG
+ if (deblog) { /* Log it */
+ if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) {
+ /* But don't log passwords */
+ debug(F110,"FTP SENT ","PASS XXXX",0);
+ } else {
+ debug(F110,"FTP SENT ",s,0);
+ }
+ }
+#endif /* DEBUG */
+
+#ifdef CK_ENCRYPTION
+ again:
+#endif /* CK_ENCRYPTION */
+ if (scommand(s) == 0) { /* Send it. */
+ signal(SIGINT, oldintr);
+ return(0);
+ }
+ cpend = 1;
+ x = !strcmp(cmd,"QUIT"); /* Is it the QUIT command? */
+ if (x) /* In case we're interrupted */
+ connected = 0; /* while waiting for the reply... */
+
+ fc = 0; /* Function code for getreply() */
+ if (!strncmp(cmd,"AUTH ",5) /* Must parse AUTH reply */
+#ifdef FTPHOST
+ && strncmp(cmd, "HOST ",5)
+#endif /* FTPHOST */
+ ) {
+ fc = GRF_AUTH;
+ } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */
+ fc = GRF_FEAT; /* But FEAT not widely understood */
+ if (!ftp_deb) /* So suppress error messages */
+ vbm = 9;
+ }
+ r = getreply(x, /* Expect connection to close */
+ lcs,rcs, /* Charsets */
+ vbm, /* Verbosity */
+ fc /* Function code */
+ );
+ if (q > -1)
+ quiet = q;
+
+#ifdef CK_ENCRYPTION
+ if (ftpcode == 533 && ftp_cpl == FPL_PRV) {
+ fprintf(stderr,
+ "ENC command not supported at server; retrying under MIC...\n");
+ ftp_cpl = FPL_SAF;
+ goto again;
+ }
+#endif /* CK_ENCRYPTION */
+#ifdef COMMENT
+ if (cancelfile && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+#endif /* COMMENT */
+ signal(SIGINT, oldintr);
+ return(r);
+}
+
+static VOID
+lostpeer() {
+ debug(F100,"lostpeer","",0);
+ if (connected) {
+ if (csocket != -1) {
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ SSL_shutdown(ssl_ftp_con);
+ SSL_free(ssl_ftp_con);
+ ssl_ftp_proxy = 0;
+ ssl_ftp_active_flag = 0;
+ ssl_ftp_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(csocket, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(csocket);
+#endif /* TCPIPLIB */
+ csocket = -1;
+ }
+ if (data != -1) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = -1;
+ }
+ connected = 0;
+ anonymous = 0;
+ loggedin = 0;
+ auth_type = NULL;
+ ftp_cpl = ftp_dpl = FPL_CLR;
+#ifdef CKLOGDIAL
+ ftplogend();
+#endif /* CKLOGDIAL */
+
+#ifdef LOCUS
+ if (autolocus) /* Auotomatic locus switching... */
+ setlocus(1,1); /* Switch locus to local. */
+#endif /* LOCUS */
+#ifdef OS2
+ DialerSend(OPT_KERMIT_HANGUP, 0);
+#endif /* OS2 */
+ }
+#ifdef FTP_PROXY
+ pswitch(1);
+ if (connected) {
+ if (csocket != -1) {
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(csocket, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(csocket);
+#endif /* TCPIPLIB */
+ csocket = -1;
+ }
+ connected = 0;
+ anonymous = 0;
+ loggedin = 0;
+ auth_type = NULL;
+ ftp_cpl = ftp_dpl = FPL_CLR;
+ }
+ proxflag = 0;
+ pswitch(0);
+#endif /* FTP_PROXY */
+}
+
+int
+ftpisopen() {
+ return(connected);
+}
+
+static int
+ftpclose() {
+ extern int quitting;
+ if (!connected)
+ return(0);
+ if (!ftp_vbm && !quiet) printlines = 1;
+ ftpcmd("QUIT",NULL,0,0,ftp_vbm);
+ if (csocket) {
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ SSL_shutdown(ssl_ftp_con);
+ SSL_free(ssl_ftp_con);
+ ssl_ftp_proxy = 0;
+ ssl_ftp_active_flag = 0;
+ ssl_ftp_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(csocket, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(csocket);
+#endif /* TCPIPLIB */
+ }
+ csocket = -1;
+ connected = 0;
+ anonymous = 0;
+ loggedin = 0;
+ mdtmok = 1;
+ sizeok = 1;
+ featok = 1;
+ stouarg = 1;
+ typesent = 0;
+ data = -1;
+ globaldin = -1;
+#ifdef FTP_PROXY
+ if (!proxy)
+ macnum = 0;
+#endif /* FTP_PROXY */
+ auth_type = NULL;
+ ftp_dpl = FPL_CLR;
+#ifdef CKLOGDIAL
+ ftplogend();
+#endif /* CKLOGDIAL */
+#ifdef LOCUS
+ /* Unprefixed file management commands are executed locally */
+ if (autolocus && !ftp_cmdlin && !quitting) {
+ setlocus(1,1);
+ }
+#endif /* LOCUS */
+#ifdef OS2
+ DialerSend(OPT_KERMIT_HANGUP, 0);
+#endif /* OS2 */
+ return(0);
+}
+
+int
+ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; {
+ char * host;
+
+ if (connected) {
+ printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host);
+ ftpcode = -1;
+ return(0);
+ }
+#ifdef FTPHOST
+ hostcmd = 0;
+#endif /* FTPHOST */
+ alike = 0;
+ ftp_srvtyp[0] = NUL;
+ if (!service) service = "";
+ if (!*service) service = use_tls ? "ftps" : "ftp";
+
+ if (!isdigit(service[0])) {
+ struct servent *destsp;
+ destsp = getservbyname(service, "tcp");
+ if (!destsp) {
+ if (!ckstrcmp(service,"ftp",-1,0)) {
+ ftp_port = 21;
+ } else if (!ckstrcmp(service,"ftps",-1,0)) {
+ ftp_port = 990;
+ } else {
+ printf("?Bad port name - \"%s\"\n", service);
+ ftpcode = -1;
+ return(0);
+ }
+ } else {
+ ftp_port = destsp->s_port;
+ ftp_port = ntohs(ftp_port);
+ }
+ } else
+ ftp_port = atoi(service);
+ if (ftp_port <= 0) {
+ printf("?Bad port name - \"%s\"\n", service);
+ ftpcode = -1;
+ return(0);
+ }
+ host = ftp_hookup(remote, ftp_port, use_tls);
+ if (host) {
+ ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN);
+ connected = 1; /* Set FTP defaults */
+ ftp_cpl = ftp_dpl = FPL_CLR;
+ curtype = FTT_ASC; /* Server uses ASCII mode */
+ form = FORM_N;
+ mode = MODE_S;
+ stru = STRU_F;
+ strcpy(bytename, "8");
+ bytesize = 8;
+
+#ifdef FTP_SECURITY
+ if (ftp_aut) {
+ if (ftp_auth()) {
+ if (ftp_cry
+#ifdef OS2
+ && ck_crypt_is_installed()
+#endif /* OS2 */
+ ) {
+ if (!quiet)
+ printf("FTP Command channel is Private (encrypted)\n");
+ ftp_cpl = FPL_PRV;
+ if (setpbsz(DEFAULT_PBSZ) < 0) {
+ /* a failure here is most likely caused by a mixup */
+ /* in the session key used by client and server */
+ printf("?Protection buffer size negotiation failed\n");
+ return(0);
+ }
+ if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) {
+ if (!quiet)
+ printf("FTP Data channel is Private (encrypted)\n");
+ ftp_dpl = FPL_PRV;
+ } else
+ printf("?Unable to enable encryption on data channel\n");
+ } else {
+ ftp_cpl = FPL_SAF;
+ }
+ }
+ if (!connected)
+ goto fail;
+ }
+#endif /* FTP_SECURITY */
+ if (ftp_log) /* ^^^ */
+ ftp_login(remote);
+
+ if (!connected)
+ goto fail;
+
+#ifdef CKLOGDIAL
+ dologftp();
+#endif /* CKLOGDIAL */
+#ifdef OS2
+ DialerSend(OPT_KERMIT_CONNECT, 0);
+#endif /* OS2 */
+ passivemode = ftp_psv;
+ sendport = ftp_spc;
+ mdtmok = 1;
+ sizeok = 1;
+ stouarg = 1;
+ typesent = 0;
+
+ if (ucbuf == NULL) {
+ actualbuf = DEFAULT_PBSZ;
+ while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL)
+ actualbuf >>= 2;
+ }
+ if (!maxbuf)
+ ucbufsiz = actualbuf - FUDGE_FACTOR;
+ debug(F101,"ftpopen ucbufsiz","",ucbufsiz);
+ return(1);
+ }
+ fail:
+ printf("?Can't FTP connect to %s:%s\n",remote,service);
+ ftpcode = -1;
+ return(0);
+}
+
+#ifdef CK_SSL
+int
+ssl_auth() {
+ int i;
+ char* p;
+
+ if (ssl_debug_flag) {
+ fprintf(stderr,"SSL DEBUG ACTIVE\n");
+ fflush(stderr);
+ /* for the moment I want the output on screen */
+ }
+ if (ssl_ftp_data_con != NULL) {
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_con = NULL;
+ }
+ if (ssl_ftp_con != NULL) {
+ SSL_free(ssl_ftp_con);
+ ssl_ftp_con=NULL;
+ }
+ if (ssl_ftp_ctx != NULL) {
+ SSL_CTX_free(ssl_ftp_ctx);
+ ssl_ftp_ctx = NULL;
+ }
+
+ /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+ * was added to OpenSSL 0.9.6e and 0.9.7. It does not exist in previous
+ * versions
+ */
+#ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+#define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L
+#endif
+ if (auth_type && !strcmp(auth_type,"TLS")) {
+ ssl_ftp_ctx=SSL_CTX_new(SSLv3_client_method());
+ if (!ssl_ftp_ctx)
+ return(0);
+ SSL_CTX_set_options(ssl_ftp_ctx,
+ SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
+ );
+ } else {
+ ssl_ftp_ctx = SSL_CTX_new(ftp_bug_use_ssl_v2 ? SSLv23_client_method() :
+ SSLv3_client_method());
+ if (!ssl_ftp_ctx)
+ return(0);
+ SSL_CTX_set_options(ssl_ftp_ctx,
+ (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
+ SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
+ );
+ }
+ SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
+ (pem_password_cb *)ssl_passwd_callback);
+ SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
+ SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
+
+#ifdef OS2
+#ifdef NT
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ {
+ char path[CKMAXPATH];
+ extern char exedir[];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,
+ (char *)GetAppData(1),"kermit 95/certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,
+ (char *)GetAppData(0),"kermit 95/certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
+ "kermit 95/ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+
+ ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
+ "kermit 95/ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+ }
+#else /* NT */
+ /* The defaults in the SSL crypto library are not appropriate for OS/2 */
+ {
+
+ char path[CKMAXPATH];
+ extern char exedir[];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
+ if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
+ debug(F110,"ftp ssl_auth unable to load path",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",path);
+ }
+ }
+#endif /* NT */
+#else /* OS2 */
+ SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
+#endif /* OS2 */
+
+ if (ssl_verify_file &&
+ SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
+ debug(F110,
+ "ftp ssl auth unable to load ssl_verify_file",
+ ssl_verify_file,
+ 0
+ );
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
+ }
+ if (ssl_verify_dir &&
+ SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
+ debug(F110,
+ "ftp ssl auth unable to load ssl_verify_dir",
+ ssl_verify_dir,
+ 0
+ );
+ if (ssl_debug_flag)
+ printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
+ }
+
+ /* set up the new CRL Store */
+ crl_store = (X509_STORE *)X509_STORE_new();
+ if (crl_store) {
+#ifdef OS2
+ char path[CKMAXPATH];
+ extern char exedir[];
+
+ ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ftp ssl auth unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,
+ (char *)GetAppData(1),"kermit 95/crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ftp ssl auth unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,
+ (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
+ debug(F110,"ftp ssl auth unable to load dir",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",path);
+ }
+#endif /* NT */
+
+ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ftp ssl auth unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+#ifdef NT
+ ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
+ "kermit 95/ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ftp ssl auth unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+ ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
+ "kermit 95/ca_crls.pem",NULL,NULL);
+ if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
+ debug(F110,"ftp ssl auth unable to load file",path,0);
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",path);
+ }
+#endif /* NT */
+#endif /* OS2 */
+
+ if (ssl_crl_file || ssl_crl_dir) {
+ if (ssl_crl_file &&
+ X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
+ debug(F110,
+ "ftp ssl auth unable to load ssl_crl_file",
+ ssl_crl_file,
+ 0
+ );
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
+ }
+ if (ssl_crl_dir &&
+ X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
+ debug(F110,
+ "ftp ssl auth unable to load ssl_crl_dir",
+ ssl_crl_dir,
+ 0
+ );
+ if (ssl_debug_flag)
+ printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
+ }
+ } else {
+ X509_STORE_set_default_paths(crl_store);
+ }
+ }
+ SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
+ ssl_client_verify_callback);
+ ssl_verify_depth = -1;
+ ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
+ tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
+ SSL_set_fd(ssl_ftp_con,csocket);
+ SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
+ if (ssl_cipher_list) {
+ SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
+ } else {
+ char * p;
+ if (p = getenv("SSL_CIPHER")) {
+ SSL_set_cipher_list(ssl_ftp_con,p);
+ } else {
+ SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
+ }
+ }
+ if (ssl_debug_flag) {
+ fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
+ fflush(stderr);
+ }
+ if (SSL_connect(ssl_ftp_con) <= 0) {
+ static char errbuf[1024];
+ ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
+ ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
+ fprintf(stderr,"%s\n", errbuf);
+ fflush(stderr);
+ ssl_ftp_active_flag=0;
+ SSL_free(ssl_ftp_con);
+ ssl_ftp_con = NULL;
+ } else {
+ ssl_ftp_active_flag = 1;
+
+ if (!ssl_certsok_flag && !tls_is_krb5(1)) {
+ char *subject = ssl_get_subject_name(ssl_ftp_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ debug(F110,"ssl_auth","[SSL - FAILED]",0);
+ return(ssl_ftp_active_flag = 0);
+ } else {
+ if (uq_ok("Warning: Server didn't provide a certificate\n",
+ "Continue? (Y/N)",3,NULL,0) <= 0) {
+ debug(F110, "ssl_auth","[SSL - FAILED]",0);
+ return(ssl_ftp_active_flag = 0);
+ }
+ }
+ } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
+ debug(F110,"ssl_auth","[SSL - FAILED]",0);
+ return(ssl_ftp_active_flag = 0);
+ }
+ }
+ debug(F110,"ssl_auth","[SSL - OK]",0);
+ ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
+ }
+ if (ssl_debug_flag) {
+ fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
+ fflush(stderr);
+ }
+ return(ssl_ftp_active_flag);
+}
+#endif /* CK_SSL */
+
+static sigtype
+cmdcancel(sig) int sig; {
+#ifdef OS2
+ /* In Unix we "chain" to trap(), which prints this */
+ printf("^C...\n");
+#endif /* OS2 */
+ debug(F100,"ftp cmdcancel caught SIGINT ","",0);
+ fflush(stdout);
+ secure_getc(0,1); /* Initialize net input buffers */
+ cancelfile++;
+ cancelgroup++;
+ mlsreset();
+#ifndef OS2
+#ifdef FTP_PROXY
+ if (ptflag) /* proxy... */
+ longjmp(ptcancel,1);
+#endif /* FTP_PROXY */
+ debug(F100,"ftp cmdcancel chain to trap()...","",0);
+ trap(SIGINT);
+ /* NOTREACHED */
+ debug(F100,"ftp cmdcancel return from trap()...","",0);
+#else
+ debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
+ PostCtrlCSem();
+#endif /* OS2 */
+}
+
+static int
+#ifdef CK_ANSIC
+scommand(char * s) /* Was secure_command() */
+#else
+scommand(s) char * s;
+#endif /* CK_ANSIC */
+{
+ int length = 0, len2;
+ char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ int error, rc;
+ length = strlen(s) + 2;
+ length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
+ rc = SSL_write(ssl_ftp_con,out,length);
+ error = SSL_get_error(ssl_ftp_con,rc);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ return(1);
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_SYSCALL:
+#ifdef NT
+ {
+ int gle = GetLastError();
+ }
+#endif /* NT */
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ lostpeer();
+ }
+ return(0);
+ }
+#endif /* CK_SSL */
+
+ if (auth_type && ftp_cpl != FPL_CLR) {
+#ifdef FTP_SRP
+ if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
+ if ((length = srp_encode(ftp_cpl == FPL_PRV,
+ (CHAR *)s,
+ (CHAR *)out,
+ strlen(s))) < 0) {
+ fprintf(stderr, "SRP failed to encode message\n");
+ return(0);
+ }
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ if (ck_krb4_is_installed() &&
+ (strcmp(auth_type, "KERBEROS_V4") == 0)) {
+ if (ftp_cpl == FPL_PRV) {
+ length =
+ krb_mk_priv((CHAR *)s, (CHAR *)out,
+ strlen(s), ftp_sched,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &myctladdr, &hisctladdr);
+ } else {
+ length =
+ krb_mk_safe((CHAR *)s,
+ (CHAR *)out,
+ strlen(s),
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &myctladdr, &hisctladdr);
+ }
+ if (length == -1) {
+ fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
+ ftp_cpl == FPL_PRV ? "priv" : "safe");
+ return(0);
+ }
+ }
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ /* Scommand (based on level) */
+ if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+ in_buf.value = s;
+ in_buf.length = strlen(s) + 1;
+ maj_stat = gss_seal(&min_stat, gcontext,
+ (ftp_cpl==FPL_PRV), /* private */
+ 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,
+ (ftp_cpl==FPL_PRV)?
+ "gss_seal ENC didn't complete":
+ "gss_seal MIC didn't complete");
+ } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
+ fprintf(stderr, "GSSAPI didn't encrypt message");
+ } else {
+ if (ftp_deb)
+ fprintf(stderr, "sealed (%s) %d bytes\n",
+ ftp_cpl==FPL_PRV?"ENC":"MIC",
+ out_buf.length);
+ memcpy(out, out_buf.value,
+ length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+ }
+ }
+#endif /* FTP_GSSAPI */
+ /* Other auth types go here ... */
+
+ len2 = FTP_BUFSIZ;
+ if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
+ length, &len2, RADIX_ENCODE))
+ ) {
+ fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
+ radix_error(kerror));
+ return(0);
+ }
+ if (ftp_deb)
+ fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
+ len2 = ckmakmsg(out,
+ FTP_BUFSIZ,
+ ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
+ in,
+ "\r\n",
+ NULL
+ );
+ send(csocket,(SENDARG2TYPE)out,len2,0);
+ } else {
+ char out[FTP_BUFSIZ];
+ int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
+ send(csocket,(SENDARG2TYPE)out,len,0);
+ }
+ return(1);
+}
+
+static int
+mygetc() {
+ static char inbuf[4096];
+ static int bp = 0, ep = 0;
+ int rc;
+
+ if (bp == ep) {
+ bp = ep = 0;
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ int error;
+ rc = SSL_read(ssl_ftp_con,inbuf,4096);
+ error = SSL_get_error(ssl_ftp_con,rc);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ return(0);
+ case SSL_ERROR_SYSCALL:
+ if (rc == 0) { /* EOF */
+ break;
+ } else {
+#ifdef NT
+ int gle = GetLastError();
+#endif /* NT */
+ break;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ break;
+ }
+ } else
+#endif /* CK_SSL */
+ rc = recv(csocket,(char *)inbuf,4096,0);
+ if (rc <= 0)
+ return(EOF);
+ ep = rc;
+ }
+ return(inbuf[bp++]);
+}
+
+/* x l a t e c -- Translate a character */
+/*
+ Call with:
+ fc = Function code: 0 = translate, 1 = initialize.
+ c = Character (as int).
+ incs = Index of charset to translate from.
+ outcs = Index of charset to translate to.
+
+ Returns:
+ 0: OK
+ -1: Error
+*/
+static int
+xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
+#ifdef NOCSETS
+ return(c);
+#else
+ static char buf[128];
+ static int cx;
+ int c0, c1;
+
+ if (fc == 1) { /* Initialize */
+ cx = 0; /* Catch-up buffer write index */
+ xgnbp = buf; /* Catch-up buffer read pointer */
+ buf[0] = NUL; /* Buffer is empty */
+ return(0);
+ }
+ if (cx >= 127) { /* Catch-up buffer full */
+ debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
+ printf("?Translation buffer overflow\n");
+ return(-1);
+ }
+ /* Add char to buffer. */
+ /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
+
+ debug(F000,"xlatec buf",ckitoa(cx),c);
+ buf[cx++] = c;
+ buf[cx] = NUL;
+
+ while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
+ if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
+ return(-1);
+ }
+ /* If we're caught up, reinitialize the buffer */
+ return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
+#endif /* NOCSETS */
+}
+
+
+/* p a r s e f e a t */
+
+/* Note: for convenience we align keyword values with table indices */
+/* If you need to insert a new keyword, adjust the SFT_xxx definitions */
+
+static struct keytab feattab[] = {
+ { "$$$$", 0, 0 }, /* Dummy for sfttab[0] */
+ { "AUTH", SFT_AUTH, 0 },
+ { "LANG", SFT_LANG, 0 },
+ { "MDTM", SFT_MDTM, 0 },
+ { "MLST", SFT_MLST, 0 },
+ { "PBSZ", SFT_PBSZ, 0 },
+ { "PROT", SFT_PROT, 0 },
+ { "REST", SFT_REST, 0 },
+ { "SIZE", SFT_SIZE, 0 },
+ { "TVFS", SFT_TVFS, 0 },
+ { "UTF8", SFT_UTF8, 0 }
+};
+static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
+
+#define FACT_CSET 1
+#define FACT_CREA 2
+#define FACT_LANG 3
+#define FACT_MTYP 4
+#define FACT_MDTM 5
+#define FACT_PERM 6
+#define FACT_SIZE 7
+#define FACT_TYPE 8
+#define FACT_UNIQ 9
+
+static struct keytab facttab[] = {
+ { "CHARSET", FACT_CSET, 0 },
+ { "CREATE", FACT_CREA, 0 },
+ { "LANG", FACT_LANG, 0 },
+ { "MEDIA-TYPE", FACT_MTYP, 0 },
+ { "MODIFY", FACT_MDTM, 0 },
+ { "PERM", FACT_PERM, 0 },
+ { "SIZE", FACT_SIZE, 0 },
+ { "TYPE", FACT_TYPE, 0 },
+ { "UNIQUE", FACT_UNIQ, 0 }
+};
+static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
+
+static struct keytab ftyptab[] = {
+ { "CDIR", FTYP_CDIR, 0 },
+ { "DIR", FTYP_DIR, 0 },
+ { "FILE", FTYP_FILE, 0 },
+ { "PDIR", FTYP_PDIR, 0 }
+};
+static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
+
+static VOID
+parsefeat(s) char * s; { /* Parse a FEATURE response */
+ char kwbuf[8];
+ int i, x;
+ if (!s) return;
+ if (!*s) return;
+ while (*s < '!')
+ s++;
+ for (i = 0; i < 4; i++) {
+ if (s[i] < '!')
+ break;
+ kwbuf[i] = s[i];
+ }
+ if (s[i] && s[i] != SP)
+ return;
+ kwbuf[i] = NUL;
+ /* xlookup requires a full (but case independent) match */
+ i = xlookup(feattab,kwbuf,nfeattab,&x);
+ debug(F111,"ftp parsefeat",s,i);
+ if (i < 0 || i > 15)
+ return;
+
+ switch (i) {
+ case SFT_MDTM: /* Controlled by ENABLE/DISABLE */
+ sfttab[i] = mdtmok;
+ if (mdtmok) sfttab[0]++;
+ break;
+ case SFT_MLST: /* ditto */
+ sfttab[i] = mlstok;
+ if (mlstok) sfttab[0]++;
+ break;
+ case SFT_SIZE: /* ditto */
+ sfttab[i] = sizeok;
+ if (sizeok) sfttab[0]++;
+ break;
+ case SFT_AUTH: /* ditto */
+ sfttab[i] = ftp_aut;
+ if (ftp_aut) sfttab[0]++;
+ break;
+ default: /* Others */
+ sfttab[0]++;
+ sfttab[i]++;
+ }
+}
+
+static char *
+parsefacts(s) char * s; { /* Parse MLS[DT] File Facts */
+ char * p;
+ int i, j, x;
+ if (!s) return(NULL);
+ if (!*s) return(NULL);
+
+ /* Maybe we should make a copy of s so we can poke it... */
+
+ while ((p = ckstrchr(s,'='))) {
+ *p = NUL; /* s points to fact */
+ i = xlookup(facttab,s,nfacttab,&x);
+ debug(F111,"ftp parsefact fact",s,i);
+ *p = '=';
+ s = p+1; /* Now s points to arg */
+ p = ckstrchr(s,';');
+ if (!p)
+ p = ckstrchr(s,SP);
+ if (!p) {
+ debug(F110,"ftp parsefact end-of-val search fail",s,0);
+ break;
+ }
+ *p = NUL;
+ debug(F110,"ftp parsefact valu",s,0);
+ switch (i) {
+ case FACT_CSET: /* Ignore these for now */
+ case FACT_CREA:
+ case FACT_LANG:
+ case FACT_PERM:
+ case FACT_MTYP:
+ case FACT_UNIQ:
+ break;
+ case FACT_MDTM: /* Modtime */
+ makestr(&havemdtm,s);
+ debug(F110,"ftp parsefact mdtm",havemdtm,0);
+ break;
+ case FACT_SIZE: /* Size */
+ havesize = atol(s);
+ debug(F101,"ftp parsefact size","",havesize);
+ break;
+ case FACT_TYPE: /* Type */
+ j = xlookup(ftyptab,s,nftyptab,NULL);
+ debug(F111,"ftp parsefact type",s,j);
+ havetype = (j < 1) ? 0 : j;
+ break;
+ }
+ *p = ';';
+ s = p+1; /* s points next fact or name */
+ }
+ while (*s == SP) /* Skip past spaces. */
+ s++;
+ if (!*s) /* Make sure we still have a name */
+ s = NULL;
+ debug(F110,"ftp parsefact name",s,0);
+ return(s);
+}
+
+/* g e t r e p l y -- (to an FTP command sent to server) */
+
+/* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
+
+static int
+getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
+ /* lcs, rcs, vbm parameters as in ftpcmd() */
+ register int i, c, n;
+ register int dig;
+ register char *cp;
+ int xlate = 0;
+ int count = 0;
+ int auth = 0;
+ int originalcode = 0, continuation = 0;
+ sig_t oldintr;
+ int pflag = 0;
+ char *pt = pasv;
+ char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
+ int safe = 0;
+ int xquiet = 0;
+
+ auth = (fc == GRF_AUTH);
+
+#ifndef NOCSETS
+ debug(F101,"ftp getreply lcs","",lcs);
+ debug(F101,"ftp getreply rcs","",rcs);
+ if (lcs > -1 && rcs > -1 && lcs != rcs) {
+ xlate = 1;
+ initxlate(rcs,lcs);
+ xlatec(1,0,rcs,lcs);
+ }
+#endif /* NOCSETS */
+ debug(F101,"ftp getreply fc","",fc);
+
+ if (quiet)
+ xquiet = 1;
+ if (vbm == 9) {
+ xquiet = 1;
+ vbm = 0;
+ }
+ if (ftp_deb) /* DEBUG */
+ vbm = 1;
+ else if (quiet || dpyactive) /* QUIET or File Transfer Active */
+ vbm = 0;
+ else if (vbm < 0) /* VERBOSE */
+ vbm = ftp_vbm;
+
+ ibuf[0] = '\0';
+ if (reply_parse)
+ reply_ptr = reply_buf;
+ havesigint = 0;
+ oldintr = signal(SIGINT, cmdcancel);
+ for (count = 0;; count++) {
+ obuf[0] = '\0';
+ dig = n = ftpcode = i = 0;
+ cp = ftp_reply_str;
+ while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
+ if (c == IAC) { /* Handle telnet commands */
+ switch (c = mygetc()) {
+ case WILL:
+ case WONT:
+ c = mygetc();
+ obuf[0] = IAC;
+ obuf[1] = DONT;
+ obuf[2] = c;
+ obuf[3] = NUL;
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ int error, rc;
+ rc = SSL_write(ssl_ftp_con,obuf,3);
+ error = SSL_get_error(ssl_ftp_con,rc);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ return(0);
+ case SSL_ERROR_SYSCALL:
+ if (rc == 0) { /* EOF */
+ break;
+ } else {
+#ifdef NT
+ int gle = GetLastError();
+#endif /* NT */
+ break;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ break;
+ }
+ } else
+#endif /* CK_SSL */
+ send(csocket,(SENDARG2TYPE)obuf,3,0);
+ break;
+ case DO:
+ case DONT:
+ c = mygetc();
+ obuf[0] = IAC;
+ obuf[1] = WONT;
+ obuf[2] = c;
+ obuf[3] = NUL;
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ int error, rc;
+ rc = SSL_write(ssl_ftp_con,obuf,3);
+ error = SSL_get_error(ssl_ftp_con,rc);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ signal(SIGINT,oldintr);
+ return(0);
+ case SSL_ERROR_SYSCALL:
+ if (rc == 0) { /* EOF */
+ break;
+ } else {
+#ifdef NT
+ int gle = GetLastError();
+#endif /* NT */
+ break;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ break;
+ }
+ } else
+#endif /* CK_SSL */
+ send(csocket,(SENDARG2TYPE)obuf,3,0);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ signal(SIGINT,oldintr);
+ ftpcode = 221;
+ debug(F101,"ftp getreply EOF","",ftpcode);
+ return(0);
+ }
+ lostpeer();
+ if (!xquiet) {
+ if (ftp_deb)
+ printf("421 ");
+ printf(
+ "Service not available, connection closed by server\n");
+ fflush(stdout);
+ }
+ signal(SIGINT,oldintr);
+ ftpcode = 421;
+ debug(F101,"ftp getreply EOF","",ftpcode);
+ return(4);
+ }
+ if (n == 0) { /* First digit */
+ n = c; /* Save it */
+ }
+ if (auth_type &&
+#ifdef CK_SSL
+ !ssl_ftp_active_flag &&
+#endif /* CK_SSL */
+ !ibuf[0] && (n == '6' || continuation)) {
+ if (c != '\r' && dig > 4)
+ obuf[i++] = c;
+ } else {
+ if (auth_type &&
+#ifdef CK_SSL
+ !ssl_ftp_active_flag &&
+#endif /* CK_SSL */
+ !ibuf[0] && dig == 1 && vbm)
+ printf("Unauthenticated reply received from server:\n");
+ if (reply_parse) {
+ *reply_ptr++ = c;
+ *reply_ptr = NUL;
+ }
+ if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
+ ftp_cmdlin < 2) {
+ if ((c != '\r') &&
+ (ftp_deb || ((vbm || (!auth && n == '5')) &&
+ (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
+ )))))
+ {
+#ifdef FTP_PROXY
+ if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
+ printf("%s:",ftp_host);
+#endif /* FTP_PROXY */
+
+ if (!xquiet) {
+#ifdef NOCSETS
+ printf("%c",c);
+#else
+ if (xlate) {
+ xlatec(0,c,rcs,lcs);
+ } else {
+ printf("%c",c);
+ }
+#endif /* NOCSETS */
+ }
+ }
+ }
+ }
+ if (auth_type &&
+#ifdef CK_SSL
+ !ssl_ftp_active_flag &&
+#endif /* CK_SSL */
+ !ibuf[0] && n != '6')
+ continue;
+ if (dig < 4 && isdigit(c))
+ ftpcode = ftpcode * 10 + (c - '0');
+ if (!pflag && ftpcode == 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)
+ ftpcode = 0;
+ continuation++;
+ }
+ if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
+ *cp++ = c;
+ *cp = NUL;
+ }
+ }
+ if (deblog ||
+#ifdef COMMENT
+/*
+ Sometimes we need to print the server reply. printlines is nonzero for any
+ command where the results are sent back on the control connection rather
+ than the data connection, e.g. STAT. In the TOPS-20 case, each file line
+ has ftpcode 213. But if you do this with a UNIX server, it sends "213-Start
+ STAT", <line with ftpcode == 0>, "213-End" or somesuch. So when printlines
+ is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
+ lines from UNIX. Further experimentation needed with other servers. Of
+ course RFC959 is mute as to the format of the server reply.
+
+ 'printlines' is also true for PWD and BYE.
+*/
+ (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
+#else
+/* No, we can't be that clever -- it breaks other things like RPWD... */
+ (printlines &&
+ (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
+#endif /* COMMENT */
+ ) {
+ char * q = cp;
+ char *r = ftp_reply_str;
+ *q-- = NUL; /* NUL-terminate */
+ while (*q < '!' && q > r) /* Strip CR, etc */
+ *q-- = NUL;
+ if (!ftp_deb && printlines) { /* If printing */
+ if (ftpcode != 0) /* strip ftpcode if any */
+ r += 4;
+#ifdef NOCSETS
+ printf("%s\n",r); /* and print */
+#else
+ if (!xlate) {
+ printf("%s\n",r);
+ } else { /* Translating */
+ xgnbp = r; /* Set up strgetc() */
+ while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
+ if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
+ signal(SIGINT,oldintr);
+ return(-1);
+ }
+ }
+ printf("\n");
+ }
+#endif /* NOCSETS */
+ }
+ }
+ debug(F110,"FTP RCVD ",ftp_reply_str,0);
+
+ if (fc == GRF_FEAT) { /* Parsing FEAT command response? */
+ if (count == 0 && n == '2') {
+ int i; /* (Re)-init server FEATure table */
+ debug(F100,"ftp getreply clearing feature table","",0);
+ for (i = 0; i < 16; i++)
+ sfttab[i] = 0;
+ } else {
+ parsefeat((char *)ftp_reply_str);
+ }
+ }
+ if (auth_type &&
+#ifdef CK_SSL
+ !ssl_ftp_active_flag &&
+#endif /* CK_SSL */
+ !ibuf[0] && n != '6') {
+ signal(SIGINT,oldintr);
+ return(getreply(expecteof,lcs,rcs,vbm,auth));
+ }
+ ibuf[0] = obuf[i] = '\0';
+ if (ftpcode && n == '6')
+ if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
+ printf("Unknown reply: %d %s\n", ftpcode, obuf);
+ n = '5';
+ } else safe = (ftpcode == 631);
+ if (obuf[0] /* if there is a string to decode */
+#ifdef CK_SSL
+ && !ssl_ftp_active_flag /* and not SSL/TLS */
+#endif /* CK_SSL */
+ ) {
+ if (!auth_type) {
+ printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
+ n = '5';
+ }
+#ifndef CK_ENCRYPTION
+ else if (ftpcode == 632) {
+ printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
+ n = '5';
+ }
+#endif /* CK_ENCRYPTION */
+#ifdef NOCONFIDENTIAL
+ else if (ftpcode == 633) {
+ printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
+ n = '5';
+ }
+#endif /* NOCONFIDENTIAL */
+ else {
+ int len = FTP_BUFSIZ;
+ if ((kerror = radix_encode((CHAR *)obuf,
+ (CHAR *)ibuf,
+ 0,
+ &len,
+ RADIX_DECODE))
+ ) {
+ printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
+ ftpcode, radix_error(kerror), obuf);
+ n = '5';
+ }
+#ifdef FTP_SRP
+ else if (strcmp(auth_type, "SRP") == 0) {
+ int outlen;
+ outlen = srp_decode(!safe, (CHAR *)ibuf,
+ (CHAR *) ibuf, len);
+ if (outlen < 0) {
+ printf("Warning: %d reply %s!\n",
+ ftpcode, safe ? "modified" : "garbled");
+ n = '5';
+ } else {
+ ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
+ if (ftp_deb)
+ printf("%c:", safe ? 'S' : 'P');
+ continue;
+ }
+ }
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+ if (safe) {
+ kerror = krb_rd_safe((CHAR *)ibuf, len,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &hisctladdr,
+ &myctladdr,
+ &ftp_msg_data
+ );
+ } else {
+ kerror = krb_rd_priv((CHAR *)ibuf, len,
+ ftp_sched,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &hisctladdr,
+ &myctladdr,
+ &ftp_msg_data
+ );
+ }
+ if (kerror != KSUCCESS) {
+ printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
+ safe ? "modified" : "garbled",
+ safe ? "safe" : "priv",
+ krb_get_err_text(kerror));
+ n = '5';
+ } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
+ kerror = KFAILURE;
+ n = '5';
+ printf("reply data too large for buffer\n");
+ } else {
+ if (ftp_deb)
+ printf("%c:", safe ? 'S' : 'P');
+ memcpy(ibuf,ftp_msg_data.app_data,
+ ftp_msg_data.app_length);
+ ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
+ FTP_BUFSIZ - ftp_msg_data.app_length);
+ continue;
+ }
+ }
+#endif /* FTP_KRB4 */
+#ifdef FTP_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");
+ n = '5';
+ } else {
+ memcpy(ibuf, msg_buf.value, msg_buf.length);
+ ckstrncpy(&ibuf[msg_buf.length], "\r\n",
+ FTP_BUFSIZ-msg_buf.length);
+ gss_release_buffer(&min_stat,&msg_buf);
+ if (ftp_deb)
+ printf("%c:", safe ? 'S' : 'P');
+ continue;
+ }
+ }
+#endif /* FTP_GSSAPI */
+ /* Other auth types go here... */
+ }
+ } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
+ !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
+#ifdef NOCSETS
+ printf("%c",c);
+#else
+ if (xlate) {
+ xlatec(0,c,rcs,lcs);
+ } else {
+ printf("%c",c);
+ }
+#endif /* NOCSETS */
+ fflush (stdout);
+ }
+ if (continuation && ftpcode != originalcode) {
+ if (originalcode == 0)
+ originalcode = ftpcode;
+ continue;
+ }
+ *cp = '\0';
+ if (n != '1')
+ cpend = 0;
+ signal(SIGINT,oldintr);
+ if (ftpcode == 421 || originalcode == 421) {
+ lostpeer();
+ if (!xquiet && !ftp_deb)
+ printf("%s\n",reply_buf);
+ }
+ if ((cancelfile != 0) &&
+#ifndef ULTRIX3
+ /* Ultrix 3.0 cc objects violently to this clause */
+ (oldintr != cmdcancel) &&
+#endif /* ULTRIX3 */
+ (oldintr != SIG_IGN)) {
+ if (oldintr)
+ (*oldintr)(SIGINT);
+ }
+ if (reply_parse) {
+ *reply_ptr = '\0';
+ if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
+ reply_parse = reply_ptr + strlen(reply_parse);
+ if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
+ *reply_ptr = '\0';
+ } else
+ reply_parse = reply_ptr;
+ }
+ while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
+ *cp-- = NUL;
+ debug(F111,"ftp getreply",ftp_reply_str,n - '0');
+ return(n - '0');
+ } /* for (;;) */
+}
+
+#ifdef BSDSELECT
+static int
+#ifdef CK_ANSIC
+empty(fd_set * mask, int sec)
+#else
+empty(mask, sec) fd_set * mask; int sec;
+#endif /* CK_ANSIC */
+{
+ struct timeval t;
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0L;
+ debug(F100,"ftp empty calling select...","",0);
+#ifdef INTSELECT
+ x = select(32, (int *)mask, NULL, NULL, &t);
+#else
+ x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
+#endif /* INTSELECT */
+ debug(F101,"ftp empty select","",x);
+ return(x);
+}
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+static int
+empty(mask, cnt, sec) int * mask, sec;
+ int cnt;
+{
+ return(select(mask,cnt,0,0,sec*1000));
+}
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+
+static sigtype
+cancelsend(sig) int sig; {
+ havesigint++;
+ cancelgroup++;
+ cancelfile = 0;
+ printf(" Canceled...\n");
+ secure_getc(0,1); /* Initialize net input buffers */
+ debug(F100,"ftp cancelsend caught SIGINT ","",0);
+ fflush(stdout);
+#ifndef OS2
+ longjmp(sendcancel, 1);
+#else
+ PostCtrlCSem();
+#endif /* OS2 */
+}
+
+static VOID
+#ifdef CK_ANSIC
+secure_error(char *fmt, ...)
+#else
+/* VARARGS1 */
+secure_error(fmt, p1, p2, p3, p4, p5)
+ char *fmt; int p1, p2, p3, p4, p5;
+#endif /* CK_ANSIC */
+{
+#ifdef CK_ANSIC
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+#else
+ fprintf(stderr, fmt, p1, p2, p3, p4, p5);
+#endif
+ fprintf(stderr, "\n");
+}
+
+/*
+ * 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.
+ */
+static VOID
+changetype(newtype, show) int newtype, show; {
+ int rc;
+ char * s;
+
+ if ((newtype == curtype) && typesent++)
+ return;
+ switch (newtype) {
+ case FTT_ASC:
+ s = "A";
+ break;
+ case FTT_BIN:
+ s = "I";
+ break;
+ case FTT_TEN:
+ s = "L 8";
+ break;
+ default:
+ s = "I";
+ break;
+ }
+ rc = ftpcmd("TYPE",s,-1,-1,show);
+ if (rc == REPLY_COMPLETE)
+ curtype = newtype;
+}
+
+/* PUT a file. Returns -1 on error, 0 on success, 1 if file skipped */
+
+static VOID
+#ifdef CK_ANSIC
+doftpsend(void * threadinfo)
+#else
+doftpsend(threadinfo) VOID * threadinfo;
+#endif
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "doftpsend called with threadinfo block","", 0);
+ } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ if (initconn()) {
+#ifndef NOHTTP
+ int y = -1;
+ debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy);
+
+ /* If the connection failed and we are using an HTTP Proxy
+ * and the reason for the failure was an authentication
+ * error, then we need to give the user to ability to
+ * enter a username and password, just like a browser.
+ *
+ * I tried to do all of this within the netopen() call
+ * but it is much too much work.
+ */
+ while (y != 0 && tcp_http_proxy != NULL ) {
+
+ if (tcp_http_proxy_errno == 401 ||
+ tcp_http_proxy_errno == 407 ) {
+ char uid[UIDBUFLEN];
+ char pwd[PWDSIZ];
+ struct txtbox tb[2];
+ int ok;
+
+ tb[0].t_buf = uid;
+ tb[0].t_len = UIDBUFLEN;
+ tb[0].t_lbl = "Proxy Userid: ";
+ tb[0].t_dflt = NULL;
+ tb[0].t_echo = 1;
+ tb[1].t_buf = pwd;
+ tb[1].t_len = 256;
+ tb[1].t_lbl = "Proxy Passphrase: ";
+ tb[1].t_dflt = NULL;
+ tb[1].t_echo = 2;
+
+ ok = uq_mtxt("Proxy Server Authentication Required\n",
+ NULL, 2, tb);
+ if (ok && uid[0]) {
+ char * proxy_user, * proxy_pwd;
+
+ proxy_user = tcp_http_proxy_user;
+ proxy_pwd = tcp_http_proxy_pwd;
+
+ tcp_http_proxy_user = uid;
+ tcp_http_proxy_pwd = pwd;
+
+ y = initconn();
+
+ debug(F101,"doftpsend","initconn",y);
+ memset(pwd,0,PWDSIZ);
+ tcp_http_proxy_user = proxy_user;
+ tcp_http_proxy_pwd = proxy_pwd;
+ } else
+ break;
+ } else
+ break;
+ }
+
+ if ( y != 0 ) {
+#endif /* NOHTTP */
+ signal(SIGINT, ftpsnd.oldintr);
+#ifdef SIGPIPE
+ if (ftpsnd.oldintp)
+ signal(SIGPIPE, ftpsnd.oldintp);
+#endif /* SIGPIPE */
+ ftpcode = -1;
+ zclose(ZIFILE);
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+#ifndef NOHTTP
+ }
+#endif /* NOHTTP */
+ }
+ ftpsndret = 0;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+}
+
+static VOID
+#ifdef CK_ANSIC
+failftpsend(void * threadinfo)
+#else
+failftpsend(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ while (cpend) {
+ ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
+ debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
+ }
+ if (data >= 0) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = -1;
+ }
+ if (ftpsnd.oldintr)
+ signal(SIGINT,ftpsnd.oldintr);
+#ifdef SIGPIPE
+ if (ftpsnd.oldintp)
+ signal(SIGPIPE,ftpsnd.oldintp);
+#endif /* SIGPIPE */
+ ftpcode = -1;
+#ifndef OS2
+ /* TEST ME IN K95 */
+ if (havesigint) {
+ havesigint = 0;
+ debug(F100,"ftp failftpsend chain to trap()...","",0);
+ if (ftpsnd.oldintr != SIG_IGN)
+ (*ftpsnd.oldintr)(SIGINT);
+ /* NOTREACHED (I hope!) */
+ debug(F100,"ftp failftpsend return from trap()...","",0);
+ }
+#endif /* OS2 */
+}
+
+static VOID
+#ifdef CK_ANSIC
+failftpsend2(void * threadinfo)
+#else
+failftpsend2(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
+ tfc += ffc;
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+#endif /* GFTIMER */
+ zclose(ZIFILE);
+#ifdef PIPESEND
+ if (sndfilter)
+ pipesend = 0;
+#endif /* PIPESEND */
+ signal(SIGINT, ftpsnd.oldintr);
+#ifdef SIGPIPE
+ if (ftpsnd.oldintp)
+ signal(SIGPIPE, ftpsnd.oldintp);
+#endif /* SIGPIPE */
+ if (!cpend) {
+ ftpcode = -1;
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (data >= 0) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = -1;
+ }
+ if (dout) {
+#ifdef TCPIPLIB
+ socket_close(dout);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(dout, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(dout);
+#endif /* TCPIPLIB */
+ }
+ ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
+ ftpcode = -1;
+ ftpsndret = -1;
+
+#ifndef OS2
+ /* TEST ME IN K95 */
+ if (havesigint) {
+ havesigint = 0;
+ debug(F100,"ftp failftpsend2 chain to trap()...","",0);
+ if (ftpsnd.oldintr != SIG_IGN)
+ (*ftpsnd.oldintr)(SIGINT);
+ /* NOTREACHED (I hope!) */
+ debug(F100,"ftp failftpsend2 return from trap()...","",0);
+ }
+#endif /* OS2 */
+}
+
+static VOID
+#ifdef CK_ANSIC
+doftpsend2(void * threadinfo)
+#else
+doftpsend2(threadinfo) VOID * threadinfo;
+#endif
+{
+ register int c, d = 0;
+ int n, t, x, notafile, unique = 0;
+ char *buf, *bufp;
+
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "doftpsend2 called with threadinfo block","", 0);
+ } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ buf = ftpsndbuf; /* (not on stack) */
+
+ unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
+ notafile = sndarray || pipesend;
+
+#ifdef FTP_RESTART
+ if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
+ char * p;
+ changetype(FTT_BIN,0); /* Change to binary */
+
+ /* Ask for remote file's size */
+ x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
+
+ if (x == REPLY_COMPLETE) { /* Have ftpsnd.reply */
+ p = &ftp_reply_str[4]; /* Parse it */
+ while (isdigit(*p)) {
+ sendstart = sendstart * 10 + (int)(*p - '0');
+ p++;
+ }
+ if (*p && *p != CR) { /* Bad number */
+ debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
+ sendstart = 0L;
+ } else if (sendstart > fsize) { /* Remote file bigger than local */
+ debug(F110,"doftpsend2 big size",ckltoa(fsize),sendstart);
+ sendstart = 0L;
+ }
+ /* Local is newer */
+ debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
+ if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
+ debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
+ sendstart = 0L; /* Send the whole file */
+ }
+ }
+ changetype(ftp_typ,0); /* Change back to appropriate type */
+ if (sendstart > 0L) { /* Still restarting? */
+ if (sendstart == fsize) { /* Same size - no need to send */
+ debug(F111,"doftpsend2 /restart SKIP",fsize,sendstart);
+ zclose(ZIFILE);
+ ftpsndret = SKP_RES;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ errno = 0; /* Restart needed, seek to the spot */
+ if (zfseek((long)sendstart) < 0) {
+ debug(F111,"doftpsend2 zfseek fails",
+ ftpsnd.local,sendstart);
+ fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
+ sendstart = 0;
+ zclose(ZIFILE);
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+#ifdef COMMENT
+ debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
+ x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
+ if (x != REPLY_CONTINUE) {
+ sendstart = 0;
+ zclose(ZIFILE);
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ } else {
+ ftpsnd.cmd = "STOR";
+ }
+#else
+ sendmode = SM_RESEND;
+ ftpsnd.cmd = "APPE";
+#endif /* COMMENT */
+ /* sendstart = 0L; */
+ }
+ }
+#endif /* FTP_RESTART */
+
+ if (unique && !stouarg) /* If we know STOU accepts no arg */
+ ftpsnd.remote = NULL; /* don't include one. */
+
+ x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
+ debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
+
+ if (x != REPLY_PRELIM && unique) {
+ /*
+ RFC959 says STOU does not take an argument. But every FTP server
+ I've encountered but one accepts the arg and constructs the unique
+ name from it, which is better than making up a totally random name
+ for the file, which is what RFC959 calls for. Especially because
+ there is no way for the client to find out the name chosen by the
+ server. So we try STOU with the argument first, which works with
+ most servers, and if it fails we retry it without the arg, for
+ the benefit of the one picky server that is not "liberal in what
+ it accepts" UNLESS the first STOU got a 502 code ("not implemented")
+ which means STOU is not accepted, period.
+ */
+ if ((x == 5) && stouarg && (ftpcode != 502)) {
+ x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
+ if (x == REPLY_PRELIM) /* If accepted */
+ stouarg = 0; /* flag no STOU arg for this server */
+ }
+ }
+ if (x != REPLY_PRELIM) {
+ signal(SIGINT, ftpsnd.oldintr);
+#ifdef SIGPIPE
+ if (ftpsnd.oldintp)
+ signal(SIGPIPE, ftpsnd.oldintp);
+#endif /* SIGPIPE */
+ zclose(ZIFILE);
+#ifdef PIPESEND
+ if (sndfilter)
+ pipesend = 0;
+#endif /* PIPESEND */
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ dout = dataconn(ftpsnd.lmode); /* Get data connection */
+ if (dout == -1) {
+ failftpsend2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ /* Initialize per-file stats */
+ ffc = 0L; /* Character counter */
+ cps = oldcps = 0L; /* Thruput */
+#ifdef GFTIMER
+ rftimer(); /* reset f.p. timer */
+#endif /* GFTIMER */
+
+#ifdef SIGPIPE
+ ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+ switch (curtype) {
+ case FTT_BIN: /* Binary mode */
+ case FTT_TEN:
+ errno = d = 0;
+ while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
+ ftpsnd.bytes += n;
+ ffc += n;
+ debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
+ hexdump("doftpsend2 zxin",buf,16);
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ for (bufp = buf; n > 0; n -= d, bufp += d) {
+ if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
+ break;
+ spackets++;
+ pktnum++;
+ if (fdispla != XYFD_B) {
+ spktl = d;
+ ftscreen(SCR_PT,'D',spackets,NULL);
+ }
+ }
+ } else {
+#endif /* CK_SSL */
+ for (bufp = buf; n > 0; n -= d, bufp += d) {
+ if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
+ || iscanceled())
+ break;
+ spackets++;
+ pktnum++;
+ if (fdispla != XYFD_B) {
+ spktl = d;
+ ftscreen(SCR_PT,'D',spackets,NULL);
+ }
+ }
+#ifdef CK_SSL
+ }
+#endif /* CK_SSL */
+ if (d <= 0)
+ break;
+ }
+ if (n < 0)
+ fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
+ if (d < 0 || (d = secure_flush(dout)) < 0) {
+ if (d == -1 && errno && errno != EPIPE)
+ perror("netout");
+ ftpsnd.bytes = -1;
+ }
+ break;
+
+ case FTT_ASC: /* Text mode */
+#ifndef NOCSETS
+ if (ftpsnd.xlate) { /* With translation */
+ initxlate(ftpsnd.incs,ftpsnd.outcs);
+ while (!cancelfile) {
+ if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
+ if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
+ }
+ } else {
+#endif /* NOCSETS */
+ /* Text mode, no translation */
+ while (((c = zminchar()) > -1) && !cancelfile) {
+ ffc++;
+ if (xxout(c) < 0)
+ break;
+ }
+ d = 0;
+#ifndef NOCSETS
+ }
+#endif /* NOCSETS */
+ if (dout == -1 || (d = secure_flush(dout)) < 0) {
+ if (d == -1 && errno && errno != EPIPE)
+ perror("netout");
+ ftpsnd.bytes = -1;
+ }
+ break;
+ }
+ tfc += ffc; /* Total file chars */
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+#endif /* GFTIMER */
+ zclose(ZIFILE); /* Close input file */
+#ifdef PIPESEND
+ if (sndfilter) /* Undo this (it's per file) */
+ pipesend = 0;
+#endif /* PIPESEND */
+
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+
+#ifdef TCPIPLIB
+ socket_close(dout); /* Close data connection */
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(dout, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(dout);
+#endif /* TCPIPLIB */
+ ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
+ signal(SIGINT, ftpsnd.oldintr); /* Put back interrupts */
+#ifdef SIGPIPE
+ if (ftpsnd.oldintp)
+ signal(SIGPIPE, ftpsnd.oldintp);
+#endif /* SIGPIPE */
+ if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
+ debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ } else if (cancelfile) {
+ debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
+ ftpsndret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
+ ftpsndret = 0;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+}
+
+static int
+sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
+ char *cmd, *local, *remote; int xlate, incs, outcs, restart;
+{
+ if (!remote) remote = ""; /* Check args */
+ if (!*remote) remote = local;
+ if (!local) local = "";
+ if (!*local) return(-1);
+ if (!cmd) cmd = "";
+ if (!*cmd) cmd = "STOR";
+
+ debug(F111,"ftp sendrequest restart",local,restart);
+
+ nout = 0; /* Init output buffer count */
+ ftpsnd.bytes = 0; /* File input byte count */
+ dout = -1;
+
+#ifdef FTP_PROXY
+ if (proxy) {
+ proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
+ return(0);
+ }
+#endif /* FTP_PROXY */
+
+ changetype(ftp_typ,0); /* Change type for this file */
+
+ ftpsnd.oldintr = NULL; /* Set up interrupt handler */
+ ftpsnd.oldintp = NULL;
+ ftpsnd.restart = restart;
+ ftpsnd.xlate = xlate;
+ ftpsnd.lmode = "wb";
+
+#ifdef PIPESEND /* Use Kermit API for file i/o... */
+ if (sndfilter) {
+ char * p = NULL, * q;
+#ifndef NOSPL
+ int n = CKMAXPATH;
+ if (cmd_quoting && (p = (char *) malloc(n + 1))) {
+ q = p;
+ debug(F110,"sendrequest pipesend filter",sndfilter,0);
+ zzstring(sndfilter,&p,&n);
+ debug(F111,"sendrequest pipename",q,n);
+ if (n <= 0) {
+ printf("?Sorry, send filter + filename too long, %d max.\n",
+ CKMAXPATH
+ );
+ free(q);
+ return(-1);
+ }
+ ckstrncpy(filnam,q,CKMAXPATH+1);
+ free(q);
+ local = filnam;
+ }
+#endif /* NOSPL */
+ }
+
+ if (sndfilter) /* If sending thru a filter */
+ pipesend = 1; /* set this for open and i/o */
+#endif /* PIPESEND */
+
+ if (openi(local) == 0) /* Try to open the input file */
+ return(-1);
+
+ ftpsndret = 0;
+ ftpsnd.incs = incs;
+ ftpsnd.outcs = outcs;
+ ftpsnd.cmd = cmd;
+ ftpsnd.local = local;
+ ftpsnd.remote = remote;
+ ftpsnd.oldintr = signal(SIGINT, cancelsend);
+ havesigint = 0;
+
+ if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
+ return(-1);
+ if (ftpsndret < 0)
+ return(-1);
+ if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
+ return(-1);
+
+ return(ftpsndret);
+}
+
+static sigtype
+cancelrecv(sig) int sig; {
+ havesigint++;
+ cancelfile = 0;
+ cancelgroup++;
+ secure_getc(0,1); /* Initialize net input buffers */
+ printf(" Canceling...\n");
+ debug(F100,"ftp cancelrecv caught SIGINT","",0);
+ fflush(stdout);
+ if (fp_nml) {
+ if (fp_nml != stdout)
+ fclose(fp_nml);
+ fp_nml = NULL;
+ }
+#ifndef OS2
+ longjmp(recvcancel, 1);
+#else
+ PostCtrlCSem();
+#endif /* OS2 */
+}
+
+/* Argumentless front-end for secure_getc() */
+
+static int
+netgetc() {
+ return(secure_getc(globaldin,0));
+}
+
+/* Returns -1 on failure, 0 on success, 1 if file skipped */
+
+/*
+ Sets ftpcode < 0 on failure if failure reason is not server reply code:
+ -1: interrupted by user.
+ -2: error opening or writing output file (reason in errno).
+ -3: failure to make data connection.
+ -4: network read error (reason in errno).
+*/
+
+struct xx_ftprecv {
+ int reply;
+ int fcs;
+ int rcs;
+ int recover;
+ int xlate;
+ int din;
+ int is_retr;
+ sig_t oldintr, oldintp;
+ char * cmd;
+ char * local;
+ char * remote;
+ char * lmode;
+ char * pipename;
+ int tcrflag;
+ long localsize;
+};
+static struct xx_ftprecv ftprecv;
+
+static int ftprecvret = 0;
+
+static VOID
+#ifdef CK_ANSIC
+failftprecv(VOID * threadinfo)
+#else
+failftprecv(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ while (cpend) {
+ ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
+ }
+ if (data >= 0) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = -1;
+ }
+ if (ftprecv.oldintr)
+ signal(SIGINT, ftprecv.oldintr);
+ ftpcode = -1;
+ ftprecvret = -1;
+
+#ifndef OS2
+ /* TEST ME IN K95 */
+ if (havesigint) {
+ havesigint = 0;
+ debug(F100,"ftp failftprecv chain to trap()...","",0);
+ if (ftprecv.oldintr != SIG_IGN)
+ (*ftprecv.oldintr)(SIGINT);
+ /* NOTREACHED (I hope!) */
+ debug(F100,"ftp failftprecv return from trap()...","",0);
+ }
+#endif /* OS2 */
+ return;
+}
+
+static VOID
+#ifdef CK_ANSIC
+doftprecv(VOID * threadinfo)
+#else
+doftprecv(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+#ifndef COMMENT
+ if (!out2screen && !ftprecv.pipename) {
+ int x;
+ char * local;
+ local = ftprecv.local;
+ x = zchko(local);
+ if (x < 0) {
+ if ((!dpyactive || ftp_deb))
+ fprintf(stderr,
+ "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
+ signal(SIGINT, ftprecv.oldintr);
+ ftpcode = -2;
+ ftprecvret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ }
+#endif /* COMMENT */
+ changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
+ if (initconn()) { /* Initialize the data connection */
+ signal(SIGINT, ftprecv.oldintr);
+ ftpcode = -1;
+ ftprecvret = -3;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ secure_getc(0,1); /* Initialize net input buffers */
+ ftprecvret = 0;
+
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+}
+
+static VOID
+#ifdef CK_ANSIC
+failftprecv2(VOID * threadinfo)
+#else
+failftprecv2(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ /* Cancel using RFC959 recommended IP,SYNC sequence */
+
+ debug(F100,"ftp recvrequest CANCEL","",0);
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+#endif /* GFTIMER */
+#ifdef SIGPIPE
+ if (ftprecv.oldintp)
+ signal(SIGPIPE, ftprecv.oldintr);
+#endif /* SIGPIPE */
+ signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ ftpcode = -1;
+ signal(SIGINT, ftprecv.oldintr);
+ ftprecvret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ cancel_remote(ftprecv.din);
+ if (ftpcode > -1)
+ ftpcode = -1;
+ if (data >= 0) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = -1;
+ }
+ if (!out2screen) {
+ int x = 0;
+ debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
+ zclose(ZOFILE);
+ switch (keep) { /* which is... */
+ case SET_AUTO: /* AUTO */
+ if (curtype == FTT_ASC) /* Delete file if TYPE A. */
+ x = 1;
+ break;
+ case SET_OFF: /* DISCARD */
+ x = 1; /* Delete file, period. */
+ break;
+ default: /* KEEP */
+ break;
+ }
+ if (x) {
+ x = zdelet(ftprecv.local);
+ debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
+ }
+ }
+ if (ftprecv.din) {
+#ifdef TCPIPLIB
+ socket_close(ftprecv.din);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(ftprecv.din, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(ftprecv.din);
+#endif /* TCPIPLIB */
+ }
+ signal(SIGINT, ftprecv.oldintr);
+ ftprecvret = -1;
+
+ if (havesigint) {
+ havesigint = 0;
+ debug(F100,"FTP failftprecv2 chain to trap()...","",0);
+#ifdef OS2
+ debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
+ PostCtrlCSem();
+#else /* OS2 */
+ if (ftprecv.oldintr != SIG_IGN)
+ (*ftprecv.oldintr)(SIGINT);
+ /* NOTREACHED (I hope!) */
+ debug(F100,"ftp failftprecv2 return from trap()...","",0);
+#endif /* OS2 */
+ }
+}
+
+static VOID
+#ifdef CK_ANSIC
+doftprecv2(VOID * threadinfo)
+#else
+doftprecv2(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+ register int c, d;
+ long bytes = 0L;
+ int bare_lfs = 0;
+ int blksize = 0;
+ ULONG start = 0L, stop;
+ char * p;
+ static char * rcvbuf = NULL;
+ static int rcvbufsiz = 0;
+#ifdef CK_URL
+ char newname[CKMAXPATH+1]; /* For file dialog */
+#endif /* CK_URL */
+ extern int adl_ask;
+
+ ftprecv.din = -1;
+#ifdef NTSIG
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+
+ if (ftprecv.recover) { /* Initiate recovery */
+ x = ftpcmd("REST",ckltoa(ftprecv.localsize),-1,-1,ftp_vbm);
+ debug(F111,"ftp reply","REST",x);
+ if (x == REPLY_CONTINUE) {
+ ftprecv.lmode = "ab";
+ rs_len = ftprecv.localsize;
+ } else {
+ ftprecv.recover = 0;
+ }
+ }
+ /* IMPORTANT: No FTP commands can come between REST and RETR! */
+
+ debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
+
+ /* Send the command and get reply */
+ debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
+ debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
+
+ if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
+ != REPLY_PRELIM) {
+ signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
+ ftprecvret = -1; /* ftpcode is set by ftpcmd() */
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ ftprecv.din = dataconn("r"); /* Good reply, open data connection */
+ globaldin = ftprecv.din; /* Global copy of file descriptor */
+ if (ftprecv.din == -1) { /* Check for failure */
+ ftpcode = -3; /* Code for no data connection */
+ ftprecvret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+#ifdef CK_URL
+ /* In K95 GUI put up a file box */
+ if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
+ int x;
+ char * preface =
+"\r\nIncoming file from FTP server...\r\n\
+Please confirm output file specification or supply an alternative:";
+
+ x = uq_file(preface, /* K95 GUI: Put up file box. */
+ NULL,
+ 4,
+ NULL,
+ ftprecv.local ? ftprecv.local : ftprecv.remote,
+ newname,
+ CKMAXPATH+1
+ );
+ if (x > 0) {
+ ftprecv.local = newname; /* Substitute user's file name */
+ if (x == 2) /* And append if user said to */
+ ftprecv.lmode = "ab";
+ }
+ }
+#endif /* CK_URL */
+ x = 1; /* Output file open OK? */
+ if (ftprecv.pipename) { /* Command */
+ x = zxcmd(ZOFILE,ftprecv.pipename);
+ debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
+ } else if (!out2screen) { /* File */
+ struct filinfo xx;
+ xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
+ xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
+ /* Append or New */
+ xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
+ x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
+ debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
+ }
+ if (x < 1) { /* Failure to open output file */
+ if ((!dpyactive || ftp_deb))
+ fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
+ ftprecvret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ blksize = FTP_BUFSIZ; /* Allocate input buffer */
+
+ debug(F101,"ftp recvrequest blksize","",blksize);
+ debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
+
+ if (rcvbufsiz < blksize) { /* if necessary */
+ if (rcvbuf) {
+ free(rcvbuf);
+ rcvbuf = NULL;
+ }
+ rcvbuf = (char *)malloc((unsigned)blksize);
+ if (!rcvbuf) {
+ debug(F100,"ftp get rcvbuf malloc failed","",0);
+ ftpcode = -2;
+#ifdef ENOMEM
+ errno = ENOMEM;
+#endif /* ENOMEM */
+ if ((!dpyactive || ftp_deb))
+ perror("malloc");
+ rcvbufsiz = 0;
+ ftprecvret = -1;
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ debug(F101,"ftp get rcvbuf malloc ok","",blksize);
+ rcvbufsiz = blksize;
+ }
+ debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
+
+ ffc = 0L; /* Character counter */
+ cps = oldcps = 0L; /* Thruput */
+ start = gmstimer(); /* Start time (msecs) */
+#ifdef GFTIMER
+ rftimer(); /* Start time (float) */
+#endif /* GFTIMER */
+
+ debug(F111,"ftp get type",ftprecv.local,curtype);
+ debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
+ switch (curtype) {
+ case FTT_BIN: /* Binary mode */
+ case FTT_TEN: /* TENEX mode */
+ d = 0;
+ while (1) {
+ errno = 0;
+ c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
+ if (cancelfile) {
+ failftprecv2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (c < 1)
+ break;
+#ifdef printf /* (What if it isn't?) */
+ if (out2screen && !ftprecv.pipename) {
+ int i;
+ for (i = 0; i < c; i++)
+ printf("%c",rcvbuf[i]);
+ } else
+#endif /* printf */
+ {
+ register int i;
+ i = 0;
+ errno = 0;
+ while (i < c) {
+ if (zmchout(rcvbuf[i++]) < 0) {
+ d = i;
+ break;
+ }
+ }
+ }
+ bytes += c;
+ ffc += c;
+ }
+ if (c < 0) {
+ debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
+ if (c == -1 && errno != EPIPE)
+ if ((!dpyactive || ftp_deb))
+ perror("netin");
+ bytes = -1;
+ ftpcode = -4;
+ }
+ if (d < c) {
+ ftpcode = -2;
+ if ((!dpyactive || ftp_deb)) {
+ char * p;
+ p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
+ if (d < 0)
+ fprintf(stderr,
+ "local(3): %s: %s\n", ftprecv.local, ck_errstr());
+ else
+ fprintf(stderr,
+ "%s: short write\n", ftprecv.local);
+ }
+ }
+ break;
+
+ case FTT_ASC: /* Text mode */
+ debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
+#ifndef NOCSETS
+ if (ftprecv.xlate) {
+ int t;
+#ifdef CK_ANSIC
+ int (*fn)(char);
+#else
+ int (*fn)();
+#endif /* CK_ANSIC */
+ debug(F110,"ftp recvrequest (data)","initxlate",0);
+ initxlate(ftprecv.rcs,ftprecv.fcs); /* (From,To) */
+ if (ftprecv.pipename) {
+ fn = pipeout;
+ debug(F110,"ftp recvrequest ASCII","pipeout",0);
+ } else {
+ fn = out2screen ? scrnout : putfil;
+ debug(F110,"ftp recvrequest ASCII",
+ out2screen ? "scrnout" : "putfil",0);
+ }
+ while (1) {
+ /* Get byte from net */
+ c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
+ if (cancelfile) {
+ failftprecv2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (c0 < 0)
+ break;
+ /* Second byte from net */
+ c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
+ if (cancelfile) {
+ failftprecv2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (c1 < 0)
+ break;
+#ifdef COMMENT
+ /* K95: Check whether we need this */
+ if (fileorder > 0) /* Little Endian */
+ bytswap(&c0,&c1); /* swap bytes*/
+#endif /* COMMENT */
+
+#ifdef OS2
+ if ( out2screen && /* we're translating to UCS-2 */
+ !k95stdout && !inserver) /* for the real screen... */
+ {
+ union {
+ USHORT ucs2;
+ UCHAR bytes[2];
+ } output;
+
+ output.bytes[0] = c1;
+ output.bytes[1] = c0;
+
+ VscrnWrtUCS2StrAtt(VCMD,
+ &output.ucs2,
+ 1,
+ wherey[VCMD],
+ wherex[VCMD],
+ &colorcmd
+ );
+
+ } else
+#endif /* OS2 */
+ {
+ if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
+ if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
+ }
+ }
+ } else {
+#endif /* NOCSETS */
+ while (1) {
+ c = secure_getc(ftprecv.din,0);
+ if (cancelfile) {
+ failftprecv2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (c < 0 || c == EOF)
+ break;
+#ifdef UNIX
+ /* Record format conversion for Unix */
+ /* SKIP THIS FOR WINDOWS! */
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ bytes++;
+ if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
+ ftprecv.tcrflag) {
+ if (cancelfile) {
+ failftprecv2(threadinfo);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+ }
+ if (c < 0 || c == EOF)
+ goto break2;
+ if (c == '\0') {
+ bytes++;
+ goto contin2;
+ }
+ }
+ }
+ if (c < 0)
+ break;
+#endif /* UNX */
+
+ if (out2screen && !ftprecv.pipename)
+#ifdef printf
+ printf("%c",(char)c);
+#else
+ putchar((char)c);
+#endif /* printf */
+ else
+ if ((d = zmchout(c)) < 0)
+ break;
+ bytes++;
+ ffc++;
+ contin2:
+ ;
+ }
+ break2:
+ if (bare_lfs && (!dpyactive || ftp_deb)) {
+ printf("WARNING! %d bare linefeeds received in ASCII mode\n",
+ bare_lfs);
+ printf("File might not have transferred correctly.\n");
+ }
+ if (ftprecv.din == -1) {
+ bytes = -1;
+ }
+ if (c == -2)
+ bytes = -1;
+ break;
+#ifndef NOCSETS
+ }
+#endif /* NOCSETS */
+ }
+ if (ftprecv.pipename || !out2screen) {
+ zclose(ZOFILE); /* Close the file */
+ debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
+ if (ftpcode < 0) { /* If download failed */
+ int x = 0;
+ switch (keep) { /* which is... */
+ case SET_AUTO: /* AUTO */
+ if (curtype == FTT_ASC) /* Delete file if TYPE A. */
+ x = 1;
+ break;
+ case SET_OFF: /* DISCARD */
+ x = 1; /* Delete file, period. */
+ break;
+ default: /* KEEP */
+ break;
+ }
+ if (x) {
+ x = zdelet(ftprecv.local);
+ debug(F111,"ftp get delete incomplete",ftprecv.local,x);
+ }
+ }
+ }
+ signal(SIGINT, ftprecv.oldintr);
+#ifdef SIGPIPE
+ if (ftprecv.oldintp)
+ signal(SIGPIPE, ftprecv.oldintp);
+#endif /* SIGPIPE */
+ stop = gmstimer();
+#ifdef GFTIMER
+ fpfsecs = gftimer();
+#endif /* GFTIMER */
+ tfc += ffc;
+
+#ifdef TCPIPLIB
+ socket_close(ftprecv.din);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(ftprecv.din, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(ftprecv.din);
+#endif /* TCPIPLIB */
+ ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
+ ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT ||
+ ftprecv.reply == REPLY_ERROR) ? -1 : 0);
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+}
+
+static int
+recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
+ xlate, fcs, rcs)
+ char *cmd, *local, *remote, *lmode, *pipename;
+ int printnames, recover, xlate, fcs, rcs;
+{
+#ifdef NT
+ struct _stat stbuf;
+#else /* NT */
+ struct stat stbuf;
+#endif /* NT */
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F111,"ftp recvrequest cmd",cmd,recover);
+ debug(F110,"ftp recvrequest local ",local,0);
+ debug(F111,"ftp recvrequest remote",remote,ftp_typ);
+ debug(F110,"ftp recvrequest pipename ",pipename,0);
+ debug(F101,"ftp recvrequest xlate","",xlate);
+ debug(F101,"ftp recvrequest fcs","",fcs);
+ debug(F101,"ftp recvrequest rcs","",rcs);
+ }
+#endif /* DEBUG */
+
+ ftprecv.localsize = 0L;
+
+ if (remfile) { /* See remcfm(), remtxt() */
+ if (rempipe) {
+ pipename = remdest;
+ } else {
+ local = remdest;
+ if (remappd) lmode = "ab";
+ }
+ }
+ out2screen = 0;
+ if (!cmd) cmd = ""; /* Core dump prevention */
+ if (!remote) remote = "";
+ if (!lmode) lmode = "";
+
+ if (pipename) { /* No recovery for pipes. */
+ recover = 0;
+ if (!local)
+ local = pipename;
+ } else {
+ if (!local) /* Output to screen? */
+ local = "-";
+ out2screen = !strcmp(local,"-");
+ }
+ debug(F101,"ftp recvrequest out2screen","",out2screen);
+
+#ifdef OS2
+ if ( ftp_xla && out2screen && !k95stdout && !inserver )
+ fcs = FC_UCS2;
+#endif /* OS2 */
+
+ if (out2screen) /* No recovery to screen */
+ recover = 0;
+ if (!ftp_typ) /* No recovery in text mode */
+ recover = 0;
+ ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
+
+ if (!ftprecv.is_retr) /* No recovery except for RETRieve */
+ recover = 0;
+
+#ifdef COMMENT
+ if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
+ if (recursive && ckstrchr(local,'/')) {
+
+ }
+ }
+#endif /* COMMENT */
+
+ ftprecv.localsize = 0L; /* Local file size */
+ rs_len = 0L; /* Recovery point */
+
+ debug(F101,"ftp recvrequest recover","",recover);
+ if (recover) { /* Recovering... */
+ if (stat(local, &stbuf) < 0) { /* Can't stat local file */
+ debug(F101,"ftp recvrequest recover stat failed","",errno);
+ recover = 0; /* So cancel recovery */
+ } else { /* Have local file info */
+ ftprecv.localsize = stbuf.st_size; /* Get size */
+ /* Remote file smaller than local */
+ if (fsize < ftprecv.localsize) {
+ debug(F101,"ftp recvrequest recover remote smaller","",fsize);
+ recover = 0; /* Recovery can't work */
+ } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
+ debug(F111,"ftp recvrequest recover equal size",
+ remote,ftprecv.localsize);
+ return(1);
+ }
+#ifdef COMMENT
+/*
+ The problem here is that the original partial file never got its date
+ set, either because FTP DATES was OFF, or because the partial file was
+ downloaded by some other program that doesn't set local file dates, or
+ because Kermit only sets the file's date when the download was complete
+ and successful. In all these cases, the local file has a later time
+ than the remote.
+*/
+ if (recover) { /* Remote is bigger */
+ x = chkmodtime(local,remote,0); /* Check file dates */
+ debug(F111,"ftp recvrequest chkmodtime",remote,x);
+ if (x != 1) /* Dates must be equal! */
+ recover = 0; /* If not, get whole file */
+ }
+#endif /* COMMENT */
+ }
+ debug(F111,"ftp recvrequest recover",remote,recover);
+ }
+
+#ifdef FTP_PROXY
+ if (proxy && ftprecv.is_retr)
+ return(proxtrans(cmd, local ? local : remote, remote));
+#endif /* FTP_PROXY */
+
+ ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
+
+ ftprecv.reply = 0;
+ ftprecv.fcs = fcs;
+ ftprecv.rcs = rcs;
+ ftprecv.recover = recover;
+ ftprecv.xlate = xlate;
+ ftprecv.cmd = cmd;
+ ftprecv.local = local;
+ ftprecv.remote = remote;
+ ftprecv.lmode = lmode;
+ ftprecv.pipename = pipename;
+ ftprecv.oldintp = NULL;
+ ftpcode = 0;
+
+ havesigint = 0;
+ ftprecv.oldintr = signal(SIGINT, cancelrecv);
+ if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
+ return -1;
+ if (ftprecvret < 0)
+ return -1;
+
+ if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
+ return -1;
+ return ftprecvret;
+}
+
+/*
+ * Need to start a listen on the data channel before we send the command,
+ * otherwise the server's connect may fail.
+ */
+static int
+initconn() {
+ register char *p, *a;
+ int result, tmpno = 0;
+ int on = 1;
+ GSOCKNAME_T len;
+
+#ifndef NO_PASSIVE_MODE
+ int a1,a2,a3,a4,p1,p2;
+
+ if (passivemode) {
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ globaldin = data;
+ if (data < 0) {
+ perror("ftp: socket");
+ return(-1);
+ }
+ if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
+ printf("Passive mode refused\n");
+ passivemode = 0;
+ return(initconn());
+ }
+/*
+ Now we have a string of comma-separated one-byte unsigned integer values,
+ 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 can make 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\n");
+ return(-1);
+ };
+#ifndef NOHTTP
+ if (tcp_http_proxy) {
+#ifdef OS2
+ char * agent = "Kermit 95"; /* Default user agent */
+#else
+ char * agent = "C-Kermit";
+#endif /* OS2 */
+ register struct hostent *hp = 0;
+ struct servent *destsp;
+ char host[512], *p, *q;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ int tos;
+#endif /* IPTOS_THROUGHPUT */
+#endif /* IP_TOS */
+ int s;
+#ifdef DEBUG
+ extern int debtim;
+ int xdebtim;
+ xdebtim = debtim;
+ debtim = 1;
+#endif /* DEBUG */
+
+ ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
+ ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
+ NULL,NULL,NULL
+ );
+ memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+ for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
+ *q = *p;
+ *q = '\0';
+
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ debug(F110,"initconn A",host,0);
+ hisctladdr.sin_family = AF_INET;
+ } else {
+ debug(F110,"initconn B",host,0);
+ hp = gethostbyname(host);
+#ifdef HADDRLIST
+ hp = ck_copyhostent(hp); /* make safe copy that won't change */
+#endif /* HADDRLIST */
+ if (hp == NULL) {
+ fprintf(stderr, "ftp: %s: Unknown host\n", host);
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(0);
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+#ifdef HADDRLIST
+ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
+ sizeof(hisctladdr.sin_addr));
+#else /* HADDRLIST */
+ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
+ sizeof(hisctladdr.sin_addr));
+#endif /* HADDRLIST */
+ }
+ data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ debug(F101,"initconn socket","",data);
+ if (data < 0) {
+ perror("ftp: socket");
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(0);
+ }
+ if (*p == ':')
+ p++;
+ else
+ p = "http";
+
+ destsp = getservbyname(p,"tcp");
+ if (destsp)
+ hisctladdr.sin_port = destsp->s_port;
+ else if (p)
+ hisctladdr.sin_port = htons(atoi(p));
+ else
+ hisctladdr.sin_port = htons(80);
+ errno = 0;
+#ifdef HADDRLIST
+ debug(F100,"initconn HADDRLIST","",0);
+ while
+#else
+ debug(F100,"initconn no HADDRLIST","",0);
+ if
+#endif /* HADDRLIST */
+ (connect(data, (struct sockaddr *)&hisctladdr,
+ sizeof (hisctladdr)) < 0) {
+ debug(F101,"initconn connect failed","",errno);
+#ifdef HADDRLIST
+ if (hp && hp->h_addr_list[1]) {
+ int oerrno = errno;
+
+ fprintf(stderr,
+ "ftp: connect to address %s: ",
+ inet_ntoa(hisctladdr.sin_addr)
+ );
+ errno = oerrno;
+ perror((char *)0);
+ hp->h_addr_list++;
+ memcpy((char *)&hisctladdr.sin_addr,
+ hp->h_addr_list[0],
+ sizeof(hisctladdr.sin_addr));
+ fprintf(stdout, "Trying %s...\n",
+ inet_ntoa(hisctladdr.sin_addr));
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+ close(data);
+#endif /* TCPIPLIB */
+ data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("ftp: socket");
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(0);
+ }
+ continue;
+ }
+#endif /* HADDRLIST */
+ perror("ftp: connect");
+ ftpcode = -1;
+ goto bad;
+ }
+ if (http_connect(data,
+ tcp_http_proxy_agent ?
+ tcp_http_proxy_agent :
+ agent,
+ NULL,
+ tcp_http_proxy_user,
+ tcp_http_proxy_pwd,
+ 0,
+ proxyhost
+ ) < 0) {
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+ close(data);
+#endif /* TCPIPLIB */
+ perror("ftp: connect");
+ ftpcode = -1;
+ goto bad;
+ }
+ } else
+#endif /* NOHTTP */
+ {
+ 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);
+ }
+ }
+ debug(F100,"initconn connect ok","",0);
+#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 /* IPTOS_THROUGHPUT */
+#endif /* IP_TOS */
+ memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
+ return(0);
+ }
+#endif /* NO_PASSIVE_MODE */
+
+ noport:
+ memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
+ if (sendport)
+ data_addr.sin_port = 0; /* let system pick one */
+ if (data != -1) {
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ }
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ globaldin = data;
+ 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;
+ }
+ 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");
+ goto bad;
+ }
+ if (sendport) {
+ a = (char *)&data_addr.sin_addr;
+ p = (char *)&data_addr.sin_port;
+ ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
+ UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
+ UC(p[0]),",", UC(p[1]));
+ result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
+ if (result == REPLY_ERROR && sendport) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ return(result != REPLY_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:
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ if (tmpno)
+ sendport = 1;
+ return(-1);
+}
+
+#ifdef CK_SSL
+static int
+ssl_dataconn() {
+ if (ssl_ftp_data_con!=NULL) { /* Do SSL */
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_con=NULL;
+ }
+ ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
+
+ SSL_set_fd(ssl_ftp_data_con,data);
+ SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
+
+ SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
+
+ if (ssl_debug_flag) {
+ fprintf(stderr,"=>START SSL connect on DATA\n");
+ fflush(stderr);
+ }
+ if (SSL_connect(ssl_ftp_data_con) <= 0) {
+ static char errbuf[1024];
+ ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
+ ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
+ fprintf(stderr,"%s\n", errbuf);
+ fflush(stderr);
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ } else {
+ ssl_ftp_data_active_flag=1;
+
+ if (!ssl_certsok_flag && !tls_is_krb5(2)) {
+ char *subject = ssl_get_subject_name(ssl_ftp_data_con);
+
+ if (!subject) {
+ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ debug(F110,"dataconn","[SSL _- FAILED]",0);
+
+ ssl_ftp_data_active_flag = 0;
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ } else {
+ if (!out2screen && displa && fdispla) {
+ ftscreen(SCR_TC,0,0L,"Display canceled");
+ /* fdispla = XYFD_B; */
+ }
+
+ if (uq_ok(
+ "Warning: Server didn't provide a certificate on data connection\n",
+ "Continue with file transfer? (Y/N)",
+ 3,NULL,0) <= 0) {
+ debug(F110, "dataconn","[SSL - FAILED]",0);
+ ssl_ftp_data_active_flag = 0;
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ }
+ }
+ } else {
+ if (!out2screen && displa && fdispla == XYFD_C) {
+ ftscreen(SCR_TC,0,0L,"Display canceled");
+ /* fdispla = XYFD_B; */
+ }
+
+ if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
+ debug(F110,"dataconn","[SSL - FAILED]",0);
+ ssl_ftp_data_active_flag = 0;
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ }
+ }
+ }
+ debug(F110,"dataconn","[SSL - OK]",0);
+#ifdef COMMENT
+ /* This messes up the full screen file transfer display */
+ ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
+#endif /* COMMENT */
+ }
+ if (ssl_debug_flag) {
+ fprintf(stderr,"=>DONE SSL connect on DATA\n");
+ fflush(stderr);
+ }
+ return(data);
+}
+#endif /* CK_SSL */
+
+static int
+dataconn(lmode) char *lmode; {
+ int s;
+#ifdef IP_TOS
+ int tos;
+#endif /* IP_TOS */
+#ifdef UCX50
+ static u_int fromlen;
+#else
+ static SOCKOPT_T fromlen;
+#endif /* UCX50 */
+
+ fromlen = sizeof(hisdataaddr);
+
+#ifndef NO_PASSIVE_MODE
+ if (passivemode) {
+#ifdef CK_SSL
+ ssl_ftp_data_active_flag=0;
+ if (ssl_ftp_active_flag &&
+ (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
+ return(ssl_dataconn());
+#endif /* CK_SSL */
+ return(data);
+ }
+#endif /* NO_PASSIVE_MODE */
+
+ s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
+ if (s < 0) {
+ perror("ftp: accept");
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ }
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = s;
+ globaldin = data;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif /* IPTOS_THROUGHPUT */
+#endif /* IP_TOS */
+
+#ifdef CK_SSL
+ ssl_ftp_data_active_flag=0;
+ if (ssl_ftp_active_flag &&
+ (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
+ return(ssl_dataconn());
+#endif /* CK_SSL */
+ return(data);
+}
+
+#ifdef FTP_PROXY
+static sigtype
+pscancel(sig) int sig; {
+ cancelfile++;
+}
+
+static VOID
+pswitch(flag) int flag; {
+ extern int proxy;
+ 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[CKMAXPATH];
+ char mo[CKMAXPATH];
+ char *authtype;
+ int clvl;
+ int dlvl;
+#ifdef FTP_KRB4
+ des_cblock session;
+ des_key_schedule ftp_sched;
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+ } proxstruct, tmpstruct;
+ struct comvars *ip, *op;
+
+ cancelfile = 0;
+ oldintr = signal(SIGINT, pscancel);
+ 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 (ftp_host) {
+ strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
+ ip->name[MAXHOSTNAMELEN - 1] = '\0';
+ ip->name[strlen(ip->name)] = '\0';
+ } else
+ ip->name[0] = 0;
+ ftp_host = op->name;
+ ip->hctl = hisctladdr;
+ hisctladdr = op->hctl;
+ ip->mctl = myctladdr;
+ myctladdr = op->mctl;
+ ip->in = csocket;
+ csocket = op->in;
+ ip->out = csocket;
+ csocket = op->out;
+ ip->tpe = ftp_typ;
+ ftp_typ = op->tpe;
+ ip->curtpe = curtype;
+ curtype = op->curtpe;
+ ip->cpnd = cpend;
+ cpend = op->cpnd;
+ ip->sunqe = ftp_usn;
+ ftp_usn = op->sunqe;
+ ip->mcse = mcase;
+ mcase = op->mcse;
+ ip->ntflg = ntflag;
+ ntflag = op->ntflg;
+ strncpy(ip->nti, ntin, 16);
+ (ip->nti)[strlen(ip->nti)] = '\0';
+ strcpy(ntin, op->nti);
+ strncpy(ip->nto, ntout, 16);
+ (ip->nto)[strlen(ip->nto)] = '\0';
+ strcpy(ntout, op->nto);
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ strncpy(ip->mi, mapin, CKMAXPATH - 1);
+ (ip->mi)[strlen(ip->mi)] = '\0';
+ strcpy(mapin, op->mi);
+ strncpy(ip->mo, mapout, CKMAXPATH - 1);
+ (ip->mo)[strlen(ip->mo)] = '\0';
+ strcpy(mapout, op->mo);
+ ip->authtype = auth_type;
+ auth_type = op->authtype;
+ ip->clvl = ftp_cpl;
+ ftp_cpl = op->clvl;
+ ip->dlvl = ftp_dpl;
+ ftp_dpl = op->dlvl;
+ if (!ftp_cpl)
+ ftp_cpl = FPL_CLR;
+ if (!ftp_dpl)
+ ftp_dpl = FPL_CLR;
+#ifdef FTP_KRB4
+ memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
+ memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
+ memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
+ memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ ip->gcontext = gcontext;
+ gcontext = op->gcontext;
+#endif /* GSSAPI */
+ signal(SIGINT, oldintr);
+ if (cancelfile) {
+ cancelfile = 0;
+ debug(F101,"pswitch cancelfile B","",cancelfile);
+ (*oldintr)(SIGINT);
+ }
+}
+
+static sigtype
+cancelpt(sig) int sig; {
+ printf("\n");
+ fflush(stdout);
+ ptabflg++;
+ cancelfile = 0;
+#ifndef OS2
+ longjmp(ptcancel, 1);
+#else
+ PostCtrlCSem();
+#endif /* OS2 */
+}
+
+void
+proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
+ sig_t oldintr;
+ int secndflag = 0, prox_type, nfnd;
+ char *cmd2;
+#ifdef BSDSELECT
+ fd_set mask;
+#endif /* BSDSELECT */
+ sigtype cancelpt();
+
+ if (strcmp(cmd, "RETR"))
+ cmd2 = "RETR";
+ else
+ cmd2 = unique ? "STOU" : "STOR";
+ if ((prox_type = type) == 0) {
+ if (servertype == SYS_UNIX && unix_proxy)
+ prox_type = FTT_BIN;
+ else
+ prox_type = FTT_ASC;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
+ printf("Proxy server does not support third party transfers.\n");
+ return;
+ }
+ pswitch(0);
+ if (!connected) {
+ printf("No primary connection\n");
+ pswitch(1);
+ ftpcode = -1;
+ return;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+
+ if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
+ pswitch(1);
+ return;
+ }
+
+ /* Replace with calls to cc_execute() */
+ if (setjmp(ptcancel))
+ goto cancel;
+ oldintr = signal(SIGINT, cancelpt);
+ if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
+ signal(SIGINT, oldintr);
+ pswitch(1);
+ return;
+ }
+ sleep(2000);
+ pswitch(1);
+ secndflag++;
+ if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
+ goto cancel;
+ ptflag++;
+ getreply(0,-1,-1,ftp_vbm,0);
+ pswitch(0);
+ getreply(0,-1,-1,ftp_vbm,0);
+ signal(SIGINT, oldintr);
+ pswitch(1);
+ ptflag = 0;
+ return;
+
+ cancel:
+ 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 (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ cancel_remote(0);
+ }
+ pswitch(1);
+ if (ptabflg)
+ ftpcode = -1;
+ signal(SIGINT, oldintr);
+ return;
+ }
+ if (cpend)
+ cancel_remote(0);
+ pswitch(!proxy);
+ if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
+ if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ cancel_remote(0);
+ pswitch(1);
+ if (ptabflg)
+ ftpcode = -1;
+ signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ if (cpend)
+ cancel_remote(0);
+ pswitch(!proxy);
+ if (cpend) {
+#ifdef BSDSELECT
+ FD_ZERO(&mask);
+ FD_SET(csocket, &mask);
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ perror("cancel");
+ }
+ if (ptabflg)
+ ftpcode = -1;
+ lostpeer();
+ }
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
+ if (nfnd < 0) {
+ perror("cancel");
+ }
+ if (ptabflg)
+ ftpcode = -1;
+ lostpeer();
+ }
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ getreply(0,-1,-1,ftp_vbm,0);
+ getreply(0,-1,-1,ftp_vbm,0);
+ }
+ if (proxy)
+ pswitch(0);
+ pswitch(1);
+ if (ptabflg)
+ ftpcode = -1;
+ signal(SIGINT, oldintr);
+}
+#endif /* FTP_PROXY */
+
+#ifdef FTP_SECURITY
+#ifdef FTP_GSSAPI
+
+struct {
+ CONST gss_OID_desc * CONST * mech_type;
+ char *service_name;
+} gss_trials[] = {
+ { &gss_mech_krb5, "ftp" },
+ { &gss_mech_krb5, "host" },
+};
+
+int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
+#endif /* FTP_GSSAPI */
+
+static int
+ftp_auth() {
+ extern int setsafe();
+ int j = 0, n;
+#ifdef FTP_KRB4
+ char *service, inst[INST_SZ];
+ ULONG cksum;
+ ULONG checksum = (ULONG) getpid();
+ CHAR out_buf[FTP_BUFSIZ];
+ int i;
+#else /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ CHAR out_buf[FTP_BUFSIZ];
+ int i;
+#endif /* FTP_GSSAPI */
+#endif /* FTP_KRB4 */
+
+ if (ssl_ftp_proxy) /* Do not allow AUTH over SSL proxy */
+ return(0);
+
+ if (auth_type)
+ return(1); /* auth already succeeded */
+
+ /* Try each auth type as specified by the end user */
+ for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
+#ifdef FTP_GSSAPI
+ if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
+ n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
+ if (n == REPLY_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];
+ int comcode, trial;
+ struct gss_channel_bindings_struct chan;
+ char * realm = NULL;
+ char tgt[256];
+
+ 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;
+
+ if (!quiet)
+ printf("GSSAPI accepted as authentication type\n");
+
+ realm = ck_krb5_realmofhost(ftp_user_host);
+ if (realm) {
+ ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
+ debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
+ if ( krb5_autoget &&
+ !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
+ (ck_krb5_is_tgt_valid() > 0)) )
+ ck_krb5_autoget_TGT(realm);
+ }
+
+ /* Blob from gss-client */
+ for (trial = 0; trial < n_gss_trials; trial++) {
+ /* ftp@hostname first, the host@hostname */
+ /* the V5 GSSAPI binding canonicalizes this for us... */
+ ckmakmsg(stbuf,FTP_BUFSIZ,
+ gss_trials[trial].service_name,
+ "@",
+ ftp_user_host,
+ NULL
+ );
+ if (ftp_deb)
+ fprintf(stderr,
+ "Authenticating to <%s>...\n", stbuf);
+ send_tok.value = stbuf;
+ send_tok.length = strlen(stbuf);
+ 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 (ftp_deb)
+ fprintf(stderr, "calling gss_init_sec_context\n");
+ maj_stat =
+ gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &gcontext,
+ target_name,
+ (gss_OID) *
+ gss_trials[trial].mech_type,
+ GSS_C_MUTUAL_FLAG |
+ GSS_C_REPLAY_FLAG |
+ (ftp_cfw ?
+ GSS_C_DELEG_FLAG : 0),
+ 0,
+ /* channel bindings */
+ (krb5_d_no_addresses ?
+ GSS_C_NO_CHANNEL_BINDINGS :
+ &chan),
+ 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) {
+ if (trial == n_gss_trials-1)
+ user_gss_error(maj_stat,
+ min_stat,
+ "initializing context"
+ );
+ gss_release_name(&min_stat, &target_name);
+ /* maybe we missed on the service name */
+ goto outer_loop;
+ }
+ if (send_tok.length != 0) {
+ int len;
+ reply_parse = "ADAT="; /* for ftpcmd() later */
+ len = FTP_BUFSIZ;
+ kerror =
+ radix_encode(send_tok.value,
+ out_buf,
+ send_tok.length,
+ &len,
+ RADIX_ENCODE
+ );
+ if (kerror) {
+ fprintf(stderr,
+ "Base 64 encoding failed: %s\n",
+ radix_error(kerror)
+ );
+ goto gss_complete_loop;
+ }
+ comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
+ if (comcode != REPLY_COMPLETE
+ && comcode != REPLY_CONTINUE /* (335) */
+ ) {
+ if (trial == n_gss_trials-1) {
+ fprintf(stderr, "GSSAPI ADAT failed\n");
+ /* force out of loop */
+ maj_stat = GSS_S_FAILURE;
+ }
+ /*
+ Backoff to the v1 gssapi is still possible.
+ Send a new AUTH command. If that fails,
+ terminate the loop.
+ */
+ if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
+ != REPLY_CONTINUE) {
+ fprintf(stderr,
+ "GSSAPI ADAT failed, AUTH restart failed\n");
+ /* force out of loop */
+ maj_stat = GSS_S_FAILURE;
+ }
+ goto outer_loop;
+ }
+ 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;
+ }
+ }
+ len = FTP_BUFSIZ;
+ kerror = radix_encode(reply_parse,out_buf,i,&len,
+ RADIX_DECODE);
+ if (kerror) {
+ fprintf(stderr,
+ "Base 64 decoding failed: %s\n",
+ radix_error(kerror));
+ goto gss_complete_loop;
+ }
+
+ /* everything worked */
+ token_ptr = &recv_tok;
+ recv_tok.value = out_buf;
+ recv_tok.length = len;
+ continue;
+
+ /* get out of loop clean */
+ gss_complete_loop:
+ trial = n_gss_trials-1;
+ 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:
+ if (maj_stat == GSS_S_COMPLETE)
+ break;
+ }
+ if (maj_stat == GSS_S_COMPLETE) {
+ printf("GSSAPI authentication succeeded\n");
+ reply_parse = NULL;
+ auth_type = "GSSAPI";
+ return(1);
+ } else {
+ fprintf(stderr, "GSSAPI authentication failed\n");
+ reply_parse = NULL;
+ }
+ } else {
+ if (ftp_deb)
+ fprintf(stderr, "GSSAPI rejected as an authentication type\n");
+ if (ftpcode == 500 || ftpcode == 502)
+ return(0);
+ }
+ }
+#endif /* FTP_GSSAPI */
+#ifdef FTP_SRP
+ if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
+ if (srp_ftp_auth(ftp_user_host,NULL,NULL))
+ return(1);
+ else if (ftpcode == 500 || ftpcode == 502)
+ return(0);
+ }
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
+ n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
+ if (n == REPLY_CONTINUE) {
+ char tgt[4*REALM_SZ+1];
+ int rc;
+
+ if (!quiet)
+ printf("KERBEROS_V4 accepted as authentication type\n");
+ ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
+ ckstrncpy(ftp_realm,
+ (char *)ck_krb4_realmofhost(ftp_user_host),
+ REALM_SZ
+ );
+
+ ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
+ rc = ck_krb4_tkt_isvalid(tgt);
+
+ if (rc <= 0 && krb4_autoget)
+ ck_krb4_autoget_TGT(ftp_realm);
+
+ service = "ftp";
+ kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
+ if (kerror == KDC_PR_UNKNOWN) {
+ service = "rcmd";
+ kerror = krb_mk_req(&ftp_tkt,
+ service,
+ inst,
+ ftp_realm,
+ checksum
+ );
+ }
+ if (kerror)
+ fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
+ krb_get_err_text(kerror));
+ if (!kerror) {
+ kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
+ if (kerror)
+ fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
+ krb_get_err_text(kerror));
+ }
+ if (!kerror) {
+ int rc;
+ rc = des_key_sched(ftp_cred.session, ftp_sched);
+ if (rc == -1) {
+ printf("?Invalid DES key specified in credentials\r\n");
+ debug(F110,"ftp_auth",
+ "invalid DES Key specified in credentials",0);
+ } else if ( rc == -2 ) {
+ printf("?Weak DES key specified in credentials\r\n");
+ debug(F110,"ftp_auth",
+ "weak DES Key specified in credentials",0);
+ } else if ( rc != 0 ) {
+ printf("?DES Key Schedule not set by credentials\r\n");
+ debug(F110,"ftp_auth",
+ "DES Key Schedule not set by credentials",0);
+ }
+ reply_parse = "ADAT=";
+ i = FTP_BUFSIZ;
+ kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
+ &i, RADIX_ENCODE);
+ if (kerror) {
+ fprintf(stderr, "Base 64 encoding failed: %s\n",
+ radix_error(kerror));
+ goto krb4_err;
+ }
+ if (i > FTP_BUFSIZ - 6)
+ printf("?ADAT data too long\n");
+ if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
+ REPLY_COMPLETE) {
+ fprintf(stderr, "Kerberos V4 authentication failed\n");
+ goto krb4_err;
+ }
+ if (!reply_parse) {
+ fprintf(stderr,
+ "No authentication data received from server\n");
+ goto krb4_err;
+ }
+ i = sizeof(out_buf);
+ kerror =
+ radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
+ if (kerror) {
+ fprintf(stderr, "Base 64 decoding failed: %s\n",
+ radix_error(kerror));
+ goto krb4_err;
+ }
+ kerror = krb_rd_safe(out_buf, i,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &hisctladdr,
+ &myctladdr,
+ &ftp_msg_data
+ );
+ if (kerror) {
+ fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
+ krb_get_err_text(kerror));
+ goto krb4_err;
+ }
+
+ /* fetch the (modified) checksum */
+ memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
+ if (ntohl(cksum) == checksum + 1) {
+ if (ftp_vbm)
+ printf("Kerberos V4 authentication succeeded\n");
+ reply_parse = NULL;
+ auth_type = "KERBEROS_V4";
+ return(1);
+ } else
+ fprintf(stderr,
+ "Kerberos V4 mutual authentication failed\n");
+ krb4_err:
+ reply_parse = NULL;
+ }
+ } else {
+ if (ftp_deb)
+ fprintf(stderr,
+ "KERBEROS_V4 rejected as an authentication type\n");
+ if (ftpcode == 500 || ftpcode == 502)
+ return(0);
+ }
+ }
+#endif /* FTP_KRB4 */
+#ifdef CK_SSL
+ if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
+#ifdef FTPHOST
+ if (!hostcmd) {
+ ftpcmd("HOST",ftp_user_host,0,0,0);
+ hostcmd = 1;
+ }
+#endif /* FTPHOST */
+ n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
+ if (n != REPLY_COMPLETE)
+ n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
+ if (n == REPLY_COMPLETE) {
+ if (!quiet)
+ printf("TLS accepted as authentication type\n");
+
+ auth_type = "TLS";
+ ssl_auth();
+ if (ssl_ftp_active_flag ) {
+ ftp_dpl = FPL_CLR;
+ ftp_cpl = FPL_PRV;
+ return(1);
+ } else {
+ fprintf(stderr,"TLS authentication failed\n");
+ auth_type = NULL;
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(csocket, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(csocket);
+#endif /* TCPIPLIB */
+ csocket = -1;
+ if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
+ return(0);
+ }
+ } else {
+ if (ftp_deb)
+ fprintf(stderr,"TLS rejected as an authentication type\n");
+ if (ftpcode == 500 || ftpcode == 502)
+ return(0);
+ }
+ }
+ if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
+#ifdef FTPHOST
+ if (!hostcmd) {
+ ftpcmd("HOST",ftp_user_host,0,0,0);
+ hostcmd = 1;
+ }
+#endif /* FTPHOST */
+ n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
+ if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
+ if (!quiet)
+ printf("SSL accepted as authentication type\n");
+ auth_type = "SSL";
+ ssl_auth();
+ if (ssl_ftp_active_flag) {
+ ftp_dpl = FPL_PRV;
+ ftp_cpl = FPL_PRV;
+ setprotbuf(1<<20);
+ return(1);
+ } else {
+ fprintf(stderr,"SSL authentication failed\n");
+ auth_type = NULL;
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(csocket, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(csocket);
+#endif /* TCPIPLIB */
+ csocket = -1;
+ if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
+ return(0);
+ }
+ } else {
+ if (ftp_deb)
+ fprintf(stderr, "SSL rejected as an authentication type\n");
+ if (ftpcode == 500 || ftpcode == 502)
+ return(0);
+ }
+ }
+#endif /* CK_SSL */
+ /* Other auth types go here ... */
+ } /* for (j;;) */
+ return(0);
+}
+#endif /* FTP_SECURITY */
+
+static int
+#ifdef CK_ANSIC
+setprotbuf(unsigned int size)
+#else
+setprotbuf(size) unsigned int size;
+#endif /* CK_ANSIC */
+/* setprotbuf */ {
+ if (ucbuf)
+ free(ucbuf);
+ ucbuf = NULL;
+ ucbufsiz = 0;
+ actualbuf = size;
+ while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
+ if (actualbuf)
+ actualbuf /= 2;
+ else
+ return(0);
+ }
+ ucbufsiz = actualbuf - FUDGE_FACTOR;
+ debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
+ if (ucbufsiz < 128) {
+ printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
+ } else if (ucbufsiz < 0) {
+ printf("ERROR: ucbuf allocation failure\n");
+ return(-1);
+ }
+ maxbuf = actualbuf;
+ return(1);
+}
+
+static int
+#ifdef CK_ANSIC
+setpbsz(unsigned int size)
+#else
+setpbsz(size) unsigned int size;
+#endif /* CK_ANSIC */
+/* setpbsz */ {
+ if (!setprotbuf(size)) {
+ perror("?Error while trying to malloc PROT buffer:");
+#ifdef FTP_SRP
+ srp_reset();
+#endif /* FTP_SRP */
+ ftpclose();
+ return(-1);
+ }
+ reply_parse = "PBSZ=";
+ ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
+#ifdef CK_SSL
+ ssl_ftp_active_flag ? "0" :
+#endif /* CK_SSL */
+ ckuitoa(actualbuf),NULL,NULL);
+ if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
+ if (connected) {
+ printf("?Unable to negotiate PROT buffer size with FTP server\n");
+ ftpclose();
+ }
+ return(-1);
+ }
+ if (reply_parse) {
+ if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
+ maxbuf = actualbuf;
+ } else
+ maxbuf = actualbuf;
+ ucbufsiz = maxbuf - FUDGE_FACTOR;
+ debug(F101,"setpbsz ucbufsiz","",ucbufsiz);
+ reply_parse = NULL;
+ return(0);
+}
+
+static VOID
+cancel_remote(din) int din; {
+ CHAR buf[FTP_BUFSIZ];
+ int x, nfnd;
+#ifdef BSDSELECT
+ fd_set mask;
+#endif /* BSDSELECT */
+#ifdef IBMSELECT
+ int fds[2], fdcnt = 0;
+#endif /* IBMSELECT */
+#ifdef DEBUG
+ extern int debtim;
+ int xdebtim;
+ xdebtim = debtim;
+ debtim = 1;
+#endif /* DEBUG */
+ debug(F100,"ftp cancel_remote entry","",0);
+#ifdef CK_SSL
+ if (ssl_ftp_active_flag) {
+ /*
+ * Send Telnet IP, Telnet DM but do so inline and within the
+ * TLS channel
+ */
+ int count, error;
+
+ buf[0] = IAC;
+ buf[1] = TN_IP;
+ buf[2] = IAC;
+ buf[3] = TN_DM;
+ buf[4] = NUL;
+
+ count = SSL_write(ssl_ftp_con, buf, 4);
+ debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
+ error = SSL_get_error(ssl_ftp_con,count);
+ debug(F111,"ftp cancel_remote","SSL_get_error()",error);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_SYSCALL:
+#ifdef NT
+ {
+ int gle = GetLastError();
+ }
+#endif /* NT */
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ lostpeer();
+ return;
+ }
+ } else
+#endif /* CK_SSL */
+ {
+ /*
+ * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
+ * after urgent byte rather than before as is protocol now.
+ */
+ buf[0] = IAC;
+ buf[1] = TN_IP;
+ buf[2] = IAC;
+ buf[3] = NUL;
+ if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
+ perror("cancel");
+ debug(F101,"ftp cancel_remote send 1","",x);
+ buf[0] = TN_DM;
+ x = send(csocket,(SENDARG2TYPE)buf,1,0);
+ debug(F101,"ftp cancel_remote send 2","",x);
+ }
+ x = scommand("ABOR");
+ debug(F101,"ftp cancel_remote scommand","",x);
+#ifdef BSDSELECT
+ FD_ZERO(&mask);
+ FD_SET(csocket, &mask);
+ if (din) {
+ FD_SET(din, &mask);
+ }
+ nfnd = empty(&mask, 10);
+ debug(F101,"ftp cancel_remote empty","",nfnd);
+ if ((nfnd) <= 0) {
+ if (nfnd < 0) {
+ perror("cancel");
+ }
+#ifdef FTP_PROXY
+ if (ptabflg)
+ ftpcode = -1;
+#endif /* FTP_PROXY */
+ lostpeer();
+ }
+ debug(F110,"ftp cancel_remote","D",0);
+ if (din && FD_ISSET(din, &mask)) {
+ /* Security: No threat associated with this read. */
+ /* But you can't simply read the TLS data stream */
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ int count, error;
+ while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
+ /* LOOP */ ;
+ } else
+#endif /* CK_SSL */
+ {
+ while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
+ /* LOOP */ ;
+ }
+ }
+ debug(F110,"ftp cancel_remote","E",0);
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ fds[0] = csocket;
+ fdcnt++;
+ if (din) {
+ fds[1] = din;
+ fdcnt++;
+ }
+ nfnd = empty(fds, fdcnt, 10);
+ debug(F101,"ftp cancel_remote empty","",nfnd);
+ if ((nfnd) <= 0) {
+ if (nfnd < 0) {
+ perror("cancel");
+ }
+#ifdef FTP_PROXY
+ if (ptabflg)
+ ftpcode = -1;
+#endif /* FTP_PROXY */
+ lostpeer();
+ }
+ debug(F110,"ftp cancel_remote","D",0);
+ if (din && select(&din, 1,0,0,1) ) {
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ int count, error;
+ while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
+ /* LOOP */ ;
+ } else
+#endif /* CK_SSL */
+ {
+ while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
+ /* LOOP */ ;
+ }
+ }
+ debug(F110,"ftp cancel_remote","E",0);
+#else /* IBMSELECT */
+ Some form of select is required.
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
+ debug(F110,"ftp cancel_remote","F",0);
+ /* 552 needed for NIC style cancel */
+ getreply(0,-1,-1,ftp_vbm,0);
+ debug(F110,"ftp cancel_remote","G",0);
+ }
+ debug(F110,"ftp cancel_remote","H",0);
+ getreply(0,-1,-1,ftp_vbm,0);
+ debug(F110,"ftp cancel_remote","I",0);
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+}
+
+static int
+fts_dpl(x) int x; {
+ if (!auth_type
+#ifdef OS2
+ || !ck_crypt_is_installed()
+#endif /* OS2 */
+ ) {
+ switch ( x ) {
+ case FPL_PRV:
+ printf("?Cannot set protection level to PRIVATE\n");
+ return(0);
+ case FPL_SAF:
+ printf("?Cannot set protection level to SAFE\n");
+ return(0);
+ }
+ ftp_dpl = x;
+ return(1);
+ }
+
+#ifdef CK_SSL
+ if (x == FPL_SAF &&
+ (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
+ printf("Cannot set protection level to safe\n");
+ return(0);
+ }
+#endif /* CK_SSL */
+ /* Start with a PBSZ of 1 meg */
+ if (x != FPL_CLR) {
+ if (setpbsz(DEFAULT_PBSZ) < 0)
+ return(0);
+ }
+ y = ftpcmd(x == FPL_CLR ? "PROT C" :
+ (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
+ if (y == REPLY_COMPLETE) {
+ ftp_dpl = x;
+ return(1);
+ }
+ return(0);
+}
+
+static int
+fts_cpl(x) int x; {
+ if (!auth_type
+#ifdef OS2
+ || !ck_crypt_is_installed()
+#endif /* OS2 */
+ ) {
+ switch ( x ) {
+ case FPL_PRV:
+ printf("?Cannot set protection level to PRIVATE\n");
+ return(0);
+ case FPL_SAF:
+ printf("?Cannot set protection level to SAFE\n");
+ return(0);
+ }
+ ftp_cpl = x;
+ return(1);
+ }
+ if (x == FPL_CLR) {
+ y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
+ if (y == REPLY_COMPLETE) {
+ ftp_cpl = x;
+ return(1);
+ }
+ return(0);
+ }
+ ftp_cpl = x;
+ return(1);
+}
+
+#ifdef FTP_GSSAPI
+static VOID
+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, msg_ctx;
+ gss_buffer_desc msg;
+ 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);
+ 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);
+ gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+ fprintf(stderr, "GSSAPI error: %s\n", s);
+}
+#endif /* FTP_GSSAPI */
+
+#ifndef NOMHHOST
+#ifdef datageneral
+#define NOMHHOST
+#else
+#ifdef HPUX5WINTCP
+#define NOMHHOST
+#endif /* HPUX5WINTCP */
+#endif /* datageneral */
+#endif /* NOMHHOST */
+
+#ifdef INADDRX
+static struct in_addr inaddrx;
+#endif /* INADDRX */
+
+static char *
+ftp_hookup(host, port, tls) char * host; int port; int tls; {
+ register struct hostent *hp = 0;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ int tos;
+#endif /* IPTOS_THROUGHPUT */
+#endif /* IP_TOS */
+ int s;
+ GSOCKNAME_T len;
+ static char hostnamebuf[MAXHOSTNAMELEN];
+ char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
+ int cport;
+#ifdef DEBUG
+ extern int debtim;
+ int xdebtim;
+ xdebtim = debtim;
+ debtim = 1;
+#endif /* DEBUG */
+
+#ifndef NOHTTP
+ if (tcp_http_proxy) {
+ struct servent *destsp;
+ char *p, *q;
+
+ ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
+ for (p = tcp_http_proxy, q = hostname;
+ *p != '\0' && *p != ':';
+ p++, q++
+ )
+ *q = *p;
+ *q = '\0';
+
+ if (*p == ':')
+ p++;
+ else
+ p = "http";
+
+ destsp = getservbyname(p,"tcp");
+ if (destsp)
+ cport = ntohs(destsp->s_port);
+ else if (p) {
+ cport = atoi(p);
+ } else
+ cport = 80;
+ } else
+#endif /* NOHTTP */
+ {
+ ckstrncpy(hostname,host,MAXHOSTNAMELEN);
+ cport = port;
+ }
+ memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ debug(F110,"ftp hookup A",hostname,0);
+ hisctladdr.sin_family = AF_INET;
+ ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
+ } else {
+ debug(F110,"ftp hookup B",hostname,0);
+ hp = gethostbyname(hostname);
+#ifdef HADDRLIST
+ hp = ck_copyhostent(hp); /* make safe copy that won't change */
+#endif /* HADDRLIST */
+ if (hp == NULL) {
+ fprintf(stderr, "ftp: %s: Unknown host\n", host);
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return((char *) 0);
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+#ifdef HADDRLIST
+ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
+ sizeof(hisctladdr.sin_addr));
+#else /* HADDRLIST */
+ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
+ sizeof(hisctladdr.sin_addr));
+#endif /* HADDRLIST */
+ ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
+ }
+ debug(F110,"ftp hookup C",hostnamebuf,0);
+ ftp_host = hostnamebuf;
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ debug(F101,"ftp hookup socket","",s);
+ if (s < 0) {
+ perror("ftp: socket");
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(0);
+ }
+ hisctladdr.sin_port = htons(cport);
+ errno = 0;
+#ifdef HADDRLIST
+ debug(F100,"ftp hookup HADDRLIST","",0);
+ while
+#else
+ debug(F100,"ftp hookup no HADDRLIST","",0);
+ if
+#endif /* HADDRLIST */
+ (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
+ debug(F101,"ftp hookup connect failed","",errno);
+#ifdef HADDRLIST
+ if (hp && hp->h_addr_list[1]) {
+ int oerrno = errno;
+
+ fprintf(stderr, "ftp: connect to address %s: ",
+ inet_ntoa(hisctladdr.sin_addr));
+ errno = oerrno;
+ perror((char *) 0);
+ hp->h_addr_list++;
+ memcpy((char *)&hisctladdr.sin_addr,
+ hp->h_addr_list[0],
+ sizeof(hisctladdr.sin_addr));
+ fprintf(stdout, "Trying %s...\n",
+ inet_ntoa(hisctladdr.sin_addr));
+#ifdef TCPIPLIB
+ socket_close(s);
+#else /* TCPIPLIB */
+ close(s);
+#endif /* TCPIPLIB */
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("ftp: socket");
+ ftpcode = -1;
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(0);
+ }
+ continue;
+ }
+#endif /* HADDRLIST */
+ perror("ftp: connect");
+ ftpcode = -1;
+ goto bad;
+ }
+ debug(F100,"ftp hookup connect ok","",0);
+
+ len = sizeof (myctladdr);
+ errno = 0;
+ if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+ debug(F101,"ftp hookup getsockname failed","",errno);
+ perror("ftp: getsockname");
+ ftpcode = -1;
+ goto bad;
+ }
+ debug(F100,"ftp hookup getsockname ok","",0);
+
+#ifndef NOHTTP
+ if (tcp_http_proxy) {
+#ifdef OS2
+ char * agent = "Kermit 95"; /* Default user agent */
+#else
+ char * agent = "C-Kermit";
+#endif /* OS2 */
+
+ if (http_connect(s,agent,NULL,
+ tcp_http_proxy_user,
+ tcp_http_proxy_pwd,
+ 0,
+ proxyhost
+ ) < 0) {
+ char * foo = NULL;
+#ifdef TCPIPLIB
+ socket_close(s);
+#else /* TCPIPLIB */
+ close(s);
+#endif /* TCPIPLIB */
+
+ while (foo == NULL && tcp_http_proxy != NULL ) {
+
+ if (tcp_http_proxy_errno == 401 ||
+ tcp_http_proxy_errno == 407 ) {
+ char uid[UIDBUFLEN];
+ char pwd[PWDSIZ];
+ struct txtbox tb[2];
+ int ok;
+
+ tb[0].t_buf = uid;
+ tb[0].t_len = UIDBUFLEN;
+ tb[0].t_lbl = "Proxy Userid: ";
+ tb[0].t_dflt = NULL;
+ tb[0].t_echo = 1;
+ tb[1].t_buf = pwd;
+ tb[1].t_len = 256;
+ tb[1].t_lbl = "Proxy Passphrase: ";
+ tb[1].t_dflt = NULL;
+ tb[1].t_echo = 2;
+
+ ok = uq_mtxt("Proxy Server Authentication Required\n",
+ NULL, 2, tb);
+
+ if (ok && uid[0]) {
+ char * proxy_user, * proxy_pwd;
+
+ proxy_user = tcp_http_proxy_user;
+ proxy_pwd = tcp_http_proxy_pwd;
+
+ tcp_http_proxy_user = uid;
+ tcp_http_proxy_pwd = pwd;
+
+ foo = ftp_hookup(host, port, 0);
+
+ debug(F110,"ftp_hookup()",foo,0);
+ memset(pwd,0,PWDSIZ);
+ tcp_http_proxy_user = proxy_user;
+ tcp_http_proxy_pwd = proxy_pwd;
+ } else
+ break;
+ } else
+ break;
+ }
+ if (foo != NULL)
+ return(foo);
+ perror("ftp: connect");
+ ftpcode = -1;
+ goto bad;
+ }
+ ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
+ }
+#endif /* NOHTTP */
+
+ csocket = s;
+
+#ifdef CK_SSL
+ if (tls) {
+ /* FTP over SSL
+ * If the connection is over an SSL proxy then the
+ * auth_type will be NULL. However, I'm not sure
+ * whether we should protect the data channel in
+ * that case or not.
+ */
+
+ debug(F100,"ftp hookup use_tls","",0);
+ if (!ssl_auth()) {
+ debug(F100,"ftp hookup ssl_auth failed","",0);
+ auth_type = NULL;
+ ftpcode = -1;
+ csocket = -1;
+ goto bad;
+ }
+ ssl_ftp_proxy = 1;
+ }
+#endif /* CK_SSL */
+
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+ if (!quiet)
+ printf("Connected to %s.\n", host);
+
+ /* Read greeting from server */
+ if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
+ debug(F100,"ftp hookup bad reply","",0);
+#ifdef TCPIPLIB
+ socket_close(csocket);
+#else /* TCPIPLIB */
+ close(csocket);
+#endif /* TCPIPLIB */
+ ftpcode = -1;
+ goto bad;
+ }
+#ifdef SO_OOBINLINE
+ {
+ int on = 1;
+ errno = 0;
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
+ sizeof(on)) < 0) {
+ perror("ftp: setsockopt");
+ debug(F101,"ftp hookup setsockopt failed","",errno);
+ }
+#ifdef DEBUG
+ else
+ debug(F100,"ftp hookup setsockopt ok","",0);
+#endif /* DEBUG */
+ }
+#endif /* SO_OOBINLINE */
+
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ return(ftp_host);
+
+ bad:
+ debug(F100,"ftp hookup bad","",0);
+#ifdef TCPIPLIB
+ socket_close(s);
+#else /* TCPIPLIB */
+ close(s);
+#endif /* TCPIPLIB */
+#ifdef DEBUG
+ debtim = xdebtim;
+#endif /* DEBUG */
+ csocket = -1;
+ return((char *)0);
+}
+
+static VOID
+ftp_init() {
+ int i, n;
+
+ /* The purpose of the initial REST 0 is not clear, but other FTP */
+ /* clients do it. In any case, failure of this command is not a */
+ /* reliable indication that the server does not support Restart. */
+
+ okrestart = 0;
+ if (!noinit) {
+ n = ftpcmd("REST 0",NULL,0,0,0);
+ if (n == REPLY_COMPLETE)
+ okrestart = 1;
+#ifdef COMMENT
+ else if (ftp_deb)
+ printf("WARNING: Unable to restore file pointer.\n");
+#endif /* COMMENT */
+ }
+ n = ftpcmd("SYST",NULL,0,0,0); /* Get server system type */
+ if (n == REPLY_COMPLETE) {
+ register char *cp, c = NUL;
+ cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
+ if (cp == NULL)
+ cp = ckstrchr(ftp_reply_str+4,'\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp; /* Save this char */
+ *cp = '\0'; /* Replace it with NUL */
+ }
+ if (!quiet)
+ printf("Remote system type is %s.\n",ftp_reply_str+4);
+ ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
+ if (cp) /* Put back saved char */
+ *cp = c;
+ }
+ alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
+
+ if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
+ else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
+ else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
+ else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
+ else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
+ else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
+ else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
+
+#ifdef FTP_PROXY
+ unix_proxy = 0;
+ if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
+#endif /* FTP_PROXY */
+
+ if (ftp_cmdlin && xfermode == XMODE_M)
+ ftp_typ = binary; /* Type given on command line */
+ else /* Otherwise set it automatically */
+ ftp_typ = alike ? FTT_BIN : FTT_ASC;
+ changetype(ftp_typ,0); /* Change to this type */
+ g_ftp_typ = ftp_typ; /* Make it the global type */
+ if (!quiet)
+ printf("Default transfer mode is %s\n",
+ ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
+ );
+ for (i = 0; i < 16; i++) /* Init server FEATure table */
+ sfttab[i] = 0;
+ if (!noinit) {
+ n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
+#ifdef COMMENT
+ if (n != REPLY_COMPLETE)
+ printf("WARNING: Server does not accept MODE S(TREAM)\n");
+#endif /* COMMENT */
+ n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
+#ifdef COMMENT
+ if (n != REPLY_COMPLETE)
+ printf("WARNING: Server does not accept STRU F(ILE)\n");
+#endif /* COMMENT */
+ if (featok) {
+ n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
+ if (n == REPLY_COMPLETE) {
+ debug(F101,"ftp_init FEAT","",sfttab[0]);
+ if (deblog || ftp_deb) {
+ int i;
+ for (i = 1; i < 16 && i < nfeattab; i++) {
+ debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
+ if (ftp_deb)
+ printf(" Server %s %s\n",
+ sfttab[i] ? "supports" : "does not support",
+ feattab[i].kwd
+ );
+ }
+ /* Deal with disabled MLST opts here if necessary */
+ /* But why would it be? */
+ }
+ }
+ }
+ }
+}
+
+static int
+ftp_login(host) char * host; { /* (also called from ckuusy.c) */
+ static char ftppass[PASSBUFSIZ]="";
+ char tmp[PASSBUFSIZ];
+ char *user = NULL, *pass = NULL, *acct = NULL;
+ int n, aflag = 0;
+ extern char uidbuf[];
+ extern char pwbuf[];
+ extern int pwflg, pwcrypt;
+
+ debug(F111,"ftp_login",ftp_logname,ftp_log);
+
+ if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
+ anonymous = 1;
+ if (!ckstrcmp(ftp_logname,"ftp",-1,0))
+ anonymous = 1;
+
+#ifdef FTP_SRP
+ if (auth_type && !strcmp(auth_type, "SRP")) {
+ user = srp_user;
+ pass = srp_pass;
+ acct = srp_acct;
+ } else
+#endif /* FTP_SRP */
+ if (anonymous) {
+ user = "anonymous";
+ if (ftp_tmp) { /* They gave a password */
+ pass = ftp_tmp;
+ } else if (ftp_apw) { /* SET FTP ANONYMOUS-PASSWORD */
+ pass = ftp_apw;
+ } else { /* Supply user@host */
+ ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
+ pass = tmp;
+ }
+ debug(F110,"ftp anonymous",pass,0);
+ } else {
+#ifdef USE_RUSERPASS
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ ftpcode = -1;
+ return(0);
+ }
+#endif /* USE_RUSERPASS */
+ if (ftp_logname) {
+ user = ftp_logname;
+ pass = ftp_tmp;
+ } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
+ user = uidbuf;
+ if (ftp_tmp) {
+ pass = ftp_tmp;
+ } else if (pwbuf[0] && pwflg) {
+ ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
+#ifdef OS2
+ if ( pwcrypt )
+ ck_encrypt((char *)ftppass);
+#endif /* OS2 */
+ pass = ftppass;
+ }
+ }
+ acct = ftp_acc;
+ while (user == NULL) {
+ char *myname, prompt[PROMPTSIZ];
+ int ok;
+
+ myname = whoami();
+ if (myname)
+ ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ else
+ ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
+ tmp[0] = '\0';
+
+ ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (!ok || *tmp == '\0')
+ user = myname;
+ else
+ user = brstrip(tmp);
+ }
+ }
+ n = ftpcmd("USER",user,-1,-1,ftp_vbm);
+ if (n == REPLY_COMPLETE) {
+ /* determine if we need to send a dummy password */
+ if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
+ ftpcmd("PASS dummy",NULL,0,0,1);
+ } else if (n == REPLY_CONTINUE) {
+#ifdef CK_ENCRYPTION
+ int oldftp_cpl;
+#endif /* CK_ENCRYPTION */
+
+ if (pass == NULL) {
+ int ok;
+ setint();
+ ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (ok)
+ pass = brstrip(ftppass);
+ }
+
+#ifdef CK_ENCRYPTION
+ oldftp_cpl = ftp_cpl;
+ ftp_cpl = FPL_PRV;
+#endif /* CK_ENCRYPTION */
+ n = ftpcmd("PASS",pass,-1,-1,1);
+ if (!anonymous && pass) {
+ char * p = pass;
+ while (*p++) *(p-1) = NUL;
+ makestr(&ftp_tmp,NULL);
+ }
+#ifdef CK_ENCRYPTION
+ /* level may have changed */
+ if (ftp_cpl == FPL_PRV)
+ ftp_cpl = oldftp_cpl;
+#endif /* CK_ENCRYPTION */
+ }
+ if (n == REPLY_CONTINUE) {
+ aflag++;
+ if (acct == NULL) {
+ static char ftpacct[80];
+ int ok;
+ setint();
+ ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (ok)
+ acct = brstrip(ftpacct);
+ }
+ n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
+ }
+ if (n != REPLY_COMPLETE) {
+ fprintf(stderr, "FTP login failed.\n");
+ if (haveurl)
+ doexit(BAD_EXIT,-1);
+ return(0);
+ }
+ if (!aflag && acct != NULL) {
+ ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
+ }
+ makestr(&ftp_logname,user);
+ loggedin = 1;
+#ifdef LOCUS
+ /* Unprefixed file management commands go to server */
+ if (autolocus && !ftp_cmdlin) {
+ setlocus(0,1);
+ }
+#endif /* LOCUS */
+ ftp_init();
+
+ if (anonymous && !quiet) {
+ printf(" Logged in as anonymous (%s)\n",pass);
+ memset(pass, 0, strlen(pass));
+ }
+ if (ftp_rdir) {
+ if (doftpcwd(ftp_rdir,-1) < 1)
+ doexit(BAD_EXIT,-1);
+ }
+
+#ifdef FTP_PROXY
+ if (proxy)
+ return(1);
+#endif /* FTP_PROXY */
+ return(1);
+}
+
+static int
+ftp_reset() {
+ int rc;
+#ifdef BSDSELECT
+ int nfnd = 1;
+ fd_set mask;
+ FD_ZERO(&mask);
+ while (nfnd > 0) {
+ FD_SET(csocket, &mask);
+ if ((nfnd = empty(&mask,0)) < 0) {
+ perror("reset");
+ ftpcode = -1;
+ lostpeer();
+ return(0);
+ } else if (nfnd) {
+ getreply(0,-1,-1,ftp_vbm,0);
+ }
+ }
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ int nfnd = 1;
+ while (nfnd > 0) {
+ if ((nfnd = empty(&csocket,1,0)) < 0) {
+ perror("reset");
+ ftpcode = -1;
+ lostpeer();
+ return(0);
+ } else if (nfnd) {
+ getreply(0,-1,-1,ftp_vbm,0);
+ }
+ }
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
+ if (rc > 0)
+ loggedin = 0;
+ return(rc);
+}
+
+static int
+ftp_rename(from, to) char * from, * to; {
+ int lcs = -1, rcs = -1;
+#ifndef NOCSETS
+ if (ftp_xla) {
+ lcs = ftp_csl;
+ if (lcs < 0) lcs = fcharset;
+ rcs = ftp_csx;
+ if (rcs < 0) rcs = ftp_csr;
+ }
+#endif /* NOCSETS */
+ if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
+ return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
+ }
+ return(0); /* Failure */
+}
+
+static int
+ftp_umask(mask) char * mask; {
+ int rc;
+ rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
+ return(rc);
+}
+
+static int
+ftp_user(user,pass,acct) char * user, * pass, * acct; {
+ int n = 0, aflag = 0;
+ char pwd[PWDSIZ];
+
+ if (!auth_type && ftp_aut) {
+#ifdef FTP_SRP
+ if (ck_srp_is_installed()) {
+ if (srp_ftp_auth( NULL, user, pass)) {
+ makestr(&pass,srp_pass);
+ }
+ }
+#endif /* FTP_SRP */
+ }
+ n = ftpcmd("USER",user,-1,-1,ftp_vbm);
+ if (n == REPLY_COMPLETE)
+ n = ftpcmd("PASS dummy",NULL,0,0,1);
+ else if (n == REPLY_CONTINUE) {
+#ifdef CK_ENCRYPTION
+ int oldftp_cpl;
+#endif /* CK_ENCRYPTION */
+ if (pass == NULL || !pass[0]) {
+ int ok;
+ pwd[0] = '\0';
+ setint();
+ ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (ok)
+ pass = brstrip(pwd);
+ }
+
+#ifdef CK_ENCRYPTION
+ if ((oldftp_cpl = ftp_cpl) == PROT_S)
+ ftp_cpl = PROT_P;
+#endif /* CK_ENCRYPTION */
+ n = ftpcmd("PASS",pass,-1,-1,1);
+ memset(pass, 0, strlen(pass));
+#ifdef CK_ENCRYPTION
+ /* level may have changed */
+ if (ftp_cpl == PROT_P)
+ ftp_cpl = oldftp_cpl;
+#endif /* CK_ENCRYPTION */
+ }
+ if (n == REPLY_CONTINUE) {
+ if (acct == NULL || !acct[0]) {
+ int ok;
+ pwd[0] = '\0';
+ setint();
+ ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (ok)
+ acct = pwd;
+ }
+ n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
+ aflag++;
+ }
+ if (n != REPLY_COMPLETE) {
+ printf("Login failed.\n");
+ return(0);
+ }
+ if (!aflag && acct != NULL && acct[0]) {
+ n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
+ }
+ if (n == REPLY_COMPLETE) {
+ makestr(&ftp_logname,user);
+ loggedin = 1;
+ ftp_init();
+ return(1);
+ }
+ return(0);
+}
+
+char *
+ftp_authtype() {
+ if (!connected)
+ return("NULL");
+ return(auth_type ? auth_type : "NULL");
+}
+
+char *
+ftp_cpl_mode() {
+ switch (ftp_cpl) {
+ case FPL_CLR:
+ return("clear");
+ case FPL_SAF:
+ return("safe");
+ case FPL_PRV:
+ return("private");
+ case FPL_CON:
+ return("confidential");
+ default:
+ return("(error)");
+ }
+}
+
+char *
+ftp_dpl_mode() {
+ switch (ftp_dpl) {
+ case FPL_CLR:
+ return("clear");
+ case FPL_SAF:
+ return("safe");
+ case FPL_PRV:
+ return("private");
+ case FPL_CON:
+ return("confidential");
+ default:
+ return("(error)");
+ }
+}
+
+
+/* remote_files() */
+/*
+ Returns next remote filename on success;
+ NULL on error or no more files with global rfrc set to:
+ -1: Bad argument
+ -2: Server error response to NLST, e.g. file not found
+ -3: No more files
+ -9: Internal error
+*/
+#define FTPNAMBUFLEN CKMAXPATH+1024
+
+/* Check: ckmaxfiles CKMAXOPEN */
+
+#define MLSDEPTH 128 /* Stack of open temp files */
+static int mlsdepth = 0; /* Temp file stack depth */
+static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
+static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
+
+static VOID
+mlsreset() { /* Reset MGET temp-file stack */
+ int i;
+ for (i = 0; i <= mlsdepth; i++) {
+ if (tmpfilptr[i]) {
+ fclose(tmpfilptr[i]);
+ tmpfilptr[i] = NULL;
+ if (tmpfilnam[i]) {
+#ifdef OS2
+ unlink(tmpfilnam[i]);
+#endif /* OS2 */
+ free(tmpfilnam[i]);
+ }
+ }
+ }
+ mlsdepth = 0;
+}
+
+static CHAR *
+#ifdef CK_ANSIC
+remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
+#else /* CK_ANSIC */
+remote_files(new_query, arg, pattern, proxy_switch)
+ int new_query;
+ CHAR * arg; /* That we send to the server */
+ CHAR * pattern; /* That we use locally */
+ int proxy_switch;
+#endif /* CK_ANSIC */
+/* remote_files */ {
+ static CHAR buf[FTPNAMBUFLEN];
+ CHAR *cp, *whicharg;
+ char * cdto = NULL;
+ char * p;
+ int i, x, forced = 0;
+ int lcs = 0, rcs = 0, xlate = 0;
+
+ debug(F101,"ftp remote_files new_query","",new_query);
+ debug(F110,"ftp remote_files arg",arg,0);
+ debug(F110,"ftp remote_files pattern",pattern,0);
+
+ rfrc = -1;
+ if (pattern) /* Treat empty pattern same as NULL */
+ if (!*pattern)
+ pattern = NULL;
+ if (arg) /* Ditto for arg */
+ if (!*arg)
+ arg = NULL;
+
+ again:
+
+ if (new_query) {
+ if (tmpfilptr[mlsdepth]) {
+ fclose(tmpfilptr[mlsdepth]);
+ tmpfilptr[mlsdepth] = NULL;
+#ifdef OS2
+ if (!ftp_deb && !deblog)
+ unlink(tmpfilnam[mlsdepth]);
+#endif /* OS2 */
+ }
+ }
+ if (tmpfilptr[mlsdepth] == NULL) {
+ extern char * tempdir;
+ char * p;
+ debug(F110,"ftp remote_files tempdir",tempdir,0);
+ if (tempdir) {
+ p = tempdir;
+ } else {
+#ifdef OS2
+#ifdef NT
+ p = getenv("K95TMP");
+#else
+ p = getenv("K2TMP");
+#endif /* NT */
+ if (!p)
+#endif /* OS2 */
+ p = getenv("CK_TMP");
+ if (!p)
+ p = getenv("TMPDIR");
+ if (!p) p = getenv("TEMP");
+ if (!p) p = getenv("TMP");
+#ifdef OS2ORUNIX
+ if (p) {
+ int len = strlen(p);
+ if (p[len-1] != '/'
+#ifdef OS2
+ && p[len-1] != '\\'
+#endif /* OS2 */
+ ) {
+ static char foo[CKMAXPATH];
+ ckstrncpy(foo,p,CKMAXPATH);
+ ckstrncat(foo,"/",CKMAXPATH);
+ p = foo;
+ }
+ } else
+#else /* OS2ORUNIX */
+ if (!p)
+#endif /* OS2ORUNIX */
+#ifdef UNIX /* Systems that have a standard */
+ p = "/tmp/"; /* temporary directory... */
+#else
+#ifdef datageneral
+ p = ":TMP:";
+#else
+ p = "";
+#endif /* datageneral */
+#endif /* UNIX */
+ }
+ debug(F110,"ftp remote_files p",p,0);
+
+ /* Get temp file */
+
+ if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
+ ckmakmsg((char *)tmpfilnam[mlsdepth],
+ CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
+ } else {
+ printf("?Malloc failure: remote_files()\n");
+ return(NULL);
+ }
+
+#ifdef NT
+ {
+ char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
+ if ( tmpfil )
+ ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
+ }
+#else /* NT */
+#ifdef MKTEMP
+#ifdef MKSTEMP
+ x = mkstemp((char *)tmpfilnam[mlsdepth]);
+ if (x > -1) close(x); /* We just want the name. */
+#else
+ mktemp((char *)tmpfilnam[mlsdepth]);
+#endif /* MKSTEMP */
+ /* if no mktmpnam() the name will just be "ckXXXXXX"... */
+#endif /* MKTEMP */
+#endif /* NT */
+
+ debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
+ tmpfilnam[mlsdepth],mlsdepth);
+
+#ifdef FTP_PROXY
+ if (proxy_switch) {
+ pswitch(!proxy);
+ }
+#endif /* FTP_PROXY */
+
+ debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
+ debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
+ debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
+
+#ifndef NOCSETS
+ xlate = ftp_xla; /* SET FTP CHARACTER-SET-TRANSLATION */
+ if (xlate) { /* ON? */
+ lcs = ftp_csl; /* Local charset */
+ if (lcs < 0) lcs = fcharset;
+ if (lcs < 0) xlate = 0;
+ }
+ if (xlate) { /* Still ON? */
+ rcs = ftp_csx; /* Remote (Server) charset */
+ if (rcs < 0) rcs = ftp_csr;
+ if (rcs < 0) xlate = 0;
+ }
+#endif /* NOCSETS */
+
+ forced = mgetforced; /* MGET method forced? */
+ if (!forced || !mgetmethod) /* Not forced... */
+ mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
+ SND_MLS :
+ SND_NLS;
+/*
+ User's Command: Result:
+ mget /nlst NLST (NULL)
+ mget /nlst foo NLST foo
+ mget /nlst *.txt NLST *.txt
+ mget /nlst /match:*.txt NLST (NULL)
+ mget /nlst /match:*.txt foo NLST foo
+ mget /mlsd MLSD (NULL)
+ mget /mlsd foo MLSD foo
+ mget /mlsd *.txt MLSD (NULL)
+ mget /mlsd /match:*.txt MLSD (NULL)
+ mget /mlsd /match:*.txt foo MLSD foo
+*/
+ x = -1;
+ while (x < 0) {
+ if (pattern) { /* Don't simplify this! */
+ whicharg = arg;
+ } else if (mgetmethod == SND_MLS) {
+ if (arg)
+ whicharg = iswild((char *)arg) ? NULL : arg;
+ else
+ whicharg = NULL;
+ } else {
+ whicharg = arg;
+ }
+ debug(F110,"ftp remote_files mgetmethod",
+ mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
+ debug(F110,"ftp remote_files whicharg",whicharg,0);
+
+ x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
+ (char *)tmpfilnam[mlsdepth],
+ (char *)whicharg,
+ "wb",
+ 0,
+ 0,
+ NULL,
+ xlate,
+ lcs,
+ rcs
+ );
+ if (x < 0) { /* Chosen method wasn't accepted */
+ if (forced) {
+ if (ftpcode > 500 && ftpcode < 505 && !quiet)
+ printf("?%s: Not supported by server\n",
+ mgetmethod == SND_MLS ? "MLSD" : "NLST"
+ );
+ rfrc = -2; /* Fail */
+ return(NULL);
+ }
+ /* Not forced - if MLSD failed, try NLST */
+ if (mgetmethod == SND_MLS) { /* Server lied about MLST */
+ sfttab[SFT_MLST] = 0; /* So disable it */
+ mlstok = 0; /* and */
+ mgetmethod = SND_NLS; /* try NLST */
+ continue;
+ }
+ rfrc = -2;
+ return(NULL);
+ }
+ }
+#ifdef FTP_PROXY
+ if (proxy_switch) {
+ pswitch(!proxy);
+ }
+#endif /* FTP_PROXY */
+ tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
+#ifndef OS2
+ if (tmpfilptr[mlsdepth]) {
+ if (!ftp_deb && !deblog)
+ unlink(tmpfilnam[mlsdepth]);
+ }
+#endif /* OS2 */
+ notemp:
+ if (!tmpfilptr[mlsdepth]) {
+ debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
+ if ((!dpyactive || ftp_deb))
+ printf("?Can't find list of remote files, oops\n");
+ rfrc = -9;
+ return(NULL);
+ }
+ if (ftp_deb)
+ printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
+ }
+ buf[0] = NUL;
+ buf[FTPNAMBUFLEN-1] = NUL;
+ buf[FTPNAMBUFLEN-2] = NUL;
+
+ /* We have to redo all this because the first time was only for */
+ /* for getting the file list, now it's for getting each file */
+
+ if (arg && mgetmethod == SND_MLS) { /* MLSD */
+ if (!pattern && iswild((char *)arg)) {
+ pattern = arg; /* Wild arg is really a pattern */
+ if (pattern)
+ if (!*pattern)
+ pattern = NULL;
+ arg = NULL; /* and not an arg */
+ }
+ if (new_query) { /* Initial query? */
+ cdto = (char *)arg; /* (nonwild) arg given? */
+ if (cdto)
+ if (!*cdto)
+ cdto = NULL;
+ if (cdto) /* If so, then CD to it */
+ doftpcwd(cdto,0);
+ }
+ }
+ new_query = 0;
+
+ if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
+ fclose(tmpfilptr[mlsdepth]);
+ tmpfilptr[mlsdepth] = NULL;
+
+#ifdef OS2
+ if (!ftp_deb && !deblog)
+ unlink(tmpfilnam[mlsdepth]);
+#endif /* OS2 */
+ if (ftp_deb && !deblog) {
+ printf("(Temporary file %s NOT deleted)\n",
+ (char *)tmpfilnam[mlsdepth]);
+ }
+ if (mlsdepth <= 0) { /* EOF at depth 0 */
+ rfrc = -3; /* means we're done */
+ return(NULL);
+ }
+ printf("POPPING(%d)...\n",mlsdepth-1);
+ if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
+ mlsdepth--;
+ doftpcdup();
+ zchdir(".."); /* <-- Not portable */
+ goto again;
+ }
+ if (buf[FTPNAMBUFLEN-1]) {
+ printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
+ FTPNAMBUFLEN
+ );
+ debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
+ return(NULL);
+ }
+ /* debug(F110,"ftp remote_files buf 1",buf,0); */
+ if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
+ *cp = '\0';
+ if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
+ *cp = '\0';
+ debug(F110,"ftp remote_files buf",buf,0);
+ rfrc = 0;
+
+ if (ftp_deb)
+ printf("[%s]\n",(char *)buf);
+
+ havesize = -1L; /* Initialize file facts... */
+ havetype = -0;
+ makestr(&havemdtm,NULL);
+ p = (char *)buf;
+
+ if (mgetmethod == SND_NLS) { /* NLST... */
+ if (pattern) {
+ if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
+ goto again;
+ }
+ } else { /* MLSD... */
+ p = parsefacts((char *)buf);
+ switch (havetype) {
+ case FTYP_FILE: /* File: Get it if it matches */
+ if (pattern) {
+ if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
+ goto again;
+ }
+ break;
+ case FTYP_CDIR: /* Current directory */
+ case FTYP_PDIR: /* Parent directory */
+ goto again; /* Skip */
+ case FTYP_DIR: /* (Sub)Directory */
+ if (!recursive) /* If not /RECURSIVE */
+ goto again; /* Skip */
+ if (mlsdepth < MLSDEPTH) {
+ char * p2 = NULL;
+ mlsdepth++;
+ printf("RECURSING [%s](%d)...\n",p,mlsdepth);
+ if (doftpcwd(p,0) > 0) {
+ int x;
+ if (!ckstrchr(p,'/')) {
+ /* zmkdir() needs dirsep */
+ if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
+ strcpy(p2,p); /* SAFE */
+ strcat(p2,"/"); /* SAFE */
+ p = p2;
+ }
+ }
+#ifdef NOMKDIR
+ x = -1;
+#else
+ x = zmkdir(p);
+#endif /* NOMKDIR */
+ if (x > -1) {
+ zchdir(p);
+ p = (char *)remote_files(1,arg,pattern,0);
+ if (p2) free(p2);
+ } else {
+ printf("?mkdir failed: [%s] Depth=%d\n",
+ p,
+ mlsdepth
+ );
+ mlsreset();
+ if (p2) free(p2);
+ return(NULL);
+ }
+ } else {
+ printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
+ mlsreset();
+ return(NULL);
+ }
+ } else {
+ printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
+ mlsdepth
+ );
+ mlsreset();
+ return(NULL);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"remote_files havesize","",havesize);
+ debug(F101,"remote_files havetype","",havetype);
+ debug(F110,"remote_files havemdtm",havemdtm,0);
+ debug(F110,"remote_files name",p,0);
+ }
+#endif /* DEBUG */
+ return((CHAR *)p);
+}
+
+/* N O T P O R T A B L E !!! */
+
+#if (SIZEOF_SHORT == 4)
+typedef unsigned short ftp_uint32;
+typedef short ftp_int32;
+#else
+#if (SIZEOF_INT == 4)
+typedef unsigned int ftp_uint32;
+typedef int ftp_int32;
+#else
+#if (SIZEOF_LONG == 4)
+typedef ULONG ftp_uint32;
+typedef long ftp_int32;
+#endif
+#endif
+#endif
+
+/* Perhaps use these in general, certainly use them for GSSAPI */
+
+#ifndef looping_write
+#define ftp_int32 int
+#define ftp_uint32 unsigned int
+static int
+looping_write(fd, buf, len)
+ int fd;
+ register CONST char *buf;
+ int len;
+{
+ int cc;
+ register int wrlen = len;
+ do {
+ cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
+ 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 = recv(fd, (char *)buf, len,0);
+ 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 /* looping_read */
+
+#define ERR -2
+
+#ifdef COMMENT
+static
+secure_putbyte(fd, c) int fd; CHAR c; {
+ int ret;
+
+ ucbuf[nout++] = c;
+ if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
+ nout = 0;
+ if (!ftpissecure())
+ ret = send(fd, (SENDARG2TYPE)ucbuf,
+ (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
+ else
+ ret = secure_putbuf(fd,
+ ucbuf,
+ (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
+ );
+ return(ret?ret:c);
+ }
+ return(c);
+}
+#endif /* COMMENT */
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+static int
+secure_flush(fd) int fd; {
+ int rc = 0;
+ int len = 0;
+
+ if (nout > 0) {
+ len = nout;
+ if (!ftpissecure()) {
+ rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
+ nout = 0;
+ goto xflush;
+ } else {
+ rc = secure_putbuf(fd, ucbuf, nout);
+ if (rc)
+ goto xflush;
+ }
+ }
+ rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
+
+ xflush:
+ if (rc > -1 && len > 0 && fdispla != XYFD_B) {
+ spackets++;
+ spktl = len;
+ ftscreen(SCR_PT,'D',spackets,NULL);
+ }
+ return(rc);
+}
+
+#ifdef COMMENT /* (not used) */
+/* returns:
+ * c>=0 on success
+ * -1 on error
+ * -2 on security error
+ */
+static int
+#ifdef CK_ANSIC
+secure_putc(char c, int fd)
+#else
+secure_putc(c, fd) char c; int fd;
+#endif /* CK_ANSIC */
+/* secure_putc */ {
+ return(secure_putbyte(fd, (CHAR) c));
+}
+#endif /* COMMENT */
+
+/* returns:
+ * nbyte on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+static int
+#ifdef CK_ANSIC
+secure_write(int fd, CHAR * buf, unsigned int nbyte)
+#else
+secure_write(fd, buf, nbyte)
+ int fd;
+ CHAR * buf;
+ unsigned int nbyte;
+#endif /* CK_ANSIC */
+{
+ int ret;
+
+ if (!ftpissecure()) {
+ if (nout > 0) {
+ if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
+ return(ret);
+ nout = 0;
+ }
+ return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
+ } else {
+ int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
+ int bsent = 0;
+
+ while (bsent < nbyte) {
+ int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
+ (ucbuflen - nout) : (nbyte - bsent));
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"secure_write ucbuflen","",ucbuflen);
+ debug(F101,"secure_write ucbufsiz","",ucbufsiz);
+ debug(F101,"secure_write bsent","",bsent);
+ debug(F101,"secure_write b2cp","",b2cp);
+ }
+#endif /* DEBUG */
+ memcpy(&ucbuf[nout],&buf[bsent],b2cp);
+ nout += b2cp;
+ bsent += b2cp;
+
+ if (nout == ucbuflen) {
+ nout = 0;
+ ret = secure_putbuf(fd, ucbuf, ucbuflen);
+ if (ret < 0)
+ return(ret);
+ }
+ }
+ return(bsent);
+ }
+}
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+static int
+#ifdef CK_ANSIC
+secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
+#else
+secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
+#endif /* CK_ANSIC */
+{
+ static char *outbuf = NULL; /* output ciphertext */
+#ifdef FTP_SECURITY
+ static unsigned int bufsize = 0; /* size of outbuf */
+#endif /* FTP_SECURITY */
+ ftp_int32 length = 0;
+ ftp_uint32 net_len = 0;
+
+ /* Other auth types go here ... */
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ int count, error;
+
+ /* there is no need to send an empty buffer when using SSL/TLS */
+ if ( nbyte == 0 )
+ return(0);
+
+ count = SSL_write(ssl_ftp_data_con, buf, nbyte);
+ error = SSL_get_error(ssl_ftp_data_con,count);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ return(0);
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_SYSCALL:
+#ifdef NT
+ {
+ int gle = GetLastError();
+ if (gle == 0)
+ return(0);
+ debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
+ }
+#endif /* NT */
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ return(-1);
+ }
+ return(-1);
+ }
+#endif /* CK_SSL */
+
+#ifdef FTP_SRP
+ if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
+ if (bufsize < nbyte + FUDGE_FACTOR) {
+ if (outbuf?
+ (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
+ (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
+ bufsize = nbyte + FUDGE_FACTOR;
+ } else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)", ck_errstr());
+ return(ERR);
+ }
+ }
+ if ((length =
+ srp_encode(ftp_dpl == FPL_PRV,
+ (CHAR *) buf,
+ (CHAR *) outbuf,
+ nbyte
+ )
+ ) < 0) {
+ secure_error ("srp_encode failed");
+ return ERR;
+ }
+ }
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
+ struct sockaddr_in myaddr, hisaddr;
+ GSOCKNAME_T len;
+ len = sizeof(myaddr);
+ if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
+ secure_error("secure_putbuf: getsockname failed");
+ return(ERR);
+ }
+ len = sizeof(hisaddr);
+ if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
+ secure_error("secure_putbuf: getpeername failed");
+ return(ERR);
+ }
+ if (bufsize < nbyte + FUDGE_FACTOR) {
+ if (outbuf ?
+ (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
+ (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
+ bufsize = nbyte + FUDGE_FACTOR;
+ } else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)", ck_errstr());
+ return(ERR);
+ }
+ }
+ if (ftp_dpl == FPL_PRV) {
+ length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
+ ftp_sched,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &myaddr,
+ &hisaddr
+ );
+ } else {
+ length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &myaddr,
+ &hisaddr
+ );
+ }
+ if (length == -1) {
+ secure_error("krb_mk_%s failed for KERBEROS_V4",
+ ftp_dpl == FPL_PRV ? "priv" : "safe");
+ return(ERR);
+ }
+ }
+#endif /* FTP_KRB4 */
+#ifdef FTP_GSSAPI
+ if (ck_gssapi_is_installed() && (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,
+ (ftp_dpl == FPL_PRV), /* 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 */
+ user_gss_error(maj_stat, min_stat,
+ ftp_dpl == FPL_PRV?
+ "GSSAPI seal failed":
+ "GSSAPI sign failed");
+ return(ERR);
+ }
+ if (bufsize < out_buf.length) {
+ if (outbuf ?
+ (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
+ (outbuf = malloc((unsigned) out_buf.length))) {
+ bufsize = out_buf.length;
+ } else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)",
+ ck_errstr());
+ return(ERR);
+ }
+ }
+ memcpy(outbuf, out_buf.value, length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+ }
+#endif /* FTP_GSSAPI */
+ net_len = htonl((ULONG) length);
+ if (looping_write(fd, (char *)&net_len, 4) == -1)
+ return(-1);
+ if (looping_write(fd, outbuf, length) != length)
+ return(-1);
+ return(0);
+}
+
+/* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
+
+static int
+secure_getbyte(fd,fc) int fd,fc; {
+ /* number of chars in ucbuf, pointer into ucbuf */
+ static unsigned int nin = 0, bufp = 0;
+ int kerror;
+ ftp_uint32 length;
+
+ if (fc) {
+ nin = bufp = 0;
+ ucbuf[0] = NUL;
+ return(0);
+ }
+ if (nin == 0) {
+ if (iscanceled())
+ return(-9);
+#ifdef CK_SSL
+ if (ssl_ftp_data_active_flag) {
+ int count, error;
+ count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
+ error = SSL_get_error(ssl_ftp_data_con,count);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ nin = bufp = count;
+ rpackets++;
+ pktnum++;
+ if (fdispla != XYFD_B) {
+ rpktl = count;
+ ftscreen(SCR_PT,'D',rpackets,NULL);
+ }
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_SYSCALL:
+#ifdef NT
+ {
+ int gle = GetLastError();
+ }
+#endif /* NT */
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ nin = bufp = count = 0;
+ SSL_shutdown(ssl_ftp_data_con);
+ SSL_free(ssl_ftp_data_con);
+ ssl_ftp_data_active_flag = 0;
+ ssl_ftp_data_con = NULL;
+#ifdef TCPIPLIB
+ socket_close(data);
+#else /* TCPIPLIB */
+#ifdef USE_SHUTDOWN
+ shutdown(data, 1+1);
+#endif /* USE_SHUTDOWN */
+ close(data);
+#endif /* TCPIPLIB */
+ data = -1;
+ globaldin = data;
+ break;
+ }
+ } else
+#endif /* CK_SSL */
+ {
+ kerror = looping_read(fd, (char *)&length, sizeof(length));
+ if (kerror != sizeof(length)) {
+ secure_error("Couldn't read PROT buffer length: %d/%s",
+ kerror,
+ kerror == -1 ? ck_errstr()
+ : "premature EOF"
+ );
+ return(ERR);
+ }
+ debug(F101,"secure_getbyte length","",length);
+ debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
+
+ length = (ULONG) ntohl(length);
+ if (length > maxbuf) {
+ secure_error("Length (%d) of PROT buffer > PBSZ=%u",
+ length,
+ maxbuf
+ );
+ return(ERR);
+ }
+ if ((kerror = looping_read(fd, ucbuf, length)) != length) {
+ secure_error("Couldn't read %u byte PROT buffer: %s",
+ length,
+ kerror == -1 ? ck_errstr() : "premature EOF"
+ );
+ return(ERR);
+ }
+
+ /* Other auth types go here ... */
+#ifdef FTP_SRP
+ if (strcmp(auth_type, "SRP") == 0) {
+ if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
+ (CHAR *) ucbuf,
+ ucbuf,
+ length
+ )
+ ) == -1) {
+ secure_error ("srp_encode failed" );
+ return ERR;
+ }
+ }
+#endif /* FTP_SRP */
+#ifdef FTP_KRB4
+ if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+ struct sockaddr_in myaddr, hisaddr;
+ GSOCKNAME_T len;
+ len = sizeof(myaddr);
+ if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
+ secure_error("secure_putbuf: getsockname failed");
+ return(ERR);
+ }
+ len = sizeof(hisaddr);
+ if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
+ secure_error("secure_putbuf: getpeername failed");
+ return(ERR);
+ }
+ if (ftp_dpl) {
+ kerror = krb_rd_priv(ucbuf, length, ftp_sched,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &hisaddr, &myaddr, &ftp_msg_data);
+ } else {
+ kerror = krb_rd_safe(ucbuf, length,
+#ifdef KRB524
+ ftp_cred.session,
+#else /* KRB524 */
+ &ftp_cred.session,
+#endif /* KRB524 */
+ &hisaddr, &myaddr, &ftp_msg_data);
+ }
+ if (kerror) {
+ secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
+ ftp_dpl == FPL_PRV ? "priv" : "safe",
+ krb_get_err_text(kerror));
+ return(ERR);
+ }
+ memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
+ nin = bufp = ftp_msg_data.app_length;
+ }
+#endif /* FTP_KRB4 */
+#ifdef FTP_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 = (ftp_dpl == FPL_PRV);
+ /* decrypt/verify the message */
+ 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,
+ (ftp_dpl == FPL_PRV)?
+ "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 /* FTP_GSSAPI */
+ /* Other auth types go here ... */
+
+ /* Update file transfer display */
+ rpackets++;
+ pktnum++;
+ if (fdispla != XYFD_B) {
+ rpktl = nin;
+ ftscreen(SCR_PT,'D',rpackets,NULL);
+ }
+ }
+ }
+ if (nin == 0)
+ return(EOF);
+ else
+ return(ucbuf[bufp - nin--]);
+}
+
+/* secure_getc(fd,fc)
+ * Call with:
+ * fd = file descriptor for connection.
+ * fc = 0 to get a character, fc != 0 to initialize buffer pointers.
+ * Returns:
+ * c>=0 on success (character value)
+ * -1 on EOF
+ * -2 on security error
+ */
+static int
+secure_getc(fd,fc) int fd,fc; { /* file descriptor, function code */
+ if (!ftpissecure()) {
+ static unsigned int nin = 0, bufp = 0;
+ if (fc) {
+ nin = bufp = 0;
+ ucbuf[0] = NUL;
+ return(0);
+ }
+ if (nin == 0) {
+ if (iscanceled())
+ return(-9);
+ nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
+ if (nin <= 0) {
+ debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
+ debug(F101,"secure_getc returns EOF","",EOF);
+ nin = bufp = 0;
+ return(EOF);
+ }
+ debug(F101,"ftp secure_getc recv","",nin);
+ hexdump("ftp secure_getc recv",ucbuf,16);
+ rpackets++;
+ pktnum++;
+ if (fdispla != XYFD_B) {
+ rpktl = nin;
+ ftscreen(SCR_PT,'D',rpackets,NULL);
+ }
+ }
+ return(ucbuf[bufp - nin--]);
+ } else
+ return(secure_getbyte(fd,fc));
+}
+
+/* returns:
+ * n>0 on success (n == # of bytes read)
+ * 0 on EOF
+ * -1 on error (errno set), only for FPL_CLR
+ * -2 on security error
+ */
+static int
+secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
+ static int c = 0;
+ int i;
+
+ debug(F101,"secure_read bytes requested","",nbyte);
+ if (c == EOF)
+ return(c = 0);
+ for (i = 0; nbyte > 0; nbyte--) {
+ c = secure_getc(fd,0);
+ switch (c) {
+ case -9: /* Canceled from keyboard */
+ debug(F101,"ftp secure_read interrupted","",c);
+ return(0);
+ case ERR:
+ debug(F101,"ftp secure_read error","",c);
+ return(c);
+ case EOF:
+ debug(F101,"ftp secure_read EOF","",c);
+ if (!i)
+ c = 0;
+ return(i);
+ default:
+ buf[i++] = c;
+ }
+ }
+ return(i);
+}
+
+#ifdef USE_RUSERPASS
+/* BEGIN_RUSERPASS
+ *
+ * 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 */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+char * renvlook();
+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 int
+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;
+#ifdef NT
+ struct _stat stb;
+#else /* NT */
+ struct stat stb;
+#endif /* NT */
+
+ hdir = getenv("HOME");
+ if (hdir == NULL)
+ hdir = ".";
+ ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
+ cfile = fopen(buf, "r");
+ if (cfile == NULL) {
+ if (errno != ENOENT)
+ perror(buf);
+ return(0);
+ }
+ if (gethostname(myname, MAXHOSTNAMELEN) < 0)
+ myname[0] = '\0';
+ if ((mydomain = ckstrchr(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 (ckstrcmp(host, tokval,-1,1) == 0)
+ goto match;
+ if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
+ goto match;
+ if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
+ ckstrcmp(tmp, mydomain,-1,1) == 0 &&
+ ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
+ tokval[tmp - ftp_host] == '\0')
+ goto match;
+ if ((tmp = ckstrchr(host, '.')) != NULL &&
+ ckstrcmp(tmp, mydomain,-1,1) == 0 &&
+ ckstrcmp(host, tokval, tmp - host, 0) == 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);
+ strcpy(*aname, tokval); /* safe */
+ } 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);
+ strcpy(*apass, tokval); /* safe */
+ }
+ 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);
+ strcpy(*aacct, tokval); /* safe */
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
+ break;
+ }
+ goto done;
+ }
+
+ done:
+ fclose(cfile);
+ return(0);
+
+ bad:
+ fclose(cfile);
+ return(-1);
+}
+#endif /* USE_RUSERPASS */
+
+static char *radixN =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static char pad = '=';
+
+static int
+radix_encode(inbuf, outbuf, inlen, outlen, decode)
+ CHAR inbuf[], outbuf[];
+ int inlen, *outlen, decode;
+{
+ int i, j, D = 0;
+ char *p;
+ CHAR c = NUL;
+
+ if (decode) {
+ for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
+ if ((p = ckstrchr(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;
+ }
+ if (j == *outlen)
+ return(4);
+ }
+ 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);
+ }
+ *outlen = j;
+ } else {
+ for (i = 0, j = 0; i < inlen; 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 (j == *outlen)
+ return(4);
+ }
+ if (i%3) outbuf[j++] = radixN[c];
+ switch (i%3) {
+ case 1: outbuf[j++] = pad;
+ case 2: outbuf[j++] = pad;
+ }
+ outbuf[*outlen = j] = '\0';
+ }
+ return(0);
+}
+
+static char *
+radix_error(e) int 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");
+ case 4: return("Output buffer too small");
+ default: return("Unknown error");
+ }
+}
+/* END_RUSERPASS */
+
+#ifdef FTP_SRP
+/*---------------------------------------------------------------------------+
+ | |
+ | Package: srpftp |
+ | Author: Eugene Jhong |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*
+ * Copyright (c) 1997-1999 The Stanford SRP Authentication Project
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * In addition, the following conditions apply:
+ *
+ * 1. Any software that incorporates the SRP authentication technology
+ * must display the following acknowlegment:
+ * "This product uses the 'Secure Remote Password' cryptographic
+ * authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
+ *
+ * 2. Any software that incorporates all or part of the SRP distribution
+ * itself must also display the following acknowledgment:
+ * "This product includes software developed by Tom Wu and Eugene
+ * Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
+ *
+ * 3. Redistributions in source or binary form must retain an intact copy
+ * of this copyright notice and list of conditions.
+ */
+
+#define SRP_PROT_VERSION 1
+
+#ifdef CK_ENCRYPTION
+#define SRP_DEFAULT_CIPHER CIPHER_ID_CAST5_CBC
+#else
+#define SRP_DEFAULT_CIPHER CIPHER_ID_NONE
+#endif /* CK_ENCRYPTION */
+
+#define SRP_DEFAULT_HASH HASH_ID_SHA
+
+CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
+CHAR srp_pref_hash = HASH_ID_SHA;
+
+static struct t_client *tc = NULL;
+static CHAR *skey = NULL;
+static krypto_context *incrypt = NULL;
+static krypto_context *outcrypt = NULL;
+
+typedef unsigned int srp_uint32;
+
+/*--------------------------------------------------------------+
+ | srp_selcipher: select cipher |
+ +--------------------------------------------------------------*/
+static int
+srp_selcipher (cname) char *cname; {
+ cipher_desc *cd;
+
+ if (!(cd = cipher_getdescbyname (cname))) {
+ int i;
+ CHAR *list = cipher_getlist ();
+
+ fprintf (stderr, "ftp: supported ciphers:\n\n");
+ for (i = 0; i < strlen (list); i++)
+ fprintf (stderr, " %s\n", (cipher_getdescbyid(list[i]))->name);
+ fprintf (stderr, "\n");
+ return -1;
+ }
+ srp_pref_cipher = cd->id;
+ return 0;
+}
+
+/*--------------------------------------------------------------+
+ | srp_selhash: select hash |
+ +--------------------------------------------------------------*/
+static int
+srp_selhash (hname) char *hname; {
+ hash_desc *hd;
+
+ if (!(hd = hash_getdescbyname (hname))) {
+ int i;
+ CHAR *list = hash_getlist ();
+
+ fprintf (stderr, "ftp: supported hash functions:\n\n");
+ for (i = 0; i < strlen (list); i++)
+ fprintf (stderr, " %s\n", (hash_getdescbyid(list[i]))->name);
+ fprintf (stderr, "\n");
+ return -1;
+ }
+ srp_pref_hash = hd->id;
+ return 0;
+}
+
+/*--------------------------------------------------------------+
+ | srp_userpass: get username and password |
+ +--------------------------------------------------------------*/
+static int
+srp_userpass (host) char *host; {
+ char tmp[BUFSIZ], prompt[PROMPTSIZ];
+ char *user;
+
+ user = NULL;
+#ifdef USE_RUSERPASS
+ ruserpass (host, &user, &srp_pass, &srp_acct);
+#endif /* USE_RUSERPASS */
+
+ while (user == NULL) {
+ char *myname;
+ int ok;
+
+ myname = whoami();
+ if (!myname) myname = "";
+ if (myname[0])
+ ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+ else
+ ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
+ tmp[0] = '\0';
+ ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (!ok || *tmp == '\0')
+ user = myname;
+ else
+ user = brstrip(tmp);
+ }
+ ckstrncpy (srp_user, user,BUFSIZ);
+ return(0);
+}
+
+/*--------------------------------------------------------------+
+ | srp_reset: reset srp information |
+ +--------------------------------------------------------------*/
+static int
+srp_reset () {
+ if (tc) { t_clientclose (tc); tc = NULL; }
+ if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
+ if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
+ return(0);
+}
+
+/*--------------------------------------------------------------+
+ | srp_ftp_auth: perform srp authentication |
+ +--------------------------------------------------------------*/
+static int
+srp_ftp_auth(host, user, pass)
+ char *host;
+ char *user;
+ char *pass;
+{
+ struct t_num *wp;
+ struct t_num N;
+ struct t_num g;
+ struct t_num s;
+ struct t_num yp;
+ CHAR buf[FTP_BUFSIZ];
+ CHAR tmp[FTP_BUFSIZ];
+ CHAR *bp, *cp;
+ int n, e, clen, blen, len, i;
+ CHAR cid = 0;
+ CHAR hid = 0;
+
+ srp_pass = srp_acct = 0;
+
+ n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
+ if (n != REPLY_CONTINUE) {
+ if (ftp_deb)
+ fprintf(stderr, "SRP rejected as an authentication type\n");
+ return(0);
+ } else { /* Send protocol version */
+ CHAR vers[4];
+ memset (vers, 0, 4);
+ vers[3] = SRP_PROT_VERSION;
+ if (!quiet)
+ printf ("SRP accepted as authentication type.\n");
+ bp = tmp; blen = 0;
+ srp_put (vers, &bp, 4, &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+ }
+ if (n == REPLY_CONTINUE) { /* Get protocol version */
+ bp = buf;
+ if (!reply_parse)
+ goto data_error;
+ blen = FTP_BUFSIZ;
+ if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
+ goto decode_error;
+ if (srp_get (&bp, &cp, &blen, &clen) != 4)
+ goto data_error;
+
+ if (host) { /* Get username/password if needed */
+ srp_userpass (host);
+ } else {
+ ckstrncpy (srp_user, user, BUFSIZ);
+ srp_pass = pass;
+ }
+ bp = tmp; blen = 0; /* Send username */
+ srp_put (srp_user, &bp, strlen (srp_user), &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+ }
+ if (n == REPLY_CONTINUE) { /* Get N, g and s */
+ bp = buf;
+ if (!reply_parse)
+ goto data_error;
+ blen = FTP_BUFSIZ;
+ if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
+ goto decode_error;
+ if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
+ goto data_error;
+ if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
+ goto data_error;
+ if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
+ goto data_error;
+ if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
+ fprintf (stderr, "Unable to open SRP client structure.\n");
+ goto bad;
+ }
+ wp = t_clientgenexp (tc); /* Send wp */
+ bp = tmp; blen = 0;
+ srp_put (wp->data, &bp, wp->len, &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+ }
+ if (n == REPLY_CONTINUE) { /* Get yp */
+ bp = buf;
+ if (!reply_parse)
+ goto data_error;
+ blen = FTP_BUFSIZ;
+ if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
+ goto decode_error;
+ if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
+ goto data_error;
+ if (!srp_pass) {
+ static char ftppass[PASSBUFSIZ];
+ int ok;
+ setint();
+ ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
+ DEFAULT_UQ_TIMEOUT);
+ if (ok)
+ srp_pass = brstrip(ftppass);
+ }
+ t_clientpasswd (tc, srp_pass);
+ memset (srp_pass, 0, strlen (srp_pass));
+ skey = t_clientgetkey (tc, &yp); /* Send response */
+ bp = tmp; blen = 0;
+ srp_put (t_clientresponse (tc), &bp, 20, &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+ }
+ if (n == REPLY_CONTINUE) { /* Get response */
+ bp = buf;
+ if (!reply_parse)
+ goto data_error;
+ blen = FTP_BUFSIZ;
+ if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
+ goto encode_error;
+ if (srp_get (&bp, &cp, &blen, &clen) != 20)
+ goto data_error;
+ if (t_clientverify (tc, cp)) {
+ fprintf (stderr, "WARNING: bad response to client challenge.\n");
+ goto bad;
+ }
+ bp = tmp; blen = 0; /* Send nothing */
+ srp_put ("\0", &bp, 1, &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+ }
+ if (n == REPLY_CONTINUE) { /* Get cipher & hash lists, seqnum */
+ CHAR seqnum[4];
+ CHAR *clist;
+ CHAR *hlist;
+ CHAR *p1;
+ int clist_len, hlist_len;
+ bp = buf;
+ if (!reply_parse)
+ goto data_error;
+ blen = FTP_BUFSIZ;
+ if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
+ goto encode_error;
+ if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
+ goto data_error;
+ if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
+ goto data_error;
+ if (srp_get (&bp, &cp, &blen, &clen) != 4)
+ goto data_error;
+ memcpy (seqnum, cp, 4);
+ if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
+ cid = srp_pref_cipher;
+ if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
+ cid = SRP_DEFAULT_CIPHER;
+ if (!cid) {
+ CHAR *loclist = cipher_getlist ();
+ for (i = 0; i < strlen (loclist); i++)
+ if (cipher_supported (clist, loclist[i])) {
+ cid = loclist[i];
+ break;
+ }
+ }
+ if (!cid) {
+ fprintf (stderr, "Unable to agree on cipher.\n");
+ goto bad;
+ }
+ /* Choose hash */
+
+ if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
+ hid = srp_pref_hash;
+
+ if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
+ hid = SRP_DEFAULT_HASH;
+
+ if (!hid) {
+ CHAR *loclist = hash_getlist ();
+ for (i = 0; i < strlen (loclist); i++)
+ if (hash_supported (hlist, loclist[i])) {
+ hid = loclist[i];
+ break;
+ }
+ }
+ if (!hid) {
+ fprintf (stderr, "Unable to agree on hash.\n");
+ goto bad;
+ }
+ /* Set incrypt */
+
+ if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
+ KRYPTO_DECODE)))
+ goto bad;
+
+ /* Generate random number for outkey and outseqnum */
+
+ t_random (seqnum, 4);
+
+ /* Send cid, hid, outkey, outseqnum */
+
+ bp = tmp; blen = 0;
+ srp_put (&cid, &bp, 1, &blen);
+ srp_put (&hid, &bp, 1, &blen);
+ srp_put (seqnum, &bp, 4, &blen);
+ len = FTP_BUFSIZ;
+ if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
+ goto encode_error;
+ reply_parse = "ADAT=";
+ n = ftpcmd("ADAT",buf,-1,-1,0);
+
+ /* Set outcrypt */
+
+ if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
+ KRYPTO_ENCODE)))
+ goto bad;
+
+ t_clientclose (tc);
+ tc = NULL;
+ }
+ if (n != REPLY_COMPLETE)
+ goto bad;
+
+ if (ftp_vbm) {
+ if (ftp_deb)
+ printf("\n");
+ printf ("SRP authentication succeeded.\n");
+ printf ("Using cipher %s and hash function %s.\n",
+ (cipher_getdescbyid(cid))->name,
+ (hash_getdescbyid(hid))->name
+ );
+ }
+ reply_parse = NULL;
+ auth_type = "SRP";
+ return(1);
+
+ encode_error:
+ fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
+ goto bad;
+
+ decode_error:
+ fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
+ goto bad;
+
+ data_error:
+ fprintf (stderr, "Unable to unmarshal authentication data.\n");
+ goto bad;
+
+ bad:
+ fprintf (stderr, "SRP authentication failed, trying regular login.\n");
+ reply_parse = NULL;
+ return(0);
+}
+
+/*--------------------------------------------------------------+
+ | srp_put: put item to send buffer |
+ +--------------------------------------------------------------*/
+static int
+srp_put (in, out, inlen, outlen)
+ CHAR *in;
+ CHAR **out;
+ int inlen;
+ int *outlen;
+{
+ srp_uint32 net_len;
+
+ net_len = htonl (inlen);
+ memcpy (*out, &net_len, 4);
+
+ *out += 4; *outlen += 4;
+
+ memcpy (*out, in, inlen);
+
+ *out += inlen; *outlen += inlen;
+ return(0);
+}
+
+/*--------------------------------------------------------------+
+ | srp_get: get item from receive buffer |
+ +--------------------------------------------------------------*/
+static int
+srp_get (in, out, inlen, outlen)
+ CHAR **in;
+ CHAR **out;
+ int *inlen;
+ int *outlen;
+{
+ srp_uint32 net_len;
+
+ if (*inlen < 4) return -1;
+
+ memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
+ *outlen = ntohl (net_len);
+
+ if (*inlen < *outlen) return -1;
+
+ *out = *in; *inlen -= *outlen; *in += *outlen;
+
+ return *outlen;
+}
+
+/*--------------------------------------------------------------+
+ | srp_encode: encode control message |
+ +--------------------------------------------------------------*/
+static int
+srp_encode (private, in, out, len)
+ int private;
+ CHAR *in;
+ CHAR *out;
+ unsigned len;
+{
+ if (private)
+ return krypto_msg_priv (outcrypt, in, out, len);
+ else
+ return krypto_msg_safe (outcrypt, in, out, len);
+}
+
+/*--------------------------------------------------------------+
+ | srp_decode: decode control message |
+ +--------------------------------------------------------------*/
+static int
+srp_decode (private, in, out, len)
+ int private;
+ CHAR *in;
+ CHAR *out;
+ unsigned len;
+{
+ if (private)
+ return krypto_msg_priv (incrypt, in, out, len);
+ else
+ return krypto_msg_safe (incrypt, in, out, len);
+}
+
+#endif /* FTP_SRP */
+
+
+
+#ifdef NOT_USED
+/*
+ The following code is from the Unix FTP client. Be sure to
+ make sure that the functionality is not lost. Especially
+ the Proxy stuff even though we have not yet implemented it.
+*/
+
+/* Send multiple files */
+
+static int
+ftp_mput(argc, argv) int argc; char **argv; {
+ register int i;
+ sig_t oldintr;
+ int ointer;
+ char *tp;
+ sigtype mcancel();
+
+ if (argc < 2 && !another(&argc, &argv, "local-files")) {
+ printf("usage: %s local-files\n", argv[0]);
+ ftpcode = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mcancel);
+
+ /* Replace with calls to cc_execute() */
+ setjmp(jcancel);
+#ifdef FTP_PROXY
+ if (proxy) {
+ char *cp, *tp2, tmpbuf[CKMAXPATH];
+
+ 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, 0, -1, -1, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ signal(SIGINT, oldintr);
+ mflag = 0;
+ return;
+ }
+#endif /* FTP_PROXY */
+ for (i = 1; i < argc; i++) {
+ register char **cpp, **gargs;
+
+ if (mflag && confirm(argv[0], argv[i])) {
+ tp = argv[i];
+ sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
+ 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 = *cpp;
+ sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ if (gargs != NULL) {
+ blkfree(gargs);
+ free((char *)gargs);
+ }
+ }
+ signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/* Get multiple files */
+
+static int
+ftp_mget(argc, argv) int argc; char **argv; {
+ int rc = -1;
+ sig_t oldintr;
+ int ointer;
+ char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
+ sigtype mcancel();
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ ftpcode = -1;
+ return(-1);
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT,mcancel);
+ /* Replace with calls to cc_execute() */
+ setjmp(jcancel);
+ 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;
+ }
+ rc = (recvrequest("RETR", tp, cp, "wb",
+ tp != cp || !interactive) == 0,0,NULL,0,0,0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mget")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ signal(SIGINT,oldintr);
+ mflag = 0;
+ return(rc);
+}
+
+/* Delete multiple files */
+
+static int
+mdelete(argc, argv) int argc; char **argv; {
+ sig_t oldintr;
+ int ointer;
+ char *cp;
+ sigtype mcancel();
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ ftpcode = -1;
+ return(-1);
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mcancel);
+ /* Replace with calls to cc_execute() */
+ setjmp(jcancel);
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mdelete")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ signal(SIGINT, oldintr);
+ mflag = 0;
+ return(rc);
+}
+
+/* Get a directory listing of multiple remote files */
+
+static int
+mls(argc, argv) int argc; char **argv; {
+ sig_t oldintr;
+ int ointer, i;
+ char *cmd, mode[1], *dest;
+ sigtype mcancel();
+ int rc = -1;
+
+ 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]);
+ ftpcode = -1;
+ return(-1);
+ }
+ dest = argv[argc - 1];
+ argv[argc - 1] = NULL;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (!globulize(&dest) ||
+ !confirm("output to local-file:", dest)) {
+ ftpcode = -1;
+ return(-1);
+ }
+ cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mcancel);
+ /* Replace with calls to cc_execute() */
+ setjmp(jcancel);
+ for (i = 1; mflag && i < argc-1; ++i) {
+ *mode = (i == 1) ? 'w' : 'a';
+ rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", argv[0])) {
+ mflag ++;
+ }
+ interactive = ointer;
+ }
+ }
+ signal(SIGINT, oldintr);
+ mflag = 0;
+ return(rc);
+}
+
+static char *
+remglob(argv,doswitch) char *argv[]; int doswitch; {
+ char temp[16];
+ static char buf[CKMAXPATH];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int 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);
+#ifdef MKTEMP
+#ifndef MKSTEMP
+ (void) mktemp(temp);
+#endif /* MKSTEMP */
+#endif /* MKTEMP */
+ verbose = 0;
+ oldhash = hash, hash = 0;
+#ifdef FTP_PROXY
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+#endif /* FTP_PROXY */
+ for (mode = "wb"; *++argv != NULL; mode = "ab")
+ recvrequest ("NLST", temp, *argv, mode, 0);
+#ifdef FTP_PROXY
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+#endif /* FTP_PROXY */
+ hash = oldhash;
+ ftemp = fopen(temp, "r");
+ unlink(temp);
+ if (ftemp == NULL && (!dpyactive || ftp_deb)) {
+ printf("Can't find list of remote files, oops\n");
+ return(NULL);
+ }
+ }
+ if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
+ fclose(ftemp), ftemp = NULL;
+ return(NULL);
+ }
+ if ((cp = ckstrchr(buf,'\n')) != NULL)
+ *cp = '\0';
+ return(buf);
+}
+#endif /* NOT_USED */
+#endif /* TCPSOCKET (top of file) */
+#endif /* SYSFTP (top of file) */
+#endif /* NOFTP (top of file) */
diff --git a/ckermit-8.0.211/ckcker.h b/ckermit-8.0.211/ckcker.h
new file mode 100644
index 0000000..b392b89
--- /dev/null
+++ b/ckermit-8.0.211/ckcker.h
@@ -0,0 +1,1418 @@
+/* ckcker.h -- Symbol and macro definitions for C-Kermit */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+#ifndef CKCKER_H
+#define CKCKER_H
+
+#define I_AM_KERMIT 0 /* Personalities */
+#define I_AM_TELNET 1
+#define I_AM_RLOGIN 2
+#define I_AM_IKSD 3
+#define I_AM_FTP 4
+#define I_AM_HTTP 5
+#define I_AM_SSHSUB 6
+#define I_AM_SSH 7
+
+#ifndef NOSTREAMING
+#ifndef STREAMING
+#define STREAMING
+#endif /* STREAMING */
+#endif /* NOSTREAMING */
+/*
+ If NEWDEFAULTS is defined then:
+ - RECEIVE PACKET-LENGTH is 4095 rather than 90
+ - WINDOW is 30 rather than 1
+ - BLOCK-CHECK is 3 rather than 1
+ - FILE TYPE is BINARY rather than TEXT
+*/
+#ifdef BIGBUFOK /* (was OS2) */
+#ifndef NEWDEFAULTS
+#define NEWDEFAULTS
+#endif /* NEWDEFAULTS */
+#endif /* BIGBUFOK */
+
+#ifdef NOICP /* No Interactive Command Parser */
+#ifndef NOSPL /* implies... */
+#define NOSPL /* No Script Programming Language */
+#endif /* NOSPL */
+#ifndef NOCSETS /* No character-set translation */
+#define NOCSETS /* because the only way to set it up */
+#endif /* NOCSETS */ /* is with interactive commands */
+#endif /* NOICP */
+
+#ifdef pdp11 /* There is a maximum number of */
+#ifndef NOCKSPEED /* of -D's allowed on the CC */
+#define NOCKSPEED /* command line, so some of them */
+#endif /* NOCKSPEED */ /* have to go here... */
+#ifndef NOREDIRECT
+#define NOREDIRECT
+#endif /* NOREDIRECT */
+#ifdef WHATAMI
+#undef WHATAMI
+#endif /* WHATAMI */
+#endif /* pdp11 */
+
+#ifdef UIDBUFLEN
+#define LOGINLEN UIDBUFLEN
+#else
+#define LOGINLEN 32 /* Length of server login field */
+#endif /* UIDBUFLEN */
+
+/* Bell values */
+
+#define XYB_NONE 0 /* No bell */
+#define XYB_AUD 1 /* Audible bell */
+#define XYB_VIS 2 /* Visible bell */
+#define XYB_BEEP 0 /* Audible Beep */
+#define XYB_SYS 4 /* Audible System Sounds */
+
+/* File status bits */
+
+#define FS_OK 1 /* File transferred OK */
+#define FS_REFU 2 /* File was refused */
+#define FS_DISC 4 /* File was discarded */
+#define FS_INTR 8 /* Transfer was interrupted by user */
+#define FS_ERR 16 /* Fatal error during transfer */
+
+/* Control-character (un)prefixing options */
+
+#define PX_ALL 0 /* Prefix all control chars */
+#define PX_CAU 1 /* Unprefix cautiously */
+#define PX_WIL 2 /* Unprefix with wild abandon */
+#define PX_NON 3 /* Unprefix all (= prefix none) */
+
+/* Destination codes */
+
+#define DEST_D 0 /* DISK */
+#define DEST_S 1 /* SCREEN */
+#define DEST_P 2 /* PRINTER */
+#define DEST_N 3 /* NOWHERE (calibration run) */
+
+/* File transfer protocols */
+
+#define PROTO_K 0 /* Kermit */
+#ifdef CK_XYZ
+#define PROTO_X 1 /* XMODEM */
+#define PROTO_XC 2 /* XMODEM-CRC */
+#define PROTO_Y 3 /* YMODEM */
+#define PROTO_G 4 /* YMODEM-g */
+#define PROTO_Z 5 /* ZMODEM */
+#define PROTO_O 6 /* OTHER */
+#define NPROTOS 7 /* How many */
+#else
+#define NPROTOS 1 /* How many */
+#endif /* CK_XYZ */
+
+struct ck_p { /* C-Kermit Protocol info structure */
+ char * p_name; /* Protocol name */
+ int rpktlen; /* Packet length - receive */
+ int spktlen; /* Packet length - send */
+ int spktflg; /* ... */
+ int winsize; /* Window size */
+ int prefix; /* Control-char prefixing options */
+ int fnca; /* Filename collision action */
+ int fncn; /* Filename conversion */
+ int fnsp; /* Send filename path stripping */
+ int fnrp; /* Receive filename path stripping */
+ char * h_b_init; /* Host receive initiation string - text */
+ char * h_t_init; /* Host receive initiation string - binary */
+ char * h_x_init; /* Host server string */
+ char * p_b_scmd; /* SEND cmd for external protocol - text */
+ char * p_t_scmd; /* SEND cmd for external protocol - binary */
+ char * p_b_rcmd; /* RECV cmd for external protocol - text */
+ char * p_t_rcmd; /* RECV cmd for external protocol - binary */
+};
+
+struct filelist { /* Send-file list element */
+ char * fl_name; /* Filename */
+ int fl_mode; /* Transfer mode */
+ char * fl_alias; /* Name to send the file under */
+ struct filelist * fl_next; /* Pointer to next element */
+};
+
+/* Kermit system IDs and associated properties... */
+
+struct sysdata {
+ char *sid_code; /* Kermit system ID code */
+ char *sid_name; /* Descriptive name */
+ short sid_unixlike; /* Tree-structured directory with separators */
+ char sid_dirsep; /* Directory separator character if unixlike */
+ short sid_dev; /* Can start with dev: */
+ short sid_case; /* Bit mapped: 1 = case matters, 2 = case preserved */
+ short sid_recfm; /* Text record separator */
+/*
+ 0 = unknown or nonstream
+ 1 = cr
+ 2 = lf
+ 3 = crlf
+*/
+};
+
+struct ssh_pf { /* SSH port forwarding */
+ int p1; /* port to be forwarded */
+ char * host; /* host */
+ int p2; /* port */
+};
+
+#define SET_ON 1 /* General values for settings that can be ON */
+#define SET_OFF 0 /* OFF, */
+#define SET_AUTO 2 /* or AUTO */
+
+#define PATH_OFF 0 /* Pathnames off (to be stripped) */
+#define PATH_REL 1 /* Pathnames on, left relative if possible */
+#define PATH_ABS 2 /* Pathnames absolute always */
+#define PATH_AUTO 4 /* Pathnames handled automatically */
+
+/* GET Options */
+
+#define GOPT_DEL 1 /* Delete source file */
+#define GOPT_REC 2 /* Recursive */
+#define GOPT_RES 4 /* Recover (Resend) */
+#define GOPT_CMD 8 /* Filename is a Command */
+
+/* GET Transfer Modes */
+
+#define GMOD_TXT 0 /* Text */
+#define GMOD_BIN 1 /* Binary */
+#define GMOD_AUT 2 /* Auto */
+#define GMOD_LBL 3 /* Labeled */
+
+/* GET Filename Options */
+
+#define GNAM_LIT 0 /* Literal */
+#define GNAM_CNV 1 /* Converted */
+
+/* GET Pathname Options */
+
+#define GPTH_OFF 0 /* Pathnames Off */
+#define GPTH_REL 1 /* Pathnames Relative */
+#define GPTH_ABX 2 /* Pathnames Absolute */
+
+#ifndef NOSPL
+/*
+ The IF REMOTE-ONLY command is available only in versions
+ that actually can be used in remote mode, and only if we have
+ an interactive command parser.
+*/
+#define CK_IFRO
+#ifdef MAC
+#undef CK_IFRO
+#else
+#ifdef GEMDOS
+#undef CK_IFRO
+#endif /* GEMDOS */
+#endif /* MAC */
+#endif /* NOSPL */
+
+/* Systems whose CONNECT modules can execute Application Program Commands */
+
+#ifdef NOSPL /* Script programming language */
+#ifdef CK_APC /* is required for APC. */
+#undef CK_APC
+#endif /* CK_APC */
+#ifndef NOAPC
+#define NOAPC
+#endif /* NOAPC */
+#ifndef NOAUTODL
+#define NOAUTODL
+#endif /* NOAUTODL */
+#endif /* NOSPL */
+
+#ifndef NOAPC /* Unless they said NO APC */
+#ifndef CK_APC /* And they didn't already define it */
+#ifdef OS2 /* OS/2 gets it */
+#define CK_APC
+#endif /* OS2 */
+#ifdef UNIX /* UNIX gets it */
+#define CK_APC
+#endif /* UNIX */
+#ifdef VMS /* VMS too */
+#define CK_APC
+#endif /* VMS */
+#endif /* CK_APC */
+#endif /* NOAPC */
+
+#ifdef CK_APC /* APC buffer length */
+#ifndef APCBUFLEN /* Should be no bigger than */
+#ifdef NOSPL /* command buffer length */
+#define APCBUFLEN 608 /* (see ckucmd.h) but we can't */
+#else /* reference ckucmd.h symbols here */
+#define APCBUFLEN 4096
+#endif /* NOSPL */
+#endif /* APCBUFLEN */
+#define APC_OFF 0 /* APC OFF (disabled) */
+#define APC_ON 1 /* APC ON (enabled for non-dangerous commands) */
+#define APC_UNCH 2 /* APC UNCHECKED (enabled for ALL commands) bitmask */
+#define APC_NOINP 4 /* APC (enabled with no input allowed - bitmask) */
+#define APC_INACTIVE 0 /* APC not in use */
+#define APC_REMOTE 1 /* APC in use from Remote */
+#define APC_LOCAL 2 /* APC being used from within Kermit */
+#ifndef NOAUTODL
+#ifndef CK_AUTODL /* Autodownload */
+#ifdef OS2
+#define CK_AUTODL
+#else
+#ifdef UNIX
+#define CK_AUTODL
+#else
+#ifdef VMS
+#define CK_AUTODL
+#else
+#ifdef CK_AUTODL
+#undef CK_AUTODL
+#endif /* CK_AUTODL */
+#endif /* NOAUTODL */
+#endif /* VMS */
+#endif /* UNIX */
+#endif /* OS2 */
+#endif /* CK_AUTODL */
+
+#else /* CK_APC not defined */
+
+#ifdef NOICP
+#ifdef UNIX
+#ifndef CK_AUTODL
+#define CK_AUTODL
+#endif /* CK_AUTODL */
+#endif /* UNIX */
+#else /* Not NOICP... */
+#ifdef CK_AUTODL
+#undef CK_AUTODL
+#endif /* CK_AUTODL */
+#endif /* NOICP */
+#endif /* CK_APC */
+
+#ifdef NOAUTODL
+#ifdef CK_AUTODL
+#undef CK_AUTODL
+#endif /* CK_AUTODL */
+#endif /* NOAUTODL */
+
+/* Codes for what we are doing now - bit mask values */
+
+#define W_NOTHING 0 /* Nothing */
+#define W_INIT 1 /* Initializing protocol */
+#define W_SEND 2 /* SENDing or MAILing */
+#define W_RECV 4 /* RECEIVEing or GETting */
+#define W_REMO 8 /* Doing a REMOTE command */
+#define W_CONNECT 16 /* CONNECT mode */
+#define W_COMMAND 32 /* Command mode */
+#define W_DIALING 64 /* Dialing a modem */
+#define W_FTP 128 /* FTP */
+#define W_FT_DELE 64 /* FTP MDELETE */
+#define W_KERMIT (W_INIT|W_SEND|W_RECV|W_REMO) /* Kermit protocol */
+#define W_XFER (W_INIT|W_SEND|W_RECV|W_REMO|W_FTP) /* File xfer any protocol */
+
+#ifndef NOWHATAMI
+#ifndef WHATAMI
+#define WHATAMI
+#endif /* WHATAMI */
+#endif /* NOWHATAMI */
+
+#ifdef WHATAMI /* Bit mask positions for WHATAMI */
+#define WMI_SERVE 1 /* Server mode */
+#define WMI_FMODE 2 /* File transfer mode */
+#define WMI_FNAME 4 /* File name conversion */
+#define WMI_STREAM 8 /* I have a reliable transport */
+#define WMI_CLEAR 16 /* I have a clear channel */
+#define WMI_FLAG 32 /* Flag that WHATAMI field is valid */
+/* WHATAMI2 bits... */
+#define WMI2_XMODE 1 /* Transfer mode auto(0)/manual(1) */
+#define WMI2_RECU 2 /* Transfer is recursive */
+#define WMI2_FLAG 32 /* Flag that WHATAMI2 field is valid */
+#endif /* WHATAMI */
+
+/* Terminal types */
+#define VT100 0 /* Also for VT52 mode */
+#define TEKTRONIX 1
+
+/* Normal packet and window size */
+
+#define MAXPACK 94 /* Maximum unextended packet size */
+ /* Can't be more than 94. */
+#ifdef pdp11 /* Maximum sliding window slots */
+#define MAXWS 8
+#else
+#define MAXWS 32 /* Can't be more than 32. */
+#endif /* pdp11 */
+
+/* Maximum long packet size for sending packets */
+/* Override these from cc command line via -DMAXSP=nnn */
+
+#ifdef IRIX /* Irix 6.4 and earlier has */
+#ifndef MAXSP /* Telnet server bug */
+#ifdef IRIX65
+#define MAXSP 9024
+#else
+#define MAXSP 4000
+#endif /* IRIX65 */
+#endif /* MAXSP */
+#endif /* IRIX */
+
+#ifdef DYNAMIC
+#ifndef MAXSP
+#define MAXSP 9024
+#endif /* MAXSP */
+#else /* not DYNAMIC */
+#ifndef MAXSP
+#ifdef pdp11
+#define MAXSP 1024
+#else
+#define MAXSP 2048
+#endif /* pdp11 */
+#endif /* MAXSP */
+#endif /* DYNAMIC */
+
+/* Maximum long packet size for receiving packets */
+/* Override these from cc command line via -DMAXRP=nnn */
+
+#ifdef DYNAMIC
+#ifndef MAXRP
+#define MAXRP 9024
+#endif /* MAXRP */
+#else /* not DYNAMIC */
+#ifndef MAXRP
+#ifdef pdp11
+#define MAXRP 1024
+#else
+#define MAXRP 2048
+#endif /* pdp11 */
+#endif /* MAXRP */
+#endif /* DYNAMIC */
+/*
+ Default sizes for windowed packet buffers.
+ Override these from cc command line via -DSBSIZ=nnn, -DRBSIZ=nnn.
+ Or just -DBIGBUFOK.
+*/
+#ifndef MAXGETPATH /* Maximum number of directories */
+#ifdef BIGBUFOK /* for GET path... */
+#define MAXGETPATH 128
+#else
+#define MAXGETPATH 16
+#endif /* BIGBUFOK */
+#endif /* MAXGETPATH */
+
+#ifndef NOSPL /* Query buffer length */
+#ifdef OS2
+#define QBUFL 4095
+#else
+#ifdef BIGBUFOK
+#define QBUFL 4095
+#else
+#define QBUFL 1023
+#endif /* BIGBUFOK */
+#endif /* OS2 */
+#endif /* NOSPL */
+
+#ifdef DYNAMIC
+#ifndef SBSIZ
+#ifdef BIGBUFOK /* If big buffers are safe... */
+#define SBSIZ 290000 /* Allow for 10 x 9024 or 20 x 4096 */
+#else /* Otherwise... */
+#ifdef pdp11
+#define SBSIZ 3020
+#else
+#define SBSIZ 9050 /* Allow for 3 x 3000, etc. */
+#endif /* pdp11 */
+#endif /* BIGBUFOK */
+#endif /* SBSIZ */
+
+#ifndef RBSIZ
+#ifdef BIGBUFOK
+#define RBSIZ 290000
+#else
+#ifdef pdp11
+#define RBSIZ 3020
+#else
+#define RBSIZ 9050
+#endif /* pdp11 */
+#endif /* BIGBUFOK */
+#endif /* RBSIZ */
+#else /* not DYNAMIC */
+#ifdef pdp11
+#define SBSIZ 3020
+#define RBSIZ 3020
+#else
+#ifndef SBSIZ
+#define SBSIZ (MAXSP * (MAXWS + 1))
+#endif /* SBSIZ */
+#ifndef RBSIZ
+#define RBSIZ (MAXRP * (MAXWS + 1))
+#endif /* RBSIZ */
+#endif /* pdp11 */
+#endif /* DYNAMIC */
+
+#ifdef BIGBUFOK
+#define PKTMSGLEN 1023
+#else
+#define PKTMSGLEN 80
+#endif /* BIGBUFOK */
+
+/* Kermit parameters and defaults */
+
+#define CTLQ '#' /* Control char prefix I will use */
+#define MYEBQ '&' /* 8th-Bit prefix char I will use */
+#define MYRPTQ '~' /* Repeat count prefix I will use */
+
+#define MAXTRY 10 /* Times to retry a packet */
+#define MYPADN 0 /* How many padding chars I need */
+#define MYPADC '\0' /* Which padding character I need */
+
+#define DMYTIM 8 /* Initial timeout interval to use. */
+#define URTIME 15 /* Timeout interval to use on me. */
+#define DSRVTIM 0 /* Default server cmd wait timeout. */
+
+#define DEFTRN 0 /* Default line turnaround handshake */
+
+#define MYEOL CR /* Incoming packet terminator. */
+
+#ifdef NEWDEFAULTS
+#define DRPSIZ 4095 /* Default incoming packet size. */
+#define DFWSIZ 30 /* Default window size */
+#define DFBCT 3 /* Default block-check type */
+#else
+#define DRPSIZ 90 /* Default incoming packet size. */
+#define DFWSIZ 1 /* Default window size */
+#define DFBCT 3 /* Default block-check type */
+#endif /* NEWDEFAULTS */
+
+/* The HP-UX 5 and 6 Telnet servers can only swallow 513 bytes at once */
+
+#ifdef HPUX5
+#ifdef DRPSIZ
+#undef DRPSIZ
+#endif /* DRPSIZ */
+#define DRPSIZ 500
+#else
+#ifdef HPUX6
+#ifdef DRPSIZ
+#undef DRPSIZ
+#endif /* DRPSIZ */
+#define DRPSIZ 500
+#endif /* HPUX6 */
+#endif /* HPUX5 */
+
+#define DSPSIZ 90 /* Default outbound packet size. */
+#define DDELAY 1 /* Default delay. */
+#define DSPEED 9600 /* Default line speed. */
+
+#ifdef OS2 /* Default CONNECT-mode */
+#define DFESC 29 /* escape character */
+#else
+#ifdef NEXT /* Ctrl-] for PC and NeXT */
+#define DFESC 29
+#else
+#ifdef GEMDOS /* And Atari ST */
+#define DFESC 29
+#else
+#define DFESC 28 /* Ctrl-backslash for others */
+#endif /* GEMDOS */
+#endif /* NEXT */
+#endif /* OS2 */
+
+#ifdef NOPUSH /* NOPUSH implies NOJC */
+#ifndef NOJC /* (no job control) */
+#define NOJC
+#endif /* NOJC */
+#endif /* NOPUSH */
+
+#ifdef UNIX /* Default for SET SUSPEND */
+#ifdef NOJC /* UNIX but job control disabled */
+#define DFSUSP 0
+#else /* UNIX, job control enabled. */
+#define DFSUSP 1
+#endif /* NOJC */
+#else
+#define DFSUSP 0
+#endif /* UNIX */
+
+#ifndef DFCDMSG
+#ifdef UNIXOROSK
+#define DFCDMSG "{{./.readme}{README.TXT}{READ.ME}}"
+#else
+#define DFCDMSG "{{README.TXT}{READ.ME}}"
+#endif /* UNIXOROSK */
+#endif /* DFCDMSG */
+
+#define NSNDEXCEPT 64 /* Max patterns for /EXCEPT: list */
+
+/* Files */
+
+#define ZCTERM 0 /* Console terminal */
+#define ZSTDIO 1 /* Standard input/output */
+#define ZIFILE 2 /* Current input file (SEND, etc) (in) */
+#define ZOFILE 3 /* Current output file (RECEIVE, GET) (out) */
+#define ZDFILE 4 /* Current debugging log file (out) */
+#define ZTFILE 5 /* Current transaction log file (out) */
+#define ZPFILE 6 /* Current packet log file (out) */
+#define ZSFILE 7 /* Current session log file (out) */
+#define ZSYSFN 8 /* Input/Output from a system function */
+#define ZRFILE 9 /* Local file for READ (in) */
+#define ZWFILE 10 /* Local file for WRITE (out) */
+#define ZMFILE 11 /* Miscellaneous file, e.g. for XLATE */
+#define ZDIFIL 12 /* DIAL log */
+#define ZNFILS 13 /* How many defined file numbers */
+
+#ifdef CKCHANNELIO
+
+/* File modes */
+
+#define FM_REA 1 /* Read */
+#define FM_WRI 2 /* Write */
+#define FM_APP 4 /* Append */
+#define FM_RWA 7 /* Read/Write/Append mask */
+#define FM_BIN 8 /* Binary */
+#define FM_RWB 15 /* Read/Write/Append/Binary mask */
+#define FM_CMD 16 /* Command */
+#define FM_EOF 64 /* (status) At EOF */
+
+/* File errors */
+
+#define FX_NER 0 /* No error */
+#define FX_SYS -1 /* System error */
+#define FX_EOF -2 /* End of file */
+#define FX_NOP -3 /* Channel not open */
+#define FX_CHN -4 /* Channel out of range */
+#define FX_RNG -5 /* Argument range error */
+#define FX_FNF -6 /* File not found */
+#define FX_BFN -7 /* Bad or missing filename */
+#define FX_NMF -8 /* No more files */
+#define FX_FOP -9 /* Forbidden operation */
+#define FX_ACC -10 /* Access denied */
+#define FX_BOM -11 /* Bad combination of open modes */
+#define FX_OFL -12 /* Buffer overflow */
+#define FX_LNU -13 /* Current line number unknown */
+#define FX_ROO -14 /* Set Root violation */
+#define FX_NYI -99 /* Feature not implemented yet */
+#define FX_UNK -999 /* Unknown error */
+
+_PROTOTYP( int z_open, (char *, int) );
+_PROTOTYP( int z_close, (int) );
+_PROTOTYP( int z_out, (int, char *, int, int) );
+_PROTOTYP( int z_in, (int, char *, int, int, int) );
+_PROTOTYP( int z_flush, (int) );
+_PROTOTYP( int z_seek, (int, long) );
+_PROTOTYP( int z_line, (int, long) );
+_PROTOTYP( int z_getmode, (int) );
+_PROTOTYP( int z_getfnum, (int) );
+_PROTOTYP( long z_getpos, (int) );
+_PROTOTYP( long z_getline, (int) );
+_PROTOTYP( long z_count, (int, int) );
+_PROTOTYP( char * z_getname, (int) );
+_PROTOTYP( char * ckferror, (int) );
+#endif /* CKCHANNELIO */
+
+_PROTOTYP( int scanfile, (char *, int *, int) );
+
+/* Buffered file i/o ... */
+#ifdef OS2 /* K-95 */
+#define INBUFSIZE 32768
+#define OBUFSIZE 32768
+#else
+#ifdef pdp11
+#define INBUFSIZE 512
+#define OBUFSIZE 512
+#else
+/* In VMS, allow for longest possible RMS record */
+#ifdef VMS
+#define INBUFSIZE 32768 /* File input buffer size */
+#define OBUFSIZE 32768 /* File output buffer size */
+#else /* Not VMS */
+#ifdef STRATUS
+#ifdef DYNAMIC
+#define INBUFSIZE 32767 /* File input buffer size */
+#define OBUFSIZE 32767 /* File output buffer size */
+#else /* STRATUS, not DYNAMIC */
+#define INBUFSIZE 4096 /* File input buffer size */
+#define OBUFSIZE 4096 /* File output buffer size */
+#endif /* DYNAMIC */
+#else /* not STRATUS */
+#ifdef BIGBUFOK /* Systems where memory is */
+#define INBUFSIZE 32768 /* not a problem... */
+#define OBUFSIZE 32768
+#else /* Not BIGBUFOK */
+#define INBUFSIZE 1024
+#define OBUFSIZE 1024
+#endif /* BIGBUFOK */
+#endif /* STRATUS */
+#endif /* VMS */
+#endif /* pdp11 */
+#endif /* OS2 */
+
+/* File-transfer character in/out macros for buffered i/o */
+
+/* Get the next file byte */
+#ifndef CKCMAI
+#ifndef NOXFER
+extern char ** sndarray;
+#endif /* NOXFER */
+#endif /* CKCMAI */
+#ifdef NOSPL
+#define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill())
+#else
+#ifdef NOXFER
+#define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill())
+#else
+#define zminchar() \
+(sndarray?agnbyte():(((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill()))
+#endif /* NOXFER */
+#endif /* NOSPL */
+
+/* Stuff a character into the input buffer */
+#define zmstuff(c) zinptr--, *zinptr = c, zincnt++
+
+/* Put a character to a file */
+#define zmchout(c) \
+((*zoutptr++=(char)(c)),(((++zoutcnt)>=zobufsize)?zoutdump():0))
+
+/* Screen functions */
+
+#define XYFD_N 0 /* File transfer display: None, Off */
+#define XYFD_R 1 /* Regular, Dots */
+#define XYFD_C 2 /* Cursor-positioning (e.g. curses) */
+#define XYFD_S 3 /* CRT Screen */
+#define XYFD_B 4 /* Brief */
+#define XYFD_G 5 /* GUI */
+
+#ifdef NODISPLAY
+#define xxscreen(a,b,c,d)
+#define ckscreen(a,b,c,d)
+#else
+_PROTOTYP( VOID ckscreen, (int, char, long, char *) );
+#ifdef VMS
+#define xxscreen(a,b,c,d) \
+if (local && fdispla != XYFD_N) \
+ckscreen((int)a,(char)b,(long)c,(char *)d)
+#else
+#define xxscreen(a,b,c,d) \
+if (local && !backgrd && fdispla != XYFD_N) \
+ckscreen((int)a,(char)b,(long)c,(char *)d)
+#endif /* VMS */
+#endif /* NODISPLAY */
+
+#define SCR_FN 1 /* filename */
+#define SCR_AN 2 /* as-name */
+#define SCR_FS 3 /* file-size */
+#define SCR_XD 4 /* x-packet data */
+#define SCR_ST 5 /* File status: */
+#define ST_OK 0 /* Transferred OK */
+#define ST_DISC 1 /* Discarded */
+#define ST_INT 2 /* Interrupted */
+#define ST_SKIP 3 /* Skipped */
+#define ST_ERR 4 /* Fatal Error */
+#define ST_REFU 5 /* Refused (use Attribute codes for reason) */
+#define ST_INC 6 /* Incompletely received */
+#define ST_MSG 7 /* Informational message */
+#define ST_SIM 8 /* Transfer simulated (e.g. would be sent) */
+#define SCR_PN 6 /* packet number */
+#define SCR_PT 7 /* packet type or pseudotype */
+#define SCR_TC 8 /* transaction complete */
+#define SCR_EM 9 /* error message */
+#define SCR_WM 10 /* warning message */
+#define SCR_TU 11 /* arbitrary undelimited text */
+#define SCR_TN 12 /* arbitrary new text, delimited at beginning */
+#define SCR_TZ 13 /* arbitrary text, delimited at end */
+#define SCR_QE 14 /* quantity equals (e.g. "foo: 7") */
+#define SCR_CW 15 /* close screen window */
+#define SCR_CD 16 /* display current directory */
+
+/* Skip reasons */
+
+#define SKP_DAT 1 /* Date-Time (Older) */
+#define SKP_EQU 2 /* Date-Time (Equal) */
+#define SKP_TYP 3 /* Type */
+#define SKP_SIZ 4 /* Size */
+#define SKP_NAM 5 /* Name collision */
+#define SKP_EXL 6 /* Exception list */
+#define SKP_DOT 7 /* Dot file */
+#define SKP_BKU 8 /* Backup file */
+#define SKP_RES 9 /* Recovery not needed */
+#define SKP_ACC 10 /* Access denied */
+#define SKP_NRF 11 /* Not a regular file */
+#define SKP_SIM 12 /* Simulation (WOULD BE SENT) */
+#define SKP_XUP 13 /* Simulation: Would be sent because remote file older */
+#define SKP_XNX 14 /* Simulation: ditto, because remote file does not exist */
+
+/* Macros */
+
+#ifndef CKCMAI
+extern int tcp_incoming; /* Used by ENABLE macro */
+#endif /* CKCMAI */
+
+#ifndef TCPSOCKET
+/*
+ ENABLED tells whether a server-side service is enabled.
+ 0 = disabled, 1 = local, 2 = remote.
+ A "set host *" connection is technically local but logically remote
+*/
+#define ENABLED(x) ((local && (x & 1)) || (!local && (x & 2)))
+#else
+#define ENABLED(x) (((local && !tcp_incoming) && (x & 1)) || \
+((!local || tcp_incoming) && (x&2)))
+#endif /* TCPSOCKET */
+
+/* These are from the book */
+
+#define tochar(ch) (((ch) + SP ) & 0xFF ) /* Number to character */
+#define xunchar(ch) (((ch) - SP ) & 0xFF ) /* Character to number */
+#define ctl(ch) (((ch) ^ 64 ) & 0xFF ) /* Control/Uncontrol toggle */
+#define unpar(ch) (((ch) & 127) & 0xFF ) /* Clear parity bit */
+
+#ifndef NOLOCAL /* CONNECT return status codes */
+
+/* Users will see the numbers so they can't be changed */
+/* Numbers >= 100 indicate connection loss */
+
+#define CSX_NONE 0 /* No CONNECT yet so no status */
+#define CSX_ESCAPE 1 /* User Escaped back */
+#define CSX_TRIGGER 2 /* Trigger was encountered */
+#define CSX_IKSD 3 /* IKSD autosynchronization */
+#define CSX_APC 4 /* Application Program Command */
+#define CSX_IDLE 5 /* Idle limit exceeded */
+#define CSX_TN_ERR 6 /* Telnet Error */
+#define CSX_MACRO 7 /* Macro bound to keystroke */
+#define CSX_TIME 8 /* Time Limit exceeded */
+#define CSX_INTERNAL 100 /* Internal error */
+#define CSX_CARRIER 101 /* Carrier required but not detected */
+#define CSX_IOERROR 102 /* I/O error on connection */
+#define CSX_HOSTDISC 103 /* Disconnected by host */
+#define CSX_USERDISC 104 /* Disconnected by user */
+#define CSX_SESSION 105 /* Session Limit exceeded */
+#define CSX_TN_POL 106 /* Rejected due to Telnet Policy */
+#define CSX_KILL_SIG 107 /* Received Kill Signal */
+
+/* SET TERMINAL IDLE-ACTION values */
+
+#define IDLE_RET 0 /* Return to prompt */
+#define IDLE_EXIT 1 /* Exit from Kermit */
+#define IDLE_HANG 2 /* Hangup the connection */
+#define IDLE_OUT 3 /* OUTPUT a string */
+#define IDLE_TNOP 4 /* TELNET NOP */
+#define IDLE_TAYT 5 /* TELNET AYT */
+#endif /* NOLOCAL */
+
+/* Modem and dialing definitions */
+
+#ifndef NODIAL
+
+/* Modem capabilities (bit values) */
+#define CKD_AT 1 /* Hayes AT commands and responses */
+#define CKD_V25 2 /* V.25bis commands and responses */
+#define CKD_SB 4 /* Speed buffering */
+#define CKD_EC 8 /* Error correction */
+#define CKD_DC 16 /* Data compression */
+#define CKD_HW 32 /* Hardware flow control */
+#define CKD_SW 64 /* (Local) software flow control */
+#define CKD_KS 128 /* Kermit spoofing */
+#define CKD_TB 256 /* Made by Telebit */
+#define CKD_ID 512 /* Has Caller ID */
+
+/* DIAL command result codes */
+#define DIA_UNK -1 /* No DIAL command given yet */
+#define DIA_OK 0 /* DIAL succeeded */
+#define DIA_NOMO 1 /* Modem type not specified */
+#define DIA_NOLI 2 /* Communication line not spec'd */
+#define DIA_OPEN 3 /* Line can't be opened */
+#define DIA_NOSP 4 /* Speed not specified */
+#define DIA_HANG 5 /* Hangup failure */
+#define DIA_IE 6 /* Internal error (malloc, etc) */
+#define DIA_IO 7 /* I/O error */
+#define DIA_TIMO 8 /* Dial timeout expired */
+#define DIA_INTR 9 /* Dialing interrupted by user */
+#define DIA_NRDY 10 /* Modem not ready */
+#define DIA_PART 11 /* Partial dial command OK */
+#define DIA_DIR 12 /* Dialing directory error */
+#define DIA_HUP 13 /* Modem was hung up OK */
+#define DIA_NRSP 19 /* No response from modem */
+#define DIA_ERR 20 /* Modem command error */
+#define DIA_NOIN 21 /* Failure to initialize modem */
+#define DIA_BUSY 22 /* Phone busy */
+#define DIA_NOCA 23 /* No carrier */
+#define DIA_NODT 24 /* No dialtone */
+#define DIA_RING 25 /* Ring, incoming call */
+#define DIA_NOAN 26 /* No answer */
+#define DIA_DISC 27 /* Disconnected */
+#define DIA_VOIC 28 /* Answered by voice */
+#define DIA_NOAC 29 /* Access denied, forbidden call */
+#define DIA_BLCK 30 /* Blacklisted */
+#define DIA_DELA 31 /* Delayed */
+#define DIA_FAX 32 /* Fax */
+#define DIA_DIGI 33 /* Digital Line */
+#define DIA_TAPI 34 /* TAPI dialing failure */
+#define DIA_UERR 98 /* Unknown error */
+#define DIA_UNSP 99 /* Unspecified failure detected by modem */
+
+#define MDMINF struct mdminf
+
+MDMINF { /* Structure for modem-specific information */
+
+ char * name; /* Descriptive name */
+ char * pulse; /* Command to force pulse dialing */
+ char * tone; /* Command to force tone dialing */
+ int dial_time; /* Time modem allows for dialing (secs) */
+ char * pause_chars; /* Character(s) to tell modem to pause */
+ int pause_time; /* Time associated with pause chars (secs) */
+ char * wake_str; /* String to wakeup modem & put in cmd mode */
+ int wake_rate; /* Delay between wake_str characters (msecs) */
+ char * wake_prompt; /* String prompt after wake_str */
+ char * dmode_str; /* String to put modem in dialing mode */
+ char * dmode_prompt; /* String prompt for dialing mode */
+ char * dial_str; /* Dialing string, with "%s" for number */
+ int dial_rate; /* Interchar delay to modem (msec) */
+ int esc_time; /* Escape sequence guard time (msec) */
+ int esc_char; /* Escape character */
+ char * hup_str; /* Hangup string */
+ char * hwfc_str; /* Hardware flow control string */
+ char * swfc_str; /* Software flow control string */
+ char * nofc_str; /* No flow control string */
+ char * ec_on_str; /* Error correction on string */
+ char * ec_off_str; /* Error correction off string */
+ char * dc_on_str; /* Data compression on string */
+ char * dc_off_str; /* Data compression off string */
+ char * aa_on_str; /* Autoanswer on string */
+ char * aa_off_str; /* Autoanswer off string */
+ char * sb_on_str; /* Speed buffering on string */
+ char * sb_off_str; /* Speed buffering off string */
+ char * sp_on_str; /* Speaker on string */
+ char * sp_off_str; /* Speaker off string */
+ char * vol1_str; /* Volume low string */
+ char * vol2_str; /* Volume med string */
+ char * vol3_str; /* Volume high string */
+ char * ignoredt; /* Ignore dialtone string */
+ char * ini2; /* Last-minute init string */
+ long max_speed; /* Maximum interface speed */
+ long capas; /* Capability bits */
+ /* function to read modem's response string to a non-dialing command */
+ _PROTOTYP( int (*ok_fn), (int,int) );
+};
+#endif /* NODIAL */
+
+/* Symbols for File Attributes */
+
+#define AT_XALL 0 /* All of them */
+#define AT_ALLY 1 /* All of them on (Yes) */
+#define AT_ALLN 2 /* All of them off (no) */
+#define AT_LENK 3 /* Length in K */
+#define AT_FTYP 4 /* File Type */
+#define AT_DATE 5 /* Creation date */
+#define AT_CREA 6 /* Creator */
+#define AT_ACCT 7 /* Account */
+#define AT_AREA 8 /* Area */
+#define AT_PSWD 9 /* Password for area */
+#define AT_BLKS 10 /* Blocksize */
+#define AT_ACCE 11 /* Access */
+#define AT_ENCO 12 /* Encoding */
+#define AT_DISP 13 /* Disposition */
+#define AT_LPRO 14 /* Local Protection */
+#define AT_GPRO 15 /* Generic Protection */
+#define AT_SYSI 16 /* System ID */
+#define AT_RECF 17 /* Record Format */
+#define AT_SYSP 18 /* System-Dependent Parameters */
+#define AT_LENB 19 /* Length in Bytes */
+#define AT_EOA 20 /* End of Attributes */
+
+/* Kermit packet information structure */
+
+struct pktinfo { /* Packet information structure */
+ CHAR *bf_adr; /* buffer address */
+ int bf_len; /* buffer length */
+ CHAR *pk_adr; /* Packet address within buffer */
+ int pk_len; /* length of data within buffer */
+ int pk_typ; /* packet type */
+ int pk_seq; /* packet sequence number */
+ int pk_rtr; /* retransmission count */
+};
+
+/* Send Modes (indicating which type of SEND command was used) */
+
+#define SM_SEND 0
+#define SM_MSEND 1
+#define SM_RESEND 2
+#define SM_PSEND 3
+#define SM_MAIL 4
+#define SM_PRINT 5
+
+#define OPTBUFLEN 256
+
+/* File-related symbols and structures */
+/* Used by SET FILE command but also by protocol and i/o modules */
+
+#define XMODE_A 0 /* Transfer mode Automatic */
+#define XMODE_M 1 /* Transfer mode Manual */
+
+#define XYFILN 0 /* Naming */
+#define XYFN_L 0 /* Literal */
+#define XYFN_C 1 /* Converted */
+#define XYFILT 1 /* Type */
+#define XYFT_T 0 /* Text */
+#define XYFT_B 1 /* Binary */
+#define XYFT_I 2 /* Image or Block (VMS) */
+#define XYFT_L 3 /* Labeled (tagged binary) (VMS or OS/2) */
+#define XYFT_U 4 /* Binary Undefined (VMS) */
+#define XYFT_M 5 /* MacBinary (Macintosh) */
+#define XYFT_X 6 /* TENEX (FTP TYPE L 8) */
+#define XYFT_D 99 /* Debug (for session logs) */
+#define XYFILW 2 /* Warning */
+#define XYFILD 3 /* Display */
+#define XYFILC 4 /* Character set */
+#define XYFILF 5 /* Record Format */
+#define XYFF_S 0 /* Stream */
+#define XYFF_V 1 /* Variable */
+#define XYFF_VB 2 /* Variable with RCW's */
+#define XYFF_F 3 /* Fixed length */
+#define XYFF_U 4 /* Undefined */
+#define XYFILR 6 /* Record length */
+#define XYFILO 7 /* Organization */
+#define XYFO_S 0 /* Sequential */
+#define XYFO_I 1 /* Indexed */
+#define XYFO_R 2 /* Relative */
+#define XYFILP 8 /* Printer carriage control */
+#define XYFP_N 0 /* Newline (imbedded control characters) */
+#define XYFP_F 1 /* FORTRAN (space, 1, +, etc, in column 1 */
+#define XYFP_P 2 /* Special printer carriage controls */
+#define XYFP_X 4 /* None */
+#define XYFILX 9 /* Collision Action */
+#define XYFX_A 3 /* Append */
+#define XYFX_Q 5 /* Ask */
+#define XYFX_B 2 /* Backup */
+#define XYFX_D 4 /* Discard */
+#define XYFX_R 0 /* Rename */
+#define XYFX_X 1 /* Replace */
+#define XYFX_U 6 /* Update */
+#define XYFX_M 7 /* Modtimes differ */
+#define XYFILB 10 /* Blocksize */
+#define XYFILZ 11 /* Disposition */
+#define XYFZ_N 0 /* New, Create */
+#define XYFZ_A 1 /* New, append if file exists, else create */
+#define XYFZ_O 2 /* Old, file must exist */
+#define XYFILS 12 /* File Byte Size */
+#define XYFILL 13 /* File Label (VMS) */
+#define XYFILI 14 /* File Incomplete */
+#define XYFILQ 15 /* File path action (strip or not) */
+#define XYFILG 16 /* File download directory */
+#define XYFILA 17 /* Line terminator for local text files */
+#define XYFA_L 012 /* LF (as in UNIX) */
+#define XYFA_C 015 /* CR (as in OS-9 or Mac OS) */
+#define XYFA_2 000 /* CRLF -- Note: this must be defined as 0 */
+#define XYFILY 18 /* Destination */
+#define XYFILV 19 /* EOF Detection Method */
+#define XYEOF_L 0 /* File length */
+#define XYEOF_Z 1 /* Ctrl-Z in file */
+#define XYFILH 20 /* OUTPUT parameters - buffered, blocking, etc */
+#define XYFIBP 21 /* BINARY-PATTERN */
+#define XYFITP 22 /* TEXT-PATTERN */
+#define XYFIPA 23 /* PATTERNS ON/OFF */
+#define XYFILU 24 /* UCS ... */
+#define XYF_PRM 25 /* PERMISSIONS, PROTECTION */
+#define XYF_INSP 26 /* INSPECTION (SCAN) */
+#define XYF_DFLT 27 /* DEFAULT (character sets) */
+#define XYF_SSPA 28 /* STRINGSPACE */
+#define XYF_LSIZ 29 /* LISTSIZE */
+
+/* File Type (return code) definitions and corresponding name strings */
+
+#define FT_7BIT 0 /* 7-bit text */
+#define FT_8BIT 1 /* 8-bit text */
+#define FT_UTF8 2 /* UTF8 */
+#define FT_UCS2 3 /* UCS2 */
+#define FT_TEXT 4 /* Unknown text */
+#define FT_BIN 5 /* Binary */
+#define SCANFILEBUF 49152 /* Size of file scan (48K) */
+
+/* Connection closed reasons */
+
+#define WC_REMO 0 /* Closed by remote */
+#define WC_CLOS 1 /* Closed from our end */
+#define WC_TELOPT 2 /* Telnet negotiation failure */
+
+#ifdef BIGBUFOK
+#define FTPATTERNS 256
+#else
+#define FTPATTERNS 64
+#endif /* BIGBUFOK */
+
+#define SYS_UNK 0 /* Selected server system types */
+#define SYS_UNIX 1
+#define SYS_WIN32 2
+#define SYS_VMS 3
+#define SYS_OS2 4
+#define SYS_DOS 5
+#define SYS_TOPS10 6
+#define SYS_TOPS20 7
+#define SYS_VOS 8
+#define SYS_DG 9
+#define SYS_OSK 10
+#define SYS_MAX 11
+
+#ifdef CK_SMALL
+#define PWBUFL 63
+#else
+#define PWBUFL 255
+#endif /* CK_SMALL */
+
+#ifdef OS2
+struct tt_info_rec { /* Terminal emulation info */
+ char *x_name;
+ char *x_aliases[4];
+ char *x_id;
+};
+#endif /* OS2 */
+
+/* BEEP TYPES */
+#define BP_BEL 0 /* Terminal bell */
+#define BP_NOTE 1 /* Info */
+#define BP_WARN 2 /* Warning */
+#define BP_FAIL 3 /* Error */
+
+#ifndef NOIKSD
+#ifdef IKSDB /* IKSD Database definitions */
+
+/* Field values */
+
+#define DBF_INUSE 1 /* Flag bits... In use */
+#define DBF_USER 2 /* Real user (versus anonymous) */
+#define DBF_LOGGED 4 /* Logged in (versus not) */
+
+/* Data Definitions... */
+
+/* Numeric fields, hex, right justified, 0-filled on left */
+
+#define db_FLAGS 0 /* Field 0: Flags */
+#define DB_FLAGS 0 /* Offset: 0 */
+#define dB_FLAGS 4 /* Length: 4 (hex digits) */
+
+#define db_ATYPE 1 /* Field 1: Authentication type */
+#define DB_ATYPE 4 /* 4 hex digits */
+#define dB_ATYPE 4
+
+#define db_AMODE 2 /* Field 2: Authentication mode */
+#define DB_AMODE 8 /* 4 hex digits */
+#define dB_AMODE 4
+
+#define db_STATE 3 /* Field 3: State - 4 hex digits*/
+#define DB_STATE 12 /* 4 hex digits */
+#define dB_STATE 4
+
+#define db_MYPID 4 /* Field 4: My PID */
+#define DB_MYPID 16 /* 16 hex digits left padded with 0 */
+#define dB_MYPID 16
+
+#define db_SADDR 5 /* Field 5: Server (my) IP address */
+#define DB_SADDR 32 /* 16 hex digits left padded with 0 */
+#define dB_SADDR 16
+
+#define db_CADDR 6 /* Field 6: Client IP address */
+#define DB_CADDR 48 /* 16 hex digits left padded with 0 */
+#define dB_CADDR 16
+
+/* Date-time fields (17 right-adjusted in 18 for Y10K readiness) */
+
+#define db_START 7 /* Field 7: Session start date-time */
+#define DB_START 65 /* 64 is leading space for Y10K */
+#define dB_START 17
+
+#define db_LASTU 8 /* Field 8: Last lastu date-time */
+#define DB_LASTU 83 /* 82 is leading space for Y10K */
+#define dB_LASTU 17
+
+#define db_ULEN 9 /* Field 9: Length of Username */
+#define DB_ULEN 100 /* 4 hex digits */
+#define dB_ULEN 4
+
+#define db_DLEN 10 /* Field 10: Length of Directory */
+#define DB_DLEN 104 /* 4 hex digits */
+#define dB_DLEN 4
+
+#define db_ILEN 11 /* Field 11: Length of Info */
+#define DB_ILEN 108 /* 4 hex digits */
+#define dB_ILEN 4
+
+#define db_PAD1 12 /* Field 12: (Reserved) */
+#define DB_PAD1 112 /* filled with spaces */
+#define dB_PAD1 912
+
+/* String fields, all right-padded with blanks */
+
+#define db_USER 13 /* Field 13: Username */
+#define DB_USER 1024 /* right-padded with spaces */
+#define dB_USER 1024
+
+#define db_DIR 14 /* Field 14: Current directory */
+#define DB_DIR 2048 /* right-padded with spaces */
+#define dB_DIR 1024
+
+#define db_INFO 15 /* Field 15: State-specific info */
+#define DB_INFO 3072 /* right-padded with spaces */
+#define dB_INFO 1024
+
+#define DB_RECL 4096 /* Database record length */
+
+/* Offset, length, and type of each field thru its db_XXX symbol */
+
+#define DBT_HEX 1 /* Hexadecimal number */
+#define DBT_STR 2 /* String */
+#define DBT_DAT 3 /* Date-Time yyyymmdd hh:mm:ss */
+#define DBT_UND 9 /* Undefined and blank */
+
+struct iksdbfld {
+ int off; /* Position (offset) */
+ int len; /* Length (bytes) */
+ int typ; /* Data type */
+};
+_PROTOTYP(int dbinit, (void));
+_PROTOTYP(int initslot, (int));
+_PROTOTYP(int getslot, (void));
+_PROTOTYP(int freeslot, (int));
+_PROTOTYP(int updslot, (int));
+_PROTOTYP(int slotstate, (int, char *, char *, char *));
+_PROTOTYP(int slotdir, (char *, char *));
+#endif /* IKSDB */
+#endif /* NOIKSD */
+
+/* ANSI forward declarations for protocol-related functions. */
+
+_PROTOTYP( int input, (void) );
+_PROTOTYP( int inibufs, (int, int) );
+_PROTOTYP( int makebuf, (int, int, CHAR [], struct pktinfo *) );
+_PROTOTYP( int mksbuf, (int) );
+_PROTOTYP( int mkrbuf, (int) );
+_PROTOTYP( int spack, (char, int, int, CHAR *) );
+_PROTOTYP( VOID proto, (void) );
+_PROTOTYP( int rpack, (void) );
+_PROTOTYP( int ack, (void) );
+_PROTOTYP( int nack, (int) );
+_PROTOTYP( int ackn, (int) );
+_PROTOTYP( int ack1, (CHAR *) );
+_PROTOTYP( int ackns, (int, CHAR *) );
+#ifdef STREAMING
+_PROTOTYP( int fastack, (void) );
+#endif /* STREAMING */
+_PROTOTYP( int resend, (int) );
+_PROTOTYP( int errpkt, (CHAR *) );
+_PROTOTYP( VOID logpkt, (char, int, CHAR *, int) );
+_PROTOTYP( CHAR dopar, (CHAR) );
+_PROTOTYP( int chk1, (CHAR *, int) );
+_PROTOTYP( unsigned int chk2, (CHAR *, int) );
+_PROTOTYP( unsigned int chk3, (CHAR *, int) );
+_PROTOTYP( int sipkt, (char) );
+_PROTOTYP( int sopkt, (void) );
+_PROTOTYP( int sinit, (void) );
+_PROTOTYP( VOID rinit, (CHAR *) );
+_PROTOTYP( int spar, (CHAR *) );
+_PROTOTYP( int rcvfil, (char *) );
+_PROTOTYP( CHAR * rpar, (void) );
+_PROTOTYP( int gnfile, (void) );
+_PROTOTYP( int getsbuf, (int) );
+_PROTOTYP( int getrbuf, (void) );
+_PROTOTYP( int freesbuf, (int) );
+_PROTOTYP( int freerbuf, (int) );
+_PROTOTYP( int dumpsbuf, (void) );
+_PROTOTYP( int dumprbuf, (void) );
+_PROTOTYP( VOID freerpkt, (int) );
+_PROTOTYP( int chkwin, (int, int, int) );
+_PROTOTYP( int rsattr, (CHAR *) );
+_PROTOTYP( char *getreason, (char *) );
+_PROTOTYP( int scmd, (char, CHAR *) );
+_PROTOTYP( int encstr, (CHAR *) );
+_PROTOTYP( int decode, (CHAR *, int (*)(char), int) );
+_PROTOTYP( int bdecode, (CHAR *, int (*)(char)) );
+_PROTOTYP( int fnparse, (char *) );
+_PROTOTYP( int syscmd, (char *, char *) );
+_PROTOTYP( int cwd, (char *) );
+_PROTOTYP( int remset, (char *) );
+_PROTOTYP( int initattr, (struct zattr *) );
+_PROTOTYP( int gattr, (CHAR *, struct zattr *) );
+_PROTOTYP( int adebu, (char *, struct zattr *) );
+_PROTOTYP( int canned, (CHAR *) );
+_PROTOTYP( int opent, (struct zattr *) );
+_PROTOTYP( int ckopenx, (struct zattr *) );
+_PROTOTYP( int opena, (char *, struct zattr *) );
+_PROTOTYP( int openi, (char *) );
+_PROTOTYP( int openo, (char *, struct zattr *, struct filinfo *) );
+_PROTOTYP( int openc, (int, char *) );
+_PROTOTYP( int reof, (char *, struct zattr *) );
+_PROTOTYP( VOID reot, (void) );
+_PROTOTYP( int sfile, (int) );
+_PROTOTYP( int sattr, (int, int) );
+_PROTOTYP( int sdata, (void) );
+_PROTOTYP( int seof, (int) );
+_PROTOTYP( int sxeof, (int) );
+_PROTOTYP( int seot, (void) );
+_PROTOTYP( int window, (int) );
+_PROTOTYP( int clsif, (void) );
+_PROTOTYP( int clsof, (int) );
+_PROTOTYP( CHAR setgen, (char, char *, char *, char *) );
+_PROTOTYP( int getpkt, (int, int) );
+_PROTOTYP( int maxdata, (void) );
+_PROTOTYP( int putsrv, (char) );
+_PROTOTYP( int puttrm, (char) );
+_PROTOTYP( int putque, (char) );
+_PROTOTYP( int putfil, (char) );
+_PROTOTYP( int putmfil, (char) );
+_PROTOTYP( int zputfil, (char) );
+_PROTOTYP( VOID zdstuff, (CHAR) );
+_PROTOTYP( int tinit, (int) );
+_PROTOTYP( VOID pktinit, (void) );
+_PROTOTYP( VOID resetc, (void) );
+_PROTOTYP( VOID xsinit, (void) );
+_PROTOTYP( int adjpkl, (int,int,int) );
+_PROTOTYP( int chktimo, (int,int) );
+_PROTOTYP( int nxtpkt, (void) );
+_PROTOTYP( VOID rcalcpsz, (void) );
+_PROTOTYP( int srinit, (int, int, int) );
+_PROTOTYP( VOID tstats, (void) );
+_PROTOTYP( VOID fstats, (void) );
+_PROTOTYP( VOID intmsg, (long) );
+_PROTOTYP( VOID ermsg, (char *) );
+_PROTOTYP( int chkint, (void) );
+_PROTOTYP( VOID sdebu, (int) );
+_PROTOTYP( VOID rdebu, (CHAR *, int) );
+_PROTOTYP( char * dbchr, ( int ) );
+#ifdef COMMENT
+_PROTOTYP( SIGTYP stptrap, (int, int) );
+_PROTOTYP( SIGTYP trap, (int, int) );
+#else
+_PROTOTYP( SIGTYP stptrap, (int) );
+_PROTOTYP( SIGTYP trap, (int) );
+#endif /* COMMENT */
+_PROTOTYP( char * ck_errstr, (void) );
+#ifndef NOXFER
+_PROTOTYP( int agnbyte, (void) );
+#endif /* NOXFER */
+_PROTOTYP( int xgnbyte, (int, int, int (*)(void)) );
+_PROTOTYP( int xpnbyte, (int, int, int, int (*)(char)) );
+
+/* User interface functions needed by main program, etc. */
+
+_PROTOTYP( int doconect, (int,int) );
+_PROTOTYP( VOID setflow, (void) );
+_PROTOTYP( VOID prescan, (int) );
+_PROTOTYP( VOID setint, (void) );
+_PROTOTYP( VOID doinit, (void) );
+_PROTOTYP( VOID dofast, (void) );
+_PROTOTYP( VOID cmdini, (void) );
+_PROTOTYP( int dotake, (char *) );
+_PROTOTYP( int cmdlin, (void) );
+#ifdef OS2
+_PROTOTYP( int conect, (int) );
+#else /* OS2 */
+_PROTOTYP( int conect, (void) );
+#endif /* OS2 */
+_PROTOTYP( int ckcgetc, (int) );
+_PROTOTYP( int ckcputc, (int) );
+_PROTOTYP (int mdmhup, (void) );
+_PROTOTYP( VOID herald, (void) );
+_PROTOTYP( VOID fixcmd, (void) );
+_PROTOTYP( int doarg, (char) );
+_PROTOTYP( int doxarg, (char **, int) );
+_PROTOTYP( VOID usage, (void) );
+_PROTOTYP( VOID doclean, (int) );
+_PROTOTYP( int sndhlp, () );
+_PROTOTYP( int sndstring, (char *) );
+_PROTOTYP( VOID ckhost, (char *, int) );
+_PROTOTYP( int gettcs, (int, int) );
+_PROTOTYP( VOID getdialenv, (void) );
+_PROTOTYP( VOID setprefix, (int) );
+_PROTOTYP(VOID initproto,(int,char *,char *,char *,char *,char *,char*,char*));
+_PROTOTYP( VOID initpat, (void) );
+_PROTOTYP( VOID initcsets, (void) );
+_PROTOTYP( char * getsysid, (char *) );
+_PROTOTYP( int getsysix, (char *) );
+#ifdef CK_TIMERS
+_PROTOTYP( VOID rttinit, (void) );
+_PROTOTYP( int getrtt, (int, int) );
+#endif /* CK_TIMERS */
+
+_PROTOTYP( int is_a_tty, (int) );
+_PROTOTYP( int snddir, (char *) );
+_PROTOTYP( int snddel, (char *) );
+_PROTOTYP( int sndtype, (char *) );
+_PROTOTYP( int dooutput, (char *, int) );
+_PROTOTYP( int isabsolute, (char *) );
+_PROTOTYP( VOID whoarewe, (void) );
+_PROTOTYP( int ckmkdir, (int, char *, char **, int, int) );
+_PROTOTYP( int autoexitchk, (CHAR) );
+_PROTOTYP( VOID fcps, (void) );
+#ifdef OS2
+_PROTOTYP( VOID logchar, (unsigned short) );
+#else /* OS2 */
+_PROTOTYP( VOID logchar, (char) );
+#endif /* OS2 */
+_PROTOTYP( VOID logstr, (char *, int) );
+
+_PROTOTYP( VOID dologend, (void) );
+#ifdef NOLOCAL
+#define dologshow()
+#else
+_PROTOTYP( long dologshow, (int) );
+#endif /* NOLOCAL */
+
+#ifdef NODISPLAY
+#define fxdinit(a)
+#else
+_PROTOTYP( VOID fxdinit, (int) );
+#endif /* NODISPLAY */
+
+_PROTOTYP( int fileselect, (char *,
+ char *, char *, char *, char *,
+ long, long,
+ int, int,
+ char **) );
+
+
+_PROTOTYP( char * whoami, (void) );
+_PROTOTYP( int shoesc, (int) );
+
+#ifdef CK_APC
+_PROTOTYP( int chkspkt, (char *) );
+_PROTOTYP( int kstart, (CHAR) );
+_PROTOTYP( VOID autodown, (int));
+#ifdef CK_XYZ
+_PROTOTYP( int zstart, (CHAR) );
+#endif /* CK_XYZ */
+#ifdef OS2
+_PROTOTYP(void apc_command, (int, char*));
+#endif /* OS2 */
+#endif /* CK_APC */
+
+/* User Query data structures and functions */
+
+struct txtbox {
+ char * t_buf; /* Destination buffer address */
+ int t_len; /* Destination buffer length */
+ char * t_lbl; /* Label for this field */
+ char * t_dflt; /* Default response for this field */
+ int t_echo; /* 0 = no, 1 = yes, 2 = asterisks */
+};
+
+#define DEFAULT_UQ_TIMEOUT 0
+_PROTOTYP(int uq_ok, (char *,char *,int,char **,int) );
+_PROTOTYP(int uq_txt, (char *,char *,int,char **,char *,int,char *,int));
+_PROTOTYP(int uq_mtxt, (char *,char **,int,struct txtbox[]) );
+_PROTOTYP(int uq_file, (char *,char *,int,char **,char *,char *,int));
+
+#ifdef CK_URL
+struct urldata {
+ char * sav; /* The URL itself */
+ char * svc; /* Service */
+ char * usr; /* User */
+ char * psw; /* Password */
+ char * hos; /* Host */
+ char * por; /* Port */
+ char * pth; /* Path */
+};
+_PROTOTYP(int urlparse, (char *, struct urldata *));
+#endif /* CK_URL */
+
+#endif /* CKCKER_H */
+
+/* End of ckcker.h */
diff --git a/ckermit-8.0.211/ckclib.c b/ckermit-8.0.211/ckclib.c
new file mode 100644
index 0000000..36bf111
--- /dev/null
+++ b/ckermit-8.0.211/ckclib.c
@@ -0,0 +1,2883 @@
+char * cklibv = "C-Kermit library, 8.0.033, 16 Mar 2003";
+
+#define CKCLIB_C
+
+/* C K C L I B . C -- C-Kermit Library routines. */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1999, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+/*
+ General-purpose, system/platform/compiler-independent routines for use
+ by all modules. Many are replacements for commonly used C library
+ functions that are not found on every platform, and/or that lack needed
+ functionality (e.g. caseless string search/compare) or safety features.
+
+ ckstrncpy() - Similar to strncpy() but different (see comments).
+ ckstrncat() - Similar to strncat() but different (see comments).
+ chartostr() - Converts a char to a string (self or ctrl char name).
+ ckstrchr() - Portable strchr().
+ ckstrpbrk() - Portable strpbrk().
+ cklower() - Lowercase a string (in place).
+ ckupper() - Uppercase a string (in place).
+ ckindex() - Left or right index.
+ ckstrstr() - Portable strstr().
+ ckitoa() - Converts int to string.
+ ckuitoa() - Converts unsigned int to string.
+ ckltoa() - Converts long to string.
+ ckultoa() - Converts unsigned long to string.
+ ckctoa() - Converts char to string.
+ ckmakmsg() - Constructs a message from 4 source strings.
+ ckmakxmsg() - Constructs a message from 12 source strings.
+ ckmatch() - Pattern matching.
+ ckmemcpy() - Portable memcpy().
+ ckrchar() - Rightmost character of a string.
+ ckstrcmp() - Possibly caseless string comparison.
+ ckstrpre() - Caseless string prefix comparison.
+ sh_sort() - Sorts an array of strings, many options.
+ brstrip() - Strips enclosing braces (and doublequotes).
+ makelist() - Splits "{{item}{item}...}" into an array.
+ makestr() - Careful malloc() front end.
+ xmakestr() - ditto (see comments).
+ ckradix() - Convert number radix (2-36).
+ b8tob64() - Convert data to base 64.
+ b64tob8() - Convert base 64 to data.
+ chknum() - Checks if string is a (possibly signed) integer.
+ rdigits() - Checks if string is composed only of decimal digits.
+ isfloat() - Checks if string is a valid floating-point number.
+ parnam() - Returns parity name string.
+ hhmmss() - Converts seconds to hh:mm:ss string.
+ lset() - Write fixed-length field left-adjusted into a record.
+ rset() - Write fixed-length field right-adjusted into a record.
+ ulongtohex() - Converts an unsigned long to a hex string.
+ hextoulong() - Converts a hex string to an unsigned long.
+ cksplit() - Splits a string into an array of words.
+
+ Prototypes are in ckclib.h.
+
+ Note: This module should not contain any extern declarations.
+*/
+#include "ckcsym.h"
+#include "ckcdeb.h"
+#include "ckcasc.h"
+
+/* Public variables */
+
+int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */
+
+char *
+ccntab[] = { /* Names of ASCII (C0) control characters 0-31 */
+ "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
+ "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
+ "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
+ "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
+};
+
+char *
+c1tab[] = { /* Names of ISO 6429 (C1) control characters 0-32 */
+ "XXX", "XXX", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
+ "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
+ "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
+ "SOS", "XXX", "SCI", "CSI", "ST", "OSC", "PM", "APC", "NBS"
+};
+
+#define RXRESULT 127
+static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static char rxresult[RXRESULT+1];
+
+/* C K S T R N C P Y */
+
+/*
+ Copies a NUL-terminated string into a buffer whose total length is given,
+ ensuring that the result is NUL-terminated even if it has to be truncated.
+
+ Call with:
+ dest = pointer to destination buffer
+ src = pointer to source string
+ len = length of destination buffer (the actual length, not one less).
+
+ Returns:
+ int, The number of bytes copied, 0 or more.
+
+ NOTE: This is NOT a replacement for strncpy():
+ . strncpy() does not require its source string to be NUL-terminated.
+ . strncpy() does not necessarily NUL-terminate its result.
+ . strncpy() right-pads dest with NULs if it is longer than src.
+ . strncpy() treats the length argument as the number of bytes to copy.
+ . ckstrncpy() treats the length argument as the size of the dest buffer.
+ . ckstrncpy() doesn't dump core if given NULL string pointers.
+ . ckstrncpy() returns a number.
+
+ Use ckstrncpy() when you want to:
+ . Copy an entire string into a buffer without overrun.
+ . Get the length of the string back.
+
+ Use strncpy() when you want to:
+ . Copy a piece of a string.
+*/
+int
+#ifdef CK_ANSIC
+ckstrncpy(char * dest, const char * src, int len)
+#else
+ckstrncpy(dest,src,len) char * dest, * src; int len;
+#endif /* CK_ANSIC */
+{
+ int i;
+ if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
+ if (dest) *dest = NUL;
+ return(0);
+ }
+#ifndef NOCKSTRNCPY
+ for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */
+ dest[i] = src[i];
+ dest[i] = NUL;
+#else
+ i = strlen(src);
+ if (i > len) i = len;
+ strncpy(dest,src,i);
+ dest[len] = NUL;
+#endif /* NOCKSTRNCPY */
+ return(i);
+}
+
+/* C K S T R N C A T */
+
+/*
+ Appends a NUL-terminated string to a buffer whose total length is given,
+ ensuring that the result is NUL-terminated even if it had to be truncated.
+
+ Call with:
+ dest = pointer to destination buffer containing a null-terminated string
+ src = pointer to null-terminated source string
+ len = length of destination buffer (the actual length, not one less).
+
+ Returns:
+ int, The number of bytes copied, 0 or more.
+*/
+int
+#ifdef CK_ANSIC
+ckstrncat(char * dest, const char * src, int len)
+#else
+ckstrncat(dest,src,len) char * dest, * src; int len;
+#endif /* CK_ANSIC */
+{
+ register int i, j;
+#ifdef NOCKSTRNCPY
+ register char * s1, * s2;
+#endif /* NOCKSTRNCPY */
+ if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
+ if (dest) *dest = NUL;
+ return(0);
+ }
+#ifndef NOCKSTRNCPY
+ /* Args OK, copy */
+ for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++)
+ dest[i+j] = src[i];
+ dest[i+j] = NUL;
+#else
+ j = 0;
+ s1 = dest;
+ while (*s1++) j++; /* j = strlen(dest); */
+ s1--; /* (back up over NUL) */
+
+ i = 0;
+ s2 = src;
+ while (*s2++) i++; /* i = strlen(src); */
+
+ if (i > (len-j))
+ i = len - j;
+ if (i <= 0)
+ return(0);
+
+#ifdef COMMENT
+ strncpy(&dest[j],src,i);
+#else
+ j = i; /* This should be a bit faster... */
+ s2 = src; /* depends on strcpy implementation; */
+ while ((*s1++ = *s2++) && j--) /* at least it shouldn't be slower. */
+ ;
+ dest[len-1] = NUL; /* In case of early exit. */
+#endif /* COMMENT */
+
+#endif /* NOCKSTRNCPY */
+ return(i);
+}
+
+/* C K M A K M S G */
+
+/*
+ Constructs a message from up to 4 pieces with length checking.
+ Result is always NUL terminated. Call with:
+ buf: Pointer to buffer for constructing message.
+ len: Length of buffer.
+ s1-s4: String pointers (can be NULL).
+ Returns:
+ 0: Nothing was copied.
+ n: (positive number) n bytes copied, all args copied successfully.
+ -n: n bytes were copied, destination buffer not big enough for all.
+ Also see:
+ ckmakxmsg() -- accepts 12 string args.
+ ckitoa(), ckltoa(), ckctoa(), ckitox(), etc.
+ Use ckmak[x]msg() plus ck?to?() as a safe replacement for sprintf().
+*/
+int
+#ifdef CK_ANSIC
+ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4)
+#else /* CK_ANSIC */
+ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len;
+#endif /* CK_ANSIC */
+{
+ int i, n = 0, m = 0;
+ char *s;
+ char *p, *a[4];
+
+ if (!buf) return(n); /* No destination */
+ if (len < 1) return(n); /* No size */
+
+ s = buf; /* Point to destination */
+ a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Array of source strings */
+ for (i = 0; i < 4; i++) { /* Loop thru array */
+ p = a[i]; /* Point to this element */
+ if (p) { /* If pointer not null */
+ n = ckstrncpy(s,p,len); /* Copy safely */
+ m += n; /* Accumulate total */
+ if (p[n]) /* Didn't get whole thing? */
+ return(-m); /* return indicating buffer full */
+ len -= n; /* Deduct from space left */
+ s += n; /* Otherwise advance dest pointer */
+ }
+ }
+ return(m); /* Return total bytes copied */
+}
+
+
+/* C K M A K X M S G */
+
+/* Exactly like ckmakmsg(), but accepts 12 string arguments. */
+
+int
+#ifdef CK_ANSIC
+ckmakxmsg(char * buf, int len,
+ char *s1, char *s2, char *s3, char *s4, char *s5, char *s6,
+ char *s7, char *s8, char *s9, char *s10, char *s11, char *s12)
+#else /* CK_ANSIC */
+ckmakxmsg(buf,len,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12)
+ char *buf, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12;
+ int len;
+#endif /* CK_ANSIC */
+{
+ int i, n = 0, m = 0;
+ char *s;
+ char *p, *a[12];
+
+ if (!buf) return(n); /* No destination */
+ if (len < 1) return(n); /* No size */
+
+ s = buf; /* Point to destination */
+ a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Source-string array */
+ a[4] = s5; a[5] = s6; a[6] = s7; a[7] = s8;
+ a[8] = s9; a[9] = s10; a[10] = s11; a[11] = s12;
+ for (i = 0; i < 12; i++) { /* Loop thru array */
+ p = a[i]; /* Point to this element */
+ if (p) { /* If pointer not null */
+ n = ckstrncpy(s,p,len); /* Copy safely */
+ m += n; /* Accumulate total */
+ if (p[n]) /* Didn't get whole thing? */
+ return(-m); /* return indicating buffer full */
+ len -= n; /* Deduct from space left */
+ s += n; /* Otherwise advance dest pointer */
+ }
+ }
+ return(m); /* Return total bytes copied */
+}
+
+/* C H A R T O S T R */
+
+/* Converts a character to a string, interpreting controls. */
+
+char *
+chartostr(x) int x; { /* Call with char x */
+ static char buf[2]; /* Returns string pointer. */
+ if (x < 32)
+ return(ccntab[x]);
+ if (x == 127)
+ return("DEL");
+ if (x > 127 && x < 161)
+ return(c1tab[x - 128]);
+ if (x == 0xAD)
+ return("SHY");
+ buf[1] = NUL;
+ buf[0] = (unsigned)(x & 0xff);
+ return((char *)buf);
+}
+
+/* C K R C H A R */
+
+/* Returns the rightmost character of the given null-terminated string */
+
+int
+ckrchar(s) char * s; {
+ register CHAR c = '\0', *p;
+ p = (CHAR *)s;
+ if (!p) p = (CHAR *)""; /* Null pointer == empty string */
+ if (!*p) return(0);
+ while (*p) /* Crawl to end of string */
+ c = *p++;
+ return((unsigned)(c & 0xff)); /* Return final character */
+}
+
+/* C K S T R C H R */
+
+/* Replacement for strchr(), which is not universal. */
+/* Call with:
+ s = pointer to string to look in.
+ c = character to look for.
+ Returns:
+ NULL if c not found in s or upon any kind of error, or:
+ pointer to first occurrence of c in s, searching from left to right.
+*/
+char *
+#ifdef CK_ANSIC
+ckstrchr(char * s, char c)
+#else
+ckstrchr(s,c) char *s, c;
+#endif /* CK_ANSIC */
+/* ckstrchr */ {
+ if (!s)
+ return(NULL);
+ while (*s && *s != c)
+ s++;
+ return((*s == c) ? s : NULL);
+}
+
+/* C K S T R R C H R */
+
+/* Replacement for strrchr(), which is not universal. */
+/* Call with:
+ s = pointer to string to look in.
+ c = character to look for.
+ Returns:
+ NULL if c not found in s or upon any kind of error, or:
+ pointer to first occurrence of c in s, searching from right to left.
+*/
+char *
+#ifdef CK_ANSIC
+ckstrrchr(char * s, char c)
+#else
+ckstrrchr(s,c) char *s, c;
+#endif /* CK_ANSIC */
+/* ckstrchr */ {
+ char * s2 = NULL;
+ if (!s)
+ return(NULL);
+ while (*s) {
+ if (*s == c)
+ s2 = s;
+ s++;
+ }
+ return(s2);
+}
+
+
+/* C K S T R P B R K -- Portable replacement for strpbrk() */
+
+/* Returns pointer to first char in s1 that is also in s2, or NULL */
+
+char *
+ckstrpbrk(s1, s2) char * s1, * s2; {
+ char c1, c2, * s3;
+ if (!s1 || !s2) return(NULL);
+ if (!*s1 || !*s2) return(NULL);
+ while ((c1 = *s1++)) {
+ s3 = s2;
+ while ((c2 = *s3++)) {
+ if (c2 == c1)
+ return(s1-1);
+ }
+ }
+ return(NULL);
+}
+
+/* C K L O W E R -- Lowercase a string IN PLACE */
+
+/* Returns the length of the string */
+
+int
+cklower(s) char *s; {
+ int n = 0;
+ if (!s) return(0);
+ while (*s) {
+ if (isupper(*s)) *s = (char) tolower(*s);
+ s++, n++;
+ }
+ return(n);
+}
+
+/* C K U P P E R -- Uppercase a string IN PLACE */
+
+/* Returns the length of the string */
+
+int
+ckupper(s) char *s; {
+ int n = 0;
+ if (!s) return(0);
+ while (*s) {
+ if (islower(*s)) *s = (char) toupper(*s);
+ s++, n++;
+ }
+ return(n);
+}
+
+/* C K L T O A -- Long to string -- FOR DISCIPLINED USE ONLY */
+
+#define NUMBUF 1024
+static char numbuf[NUMBUF+32] = { NUL, NUL };
+static int numbp = 0;
+/*
+ ckltoa() and ckitoa() are like atol() and atoi() in the reverse direction,
+ returning a pointer to the string representation of the given number without
+ the caller having to worry about allocating or defining a buffer first.
+ They manage their own internal buffer, so successive calls return different
+ pointers. However, to keep memory consumption from growing without bound,
+ the buffer recycles itself. So after several hundred calls (depending on
+ the size of the numbers), some of the earlier pointers might well find
+ themselves referencing something different. Moral: You can't win in C.
+ Therefore, these routines are intended mainly for generating numeric strings
+ for short-term use, e.g. for passing numbers in string form as parameters to
+ functions. For long-term use, the result must be copied to a safe place.
+*/
+char *
+#ifdef CK_ANSIC
+ckltoa(long n)
+#else
+ckltoa(n) long n;
+#endif /* CK_ANSIC */
+/* ckltoa */ {
+ char buf[32]; /* Internal working buffer */
+ char * p, * s, * q;
+ int k, x, len = 0, sign = 0;
+ if (n < 0L) { /* Sign */
+ n = 0L - n;
+ sign = 1;
+ }
+ buf[31] = NUL;
+ for (k = 30; k > 0; k--) { /* Convert number to string */
+ x = n % 10L;
+ buf[k] = x + '0';
+ n = n / 10L;
+ if (!n)
+ break;
+ }
+ if (sign) buf[--k] = '-'; /* Add sign if necessary */
+ len = 31 - k;
+ if (len + numbp > NUMBUF)
+ numbp = 0;
+ p = numbuf + numbp;
+ q = p;
+ s = buf + k;
+ while ((*p++ = *s++)) ; /* Copy */
+ *p++ = NUL;
+ numbp += len+1;
+ return(q); /* Return pointer */
+}
+
+/* C K U L T O A -- Unsigned long to string */
+
+char *
+#ifdef CK_ANSIC
+ckultoa(unsigned long n)
+#else
+ckultoa(n) unsigned long n;
+#endif /* CK_ANSIC */
+/* ckltoa */ {
+ char buf[32]; /* Internal working buffer */
+ char * p, * s, * q;
+ int k, x, len = 0;
+ buf[31] = NUL;
+ for (k = 30; k > 0; k--) { /* Convert number to string */
+ x = n % 10L;
+ buf[k] = x + '0';
+ n = n / 10L;
+ if (!n)
+ break;
+ }
+ len = 31 - k;
+ if (len + numbp > NUMBUF)
+ numbp = 0;
+ p = numbuf + numbp;
+ q = p;
+ s = buf + k;
+ while ((*p++ = *s++)) ; /* Copy */
+ numbp += len+1;
+ return(q); /* Return pointer */
+}
+
+char *
+#ifdef CK_ANSIC
+ckltox(long n) /* Long int to "0x.." hex string */
+#else
+ckltox(n) long n;
+#endif /* CK_ANSIC */
+/* ckltox */ {
+ char buf[32]; /* Internal working buffer */
+ char *p, *q, *s, *bp = buf + 2;
+ int k;
+ buf[0] = '0';
+ buf[1] = 'x';
+ sprintf(bp, "%lx", n);
+ k = strlen(bp);
+ if (k&1) {
+ sprintf(bp, "0%lx", n);
+ k++;
+ }
+ k += 2; /* "0x" */
+ if (numbp + k >= NUMBUF)
+ numbp = 0;
+ p = numbuf + numbp;
+ q = p;
+ s = buf;
+ while ((*p++ = *s++)) ; /* Copy */
+ *p++ = NUL;
+ numbp += k+1;
+ return(q); /* Return pointer */
+}
+
+
+/* C K I T O A -- Int to string -- FOR DISCIPLINED USE ONLY */
+
+char *
+ckitoa(n) int n; { /* See comments with ckltoa(). */
+ long nn;
+ nn = n;
+ return(ckltoa(nn));
+}
+
+
+char * /* Unsigned int to string */
+ckuitoa(n) unsigned int n; {
+ unsigned long nn;
+ nn = n;
+ return(ckultoa(nn));
+}
+
+char *
+ckitox(n) int n; { /* Int to hex */
+ long nn;
+ nn = n;
+ return(ckltox(nn));
+}
+
+char *
+#ifdef CK_ANSIC
+ckctoa(char c) /* Char to string */
+#else
+ckctoa(c) char c;
+#endif
+/* ckctoa */ {
+ static char buf[32];
+ static int current = 0;
+ if (current >= 30)
+ current = 0;
+ buf[current++] = c;
+ buf[current++] = '\0';
+ return((char *)(buf + current - 2));
+}
+
+char *
+#ifdef CK_ANSIC
+ckctox(CHAR c, int flag) /* Unsigned char to hex */
+#else
+ckctox(c, flag) CHAR c; int flag;
+#endif
+/* ckctox */ {
+ static char buf[48];
+ static int current = 0;
+ int x;
+ char h;
+ if (current > 45)
+ current = 0;
+ x = (c >> 4) & 0x0f;
+ h = rxdigits[x];
+ if (!flag && isupper(rxdigits[x]))
+ h = tolower(rxdigits[x]);
+ buf[current++] = h;
+ x = c & 0x0f;
+ h = rxdigits[x];
+ if (!flag && isupper(rxdigits[x]))
+ h = tolower(rxdigits[x]);
+ buf[current++] = h;
+ buf[current++] = '\0';
+ return((char *)(buf + current - 3));
+}
+
+/* C K I N D E X -- C-Kermit's index function */
+/*
+ We can't depend on C libraries to have one, so here is our own.
+ Call with:
+ s1 - String to look for.
+ s2 - String to look in.
+ t - Offset from right or left of s2, 0 based; -1 for rightmost char in s2.
+ r - 0 for left-to-right search, non-0 for right-to-left.
+ icase 0 for case independence, non-0 if alphabetic case matters.
+ Returns 0 if string not found, otherwise a 1-based result.
+ Also returns 0 on any kind of error, e.g. junk parameters.
+*/
+int
+ckindex(s1,s2,t,r,icase) char *s1, *s2; int t, r, icase; {
+ int len1 = 0, len2 = 0, i, j, x, ot = t; /* ot = original t */
+ char * s;
+
+ if (!s1 || !s2) return(0);
+ s = s1;
+ while (*s++) len1++; /* length of string to look for */
+ s = s2;
+ while (*s++) len2++; /* length of string to look in */
+ s = s2;
+ if (t < 0) t = len2 - 1;
+
+ j = len2 - len1; /* length difference */
+
+ if (j < 0 || (r == 0 && t > j)) /* search string is longer */
+ return(0);
+ if (r == 0) { /* Index */
+ s = s2 + t; /* Point to beginning of target */
+ for (i = 0; i <= (j - t); i++) { /* Now compare */
+ x = ckstrcmp(s1,s++,len1,icase);
+ if (!x)
+ return(i+1+t);
+ }
+ } else { /* Reverse Index */
+ i = len2 - len1; /* Where to start looking */
+ if (ot > 0) /* Figure in offset if any */
+ i -= t;
+ for (j = i; j > -1; j--) {
+ if (!ckstrcmp(s1,&s2[j],len1,icase))
+ return(j+1);
+ }
+ }
+ return(0);
+}
+
+/* C K S T R S T R -- Portable replacement for strstr() */
+
+/* Returns pointer to first occurrence of s1 in s2, or NULL */
+
+char *
+ckstrstr(s1, s2) char * s1, * s2; {
+ int k;
+ k = ckindex(s2,s1,0,0,1);
+ return((k < 1) ? NULL : &s1[k-1]);
+}
+
+
+/* B R S T R I P -- Strip enclosing braces from arg string, in place. */
+/*
+ Call with:
+ Pointer to string that can be poked.
+ Returns:
+ Pointer to string without enclosing braces.
+ If original string was not braced, this is the arg pointer;
+ otherwise it is 1 + the arg pointer, with the matching closing
+ brace zero'd out. If the string starts with a brace but does
+ not end with a matching brace, the original pointer to the original
+ string is returned. If the arg pointer is NULL, a pointer to an
+ empty string is returned.
+*/
+#ifdef COMMENT
+
+/* This is the original version, handling only braces */
+
+char *
+brstrip(p) char *p; {
+ if (!p) return("");
+ if (*p == '{') {
+ int x;
+ x = (int)strlen(p) - 1;
+ if (p[x] == '}') {
+ p[x] = NUL;
+ p++;
+ }
+ }
+ return(p);
+}
+
+#else
+/* New version handles braces and doublequotes */
+
+char *
+brstrip(p) char *p; {
+ if (!p) return("");
+ if (*p == '{' || (*p == '"' && dblquo)) {
+ int x;
+ x = (int)strlen(p) - 1;
+ if (x > 0) {
+ if ((*p == '{' && p[x] == '}') ||
+ (*p == '"' && p[x] == '"')) {
+ if (x > 0 && p[x-1] != CMDQ) {
+ p[x] = NUL;
+ p++;
+ }
+ }
+ }
+ }
+ return(p);
+}
+#endif /* COMMENT */
+
+#ifdef COMMENT
+
+/* Even newer experimental version -- breaks many things */
+
+char *
+fnstrip(p) char *p; {
+ int i, j, k, n, len;
+ extern int cmd_quoting; /* Bad - no externs allowed! */
+
+ if (!p)
+ return("");
+
+ if (*p == '{') {
+ len = strlen(p);
+ n = 0;
+
+ for (j = 0; j < len; j++ ) {
+ if (p[j] == '{' &&
+ (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
+ for (n = 1, i = j+1; i < len; i++ ) {
+ if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ))
+ n++;
+ else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) {
+ if (--n == 0) {
+ for (k = j; k < i - 1; k++)
+ p[k] = p[k+1];
+ for (; i < len; i++ )
+ p[i-1] = p[i+1];
+ len -= 2;
+ j = i - 1;
+ }
+ }
+ }
+ }
+ }
+ if (n == 1) { /* Implied right brace at end of field */
+ for (k = j; k < len; k++)
+ p[k] = p[k+1];
+ len -= 1;
+ }
+ } else if (*p == '"') {
+ len = strlen(p);
+ n = 0;
+
+ for (j = 0; j < len; j++) {
+ if (p[j] == '"' &&
+ (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
+ n++;
+
+ for (i = j + 1; i < len; i++) {
+ if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) {
+ n--;
+
+ for (k = j; k < i - 1; k++)
+ p[k] = p[k+1];
+ for (; i < len; i++)
+ p[i-1] = p[i+1];
+ len -= 2;
+ j = i - 1;
+ }
+ }
+ }
+ }
+ if (n == 1) { /* Implied double quote at end of field */
+ for (k = j; k < len; k++ )
+ p[k] = p[k+1];
+ len -= 1;
+ }
+ }
+ return(p);
+}
+#endif /* COMMENT */
+
+#ifdef COMMENT
+/*
+ Not used -- Note: these not only write into their arg, but write past
+ past the end.
+*/
+char *
+brace(fn) char *fn; {
+ int spaces = 0;
+ char * p, ch, ch2;
+ for (p = fn; *p; p++) {
+ if (*p == SP) {
+ spaces = 1;
+ break;
+ }
+ }
+ if (spaces) {
+ p = fn;
+ ch = *p;
+ *p = '{';
+ p++;
+
+ while (*p) {
+ ch2 = *p;
+ *p = ch;
+ ch = ch2;
+ p++;
+ }
+ *p = ch;
+ p++;
+ *p = '}';
+ p++;
+ *p = '\0';
+ }
+ return(fn);
+}
+#endif /* COMMENT */
+
+/* d q u o t e -- Puts doublequotes around arg in place. */
+/*
+ Call with:
+ Pointer to buffer and its total length and flag = 0 to use
+ doublequotes, 1 to use braces.
+ Returns:
+ Number: length of result.
+*/
+int
+dquote(fn, len, flag) char *fn; int len; int flag; {
+ int spaces = 0, k = 0;
+ char * p, ch, ch2;
+ if (!fn)
+ return(0);
+
+ k = strlen(fn);
+ for (p = fn; *p; p++) {
+ if (*p == SP) {
+ spaces = 1;
+ break;
+ }
+ }
+ if (spaces) {
+ if (k + 2 >= len)
+ return(k);
+ p = fn;
+ ch = *p;
+ *p = flag ? '{' : '"';
+ p++;
+
+ while (*p) {
+ ch2 = *p;
+ *p = ch;
+ ch = ch2;
+ p++;
+ }
+ *p = ch;
+ p++;
+ *p = flag ? '}' : '"';
+ p++;
+ *p = '\0';
+ }
+ return(k+2);
+}
+
+
+/* U N T A B I F Y --- Untabify s1 into s2, assuming tabs every 8 space */
+
+int
+untabify(s1,s2,max) char * s1, * s2; int max; {
+ int i, j, k, x, z;
+ x = strlen(s1);
+ for (i = 0, k = 0; k < x; k++) {
+ if (s1[k] != '\t') {
+ if (i >= max-1) {
+ s2[max-1] = '\0';
+ return(-1);
+ }
+ s2[i++] = s1[k];
+ continue;
+ }
+ z = 8 - i%8;
+ if (z == 0) z = 8;
+ for (j = 0; j < z && i < max; j++)
+ s2[i++] = ' ';
+ }
+ s2[i] = '\0';
+ return(0);
+}
+
+
+/* M A K E L I S T --- Breaks {{s1}{s2}..{sn}} into an array of strings */
+/*
+ Call with:
+ s = pointer to string to break up.
+ list = array of string pointers.
+ len = number of elements in array.
+ NOTE: The array must be preinitialized to all NULL pointers.
+ If any array element is not NULL, it is assumed to have been malloc'd
+ and is therefore freed. Do NOT call this function with an uninitialized
+ array, or with an array that has had any static elements assigned to it.
+*/
+VOID
+makelist(s,list,len) char * s; char *list[]; int len; {
+ int i, n, q, bc = 0;
+ char *p = NULL, *s2 = NULL;
+ debug(F110,"makelist s",s,0);
+ if (!s) { /* Check for null or empty string */
+ list[0] = NULL;
+ return;
+ }
+ n = strlen(s);
+ if (n == 0) {
+ list[0] = NULL;
+ return;
+ }
+ if ((s2 = (char *)malloc(n+1))) { /* Safe copy for poking */
+ strcpy(s2,s); /* (no need for ckstrncpy here) */
+ s = s2;
+ }
+ s = brstrip(s); /* Strip braces */
+ n = strlen(s); /* Get length */
+ if (*s != '{') { /* Outer braces only */
+ if ((p = (char *)malloc(n+1))) { /* So just one pattern */
+ strcpy(p,s); /* (no need for ckstrncpy here) */
+ if (list[0])
+ free(list[0]);
+ list[0] = p;
+ }
+ if (s2) free(s2);
+ return;
+ }
+ q = 0; /* Inner ones too */
+ i = 0; /* so a list of patterns. */
+ n = 0;
+ while (*s && i < len) {
+ if (*s == CMDQ) { /* Quote... */
+ q = 1;
+ s++;
+ n++;
+ continue;
+ }
+ if (*s == '{' && !q) { /* Opening brace */
+ if (bc++ == 0) { /* Beginning of a group */
+ p = ++s;
+ n = 0;
+ } else { /* It's a brace inside the group */
+ n++;
+ s++;
+ }
+ continue;
+ } else if (*s == '}' && !q) { /* Closing brace */
+ if (--bc == 0) { /* End of a group */
+ *s++ = NUL;
+ debug(F111,"makelist element",p,i);
+ if (list[i])
+ free(list[i]);
+ if ((list[i] = (char *)malloc(n+1))) {
+ ckstrncpy(list[i],p,n+1); /* Note: n+1 */
+ i++;
+ }
+ while (*s == SP) s++;
+ p = s;
+ n = 0;
+ continue;
+ } else { /* Within a group */
+ n++;
+ s++;
+ }
+ } else { /* Regular character */
+ q = 0;
+ s++;
+ n++;
+ }
+ }
+ if (*p && i < len) { /* Last one */
+ if (list[i])
+ free(list[i]);
+ if ((list[i] = (char *)malloc(n+1))) {
+ ckstrncpy(list[i],p,n+1);
+ debug(F111,"makelist last element",p,i);
+ }
+ }
+ i++; /* Clear out the rest of the list */
+ for ( ; i < len; i++) {
+ if (list[i])
+ free (list[i]);
+ list[i] = NULL;
+ }
+ if (s2) free(s2);
+}
+
+/*
+ M A K E S T R -- Creates a dynamically allocated string.
+
+ Makes a new copy of string s and sets pointer p to its address.
+ Handles degenerate cases, like when buffers overlap or are the same,
+ one or both arguments are NULL, etc.
+
+ The source string is assumed to be NUL-terminated. Therefore it can not
+ be a UCS-2 string or arbitrary binary data.
+
+ The target pointer must be either NULL or else a pointer to a previously
+ malloc'ed buffer. If not, expect a core dump or segmentation fault.
+
+ Note: The caller can tell whether this routine failed as follows:
+
+ malloc(&p,q);
+ if (q & !p) { makestr() failed };
+
+ Really this routine should have returned a length, but since it doesn't
+ we set the global variable makestrlen to the length of the result string.
+*/
+int makestrlen = 0;
+
+VOID
+#ifdef CK_ANSIC
+makestr(char **p, const char *s)
+#else
+makestr(p,s) char **p, *s;
+#endif
+/* makestr */ {
+ int x = 0;
+ char *q = NULL;
+#ifdef CK_ANSIC
+ register const char * s2;
+#else
+ register char * s2;
+#endif /* CK_ANSIC */
+ register char * q2;
+
+ if (*p == s) /* The two pointers are the same. */
+ return; /* Don't do anything. */
+
+ if (!s) { /* New definition is null? */
+ if (*p) /* Free old storage. */
+ free(*p);
+ *p = NULL; /* Return null pointer. */
+ makestrlen = 0;
+ return;
+ }
+ s2 = s; /* Maybe new string will fit */
+
+#ifdef COMMENT
+/*
+ This is a fairly big win, allowing us to skip the malloc() and free if the
+ destination string already exists and is not shorter than the source string.
+ But it doesn't allow for possible overlap of source and destination.
+*/
+ if (*p) { /* into old storage... */
+ char * p2 = *p;
+ char c;
+ while (c = *p2) {
+ if (!(*p2++ = *s2++))
+ break;
+ x++;
+ }
+ makestrlen = x;
+ if (c) return;
+ }
+#endif /* COMMENT */
+
+/* Didn't fit */
+
+ x = 0;
+ while (*s2++) x++; /* Get (rest of) length of s. */
+
+ if (x >= 0) { /* Get length, even of empty string. */
+ q = malloc(x+1); /* Get and point to temp storage. */
+ if (q) {
+ makestrlen = x; /* Remember length for stats */
+ s2 = s; /* Point back to beginning of source */
+ q2 = q; /* Copy dest pointer to increment... */
+ while ((*q2++ = *s2++)) ; /* Instead of calling strcpy(). */
+/*
+ Note: HP flexelint says that the above loop can result in creation (++) and
+ access (*) of out-of-bounds pointers. I really don't see it.
+*/
+ }
+#ifdef DEBUG
+ else { /* This would be a really bad error */
+ char tmp[24]; /* So get a good record of it. */
+ if (x > 23) {
+ ckstrncpy(tmp,s,20);
+ strcpy(tmp+20,"...");
+ tmp[23] = NUL;
+ } else {
+ strcpy(tmp,s); /* We already checked the length */
+ }
+ debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0);
+ }
+#endif /* DEBUG */
+ } else
+ q = NULL; /* Length of string is zero */
+
+ if (*p) /* Now free the original storage. */
+ free(*p);
+ *p = q;
+}
+
+/* X M A K E S T R -- Non-destructive makestr() if s is NULL. */
+
+VOID
+#ifdef CK_ANSIC
+xmakestr(char **p, const char *s)
+#else
+xmakestr(p,s) char **p, *s;
+#endif
+/* xmakestr */ {
+ if (s) makestr(p,s);
+}
+
+#ifndef USE_MEMCPY
+/* C K M E M C P Y -- Portable (but slow) memcpy() */
+
+/* Copies n bytes from s to p, allowing for overlap. */
+/* For use when real memcpy() not available. */
+
+VOID
+ckmemcpy(p,s,n) char *p, *s; int n; {
+ char * q = NULL;
+ register int i;
+ int x;
+
+ if (!s || !p || n <= 0 || p == s) /* Verify args */
+ return;
+ x = p - s; /* Check for overlap */
+ if (x < 0)
+ x = 0 - x;
+ if (x < n) { /* They overlap */
+ q = p;
+ if (!(p = (char *)malloc(n))) /* So use a temporary buffer */
+ return;
+ }
+ for (i = 0; i < n; i++) /* Copy n bytes */
+ p[i] = s[i];
+ if (q) { /* If we used a temporary buffer */
+ for (i = 0; i < n; i++) /* copy from it to destination */
+ q[i] = p[i];
+ if (p) free(p); /* and free the temporary buffer */
+ }
+}
+#endif /* USE_MEMCPY */
+
+
+/* C K S T R C M P -- String comparison with case-matters selection */
+/*
+ Call with pointers to the two strings, s1 and s2, a length, n,
+ and c == 0 for caseless comparison, nonzero for case matters.
+ Call with n == -1 to compare without a length limit.
+ Compares up to n characters of the two strings and returns:
+ 1 if s1 > s2
+ 0 if s1 = s2
+ -1 if s1 < s2
+ Note: case handling is only as good as isupper() and tolower().
+*/
+int
+ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; {
+ register CHAR t1, t2;
+ if (n == 0) return(0);
+ if (!s1) s1 = ""; /* Watch out for null pointers. */
+ if (!s2) s2 = "";
+ if (!*s1) return(*s2 ? -1 : 0);
+ if (!*s2) return(1);
+ while (n--) {
+ t1 = (CHAR) *s1++; /* Get next character from each. */
+ t2 = (CHAR) *s2++;
+ if (!t1) return(t2 ? -1 : 0);
+ if (!t2) return(1);
+ if (!c) { /* If case doesn't matter */
+ if (isupper(t1)) t1 = tolower(t1); /* Convert case. */
+ if (isupper(t2)) t2 = tolower(t2);
+ }
+ if (t1 < t2) return(-1); /* s1 < s2 */
+ if (t1 > t2) return(1); /* s1 > s2 */
+ }
+ return(0); /* They're equal */
+}
+
+/* C K S T R P R E -- Caseless string prefix comparison */
+
+/* Returns position of the first char in the 2 strings that doesn't match */
+
+int
+ckstrpre(s1,s2) char *s1, *s2; {
+ CHAR t1, t2;
+ int n = 0;
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ while (1) {
+ t1 = (CHAR) *s1++;
+ t2 = (CHAR) *s2++;
+ if (!t1 || !t2) return(n);
+ if (isupper(t1)) t1 = tolower(t1);
+ if (isupper(t2)) t2 = tolower(t2);
+ if (t1 != t2)
+ return(n);
+ n++;
+ }
+}
+
+#define GLOBBING
+
+/* C K M A T C H -- Match a string against a pattern */
+/*
+ Call with:
+ pattern to be matched.
+ string to look for the pattern in.
+ icase is 1 if case-sensitive, 0 otherwise.
+ opts is a bitmask:
+ Bit 0 (=1):
+ 1 = Match strings starting with '.'
+ 0 = Don't match them (used with UNIX filenames).
+ Bit 1 (=2):
+ 1 = File globbing (dirseps are fences);
+ 0 = Dirseps are not fences.
+ Bit 2 (=4):
+ 1 = Allow ^ and $ anchors at beginning and end of pattern.
+ 0 = Don't allow them (normal case for filename matching).
+ Bit 3 (and beyond): Undefined.
+ Works only with NUL-terminated strings.
+ Pattern may contain any number of ? and/or *.
+ If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}.
+ (Note: REGEX is a misnomer, see below.)
+
+ Returns:
+ 0 if string does not match pattern,
+ >= 1, the 1-based position in the string where the match was found.
+
+ To be done:
+ Find a way to identify the piece of the string that matched the pattern,
+ as in Snobol "LINE (PAT . RESULT)". This is now partially done by
+ setting matchpos and matchend (except matchend needs some tuning). But
+ these are useless unless a copy of the string is kept, or a copy of the
+ matching part is made. But that would be too costly in performance --
+ this routine has to be fast because it's used for wildcard expansion.
+
+ Note:
+ Patterns are not the same as regular expressions, in which '*' means
+ 0 or more repetitions of the preceding item. For example "a*b" as a
+ pattern matches any string that starts with 'a' and ends with 'b'; as a
+ regular expression it matches any string of zero or more a's followed by
+ one b. Regular expressions are especially useful in matching strings of
+ (say) digits, or letters, e.g. "[0-9]*" matches any string of digits.
+*/
+static char * mypat = NULL; /* For rewriting pattern */
+static int matchpos = 0;
+int matchend = 0;
+static int matchdepth = 0;
+static int stringpos = 0;
+static char * ostring = NULL;
+
+#define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; }
+static char * lastpat = NULL;
+
+int
+ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; {
+ int q = 0, i = 0, k = -1, x, flag = 0;
+ int rc = 0; /* Return code */
+ int where = -1;
+ CHAR cp; /* Current character from pattern */
+ CHAR cs; /* Current character from string */
+ int plen, dot, globbing, xstar = 0;
+ int bronly = 0; /* Whole pattern is {a,b,c,...} */
+
+ debug(F111,"CKMATCH ENTRY pat opt",pattern,opts);
+ debug(F111,"CKMATCH ENTRY str dep",string,matchdepth);
+ /* debug(F101,"CKMATCH ENTRY icase","",icase); */
+
+ globbing = opts & 2;
+
+ if (!string) string = "";
+ if (!pattern) pattern = "";
+ if (!*pattern) { /* Empty pattern matches anything */
+ matchdepth++; /* (it wasn't incremented yet) */
+ MATCHRETURN(0,1);
+ }
+ if (matchdepth == 0) { /* Top-level call? */
+ stringpos = 0; /* Reset indices etc. */
+ matchpos = 0;
+ matchend = 0;
+ ostring = string;
+ lastpat = pattern;
+ if (*pattern == '{') /* Entire pattern is {a,b.c} */
+ bronly = 1; /* Maybe */
+ dot = (opts & 1) || /* Match leading dot (if file) */
+ (opts & 2 == 0) || /* always if not file */
+ (pattern[0] == '.'); /* or if pattern starts with '.' */
+
+ plen = strlen(pattern); /* Length of pattern */
+/* This would be used in calculating length of matching segment */
+ if (plen > 0) /* User's pattern ends with '*' */
+ if (pattern[plen - 1] == '*')
+ xstar = 1;
+ if (pattern[0] == '*') { /* User's pattern starts with '*' */
+ matchpos = 1;
+ debug(F111,"CKMATCH 1",string, matchpos);
+ }
+ if (opts & 4) { /* ^..$ allowed (top level only) */
+ /* Rewrite pattern to account for ^..$ anchoring... */
+
+ if (mypat) free(mypat); /* Get space for "*pattern*" */
+ mypat = (char *)malloc(plen + 4);
+ if (mypat) { /* Got space? */
+ char * s = pattern, * p = mypat; /* Set working pointers */
+ if (*s == '^') { /* First source char is ^ */
+ s++; /* so skip past it */
+ } else if (*s != '*') { /* otherwise */
+ *p++ = '*'; /* prepend '*' to pattern */
+ }
+ while (*s) { /* Copy rest of pattern */
+ if (!*(s+1)) { /* Final pattern character? */
+ if (*s != '$') { /* If it's not '$' */
+ *p++ = *s; /* Copy it into the pattern */
+ if (*s++ != '*') /* And if it's also not '*' */
+ *p++ = '*'; /* append '*'. */
+ }
+ break; /* Done */
+ } else /* Not final character */
+ *p++ = *s++; /* Just copy it */
+ }
+ *p = NUL; /* Terminate the new pattern */
+ pattern = mypat; /* Make the switch */
+ }
+ debug(F110,"CKMATCH INIT pat",pattern,0);
+ }
+ }
+ matchdepth++; /* Now increment call depth */
+
+#ifdef UNIX
+ if (!dot) { /* For UNIX file globbing */
+ if (*string == '.' && *pattern != '.' && !matchdot) {
+ if (
+#ifdef CKREGEX
+ *pattern != '{' && *pattern != '['
+#else
+ 1
+#endif /* CKREGEX */
+ ) {
+ debug(F110,"ckmatch skip",string,0);
+ MATCHRETURN(1,0);
+ }
+ }
+ }
+#endif /* UNIX */
+ while (1) {
+ k++;
+ cp = *pattern; /* Character from pattern */
+ cs = *string; /* Character from string */
+
+#ifdef COMMENT
+ debug(F000,"CKMATCH pat cp",pattern,cp);
+ debug(F000,"CKMATCH str cs",string,cs);
+#endif /* COMMENT */
+
+ if (!cs) { /* End of string - done. */
+ x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0;
+ if (x) {
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH A",string, matchpos);
+ }
+ matchend = stringpos;
+ MATCHRETURN(2,matchpos);
+ }
+ debug(F111,"CKMATCH ZERO d",string, matchpos);
+ matchpos = 0;
+ MATCHRETURN(16,matchpos);
+ }
+ if (!icase) { /* If ignoring case */
+ if (isupper(cp)) /* convert both to lowercase. */
+ cp = tolower(cp);
+ if (isupper(cs))
+ cs = tolower(cs);
+ }
+ if (q) { /* This character was quoted */
+ debug(F000,"CKMATCH QUOTED",pattern,cp);
+ q = 0; /* Turn off quote flag */
+
+ if (cs == cp) { /* Compare directly */
+ if (!matchpos) { /* Matches */
+ matchpos = stringpos;
+ debug(F111,"CKMATCH \\ new match",string, matchpos);
+ }
+ pattern++;
+ } else { /* Doesn't match */
+ pattern = lastpat; /* Back up the pattern */
+ matchpos = 0;
+ debug(F111,"CKMATCH \\ no match",pattern, matchpos);
+ }
+ string++;
+ stringpos++;
+ continue;
+ }
+ if (cp == CMDQ && !q) { /* Quote in pattern */
+ debug(F000,"CKMATCH QUOTE",pattern,cp);
+ q = 1; /* Set flag */
+ pattern++; /* Advance to next pattern character */
+ continue; /* and continue. */
+ }
+ if (cs && cp == '?') { /* '?' matches any char */
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH D",string, matchpos);
+ }
+ debug(F110,"CKMATCH ? pat",pattern,0);
+ debug(F110,"CKMATCH ? str",string,0);
+ pattern++, string++;
+ stringpos++;
+ continue;
+#ifdef CKREGEX
+ } else if (cp == '[') { /* Have bracket */
+ int q = 0; /* My own private q */
+ char * psave = NULL; /* and backup pointer */
+ CHAR clist[256]; /* Character list from brackets */
+ CHAR c, c1, c2;
+ for (i = 0; i < 256; i++) /* memset() etc not portable */
+ clist[i] = NUL;
+ psave = ++pattern; /* Where pattern starts */
+ debug(F111,"CKMATCH [] ",pattern-1, matchpos);
+ for (flag = 0; !flag; pattern++) { /* Loop thru pattern */
+ c = (CHAR)*pattern; /* Current char */
+ if (q) { /* Quote within brackets */
+ q = 0;
+ clist[c] = 1;
+ continue;
+ }
+ if (!icase) /* Case conversion */
+ if (isupper(c))
+ c = tolower(c);
+ switch (c) { /* Handle unquoted character */
+ case NUL: /* End of string */
+ MATCHRETURN(4,0); /* No matching ']' so fail */
+ case CMDQ: /* Next char is quoted */
+ q = 1; /* Set flag */
+ continue; /* and continue. */
+ case '-': /* A range is specified */
+ c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL;
+ c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */
+ if (c2 == ']') c2 = NUL; /* (it can't happen) */
+ if (c1 == NUL) c1 = c2;
+ for (c = c1; c <= c2; c++) {
+ clist[c] = 1;
+ if (!icase) {
+ if (islower(c)) {
+ clist[toupper(c)] = 1;
+ } else if (isupper(c)) {
+ clist[tolower(c)] = 1;
+ }
+ }
+ }
+ continue;
+ case ']': /* End of bracketed sequence */
+ flag = 1; /* Done with FOR loop */
+ break; /* Compare what we have */
+ default: /* Just a char */
+ clist[c] = 1; /* Record it */
+ if (!icase) {
+ if (islower(c)) {
+ clist[toupper(c)] = 1;
+ } else if (isupper(c)) {
+ clist[tolower(c)] = 1;
+ }
+ }
+ continue;
+ }
+ }
+ if (!clist[(unsigned)cs]) { /* Match? */
+ MATCHRETURN(5,0); /* Nope, done. */
+ }
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH [] match",string, matchpos);
+ }
+ string++; /* Yes, advance string pointer */
+ stringpos++;
+ continue; /* and go on. */
+ } else if (cp == '{') { /* Braces enclosing list of strings */
+ char * p, * s, * s2, * buf = NULL;
+ int n, bc = 0;
+ int len = 0;
+ debug(F111,"CKMATCH {} ",string, matchpos);
+ for (p = pattern++; *p; p++) {
+ if (*p == '{') bc++;
+ if (*p == '}') bc--;
+ if (bc < 1) break;
+ }
+ if (bc != 0) { /* Braces don't match */
+ MATCHRETURN(6,0); /* Fail */
+ } else { /* Braces do match */
+ int q = 0, done = 0;
+ len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */
+ if (len)
+ bronly = 0;
+ if (bronly && (matchdepth != 1))
+ bronly = 0;
+ n = p - pattern; /* Size of list in braces */
+ if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */
+ char * tp = NULL;
+ int k, sofar;
+ ckstrncpy(buf,pattern,n+1);
+ sofar = string - ostring - matchpos + 1;
+ if (sofar < 0) sofar = 0;
+ debug(F111,"CKMATCH .. string",string,sofar);
+ debug(F111,"CKMATCH .. ostring",ostring,sofar);
+ n = 0;
+ for (s = s2 = buf; 1; s++) { /* Loop through segments */
+ n++;
+ if (q) { /* This char is quoted */
+ q = 0;
+ if (!*s)
+ done = 1;
+ continue;
+ }
+ if (*s == CMDQ && !q) { /* Quote next char */
+ q = 1;
+ continue;
+ }
+ if (!*s || *s == ',') { /* End of this segment */
+ int tplen = 0;
+ if (!*s) /* If end of buffer */
+ done = 1; /* then end of last segment */
+ *s = NUL; /* Overwrite comma with NUL */
+ debug(F111,"CKMATCH {} segment",s2,done);
+ tplen = n + len + sofar + 2;
+ if (!*s2) { /* Empty segment, no advancement */
+ k = 0;
+ } else if ((tp = (char *)malloc(tplen))) {
+ int savpos, opts2;
+ char * pp;
+ pp = matchpos > 0 ?
+ &ostring[matchpos-1] :
+ ostring;
+ if (bronly) {
+ if (matchpos > 0)
+ ckstrncpy(tp,pp,sofar+1);
+ else
+ ckstrncpy(tp,pp,sofar);
+ } else {
+ tp[0] = '*';
+ tp[1] = NUL;
+ if (matchpos > 0)
+ ckstrncpy(&tp[1],pp,sofar+1);
+ else
+ ckstrncpy(&tp[1],pp,sofar);
+ }
+ ckstrncat(tp,s2,tplen); /* Current segment */
+ ckstrncat(tp,p+1,tplen); /* rest of pattern */
+
+ debug(F101,"CKMATCH {} matchpos","",matchpos);
+ savpos = matchpos;
+ matchpos = 0;
+#ifdef DEBUG
+ if (deblog) {
+ debug(F111,"CKMATCH {} tp",tp,matchpos);
+ debug(F111,"CKMATCH {} string",
+ string,matchpos);
+ debug(F111,"CKMATCH {} ostring",
+ ostring,savpos);
+ }
+#endif /* DEBUG */
+ /* If segment starts with dot */
+ /* then set matchdot option.. */
+ opts2 = opts;
+ if (*s2 == '.') opts2 |= 1;
+ debug(F111,"CKMATCH {} recursing",s2,opts2);
+ k = ckmatch(tp,
+ (string > ostring) ?
+ &ostring[savpos-1] : string,
+ icase,opts2);
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"CKMATCH {} k","",k);
+ debug(F101,"CKMATCH {} savpos","",savpos);
+ }
+#endif /* DEBUG */
+ free(tp);
+ tp = NULL;
+ if (k == 0) {
+ matchpos = savpos;
+ }
+ if (k > 0) { /* If it matched we're done */
+ MATCHRETURN(7,k);
+ }
+ } else { /* Malloc failure */
+ MATCHRETURN(14,0);
+ }
+ if (k) { /* Successful comparison */
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH {} match",
+ string, matchpos);
+ }
+ string += n-1; /* Advance pointers */
+ pattern = p+1;
+ break;
+ }
+ if (done) /* If no more segments */
+ break; /* break out of segment loop. */
+ s2 = s+1; /* Otherwise, on to next segment */
+ n = 0;
+ }
+ }
+ free(buf);
+ }
+ }
+#endif /* CKREGEX */
+ } else if (cp == '*') { /* Pattern char is asterisk */
+ char * psave;
+ char * p, * s = NULL; /* meaning match anything */
+ int k, n, q = 0;
+ while (*pattern == '*') /* Collapse successive asterisks */
+ pattern++;
+ psave = pattern; /* First non-asterisk after asterisk */
+ lastpat = pattern - 1; /* Ditto, global */
+ debug(F111,"CKMATCH * ",string,matchpos);
+ for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */
+ if (!q) {
+ if (*p == '?' || *p == '*' || *p == CMDQ
+#ifdef CKREGEX
+ || *p == '[' || *p == '{'
+#endif /* CKREGEX */
+ )
+ break;
+#ifdef GLOBBING
+ if (globbing
+#ifdef UNIXOROSK
+ && *p == '/'
+#else
+#ifdef VMS
+ && (*p == '.' || *p == ']' ||
+ *p == '<' || *p == '>' ||
+ *p == ':' || *p == ';')
+#else
+#ifdef datageneral
+ && *p == ':'
+#else
+#ifdef STRATUS
+ && *p == '>'
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIXOROSK */
+ )
+ break;
+#endif /* GLOBBING */
+ }
+ }
+ debug(F111,"CKMATCH * n string",string,n);
+ debug(F111,"CKMATCH * n pattrn",pattern,n);
+ debug(F111,"CKMATCH * n p",p,n);
+ if (n > 0) { /* Literal string to match */
+ s = (char *)malloc(n+1);
+ if (s) {
+ ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */
+ if (*p) {
+ k = ckindex(s,string,0,0,icase); /* 1-based index() */
+ debug(F110,"CKMATCH * Index() string",string,0);
+ debug(F110,"CKMATCH * Index() pattrn",s,0);
+ debug(F101,"CKMATCH * Index() result","",k);
+ } else { /* String is right-anchored */
+ k = ckindex(s,string,-1,1,icase); /* rindex() */
+ debug(F111,"CKMATCH * Rindex()",string,k);
+ debug(F110,"CKMATCH * Rindex() pattrn",s,0);
+ debug(F101,"CKMATCH * Rindex() result","",k);
+ }
+ free(s);
+ if (k < 1) {
+ MATCHRETURN(8,0);
+ }
+ debug(F111,"CKMATCH * stringpos matchpos",
+ ckitoa(stringpos), matchpos);
+ if (!matchpos) {
+ matchpos = string - ostring + k;
+ debug(F111,"CKMATCH * new match ", string, matchpos);
+ }
+ string += k + n - 1;
+ stringpos += k + n - 1;
+ pattern += n;
+ debug(F111,"CKMATCH * new string", string, stringpos);
+ debug(F110,"CKMATCH * new pattrn", pattern, 0);
+ continue;
+ }
+ } else if (!*p) { /* Asterisk at end matches the rest */
+ if (!globbing) { /* (if not filename globbing) */
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH *$ ",string, matchpos);
+ }
+ matchend = stringpos;
+ MATCHRETURN(9,matchpos);
+ }
+#ifdef GLOBBING
+ while (*string) {
+ if (globbing /* Filespec so don't cross fields */
+#ifdef OS2
+ && *string == '/' || *string == '\\' ||
+ *string == ':'
+#else
+#ifdef UNIXOROSK
+ && *string == '/'
+#else
+#ifdef VMS
+ && (*string == '.' || *string == ']' ||
+ *string == '<' || *string == '>' ||
+ *string == ':' || *string == ';')
+#else
+#ifdef datageneral
+ && *string == ':'
+#else
+#ifdef STRATUS
+ && *string == '>'
+#else
+ && *string == '/' /* (catch-all) */
+#endif /* STRATUS */
+#endif /* datageneral */
+#endif /* VMS */
+#endif /* UNIXOROSK */
+#endif /* OS2 */
+ ) {
+ matchend = stringpos;
+ MATCHRETURN(10,0);
+ }
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH *$ match",string, matchpos);
+ }
+ string++;
+ stringpos++;
+ }
+#endif /* GLOBBING */
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH ** match",string, matchpos);
+ }
+ matchend = stringpos;
+ MATCHRETURN(11,matchpos);
+
+ } else { /* A meta char follows asterisk */
+ if (!*string)
+ MATCHRETURN(17, matchpos = 0);
+ while (*string && (k = ckmatch(p,string,icase,opts) < 1)) {
+ string++;
+ stringpos++;
+ }
+ debug(F111,"CKMATCH *<meta> k",string, k);
+ if (!matchpos && k > 0) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH *<meta>",string, matchpos);
+ }
+ MATCHRETURN(12, (*string) ? matchpos : 0);
+ }
+ } else if (cs == cp) {
+ pattern++, string++;
+ stringpos++;
+ if (!matchpos) {
+ matchpos = stringpos;
+ debug(F111,"CKMATCH cs=cp",string, matchpos);
+ }
+ continue;
+ } else {
+ MATCHRETURN(13,0);
+ }
+ }
+ xckmatch:
+ {
+#ifdef DEBUG
+ char msgbuf[96];
+#endif /* DEBUG */
+ if (matchdepth > 0)
+ matchdepth--;
+ matchpos = rc;
+#ifdef DEBUG
+ ckmakxmsg(msgbuf,96,
+ "CKMATCH RETURN[",
+ ckitoa(where),
+ "] matchpos=",
+ ckitoa(matchpos),
+ " matchdepth=",
+ ckitoa(matchdepth),
+ " string=",NULL,NULL,NULL,NULL,NULL
+ );
+ debug(F110,msgbuf,string,0);
+#endif /* DEBUG */
+ return(rc);
+ }
+}
+
+
+#ifdef CKFLOAT
+/* I S F L O A T -- Verify that arg represents a floating-point number */
+
+/*
+ Portable replacement for atof(), strtod(), scanf(), etc.
+
+ Call with:
+ s = pointer to string
+ flag == 0 means entire string must be a (floating-pointing) number.
+ flag != 0 means to terminate scan on first character that is not legal.
+
+ Returns:
+ 1 if result is a legal number;
+ 2 if result has a fractional part;
+ 0 if not or if string empty.
+
+ Side effect:
+ Sets global floatval to floating-point value if successful.
+
+ Number need not contain a decimal point -- integer is subcase of float.
+ Scientific notation not supported.
+*/
+CKFLOAT floatval = 0.0; /* For returning value */
+
+int
+isfloat(s,flag) char *s; int flag; {
+ int state = 0;
+ int sign = 0;
+ char c;
+ CKFLOAT d = 0.0, f = 0.0;
+
+ if (!s) return(0);
+ if (!*s) return(0);
+
+ while (isspace(*s)) s++;
+
+ if (*s == '-') { /* Handle optional sign */
+ sign = 1;
+ s++;
+ } else if (*s == '+')
+ s++;
+ while ((c = *s++)) { /* Handle numeric part */
+ switch (state) {
+ case 0: /* Mantissa... */
+ if (isdigit(c)) {
+ f = f * 10.0 + (CKFLOAT)(c - '0');
+ continue;
+ } else if (c == '.') {
+ state = 1;
+ d = 1.0;
+ continue;
+ }
+ if (flag) /* Not digit or period */
+ goto done; /* break if flag != 0 */
+ return(0); /* otherwise fail. */
+ case 1: /* Fraction... */
+ if (isdigit(c)) {
+ d *= 10.0;
+ f += (CKFLOAT)(c - '0') / d;
+ continue;
+ }
+ default:
+ if (flag) /* Illegal character */
+ goto done; /* Break */
+ return(0); /* or fail, depending on flag */
+ }
+ }
+ done:
+ if (sign) f = 0.0 - f; /* Apply sign to result */
+ floatval = f; /* Set result */
+ return(d ? 2 : 1); /* Succeed */
+}
+#endif /* CKFLOAT */
+
+/* Sorting routines... */
+
+/* S H _ S O R T -- Shell sort -- sorts string array s in place. */
+
+/*
+ Highly defensive and relatively quick.
+ Uses shell sort algorithm.
+
+ Args:
+ s = pointer to array of strings.
+ p = pointer to a second array to sort in parallel s, or NULL for none.
+ n = number of elements in s.
+ k = position of key.
+ r = ascending lexical order if zero, reverse lexical order if nonzero.
+ c = 0 for case independence, 1 for case matters, 2 for numeric.
+
+ If k is past the right end of a string, the string is considered empty
+ for comparison purposes.
+
+ Hint:
+ To sort a piece of an array, call with s pointing to the first element
+ and n the number of elements to sort.
+
+ Return value:
+ None. Always succeeds, unless any of s[0]..s[n-1] are bad pointers,
+ in which case memory violations are possible, but C offers no defense
+ against this, so no way to gracefully return an error code.
+*/
+VOID
+sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; {
+ int m, i, j, x;
+ char *t, *t1, *t2, *u = NULL;
+#ifdef CKFLOAT
+ CKFLOAT f1, f2;
+#else
+ long n1, n2;
+#endif /* CKFLOAT */
+
+ if (!s) return; /* Nothing to sort? */
+ if (n < 2) return; /* Not enough elements to sort? */
+ if (k < 0) k = 0; /* Key */
+
+ m = n; /* Initial group size is whole array */
+ while (1) {
+ m = m / 2; /* Divide group size in half */
+ if (m < 1) /* Small as can be, so done */
+ break;
+ for (j = 0; j < n-m; j++) { /* Sort each group */
+ t = t2 = s[j+m]; /* Compare this one... */
+ if (!t) /* But if it's NULL */
+ t2 = ""; /* make it the empty string */
+ if (p) /* Handle parallel array, if any */
+ u = p[j+m];
+ if (k > 0 && *t2) {
+ if ((int)strlen(t2) < k) /* If key too big */
+ t2 = ""; /* make key the empty string */
+ else /* Key is in string */
+ t2 = t + k; /* so point to key position */
+ }
+ for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/
+ t1 = s[i];
+ if (!t1) /* Same deal */
+ t1 = "";
+ if (k > 0 && *t1) {
+ if ((int)strlen(t1) < k)
+ t1 = "";
+ else
+ t1 = s[i]+k;
+ }
+ if (c == 2) { /* Numeric comparison */
+ x = 0;
+#ifdef CKFLOAT
+ f2 = 0.0;
+ f1 = 0.0;
+ if (isfloat(t1,1)) {
+ f1 = floatval;
+ if (isfloat(t2,1))
+ f2 = floatval;
+ else
+ f1 = 0.0;
+ }
+ if (f2 < f1)
+ x = 1;
+ else
+ x = -1;
+#else
+ n2 = 0L;
+ n1 = 0L;
+ if (rdigits(t1)) {
+ n1 = atol(t1);
+ if (rdigits(t2))
+ n2 = atol(t2);
+ else
+ n1 = 0L;
+ }
+ if (n2 < n1)
+ x = 1;
+ else
+ x = -1;
+#endif /* CKFLOAT */
+ } else {
+ x = ckstrcmp(t1,t2,-1,c); /* Compare */
+ }
+ if (r == 0 && x < 0)
+ break;
+ if (r != 0 && x > 0)
+ break;
+ s[i+m] = s[i];
+ if (p) p[i+m] = p[i];
+ }
+ s[i+m] = t;
+ if (p) p[i+m] = u;
+ }
+ }
+}
+
+
+/* C K R A D I X -- Radix converter */
+/*
+ Call with:
+ s: a number in string format.
+ in: int, specifying the radix of s, 2-36.
+ out: int, specifying the radix to convert to, 2-36.
+ Returns:
+ NULL on error (illegal radix, illegal number, etc.).
+ "-1" on overflow (number too big for unsigned long).
+ Otherwise: Pointer to result.
+*/
+char *
+ckradix(s,in,out) char * s; int in, out; {
+ char c, *r = rxresult;
+ int d, minus = 0;
+ unsigned long zz = 0L;
+ long z = 0L;
+ if (in < 2 || in > 36) /* Verify legal input radix */
+ return(NULL);
+ if (out < 2 || out > 36) /* and output radix. */
+ return(NULL);
+ if (*s == '+') { /* Get sign if any */
+ s++;
+ } else if (*s == '-') {
+ minus++;
+ s++;
+ }
+ while (*s == SP || *s == '0') /* Trim leading blanks or 0's */
+ s++;
+/*
+ For detecting overflow, we use a signed copy of the unsigned long
+ accumulator. If it goes negative, we know we'll overflow NEXT time
+ through the loop.
+*/
+ for (; *s; s++) { /* Convert from input radix to */
+ c = *s; /* unsigned long */
+ if (islower(c)) c = toupper(c);
+ if (c >= '0' && c <= '9')
+ d = c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ d = c - 'A' + 10;
+ else
+ return(NULL);
+ if (d >= in) /* Check for illegal digit */
+ return(NULL);
+ zz = zz * in + d;
+ if (z < 0L) /* Clever(?) overflow detector */
+ return("-1");
+ z = zz;
+ }
+ if (!zz) return("0");
+ r = &rxresult[RXRESULT]; /* Convert from unsigned long */
+ *r-- = NUL; /* to output radix. */
+ while (zz > 0 && r > rxresult) {
+ d = zz % (unsigned)out;
+ *r-- = rxdigits[d];
+ zz = zz / (unsigned)out;
+ }
+ if (minus) *r-- = '-'; /* Replace original sign */
+ return((char *)(r+1));
+}
+
+#ifndef NOB64
+/* Base-64 conversion routines */
+
+static char b64[] = { /* Encoding vector */
+#ifdef pdp11
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+#else
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
+ 'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
+ 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
+ '5','6','7','8','9','+','/','=','\0'
+#endif /* pdp11 */
+};
+static int b64tbl[] = { /* Decoding vector */
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/*
+ B 8 T O B 6 4 -- Converts 8-bit data to Base64 encoding.
+
+ Call with:
+ s = Pointer to 8-bit data;
+ n = Number of source bytes to encode (SEE NOTE).
+ If it's a null-terminated string, you can use -1 here.
+ out = Address of output buffer.
+ len = Length of output buffer (should > 4/3 longer than input).
+
+ Returns:
+ >= 0 if OK, number of bytes placed in output buffer,
+ with the subsequent byte set to NUL if space permits.
+ -1 on error (output buffer space exhausted).
+
+ NOTE:
+ If this function is to be called repeatedly, e.g. to encode a data
+ stream a chunk at a time, the source length must be a multiple of 3
+ in all calls but the final one to avoid the generation of extraneous
+ pad characters that would throw the decoder out of sync. When encoding
+ only a single string, this is not a consideration. No internal state
+ is kept, so there is no reset function.
+*/
+int
+b8tob64(s,n,out,len) char * s,* out; int n, len; {
+ int b3, b4, i, x = 0;
+ unsigned int t;
+
+ if (n < 0) n = strlen(s);
+
+ for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */
+ b3 = b4 = 0;
+ t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8);
+ if (n - 1 > i) { /* Do we have another after this? */
+ t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */
+ b3 = 1; /* And remember */
+ }
+ t <<= 8; /* Move over */
+ if (n - 2 > i) { /* Another one after that? */
+ t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */
+ b4 = 1; /* and remember */
+ }
+ if (x + 4 > len) /* Check output space */
+ return(-1);
+ out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */
+ t >>= 6;
+ out[x+2] = b64[b3 ? (t & 0x3f) : 64];
+ t >>= 6;
+ out[x+1] = b64[t & 0x3f];
+ t >>= 6;
+ out[x] = b64[t & 0x3f];
+ }
+ if (x < len) out[x] = NUL; /* Null-terminate the string */
+ return(x);
+}
+
+
+/*
+ B 6 4 T O B 8 -- Converts Base64 string to 8-bit data.
+
+ Call with:
+ s = pointer to Base64 string (whitespace ignored).
+ n = length of string, or -1 if null terminated, or 0 to reset.
+ out = address of output buffer.
+ len = length of output buffer.
+
+ Returns:
+ >= 0 if OK, number of bytes placed in output buffer,
+ with the subsequent byte set to NUL if space permits.
+ < 0 on error:
+ -1 = output buffer too small for input.
+ -2 = input contains illegal characters.
+ -3 = internal coding error.
+
+ NOTE:
+ Can be called repeatedly to decode a Base64 stream, one chunk at a
+ time. However, if it is to be called for multiple streams in
+ succession, its internal state must be reset at the beginning of
+ the new stream.
+*/
+int
+b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */
+ static int bits = 0;
+ static unsigned int r = 0;
+ int i, k = 0, x, t;
+ unsigned char c;
+
+ if (n == 0) { /* Reset state */
+ bits = 0;
+ r = 0;
+ return(0);
+ }
+ x = (n < 0) ? strlen(s) : n; /* Source length */
+
+ n = ((x + 3) / 4) * 3; /* Compute destination length */
+ if (x > 0 && s[x-1] == '=') n--; /* Account for padding */
+ if (x > 1 && s[x-2] == '=') n--;
+ if (n > len) /* Destination not big enough */
+ return(-1); /* Fail */
+
+ for (i = 0; i < x; i++) { /* Loop thru source */
+ c = (CHAR)s[i]; /* Next char */
+ t = b64tbl[c]; /* Code for this char */
+ if (t == -2) { /* Whitespace or Ctrl */
+ n--; /* Ignore */
+ continue;
+ } else if (t == -1) { /* Illegal code */
+ return(-2); /* Fail. */
+ } else if (t > 63 || t < 0) /* Illegal value */
+ return(-3); /* fail. */
+ bits += 6; /* Count bits */
+ r <<= 6; /* Make space */
+ r |= (unsigned) t; /* OR in new code */
+ if (bits >= 8) { /* Have a byte yet? */
+ bits -= 8; /* Output it */
+ c = (unsigned) ((r >> bits) & 0xff);
+ out[k++] = c;
+ }
+ }
+ if (k < len) out[k] = NUL; /* Null-terminate in case it's */
+ return(k); /* a text string */
+}
+#endif /* NOB64 */
+
+/* C H K N U M -- See if argument string is an integer */
+
+/* Returns 1 if OK, zero if not OK */
+/* If OK, string should be acceptable to atoi() */
+/* Allows leading space, sign */
+
+int
+chknum(s) char *s; { /* Check Numeric String */
+ int x = 0; /* Flag for past leading space */
+ int y = 0; /* Flag for digit seen */
+ char c;
+ debug(F110,"chknum",s,0);
+ if (!s) return(0);
+ if (!*s) return(0);
+ while ((c = *s++)) { /* For each character in the string */
+ switch (c) {
+ case SP: /* Allow leading spaces */
+ case HT:
+ if (x == 0) continue;
+ else return(0);
+ case '+': /* Allow leading sign */
+ case '-':
+ if (x == 0) x = 1;
+ else return(0);
+ break;
+ default: /* After that, only decimal digits */
+ if (c >= '0' && c <= '9') {
+ x = y = 1;
+ continue;
+ } else return(0);
+ }
+ }
+ return(y);
+}
+
+
+/* R D I G I T S -- Verify that all characters in arg ARE DIGITS */
+
+/* Returns 1 if so, 0 if not or if string is empty */
+
+int
+rdigits(s) char *s; {
+ if (!s) return(0);
+ do {
+ if (!isdigit(*s)) return(0);
+ s++;
+ } while (*s);
+ return(1);
+}
+
+/* P A R N A M -- Return parity name */
+
+char *
+#ifdef CK_ANSIC
+parnam(char c)
+#else
+parnam(c) char c;
+#endif /* CK_ANSIC */
+/* parnam */ {
+ switch (c) {
+ case 'e': return("even");
+ case 'o': return("odd");
+ case 'm': return("mark");
+ case 's': return("space");
+ case 0: return("none");
+ default: return("invalid");
+ }
+}
+
+char * /* Convert seconds to hh:mm:ss */
+#ifdef CK_ANSIC
+hhmmss(long x)
+#else
+hhmmss(x) long x;
+#endif /* CK_ANSIC */
+/* hhmmss(x) */ {
+ static char buf[10];
+ long s, h, m;
+ h = x / 3600L; /* Hours */
+ x = x % 3600L;
+ m = x / 60L; /* Minutes */
+ s = x % 60L; /* Seconds */
+ if (x > -1L)
+ sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
+ else
+ buf[0] = NUL;
+ return((char *)buf);
+}
+
+/* L S E T -- Set s into p, right padding to length n with char c; */
+/*
+ s is a NUL-terminated string.
+ If length(s) > n, only n bytes are moved.
+ The result is NOT NUL terminated unless c == NUL and length(s) < n.
+ The intended of this routine is for filling in fixed-length record fields.
+*/
+VOID
+lset(p,s,n,c) char *s; char *p; int n; int c; {
+ int x;
+#ifndef USE_MEMCPY
+ int i;
+#endif /* USE_MEMCPY */
+ if (!s) s = "";
+ x = strlen(s);
+ if (x > n) x = n;
+#ifdef USE_MEMCPY
+ memcpy(p,s,x);
+ if (n > x)
+ memset(p+x,c,n-x);
+#else
+ for (i = 0; i < x; i++)
+ *p++ = *s++;
+ for (; i < n; i++)
+ *p++ = c;
+#endif /* USE_MEMCPY */
+}
+
+/* R S E T -- Right-adjust s in p, left padding to length n with char c */
+
+VOID
+rset(p,s,n,c) char *s; char *p; int n; int c; {
+ int x;
+#ifndef USE_MEMCPY
+ int i;
+#endif /* USE_MEMCPY */
+ if (!s) s = "";
+ x = strlen(s);
+ if (x > n) x = n;
+#ifdef USE_MEMCPY
+ memset(p,c,n-x);
+ memcpy(p+n-x,s,x);
+#else
+ for (i = 0; i < (n - x); i++)
+ *p++ = c;
+ for (; i < n; i++)
+ *p++ = *s++;
+#endif /* USE_MEMCPY */
+}
+
+/* U L O N G T O H E X -- Unsigned long to hex */
+
+/*
+ Converts unsigned long arg to hex and returns string pointer to
+ rightmost n hex digits left padded with 0's. Allows for longs
+ up to 64 bits. Returns pointer to result.
+*/
+char *
+#ifdef CK_ANSIC
+ulongtohex( unsigned long z, int n )
+#else
+ulongtohex(z,n) unsigned long z; int n;
+#endif /* CK_ANSIC */
+/* ulongtohex */ {
+ static char hexbuf[17];
+ int i = 16, x, k = 0;
+ hexbuf[16] = '\0';
+ if (n > 16) n = 16;
+ k = 2 * (sizeof(long));
+ for (i = 0; i < n; i++) {
+ if (i > k || z == 0) {
+ hexbuf[15-i] = '0';
+ } else {
+ x = z & 0x0f;
+ z = z >> 4;
+ hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37);
+ }
+ }
+ return((char *)(&hexbuf[16-i]));
+}
+
+/* H E X T O U L O N G -- Hex string to unsigned long */
+
+/*
+ Converts n chars from s from hex to unsigned long.
+ Returns:
+ 0L or positive, good result (0L is returned if arg is NULL or empty).
+ -1L on error: non-hex arg or overflow.
+*/
+long
+hextoulong(s,n) char *s; int n; {
+ char buf[64];
+ unsigned long result = 0L;
+ int d, count = 0;
+ int flag = 0;
+ if (!s) s = "";
+ if (!*s) {
+ return(0L);
+ }
+ if (n < 1)
+ return(0L);
+ if (n > 63) n = 63;
+ strncpy(buf,s,n);
+ buf[n] = '\0';
+ s = buf;
+ while (*s) {
+ d = *s++;
+ if ((d == '0' || d == ' ')) {
+ if (!flag)
+ continue;
+ } else {
+ flag = 1;
+ }
+ if (islower(d))
+ d = toupper(d);
+ if (d >= '0' && d <= '9') {
+ d -= 0x30;
+ } else if (d >= 'A' && d <= 'F') {
+ d -= 0x37;
+ } else {
+ return(-1L);
+ }
+ if (++count > (sizeof(long) * 2))
+ return(-1L);
+ result = (result << 4) | (d & 0x0f);
+ }
+ return(result);
+}
+
+/*
+ c k s p l i t -- Splits a string into words, or:
+ extracts a given word from a string.
+
+ Allows for grouping.
+ Operates on a copy of the string; does not alter the original string.
+ All strings NUL-terminated.
+
+ Call with:
+ fc = function code:
+ 1 = split, 0 = word.
+ n1 = desired word number if fc == 0.
+ s1 = source string.
+ s2 = break string (NULL to accept default = all non-alphanum).
+ s3 = include string (NULL to accept default = all alphanum).
+ n2 = grouping mask (OR desired ones together):
+ 1 = doublequotes, 2 = braces, 4 = apostrophes,
+ 8 = parens, 16 = brackets, 32 = angle brackets,
+ -1 = 63 = all of these.
+ n3 = group quote character, ASCII value, used and tested only for
+ LISP quote, e.g. (a 'b c '(d e f)).
+ n4 = 0 to collapse adjacent separators;
+ nonzero not to collapse them.
+
+ Returns:
+ Pointer to struct stringarray, with size:
+ -1 = memory allocation error.
+ -2 = too many words in string.
+ n = number of words (0 or more).
+
+ With:
+ wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based.
+ Each pointer is to a malloc'd string. This array is recycled upon each
+ call; if you need to keep the strings, make copies of them. This routine
+ must have control of allocation and deallocation.
+
+ If a given character is included in the include list, it is not treated
+ as a separator or as a grouping character.
+
+ Groups may be nested only if they are formed with braces, parens, or
+ brackets, but not with quotes or apostrophes since ASCII quotes have no
+ intrinsic handedness. Group-start and end characters are treated as
+ separators even in the absence of other separators, so a string such as
+ "a{b}c" results in three words, not one.
+
+ Sample call to split a string into an array:
+ struct stringarray * q;
+ q = cksplit(1,0,s1,s2,s3,-1,0);
+ q->a_size = size (>=0) or failure code (<0)
+ q->a_head = pointer to array (elements 0 thru q->asize - 1).
+
+ Sample call to extract word n from a string:
+ struct stringarray * q;
+ q = cksplit(0,n,s1,s2,s3,-1,0);
+ q->a_size = size (1) or failure code (<0)
+ q->a_head = pointer to array (element 1 is the desired word).
+*/
+
+/* States */
+
+#define ST_BW 0 /* Between Words */
+#define ST_IW 1 /* In Word */
+#define ST_IG 2 /* Start Group */
+
+/* Character Classes (bitmap) */
+
+#define CL_SEP 1 /* Separator */
+#define CL_OPN 2 /* Group Open */
+#define CL_CLS 4 /* Group Close */
+#define CL_DAT 8 /* Data */
+#define CL_QUO 16 /* Group quote */
+
+#ifdef BIGBUFOK
+#ifndef MAXWORDS
+#define MAXWORDS 4096 /* Max number of words */
+#endif /* MAXWORDS */
+#ifndef NESTMAX
+#define NESTMAX 64 /* Maximum nesting level */
+#endif /* NESTMAX */
+#else
+#ifndef MAXWORDS
+#define MAXWORDS 128 /* Max number of words */
+#endif /* MAXWORDS */
+#ifndef NESTMAX
+#define NESTMAX 16 /* Maximum nesting level */
+#endif /* NESTMAX */
+#endif /* BIGBUFOK */
+
+/* static */ char ** wordarray = NULL; /* Result array of word pointers */
+
+static struct stringarray ck_sval = { /* Return value structure */
+ NULL, /* Pointer to array */
+ 0 /* Size */
+};
+static int * wordsize = NULL;
+
+static VOID
+setword(n,s,len) int n, len; char * s; {
+ register char * p;
+ register int i, k;
+
+ if (!s) s = "";
+
+ if (!wordarray) { /* Allocate result array (only once) */
+ if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *))))
+ return;
+ if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int))))
+ return;
+ for (i = 0; i <= MAXWORDS; i++) { /* Initialize result array */
+ wordarray[i] = NULL;
+ wordsize[i] = 0;
+ }
+ }
+ if (wordsize[n] < len /* || !wordarray[n] */ ) {
+ k = (len < 16) ? 16 : len + (len / 4);
+ wordarray[n] = (char *) malloc(k+1);
+ wordsize[n] = (wordarray[n]) ? k : 0;
+ if (wordarray[n]) {
+ p = wordarray[n];
+ while ((*p++ = *s++) && k-- > 0) ;
+ }
+ } else if (len > 0) {
+ k = wordsize[n]; /* (In case len arg is a lie) */
+ p = wordarray[n];
+ while ((*p++ = *s++) && k-- > 0) {
+#ifdef COMMENT
+ if ((*(s-1) == CMDQ) && *s != CMDQ) {
+ k++;
+ p--;
+ }
+#endif /* COMMENT */
+ }
+ } else if (wordarray[n]) {
+ wordarray[n][0] = NUL;
+ }
+}
+
+static char * splitbuf = NULL;
+static int nsplitbuf = 0;
+
+/* n4 = 1 to NOT collapse adjacent separators */
+
+
+struct stringarray *
+cksplit(fc,n1,s1,s2,s3,n2,n3,n4) int fc,n1,n2,n3,n4; char *s1, *s2, *s3; {
+ int splitting = 0; /* What I was asked to do */
+ int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */
+ char * s = NULL, * ss, * p; /* Workers */
+ char * sep = ""; /* Default break set */
+ char * notsep = ""; /* Default include set */
+ int grouping = 0; /* Grouping option */
+ char * gr_opn = "\"{'([<"; /* Group open brackets */
+ char * gr_cls = "\"}')]>"; /* Group close brackets */
+ int gr_stk[NESTMAX]; /* Nesting bracket stack */
+ int gr_lvl = 0; /* Nesting level */
+ int wordnum = 0; /* Current word number */
+ char c = 'A'; /* Current char (dummy start value) */
+ int class = 0; /* Current character class */
+ int state = ST_BW; /* Current FSA state */
+ int len = 0; /* Length of current word */
+ int slen = 0; /* Length of s1 */
+ int gquote = 0; /* Quoted group */
+ int cquote = 0; /* Quoted character */
+ int collapse = 1; /* Collapse adjacent separators */
+
+ if (n4) collapse = 0; /* Don't collapse */
+
+ for (i = 0; i < NESTMAX; i++) /* Initialize nesting stack */
+ gr_stk[i] = 0;
+
+ setword(1,NULL,0);
+ ck_sval.a_head = wordarray; /* Initialize return value struct */
+ ck_sval.a_size = 0;
+
+ if (!s1) s1 = ""; /* s1 = source string */
+ if (!*s1) { /* If none, nothing to do */
+ return(&ck_sval);
+ }
+ splitting = fc; /* Our job */
+ if (splitting) { /* If splitting n = word count */
+ n = 0; /* Initialize it */
+ } else { /* Otherwise */
+ if (n1 < 1) { /* If 0 (or less) */
+ ck_sval.a_size = 0; /* nothing to do. */
+ return(&ck_sval);
+ }
+ n = n1; /* n = desired word number. */
+ }
+ slen = 0; /* Get length of s1 */
+ debug(F111,"cksplit",s1,n);
+
+ p = s1;
+#ifdef COMMENT
+ while (*p++) slen++; /* Make pokeable copy of s1 */
+ if (!splitbuf || slen > nsplitbuf) { /* Allocate buffer if needed */
+ int xx;
+ if (splitbuf)
+ free(splitbuf);
+ xx = (slen < 255) ? 255 : xx + (xx / 4);
+ debug(F101,"cksplit splitbuf","",xx);
+ splitbuf = (char *)malloc(xx+1);
+ if (!splitbuf) { /* Memory allocation failure... */
+ ck_sval.a_size = -1;
+ return(&ck_sval);
+ }
+ nsplitbuf = xx; /* Remember size of buffer */
+ }
+#else
+/*
+ The idea is to just copy the string into splitbuf (if it exists). This
+ gives us both the copy and the length so we only need to grovel through the
+ string once in most cases. Only when splitbuf doesn't exist or is too short
+ do we re-malloc(), which should be very infrequent so who cares if we have
+ to go through the string again in that case.
+*/
+ p = s1;
+ s = splitbuf;
+ if (splitbuf) { /* Make pokeable copy of s1 */
+ while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */
+ ;
+ }
+ if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */
+ int xx;
+ if (splitbuf) /* Free previous buf if any */
+ free(splitbuf);
+ while (*p++) slen++; /* Get (rest of) length */
+ xx = (slen < 255) ? 255 : slen + (slen / 4); /* Size of new buffer */
+ splitbuf = (char *)malloc(xx+1); /* Allocate it */
+ if (!splitbuf) { /* Memory allocation failure... */
+ ck_sval.a_size = -1;
+ return(&ck_sval);
+ }
+ nsplitbuf = xx; /* Remember (new) buffer size */
+ s = splitbuf;
+ p = s1;
+ while ((*s++ = *p++)) ;
+ }
+
+#endif /* COMMENT */
+ s = splitbuf;
+ sep = s2; /* s2 = break set */
+ if (!sep) sep = "";
+ notsep = s3; /* s3 = include set */
+ if (!notsep) notsep = "";
+ if (n2 < 0) n2 = 63; /* n2 = grouping mask */
+ grouping = n2;
+ p = ""; /* Pointer to current word */
+ while (c) { /* Loop through string */
+ c = *s;
+ class = 0;
+ if (!cquote && c == CMDQ) { /* If CMDQ */
+ cquote++; /* next one is quoted */
+ goto nextc; /* go get it */
+ }
+ if (cquote && c == CMDQ) { /* Quoted CMDQ is special */
+ if (state != ST_BW) { /* because it can still separate */
+ char * s2 = s-1;
+ while (s2 > p) { *s2 = *(s2-1); s2--; }
+ p++;
+ }
+ cquote = 0;
+ }
+ if (cquote) { /* Other quoted character */
+ if (state != ST_BW) { /* can't separate or group */
+ char * s2 = s-1;
+ while (s2 > p) { *s2 = *(s2-1); s2--; }
+ p++;
+ }
+ class = CL_DAT; /* so treat it as data */
+ cquote = 0;
+ x = 1;
+ } else { /* Character is not quoted */
+ if (c < SP) { /* Get its class */
+ x = 0; /* x == 0 means "is separator" */
+ } else if (*sep) { /* Break set given */
+ ss = sep;
+ while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
+ x = (*ss != c);
+ } else { /* Default break set is */
+ x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */
+ (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z')
+ );
+ }
+ if (x == 0 && *notsep && c) { /* Include set if given */
+ ss = notsep;
+ while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
+ x = (*ss == c);
+ }
+ if (c == n3 && grouping && state == ST_BW) { /* Group quote? */
+ class = CL_QUO;
+ } else {
+ class = x ? CL_DAT : CL_SEP; /* Class = data or separator */
+ }
+ if (grouping) { /* Grouping? */
+ int j; /* Look for group start */
+ for (k = 0; k < 6; k++) { /* Group-start char? */
+ j = 1 << k; /* Get its mask bit value */
+ if (grouping & j) {
+ if (c == gr_opn[k]) { /* Selected group opener? */
+ ko = k;
+ class |= CL_OPN;
+ if (c == '"' || c == '\'') { /* These can also */
+ class |= CL_CLS; /* be closers */
+ break;
+ }
+ } else if (c == gr_cls[k]) { /* Group closer? */
+ class |= CL_CLS;
+ break;
+ }
+ }
+ }
+ }
+ }
+ switch (state) { /* State switcher... */
+ case ST_BW: /* BETWEENWORDS */
+ if (class & CL_OPN) { /* Group opener */
+ if (gr_lvl == 0 && !gquote) { /* If not in group */
+ p = s; /* point to beginning of word */
+ }
+ gr_lvl++; /* Push closer on nesting stack */
+ if (gr_lvl >= NESTMAX)
+ goto xxsplit;
+ gr_stk[gr_lvl] = gr_cls[ko];
+ state = ST_IG; /* Switch to INGROUP state */
+ } else if (class & CL_DAT) { /* Data character */
+ gr_lvl = 0; /* Clear group nesting stack */
+ gr_stk[gr_lvl] = 0;
+ len = 0;
+ p = s; /* Point to beginning of word */
+ if (gquote) { /* Adjust for quote */
+ len++;
+ p--;
+ gquote = 0;
+ }
+ state = ST_IW; /* Switch to INWORD state */
+ } else if (class & CL_QUO) { /* Group quote */
+ gquote = gr_lvl+1; /* Remember quoted level */
+ p = s - 1;
+ len = 1;
+ }
+ if (collapse)
+ break;
+
+ case ST_IW: /* INWORD (but not in a group) */
+ if (class & CL_SEP) { /* Ends on any kind of separator */
+ *s = NUL; /* Terminate this word */
+ wordnum++; /* Count it */
+ if (splitting) { /* Dispose of it appropriately */
+ if (wordnum > max) { /* Add to array if splitting */
+ ck_sval.a_size = -2;
+ return(&ck_sval);
+ }
+ setword(wordnum,p,len);
+ } else if (wordnum == n) { /* Searching for word n */
+ setword(1,p,len);
+ ck_sval.a_size = 1;
+ return(&ck_sval);
+ }
+ state = ST_BW; /* Switch to BETWEENWORDS state */
+ len = 0;
+ }
+ break;
+
+ case ST_IG: /* INGROUP */
+ if (class & CL_CLS) { /* Have group closer? */
+ if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */
+ gr_lvl--; /* Yes, pop stack */
+ if (gr_lvl < 0) /* Don't pop it too much */
+ gr_lvl = 0;
+ if (gr_lvl == 0) { /* If at top of stack */
+ if (gquote)
+ s++;
+ c = *s;
+ *s = NUL; /* we have word. */
+
+ wordnum++; /* Count and dispose of it. */
+ len--;
+ if (splitting) {
+ if (wordnum > max) {
+ ck_sval.a_size = -2;
+ return(&ck_sval);
+ }
+ setword(wordnum,p+1,len);
+ } else if (wordnum == n) {
+ setword(1,p+1,len);
+ ck_sval.a_size = 1;
+ return(&ck_sval);
+ }
+ state = ST_BW; /* Switch to BETWEENWORDS state */
+ len = 0;
+ }
+ if (gr_lvl < gquote)
+ gquote = 0;
+ }
+ } else if (class & CL_OPN) { /* Have group opener */
+ gr_lvl++; /* Push on nesting stack */
+ if (gr_lvl >= NESTMAX) goto xxsplit;
+ gr_stk[gr_lvl] = gr_cls[ko];
+ }
+ } /* switch */
+
+ nextc:
+ s++; /* Next char */
+ if (state)
+ len++;
+ } /* while (c) */
+
+ if (gr_lvl > 0) { /* In case of an unclosed group */
+ if (splitting) { /* make it the last word. */
+ if (++wordnum > max) {
+ ck_sval.a_size = -2;
+ return(&ck_sval);
+ }
+ setword(wordnum,p+1,len);
+ } else if (wordnum == n) {
+ setword(1,p+1,len);
+ ck_sval.a_size = 1;
+ return(&ck_sval);
+ }
+ }
+ if (!splitting) { /* Wanted word n but there was none? */
+ setword(1,NULL,0);
+ ck_sval.a_size = 0;
+ return(&ck_sval);
+ } else { /* Succeed otherwise */
+ ck_sval.a_size = wordnum;
+ if (wordnum < MAXWORDS)
+ setword(wordnum+1,NULL,0);
+ }
+#ifdef DEBUG
+ if (deblog) {
+ for (i = 1; i <= wordnum; i++)
+ debug(F111,"cksplit result",wordarray[i],i);
+ }
+#endif /* DEBUG */
+ return(&ck_sval);
+
+ xxsplit: /* Error return */
+ ck_sval.a_size = -2;
+ return(&ck_sval);
+}
+
+/* End of ckclib.c */
diff --git a/ckermit-8.0.211/ckclib.h b/ckermit-8.0.211/ckclib.h
new file mode 100644
index 0000000..a258664
--- /dev/null
+++ b/ckermit-8.0.211/ckclib.h
@@ -0,0 +1,100 @@
+/* ckclib.h -- C-Kermit library routine prototypes */
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>,
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 2002, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+#ifndef CKCLIB_H
+#define CKCLIB_H
+
+struct stringarray {
+ char ** a_head;
+ int a_size;
+};
+
+#ifdef CK_ANSIC
+_PROTOTYP( int ckstrncpy, (char *, const char *, int) );
+_PROTOTYP( int ckstrncat, (char *, const char *, int) );
+#else
+_PROTOTYP( int ckstrncpy, (char *, char *, int) );
+_PROTOTYP( int ckstrncat, (char *, char *, int) );
+#endif /* CK_ANSIC */
+
+_PROTOTYP( int ckmakmsg, (char *, int, char *, char *, char *, char *) );
+_PROTOTYP( int ckmakxmsg, (char *, int,
+ char *, char *, char *, char *, char *, char *,
+ char *, char *, char *, char *, char *, char *) );
+
+_PROTOTYP( char * ckstrpbrk, (char *, char *) );
+_PROTOTYP( char * ckstrstr, (char *, char *) );
+_PROTOTYP( char * chartostr, (int) );
+_PROTOTYP( int cklower, (char *) );
+_PROTOTYP( int ckupper, (char *) );
+_PROTOTYP( int ckindex, (char *, char *, int, int, int) );
+_PROTOTYP( char * ckctoa, (char) );
+_PROTOTYP( char * ckctox, (CHAR, int) );
+_PROTOTYP( char * ckitoa, (int) );
+_PROTOTYP( char * ckuitoa, (unsigned int) );
+_PROTOTYP( char * ckltoa, (long) );
+_PROTOTYP( char * ckultoa, (unsigned long) );
+_PROTOTYP( char * ckitox, (int) );
+_PROTOTYP( char * ckltox, (long) );
+_PROTOTYP( int ckmatch, (char *, char *, int, int ) );
+_PROTOTYP( VOID ckmemcpy, (char *, char *, int) );
+_PROTOTYP( char * ckstrchr, (char *, char) );
+_PROTOTYP( char * ckstrrchr, (char *, char) );
+_PROTOTYP( int ckrchar, (char *) );
+_PROTOTYP( int ckstrcmp, (char *, char *, int, int) );
+#define xxstrcmp(a,b,c) ckstrcmp(a,b,c,0)
+_PROTOTYP( int ckstrpre, (char *, char *) );
+_PROTOTYP( VOID sh_sort, (char **, char **, int, int, int, int) );
+_PROTOTYP( char * brstrip, (char *) );
+_PROTOTYP( char * fnstrip, (char *) );
+#ifdef COMMENT
+_PROTOTYP( char * brace, (char *) );
+#endif /* COMMENT */
+_PROTOTYP( int dquote, (char *, int, int) );
+_PROTOTYP( int untabify, (char *, char *, int) );
+_PROTOTYP( VOID makelist, (char *, char *[], int) );
+#ifndef CK_ANSIC
+_PROTOTYP( VOID makestr, (char **, char *) );
+_PROTOTYP( VOID xmakestr, (char **, char *) );
+#else /* CK_ANSIC */
+_PROTOTYP( VOID makestr, (char **, const char *) );
+_PROTOTYP( VOID xmakestr, (char **, const char *) );
+#endif /* CK_ANSIC */
+_PROTOTYP( int chknum, (char *) );
+_PROTOTYP( int rdigits, (char *) );
+_PROTOTYP( char * ckradix, (char *,int,int) );
+
+/* Base-64 conversion needed for script programming and HTTP */
+
+#ifndef NOB64
+_PROTOTYP( int b8tob64, (char *,int,char *,int));
+_PROTOTYP( int b64tob8, (char *,int,char *,int));
+#endif /* NOB64 */
+
+#ifdef CKFLOAT
+_PROTOTYP( int isfloat, (char *,int) );
+#ifndef CKCLIB_C
+#ifndef CKWART_C
+extern CKFLOAT floatval;
+#endif /* CKWART_C */
+#endif /* CKCLIB_C */
+#endif /* CKFLOAT */
+
+_PROTOTYP( char * parnam, (char) );
+_PROTOTYP( char *hhmmss, (long) );
+
+_PROTOTYP( VOID lset, (char *, char *, int, int) );
+_PROTOTYP( VOID rset, (char *, char *, int, int) );
+_PROTOTYP( char * ulongtohex, (unsigned long, int) );
+_PROTOTYP( long hextoulong, (char *, int) );
+_PROTOTYP( struct stringarray * cksplit, (int,int,
+ char *,char *,char *,int,int,int) );
+
+#endif /* CKCLIB_H */
diff --git a/ckermit-8.0.211/ckcmai.c b/ckermit-8.0.211/ckcmai.c
new file mode 100644
index 0000000..33504dc
--- /dev/null
+++ b/ckermit-8.0.211/ckcmai.c
@@ -0,0 +1,3592 @@
+#define EDITDATE "10 Apr 2004" /* Update these with each edit */
+#define EDITNDATE "20040410" /* Keep them in sync */
+/* Sat Apr 10 12:05:49 2004 */
+
+/*
+ ckcsym.h is used for for defining symbols that normally would be defined
+ using -D or -d on the cc command line, for use with compilers that don't
+ support this feature. Must be before any tests for preprocessor symbols.
+*/
+#include "ckcsym.h"
+/*
+ Consolidated program version information (for UNIX also see ckuver.h).
+ See makever() below for how they are used.
+*/
+/* #ifdef COMMENT */ /* Uncomment this for test version */
+#ifndef OS2
+#ifndef BETATEST
+#define BETATEST
+#endif /* BETATEST */
+#endif /* OS2 */
+/* #endif */ /* COMMENT */
+
+#ifdef BETATEST
+#ifdef OS2
+#ifdef __DATE__
+#define BETADATE
+#endif /* __DATE__ */
+#endif /* OS2 */
+#endif /* BETATEST */
+
+#ifndef MAC
+/*
+ Note: initialize ck_s_test to "" if this is not a test version.
+ Use (*ck_s_test != '\0') to decide whether to print test-related messages.
+*/
+#ifndef BETATEST
+#ifndef OS2 /* UNIX, VMS, etc... (i.e. C-Kermit) */
+char *ck_s_test = ""; /* "Dev","Alpha","Beta","RC", or "" */
+char *ck_s_tver = ""; /* Test version number or "" */
+#else /* OS2 */
+char *ck_s_test = ""; /* (i.e. K95) */
+char *ck_s_tver = "";
+#endif /* OS2 */
+#else
+char *ck_s_test = ""; /* Development */
+char *ck_s_tver = "";
+#endif /* BETATEST */
+#else /* MAC */
+char *ck_s_test = "Pre-Alpha"; /* Mac Kermit is always a test... */
+char *ck_s_tver = "";
+#endif /* MAC */
+
+#ifdef BETADATE /* Date of this version or edit */
+char *ck_s_date = __DATE__; /* Compilation date */
+#else
+char *ck_s_date = EDITDATE; /* See top */
+
+#endif /* BETADATE */
+char *buildid = EDITNDATE; /* See top */
+
+#ifdef UNIX
+static char sccsid[] = "@(#)C-Kermit 8.0.211";
+#endif /* UNIX */
+
+char *ck_s_ver = "8.0.211"; /* C-Kermit version string */
+long ck_l_ver = 800211L; /* C-Kermit version number */
+
+#ifdef OS2
+char *ck_s_xver = "2.2.0"; /* Product-specific version string */
+long ck_l_xver = 2200L; /* Product-specific version number */
+#else
+#ifdef MAC
+char *ck_s_xver = "0.995"; /* Product-specific version string */
+long ck_l_xver = 995L; /* Product-specific version number */
+#else
+char *ck_s_xver = ""; /* Don't touch these... */
+long ck_l_xver = 0L; /* they are computed at runtime */
+#endif /* MAC */
+#endif /* OS2 */
+
+#ifdef OS2
+#ifdef IKSDONLY
+#ifdef NT
+char *ck_s_name = "IKS-NT";
+#else /* NT */
+char *ck_s_name = "IKS-OS/2";
+#endif /* NT */
+#else /* IKSDONLY */
+char *ck_s_name = "Kermit 95"; /* Program name */
+#endif /* IKSDONLY */
+#else
+#ifdef MAC
+char *ck_s_name = "Mac Kermit";
+#else
+char *ck_s_name = "C-Kermit";
+#endif /* MAC */
+#endif /* OS2 */
+
+char *ck_s_who = ""; /* Where customized, "" = not. */
+char *ck_patch = ""; /* Patch info, if any. */
+
+#define CKVERLEN 128
+char versiox[CKVERLEN]; /* Version string buffer */
+char *versio = versiox; /* These are filled in at */
+long vernum, xvernum; /* runtime from above. */
+
+#define CKCMAI
+
+#include "ckcasc.h" /* ASCII character symbols */
+#include "ckcdeb.h" /* Debug & other symbols */
+
+char * myname = NULL; /* The name I am called by */
+#ifndef OS2
+char * exedir = NULL; /* Directory I was executed from */
+#endif /* OS2 */
+char * myhome = NULL; /* Home directory override */
+
+/* C K C M A I -- C-Kermit Main program */
+
+/*
+ Author: Frank da Cruz (fdc@columbia.edu),
+ Columbia University Academic Information Systems, New York City.
+
+COPYRIGHT NOTICE:
+*/
+
+#ifdef OS2
+char *wiksdcpr[] = {
+"Windows Internet Kermit Service Daemon (WIKSD):",
+"Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New",
+"York. All rights reserved.",
+" ",
+"PERMISSIONS:",
+" ",
+" The WIKSD software may be obtained directly, in binary form only, from",
+" the Kermit Project at Columbia University by any individual for his or",
+" her OWN USE, and by any company or other organization for its own",
+" INTERNAL DISTRIBUTION and use, including installation on servers that",
+" are accessed by customers or clients, WITHOUT EXPLICIT LICENSE. All",
+" other forms of redistribution must be licensed from the Kermit Project",
+" at Columbia University. These permissions apply only to the nonsecure",
+" version of WIKSD.",
+" ",
+"DISCLAIMER:",
+" ",
+" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE",
+" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS",
+" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF",
+" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER",
+" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED",
+" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.",
+" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT",
+" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,",
+" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OR IN",
+" CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS",
+" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL",
+" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN",
+" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY",
+" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING",
+" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.",
+" ",
+"The above copyright notice, permissions notice, and disclaimer may not be",
+"removed, altered, or obscured and shall be included in all copies of the",
+"WIKSD software. The Trustees of Columbia University in the City of",
+"New York reserve the right to revoke this permission if any of the terms",
+"of use set forth above are breached.",
+" ",
+"For further information, contact the Kermit Project, Columbia University,",
+"612 West 115th Street, New York NY 10025-7799, USA; Phone +1 (212) 854 3703,",
+"Fax +1 (212) 662 6442, kermit@columbia.edu, http://www.columbia.edu/kermit/",
+""
+};
+#endif /* OS2 */
+
+char *copyright[] = {
+
+#ifdef pdp11
+"Copyright (C) 1985, 2004, Trustees of Columbia University, NYC.",
+"All rights reserved.",
+" ",
+#else
+#ifdef OS2
+"Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New",
+"York. All rights reserved. This software is furnished under license",
+"and may not be reproduced without license to do so. This copyright notice",
+"must not be removed, altered, or obscured.",
+" ",
+" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE",
+" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS",
+" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF",
+" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER",
+" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED",
+" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.",
+" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT",
+" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,",
+" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OR IN",
+" CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS",
+" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL",
+" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN",
+" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY",
+" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING",
+" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.",
+" ",
+#else
+"Copyright (C) 1985, 2004,",
+" The Trustees of Columbia University in the City of New York.",
+" All rights reserved.",
+" ",
+"PERMISSIONS:",
+" ",
+"The C-Kermit software may be obtained directly from the Kermit Project at",
+"Columbia University (or from any source explicitly licensed by the Kermit",
+"Project or implicitly licensed by Clause (A) below) by any individual for",
+"his or her OWN USE, and by any company or other organization for its own",
+"INTERNAL DISTRIBUTION and use, including installation on servers that are",
+"accessed by customers or clients, WITHOUT EXPLICIT LICENSE.",
+" ",
+"Conditions for REDISTRIBUTION are as follows:",
+" ",
+"(A) The C-Kermit software, in source and/or binary form, may be",
+" included WITHOUT EXPLICIT LICENSE in distributions of OPERATING",
+" SYSTEMS that have OSI (Open Source Initiative, www.opensource.org)",
+" approved licenses, even if non-Open-Source applications (but not",
+" operating systems) are included in the same distribution. Such",
+" distributions include, but are not limited to, CD-ROM, FTP site,",
+" Web site, or preinstalled software on a new GENERAL-PURPOSE",
+" computer, as long as the primary character of the distribution is",
+" an Open Source operating system with accompanying utilities. The",
+" C-Kermit source code may not be changed without the consent of the",
+" Kermit Project, which will not be unreasonably withheld (this is",
+" simply a matter of keeping a consistent and supportable code base).",
+" ",
+"(B) Inclusion of C-Kermit software in whole or in part, in any form, in",
+" or with any product not covered by Clause (A), or its distribution",
+" by any commercial enterprise to its actual or potential customers",
+" or clients except as in Clause (A), requires a license from the",
+" Kermit Project, Columbia University; contact kermit@columbia.edu.",
+" ",
+"The name of Columbia University may not be used to endorse or promote",
+"products derived from or including the C-Kermit software without specific",
+"prior written permission.",
+" ",
+"DISCLAIMER:",
+" ",
+" THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE",
+" TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK AS TO ITS",
+" FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE TRUSTEES OF",
+" COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK OF ANY KIND, EITHER",
+" EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED",
+" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.",
+" THE TRUSTEES OF COLUMBIA UNIVERSITY IN THE CITY OF NEW YORK SHALL NOT",
+" BE LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL,",
+" OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR",
+" IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS",
+" HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. YOU SHALL",
+" INDEMNIFY AND HOLD HARMLESS THE TRUSTEES OF COLUMBIA UNIVERSITY IN",
+" THE CITY OF NEW YORK, ITS EMPLOYEES AND AGENTS FROM AND AGAINST ANY",
+" AND ALL CLAIMS, DEMANDS, LOSS, DAMAGE OR EXPENSE (INCLUDING",
+" ATTORNEYS' FEES) ARISING OUT OF YOUR USE OF THIS SOFTWARE.",
+" ",
+"The above copyright notice, permissions notice, and disclaimer may not be",
+"removed, altered, or obscured and shall be included in all copies of the",
+"C-Kermit software. The Trustees of Columbia University in the City of",
+"New York reserve the right to revoke this permission if any of the terms",
+"of use set forth above are breached.",
+#endif /* OS2 */
+#endif /* pdp11 */
+
+#ifdef OS2
+"Portions Copyright (C) 1995, Oy Online Solutions Ltd., Jyvaskyla, Finland.",
+#endif /* OS2 */
+
+#ifdef CK_AUTHENTICATION
+"Portions Copyright (C) 1990, Massachusetts Institute of Technology.",
+#ifdef CK_ENCRYPTION
+"Portions Copyright (C) 1991, 1993 Regents of the University of California.",
+"Portions Copyright (C) 1991, 1992, 1993, 1994, 1995 by AT&T.",
+"Portions Copyright (C) 1995, 1997, Eric Young <eay@cryptosoft.com>.",
+#endif /* CK_ENCRYPTION */
+#ifdef CK_SRP
+"Portions Copyright (C) 1997, Stanford University.",
+#endif /* CK_SRP */
+#endif /* CK_AUTHENTICATION */
+
+#ifndef pdp11
+" ",
+"For further information, contact the Kermit Project, Columbia University,",
+"612 West 115th Street, New York NY 10025-7799, USA; phone +1 (212) 854 3703,",
+"fax +1 (212) 663 8202 or +1 (212) 662 6442, email kermit@columbia.edu,",
+"Web http://www.columbia.edu/kermit/ or http://www.kermit-project.org/.",
+#endif /* pdp11 */
+""};
+
+/*
+DOCUMENTATION:
+
+ "Using C-Kermit" by Frank da Cruz and Christine M. Gianone,
+ Digital Press / Butterworth-Heinemann, Woburn MA, USA.
+ Second edition (1997), ISBN 1-55558-164-1.
+ Order from Digital Press: +1 (800) 366-2665
+ Or from Columbia University: +1 (212) 854-3703
+
+For Kermit 95, also:
+
+ "Kermit 95" by Christine M. Gianone and Frank da Cruz,
+ Manning Publications, Greenwich CT, USA (1998) - Online.
+
+ACKNOWLEDGMENTS:
+
+ The Kermit file transfer protocol was developed at the Columbia University
+ Center for Computing Activities (CUCCA), which was since renamed to Columbia
+ University Academic Information Systems (AcIS). Kermit is named after
+ Kermit the Frog, star of the television series THE MUPPET SHOW; the name is
+ used by permission of Henson Associates, Inc.
+
+ Thanks to at least the following people for their contributions to this
+ program over the years, and apologies to anyone who was inadvertantly
+ omitted:
+
+ Chris Adie, Edinburgh U, Scotland (OS/2)
+ Robert Adsett, University of Waterloo, Canada
+ Larry Afrin, Clemson U
+ Russ Allbery, Stanford U
+ Jeffrey Altman, Columbia University
+ Greg Andrews, Telebit Corp
+ Barry Archer, U of Missouri
+ Robert Andersson, International Systems A/S, Oslo, Norway
+ Chris Armstrong, Brookhaven National Lab (OS/2)
+ William Bader, Software Consulting Services, Nazareth, PA
+ Fuat Baran, Columbia U
+ Stan Barber, Rice U
+ Jim Barbour, U of Colorado
+ Donn Baumgartner, Dell
+ Nelson Beebe, U of Utah
+ Gerry Belanger, Cognitronics
+ Karl Berry, UMB
+ Mark Berryman, SAIC
+ Dean W Bettinger, SUNY
+ Gary Bilkus
+ Peter Binderup, Denmark
+ David Bolen, Advanced Networks and Services, Inc.
+ Marc Boucher, U of Montreal
+ Charles Brooks, EDN
+ Bob Brown
+ Mike Brown, Purdue U
+ Jack Bryans, California State U at Long Beach
+ Mark Buda, DEC (VMS)
+ Fernando Cabral, Padrao iX, Brasilia
+ Bjorn Carlsson, Stockholm University Computer Centre QZ, Sweden
+ Bill Catchings, (formerly of) Columbia U
+ Bob Cattani, Columbia U CS Dept
+ Davide Cervone, Rochester U
+ Seth Chaiklin, Denmark
+ John Chandler, Harvard U / Smithsonian Astronomical Observatory
+ Bernard Chen, UCLA
+ Andrew A Chernov, RELCOM Team, Moscow
+ John L Chmielewski, AT&T, Lisle, IL
+ Howard Chu, U of Michigan
+ Bill Coalson, McDonnell Douglas
+ Bertie Coopersmith, London
+ Chet Creider, U of Western Ontario
+ Alan Crosswell, Columbia U
+ Jeff Damens, (formerly of) Columbia U
+ Mark Davies, Bath U, UK
+ Sin-itirou Dezawa, Fujifilm, Japan
+ Joe R. Doupnik, Utah State U
+ Frank Dreano, Honeywell
+ John Dunlap, U of Washington
+ Alex Dupuy, SMART.COM
+ David Dyck, John Fluke Mfg Co.
+ Stefaan A. Eeckels, Eurokom, Luxembourg
+ Nick Efthymiou
+ Paul Eggert, Twin Sun, Inc., El Segundo, CA
+ Bernie Eiben, DEC
+ Peter Eichhorn, Assyst International
+ Kristoffer Eriksson, Peridot Konsult AB, Oerebro, Sweden
+ John R. Evans, IRS, Kansas City
+ Glenn Everhart, RCA Labs
+ Charlie Finan, Cray Research
+ Herm Fischer, Encino, CA (extensive contributions to version 4.0)
+ Carl Fongheiser, CWRU
+ Mike Freeman, Bonneville Power Authority
+ Marcello Frutig, Catholic University, Sao Paulo, Brazil (X.25 support)
+ Hirofumi Fujii, Japan Nat'l Lab for High Energy Physics, Tokyo (Kanji)
+ Chuck Fuller, Westinghouse Corporate Computer Services
+ Andy Fyfe, Caltech
+ Christine M. Gianone, Columbia U
+ John Gilmore, UC Berkeley
+ Madhusudan Giyyarpuram, HP
+ Rainer Glaschick, Siemens AG, Paderborn
+ William H. Glass
+ German Goldszmidt, IBM
+ Chuck Goodhart, NASA
+ Alistair Gorman, New Zealand
+ Richard Gration, ADFA, Australia
+ Chris Green, Essex U, UK
+ Alan Grieg, Dundee Tech, Scotland
+ Yekta Gursel, MIT
+ Jim Guyton, Rand Corp
+ Michael Haertel
+ Bruno Haible
+ Bob Hain, UMN
+ Marion Hakanson, ORST
+ Richard Hamilton
+ John Hamilston, Iowa State U
+ Simon Hania, Netherlands
+ Stan Hanks, Rice U.
+ Ken Harrenstein, SRI
+ Eugenia Harris, Data General (AOS/VS)
+ David Harrison, Kingston Warren Corp
+ Lucas Hart, Oregon State University
+ James Harvey, Indiana/Purdue U (VMS)
+ Rob Healey
+ Chuck Hedrick, Rutgers U
+ Ron Heiby, Technical Systems Division, Motorola Computer Group
+ Steve Hemminger, Tektronix
+ Christian Hemsing, RWTH Aachen, Germany (OS-9)
+ Randolph Herber, US DOE,
+ Andrew Herbert, Monash Univ, Australia
+ Marcus Herbert, Germany
+ Mike Hickey, ITI
+ Dan Hildebrand, QNX Software Systems Inc, Kanata, ON (QNX)
+ R E Hill
+ Stephan Hoffman-Emden
+ Sven Holmstrom, ABB Utilities AB, Sweden
+ Bill Homer, Cray Research
+ Ray Hunter, The Wollongong Group
+ Randy Huntziger, National Library of Medicine
+ Larry Jacobs, Transarc
+ Steve Jenkins, Lancaster University, UK
+ Dave Johnson, Gradient Technologies
+ Mark B Johnson, Apple Computer
+ Jyke Jokinen, Tampere University of Technology, Finland (QNX)
+ Eric F Jones, AT&T
+ Luke Jones, AT&T
+ Peter Jones, U of Quebec Montreal
+ Phil Julian, SAS Institute
+ Peter Kabal, U of Quebec
+ Mic Kaczmarczik, U of Texas at Austin
+ Sergey Kartashoff, Inst. of Precise Mechanics & Computer Equipment, Moscow
+ Howie Kaye, Columbia U
+ Rob Kedoin, Linotype Co, Hauppauge, NY (OS/2)
+ Phil Keegstra
+ Mark Kennedy, IBM
+ Terry Kennedy, St Peter's College, Jersey City, NJ (VMS and more)
+ "Carlo Kid", Technical University of Delft, Netherlands
+ Tim Kientzle
+ Paul Kimoto, Cornell U
+ Douglas Kingston, morgan.com
+ Lawrence Kirby, Wiltshire, UK
+ Tom Kloos, Sequent Computer Systems
+ Jim Knutson, U of Texas at Austin
+ John T. Kohl (BSDI)
+ Scott Kramer, SRI International, Menlo Park, CA
+ John Kraynack, US Postal Service
+ David Kricker, Encore Computer
+ Thomas Krueger, UWM
+ Bo Kullmar, ABC Klubben, Stockholm, and Central Bank of Sweden, Kista
+ R. Brad Kummer, AT&T Bell Labs, Atlanta, GA
+ John Kunze, UC Berkeley
+ David Lane, BSSI / BellSouth (Stratus VOS, X.25)
+ Bob Larson, USC (OS-9)
+ Bert Laverman, Groningen U, Netherlands
+ Steve Layton
+ David Lawyer, UC Irvine
+ David LeVine, National Semiconductor Corporation
+ Daniel S. Lewart, UIUC
+ S.O. Lidie, Lehigh U
+ Tor Lillqvist, Helsinki U, Finland
+ David-Michael Lincke, U of St Gallen, Switzerland
+ Robert Lipe (for SCO makefile entries & advice)
+ Dean Long
+ Mike Long, Analog Devices, Norwood MA
+ Kevin Lowey, U of Saskatchewan (OS/2)
+ Andy Lowry, Columbia U
+ James Lummel, Caprica Telecomputing Resources (QNX)
+ David MacKenzie, Environmental Defense Fund, U of Maryland
+ John Mackin, University of Sidney, Australia
+ Martin Maclaren, Bath U, UK
+ Chris Maio, Columbia U CS Dept
+ Montserrat Mane, HP, Grenoble, France
+ Fulvio Marino, Olivetti, Ivrea, Italy
+ Arthur Marsh, dircsa.org.au
+ Peter Mauzey, Lucent Technologies
+ Tye McQueen, Utah State U
+ Ted Medin
+ Hellmuth Michaelis, Hanseatischer Computerservice GmbH, Hamburg, Germany
+ Leslie Mikesell, American Farm Bureau
+ Todd Miller, Courtesan Consulting
+ Martin Minow, DEC (VMS)
+ Pawan Misra, Bellcore
+ Ken Mizialko, IBM, Manassas, VA
+ Wolfgang Moeller, DECUS Germany
+ Ray Moody, Purdue U
+ Bruce J Moore, Allen-Bradley Co, Highland Heights, OH (Atari ST)
+ Steve Morley, Convex
+ Peter Mossel, Columbia U
+ Tony Movshon, NYU
+ Lou Muccioli, Swanson Analysis Systems
+ Dan Murphy
+ Neal P. Murphy, Harsof Systems, Wonder Lake IL
+ Gary Mussar
+ John Nall, FSU
+ Jack Nelson, U of Pittsburgh
+ Jim Noble, Planning Research Corporation (Macintosh)
+ Ian O'Brien, Bath U, UK
+ Melissa O'Neill, SFU
+ John Owens
+ Thomas Pinkl, Health Business Systems Inc.
+ Michael Pins, Iowa Computer Aided Engineering Network
+ Andre' Pirard, University of Liege, Belgium
+ Paul Placeway, Ohio State U
+ Piet W. Plomp, ICCE, Groningen University, Netherlands
+ Ken Poulton, HP Labs
+ Manfred Prange, Oakland U
+ Christopher Pratt, APV Baker, UK
+ Frank Prindle, NADC
+ Tony Querubin, U of Hawaii
+ Jean-Pierre Radley
+ Anton Rang
+ Scott Ribe
+ Alan Robiette, Oxford University, UK
+ Michel Robitaille, U of Montreal (Mac)
+ Huw Rogers, Schweizerische Kreditanstalt, Zuerich
+ Nigel Roles, Cambridge, England
+ Kai Uwe Rommel, Technische Universitaet Muenchen (OS/2)
+ Larry Rosenman (Amiga)
+ Jay Rouman, U of Michigan
+ Jack Rouse, SAS Institute (Data General and/or Apollo)
+ Stew Rubenstein, Harvard U (VMS)
+ Gerhard Rueckle, FH Darmstadt, Fb. E/Automatisierungstechnik
+ John Santos, EG&H
+ Bill Schilit, Columbia U
+ Ulli Schlueter, RWTH Aachen, Germany (OS-9, etc)
+ Michael Schmidt, U of Paderborn, Germany
+ Eric Schnoebelen, Convex
+ Benn Schreiber, DEC
+ Dan Schullman, DEC (modems, DIAL command, etc)
+ John Schultz, 3M
+ Steven Schultz, Contel (PDP-11)
+ APPP Scorer, Leeds Polytechnic, UK
+ Gordon Scott, Micro Focus, Newbury UK
+ Gisbert W. Selke, WIdO, Bonn, Germany
+ David Singer, IBM Almaden Research Labs
+ David Sizeland, U of London Medical School
+ Fridrik Skulason, Iceland
+ Rick Sladkey (Linux)
+ Dave Slate
+ Bradley Smith, UCLA
+ Fred Smith, Merk / Computrition
+ Richard S Smith, Cal State
+ Ryan Stanisfer, UNT
+ Bertil Stenstroem, Stockholm University Computer Centre (QZ), Sweden
+ James Sturdevant, CAP GEMENI AMERICA, Minneapolis
+ Peter Svanberg, Royal Techn. HS, Sweden
+ James R. Swenson, Accu-Weather, Inc.
+ Ted T'so, MIT (Linux)
+ Andy Tanenbaum, Vrije U, Amsterdam, Netherlands
+ Glen Thobe
+ Markku Toijala, Helsinki U of Technology
+ Teemu Torma, Helsinki U of Technology
+ Linus Torvalds, Helsinki
+ Rick Troxel, NIH
+ Warren Tucker, Tridom Corp, Mountain Park, GA
+ Dave Tweten, AMES-NAS
+ G Uddeborg, Sweden
+ Walter Underwood, Ford Aerospace
+ Pieter Van Der Linden, Centre Mondial, Paris
+ Ge van Geldorp, Netherlands
+ Fred van Kempen, MINIX User Group, Voorhout, Netherlands
+ Wayne Van Pelt, GE/CRD
+ Mark Vasoll, Oklahoma State U (V7 UNIX)
+ Konstantin Vinogradov, ICSTI, Moscow
+ Paul Vixie, DEC
+ Bernie Volz, Process Software
+ Eduard Vopicka, Prague University of Economics, Czech Republic
+ Dimitri Vulis, CUNY
+ Roger Wallace, Raytheon
+ Stephen Walton, Calif State U, Northridge (Amiga)
+ Jamie Watson, Adasoft, Switzerland (AIX)
+ Rick Watson, U of Texas (Macintosh)
+ Scott Weikart (Association for Progressive Communications)
+ Robert Weiner, Programming Plus, New York City
+ Lauren Weinstein, Vortex Technlogy
+ David Wexelblat, AT&T
+ Clark Wierda, Illuminati Online
+ Joachim Wiesel, U of Karlsruhe
+ Lon Willett, U of Utah
+ Michael Williams, UCLA
+ Nate Williams, U of Montana
+ David Wilson
+ Joellen Windsor, U of Arizona
+ Patrick Wolfe, Kuck & Associates, Inc.
+ Gregg Wonderly, Oklahoma State U (V7 UNIX)
+ Farrell Woods, Concurrent (formerly Masscomp)
+ Dave Woolley, CAP Communication Systems, London
+ Jack Woolley, SCT Corp
+ Frank Wortner
+ Ken Yap, formerly of U of Rochester
+ John Zeeff, Ann Arbor, MI
+*/
+
+#include "ckcker.h" /* Kermit symbols */
+#include "ckcnet.h" /* Network symbols */
+
+#ifdef CK_SSL
+#include "ck_ssl.h"
+#endif /* CK_SSL */
+
+#ifndef NOSPL
+#include "ckuusr.h"
+#endif /* NOSPL */
+
+#ifdef OS2ONLY
+#define INCL_VIO /* Needed for ckocon.h */
+#include <os2.h>
+#undef COMMENT
+#endif /* OS2ONLY */
+
+#ifdef NT
+#include <windows.h>
+#include <tapi.h>
+#include "ckntap.h"
+#endif /* NT */
+
+#ifndef NOSERVER
+/* Text message definitions.. each should be 256 chars long, or less. */
+#ifdef MINIX
+char *srvtxt = "\r\n\
+Entering server mode.\r\n\0";
+#else
+#ifdef OLDMSG
+/*
+ It seems there was a large installation that was using C-Kermit 5A(165)
+ or thereabouts, which had deployed thousands of MS-DOS Kermit scripts in
+ scattered locations that looked for strings in the old server message,
+ which changed in 5A(183), August 1992.
+*/
+char *srvtxt = "\r\n\
+C-Kermit server starting. Return to your local machine by typing\r\n\
+its escape sequence for closing the connection, and issue further\r\n\
+commands from there. To shut down the C-Kermit server, issue the\r\n\
+FINISH or BYE command and then reconnect.\n\
+\r\n\0";
+#else
+#ifdef OSK
+char *srvtxt = "\r\012\
+Entering server mode. If your local Kermit software is menu driven, use\r\012\
+the menus to send commands to the server. Otherwise, enter the escape\r\012\
+sequence to return to your local Kermit prompt and issue commands from\r\012\
+there. Use SEND and GET for file transfer. Use REMOTE HELP for a list of\r\012\
+other available services. Use BYE or FINISH to end server mode.\r\012\0";
+#else /* UNIX, VMS, AOS/VS, and all others */
+char *srvtxt = "\r\n\
+Entering server mode. If your local Kermit software is menu driven, use\r\n\
+the menus to send commands to the server. Otherwise, enter the escape\r\n\
+sequence to return to your local Kermit prompt and issue commands from\r\n\
+there. Use SEND and GET for file transfer. Use REMOTE HELP for a list of\r\n\
+other available services. Use BYE or FINISH to end server mode.\r\n\0";
+#endif /* OSK */
+#endif /* OLDMSG */
+#endif /* MINIX */
+#else /* server mode disabled */
+char *srvtxt = "";
+#endif /* NOSERVER */
+
+int initflg = 0; /* sysinit() has executed... */
+int howcalled = I_AM_KERMIT; /* How I was called */
+int hmtopline = 0;
+int quitting = 0; /* I'm in the act of quitting */
+
+#ifdef IKSDCONF
+char * iksdconf = IKSDCONF; /* IKSD configuration file */
+int iksdcf = 0; /* Has IKSD c.f. been processed? */
+#endif /* IKSDCONF */
+
+int srvcdmsg = 0; /* [Server] CD message */
+char * cdmsgfile[8] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL };
+char * cdmsgstr = NULL;
+char * ckcdpath = NULL;
+
+#ifdef NLCHAR /* Text-file line terminator */
+CHAR feol = NLCHAR;
+#else
+CHAR feol = 0;
+#endif /* NLCHAR */
+
+int fblksiz = DBLKSIZ; /* File blocksize */
+int frecl = DLRECL; /* File record length */
+int frecfm = XYFF_S; /* File record format (default = stream) */
+int forg = XYFO_S; /* File organization (sequential) */
+int fcctrl = XYFP_N; /* File carriage control (ctrl chars) */
+int filecase = FILECASE; /* Case matters in filenames */
+int stathack = 1; /* Fast directory lookups by default */
+
+char uidbuf[UIDBUFLEN] = { NUL, NUL }; /* User ID buffer */
+int cfilef = 0; /* Application ("kerbang") file flag */
+char cmdfil[CKMAXPATH + 1] = { NUL, NUL }; /* Application file name */
+int haveurl = 0; /* URL given on command line */
+
+#ifndef NOXFER
+/* Multi-protocol support */
+
+struct ck_p ptab[NPROTOS] = { /* Initialize the Kermit part ... */
+ { "Kermit",
+ DRPSIZ, /* Receive packet size */
+ DSPSIZ, /* Send packet size */
+ 0, /* Send-packet-size-set flag */
+ DFWSIZ, /* Window size */
+
+#ifdef NEWDEFAULTS
+ PX_CAU, /* Control char unprefixing... */
+#else
+ PX_ALL,
+#endif /* NEWDEFAULTS */
+
+#ifdef VMS /* Default filename collision action */
+ XYFX_X, /* REPLACE for VAX/VMS */
+#else
+ XYFX_B, /* BACKUP for everybody else */
+#endif /* VMS */
+
+#ifdef OS2 /* Flag for file name conversion */
+ XYFN_L, /* Literal for OS2 */
+#else
+ XYFN_C, /* Converted for others */
+#endif /* OS2 */
+
+ PATH_OFF, /* Send pathnames OFF */
+ PATH_AUTO, /* Receive pathnames AUTO */
+ NULL, /* Host receive initiation string (binary) */
+ NULL, /* Host receive initiation string (text) */
+ NULL, /* Host server string */
+ NULL, /* External protocol send command (binary) */
+ NULL, /* External protocol send command (text) */
+ NULL, /* External protocol receive command (bin) */
+ NULL } /* External protocol receive command (txt) */
+#ifdef CK_XYZ
+,
+{"XMODEM", 128,128,-1,-1, 1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL},
+{"XMODEM-CRC",128,128,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL},
+{"YMODEM", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL},
+{"YMODEM-g", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL},
+{"ZMODEM", -1, -1,-1,-1,PX_WIL,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL},
+{"Other", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}
+#endif /* CK_XYZ */
+};
+
+/* Declarations for Send-Init Parameters */
+
+int spsiz = DSPSIZ, /* Current packet size to send */
+ spmax = DSPSIZ, /* Biggest packet size we can send */
+ lastspmax = DSPSIZ, /* Send-packet size last used */
+ spsizr = DSPSIZ, /* Send-packet size requested */
+ spsizf = 0, /* Flag to override size negotiation */
+ rpsiz = DRPSIZ, /* Biggest we want to receive */
+ urpsiz = DRPSIZ, /* User-requested receive pkt size */
+ maxrps = MAXRP, /* Maximum incoming long packet size */
+ maxsps = MAXSP, /* Maximum outbound l.p. size */
+ maxtry = MAXTRY, /* Maximum retries per packet */
+ wslots = 1, /* Window size currently in use */
+ wslotr = DFWSIZ, /* Window size from SET WINDOW */
+ wslotn = 1, /* Window size negotiated in S-pkt */
+ timeouts = 0, /* For statistics reporting */
+ spackets = 0, /* ... */
+ rpackets = 0, /* ... */
+ retrans = 0, /* ... */
+ crunched = 0, /* ... */
+ wmax = 0, /* ... */
+ wcur = 0, /* ... */
+ srvidl = 0, /* Server idle timeout */
+ srvdis = 1, /* Server file xfer display */
+ srvtim = DSRVTIM, /* Server command wait timeout */
+ srvping = 1, /* Server keepalive */
+/*
+ timint is the timeout interval I use when waiting for a packet.
+ pkttim is the SET RECEIVE TIMEOUT value, sent to the other Kermit.
+ rtimo is the SET SEND TIMEOUT value. rtimo is the initial value of
+ timint. timint is changed by the value in the incoming negotiation
+ packet unless a SET SEND TIMEOUT command was given.
+*/
+ timint = DMYTIM, /* Timeout interval I use */
+ pkttim = URTIME, /* Timeout I want you to use */
+ rtimo = DMYTIM, /* Normal packet wait timeout */
+ timef = 0, /* Flag to override what you ask */
+#ifdef CK_TIMERS
+ rttflg = 1, /* Use dynamic round-trip timers */
+#else
+ rttflg = 0, /* Use fixed timer */
+#endif /* CK_TIMERS */
+ mintime = 1, /* Minimum timeout */
+ maxtime = 0, /* Maximum timeout */
+
+ npad = MYPADN, /* How much padding to send */
+ mypadn = MYPADN, /* How much padding to ask for */
+ bctr = DFBCT, /* Block check type requested */
+ bctu = 1, /* Block check type used */
+ bctl = 1, /* Block check length */
+ c_save = -1, /* Block check saving and restoring */
+ ss_save = -1, /* Slow-start saving and restoring */
+ ebq = MYEBQ, /* 8th bit prefix */
+ ebqflg = 0, /* 8th-bit quoting flag */
+ rqf = -1, /* Flag used in 8bq negotiation */
+ rq = 0, /* Received 8bq bid */
+ sq = 'Y', /* Sent 8bq bid */
+ rpt = 0, /* Repeat count */
+ rptq = MYRPTQ, /* Repeat prefix */
+ rptflg = 0, /* Repeat processing flag */
+ rptena = 1, /* Repeat processing enabled */
+ xfrcan = 1, /* Transfer cancellation enabled */
+ xfrint = 1, /* Transfer interruption enabled */
+ xfrchr = 3, /* Transfer cancel char = Ctrl-C */
+ xfrnum = 3, /* Need three of them by default */
+ g_xfrxla = -1;
+ char * xfrmsg = NULL; /* Message for f.t. display screen */
+#endif /* NOXFER */
+
+#ifdef NOCSETS
+int xfrxla = 0; /* Character-set translation */
+#else
+int xfrxla = 1; /* enabled or disabled */
+#endif /* NOCSETS */
+
+#ifndef NOXFER
+int epktflg = 0; /* E-PACKET command active */
+
+int capas = 9, /* Position of Capabilities */
+ lpcapb = 2, /* Long Packet capability */
+ lpcapr = 1, /* requested */
+ lpcapu = 0, /* used */
+ swcapb = 4, /* Sliding Window capability */
+ swcapr = 1, /* requested (allowed) */
+ swcapu = 0, /* used */
+ atcapb = 8, /* Attribute capability */
+ atcapr = 1, /* requested */
+ atcapu = 0, /* used */
+ rscapb = 16, /* RESEND capability */
+ rscapr = 1, /* requested by default */
+ rscapu = 0, /* used */
+ lscapb = 32, /* Locking Shift capability */
+ lscapr = 1, /* requested by default */
+ lscapu = 0; /* used */
+
+/* Flags for whether to use particular attributes */
+
+int atenci = 1, /* Encoding in */
+ atenco = 1, /* Encoding out */
+ atdati = 1, /* Date in */
+ atdato = 1, /* Date out */
+ atdisi = 1, /* Disposition in/out */
+ atdiso = 1,
+ atleni = 1, /* Length in/out (both kinds) */
+ atleno = 1,
+ atblki = 1, /* Blocksize in/out */
+ atblko = 1,
+ attypi = 1, /* File type in/out */
+ attypo = 1,
+ atsidi = 1, /* System ID in/out */
+ atsido = 1,
+ atsysi = 1, /* System-dependent parameters in/out */
+ atsyso = 1;
+
+int dispos = 0; /* Disposition */
+
+#ifdef CK_PERMS
+int atlpri = 1,
+ atlpro = 1,
+ atgpri = 1,
+ atgpro = 1;
+#endif /* CK_PERMS */
+
+int atfrmi = 1, /* Record Format in/out */
+ atfrmo = 1;
+
+#ifdef STRATUS
+int atcrei = 1, /* Creator ID in/out */
+ atcreo = 1,
+ atacti = 1, /* Account in/out */
+ atacto = 1;
+#endif /* STRATUS */
+
+int sprmlen = -1; /* Send/Receive protocol parameter */
+int rprmlen = -1; /* string length limits */
+int sendipkts = 1; /* Send I packets */
+
+CHAR padch = MYPADC, /* Padding character to send */
+ mypadc = MYPADC, /* Padding character to ask for */
+ seol = MYEOL, /* End-Of-Line character to send */
+ eol = MYEOL, /* End-Of-Line character to look for */
+ ctlq = CTLQ, /* Control prefix in incoming data */
+ myctlq = CTLQ, /* Outbound control character prefix */
+ myrptq = MYRPTQ; /* Repeat prefix I want to use */
+
+int rptmin = 3; /* Repeat-count minimum */
+
+int usepipes = 0, /* Used for xfer to/from pipes */
+ g_usepipes = -1;
+
+char * filefile = NULL; /* File containing list of filenames */
+/* CD message filename list */
+
+char whoareu[16] = { NUL, NUL }; /* System ID of other Kermit */
+int sysindex = -1; /* and index to its system ID struct */
+int myindex = -1;
+int wearealike = 0; /* 2 Kermits have compatible sysids */
+char * cksysid = /* My system ID */
+#ifdef UNIX
+ "U1"
+#else
+#ifdef VMS
+ "D7"
+#else
+#ifdef OSK
+ "UD"
+#else
+#ifdef AMIGA
+ "L3"
+#else
+#ifdef MAC
+ "A3"
+#else
+#ifdef OS2
+#ifdef NT
+ "UN"
+#else /* NT */
+ "UO"
+#endif /* NT */
+#else /* OS2 */
+#ifdef datageneral
+ "F3"
+#else
+#ifdef GEMDOS
+ "K2"
+#else
+#ifdef STRATUS
+ "MV"
+#else
+ ""
+#endif /* STRATUS */
+#endif /* GEMDOS */
+#endif /* datageneral */
+#endif /* OS2 */
+#endif /* MAC */
+#endif /* AMIGA */
+#endif /* OSK */
+#endif /* VMS */
+#endif /* UNIX */
+ ;
+
+int oopts = -1; /* O-Packet Options */
+int omode = -1; /* O-Packet Transfer Mode */
+int oname = -1; /* O-Packet Filename Options */
+int opath = -1; /* O-Packet Pathname Options */
+
+struct zattr iattr; /* Incoming file attributes */
+
+#ifdef VMS
+/* VMS labeled file default options - name only. */
+int lf_opts = LBL_NAM;
+#else
+#ifdef OS2
+/* OS/2 labeled file default options, all attributes but archived. */
+unsigned long int lf_opts = LBL_EXT|LBL_HID|LBL_RO|LBL_SYS;
+#else
+int lf_opts = 0;
+#endif /* OS2 */
+#endif /* VMS */
+
+/* Packet-related variables */
+
+int pktnum = 0, /* Current packet number */
+ sndtyp = 0, /* Type of packet just sent */
+ rcvtyp = 0, /* Type of packet just received */
+ rsn, /* Received packet sequence number */
+ rln, /* Received packet length */
+ size, /* Current size of output pkt data */
+ osize, /* Previous output packet data size */
+ maxsize, /* Max size for building data field */
+ spktl = 0, /* Length packet being sent */
+ rpktl = 0, /* Length of packet just received */
+ pktpaus = 0, /* Interpacket pause interval, msec */
+ rprintf, /* REMOTE PRINT flag */
+ rmailf, /* MAIL flag */
+ xferstat = -1, /* Status of last transaction */
+ filestatus = 0; /* Status of last file transfer */
+
+CHAR pktmsgbuf[PKTMSGLEN+1];
+CHAR *epktmsg = pktmsgbuf;
+
+#ifdef pdp11
+int srvcmdlen = MAXRP; /* srvcmd buffer length */
+#else
+#ifdef DYNAMIC
+int srvcmdlen = MAXRP;
+#else
+int srvcmdlen = 0;
+#endif /* DYNAMIC */
+#endif /* pdp11 */
+
+CHAR
+#ifdef pdp11
+ srvcmdbuf[MAXRP+4],
+ *srvcmd = srvcmdbuf,
+#else
+#ifdef DYNAMIC
+ *srvcmd = (CHAR *)0, /* Where to decode server command */
+#else
+ srvcmdbuf[MAXRP+4],
+ *srvcmd = srvcmdbuf,
+#endif /* DYNAMIC */
+#endif /* pdp11 */
+ padbuf[96], /* Buffer for send-padding */
+ *recpkt,
+ *rdatap, /* Pointer to received packet data */
+ *data = (CHAR *)0, /* Pointer to send-packet data */
+ *srvptr, /* Pointer to srvcmd */
+ mystch = SOH, /* Outbound packet-start character */
+ stchr = SOH; /* Incoming packet-start character */
+
+/* File-related variables */
+
+#ifndef NOMSEND /* Multiple SEND */
+struct filelist * filehead = NULL; /* SEND list */
+struct filelist * filetail = NULL;
+struct filelist * filenext = NULL;
+int addlist = 0;
+#endif /* NOMSEND */
+
+char filnam[CKMAXPATH + 1]; /* Name of current file. */
+char ofilnam[CKMAXPATH + 1]; /* Original name. */
+
+int pipesend = 0; /* Nonzero if sending from pipe */
+#ifdef PIPESEND
+char * sndfilter = NULL; /* Send and receive filters */
+char * rcvfilter = NULL;
+#endif /* PIPESEND */
+
+char ** sndarray = NULL; /* SEND /ARRAY pointer and range */
+#ifndef NOSPL
+int sndxlo = -1, sndxhi = -1, sndxin = -1;
+#endif /* NOSPL */
+#endif /* NOXFER */
+
+#ifndef NOSERVER
+int ngetpath = 0; /* GET search path */
+int fromgetpath = 0;
+char * getpath[MAXGETPATH];
+char * x_user = NULL; /* Server login information */
+char * x_passwd = NULL;
+char * x_acct = NULL;
+#endif /* NOSERVER */
+
+int x_login = 0; /* Login required */
+int x_logged = 0; /* User is logged in */
+
+extern int timelimit;
+
+#ifdef CK_LOGIN
+int logintimo = 300; /* Login timeout */
+char * userfile = NULL; /* Forbidden user file */
+#endif /* CK_LOGIN */
+#ifdef IKSD
+char * anonfile = NULL; /* Anonymous login init file */
+char * anonroot = NULL; /* Anonymous file-system root */
+int iks_timo = 300; /* 5 minutes idle timo */
+int iks_retry = 3; /* 3 attempts at login */
+#endif /* IKSD */
+
+#ifdef CKSYSLOG
+extern VOID zsyslog();
+extern int ckxlogging, ckxsyslog;
+#endif /* CKSYSLOG */
+
+int nzxopts = 0; /* Options for nzxpand() */
+int nfils = 0; /* Number of files in file group */
+long fsize = 0L; /* Size of current file */
+#ifdef UNIX
+int wildxpand = 0; /* Who expands wildcards */
+#else /* UNIX */
+#ifdef STRATUS
+int wildxpand = 1;
+#endif /* STRATUS */
+#endif /* UNIX */
+#ifdef UNIXOROSK
+int matchdot = 0; /* Whether to match dot files */
+#else
+int matchdot = 1;
+#endif /* UNIXOROSK */
+int matchfifo = 0; /* Whether to match FIFO "files" */
+int clfils = 0; /* Flag for command-line files */
+int stayflg = 0; /* Flag for "stay", i.e. "-S" */
+int xfinish = 0; /* Flag for FINISH = EXIT */
+long ztusec = -1L; /* Used with ztime() */
+long ztmsec = -1L; /* Ditto */
+
+/* Communication device / connection variables */
+
+char ttname[TTNAMLEN+1]; /* Name of communication device */
+
+#ifdef MAC
+int connected = 0; /* True if connected */
+int startconnected; /* initial state of connected */
+#endif /* MAC */
+
+long speed = -1L; /* Communication device speed */
+int wasclosed = 0; /* Connection was just closed */
+int whyclosed = WC_REMO; /* why it was closed */
+int qnxportlock = 0; /* QNX port locking on/off */
+
+#ifndef CLSONDISC
+#define CLSONDISC 0
+#endif /* CLSONDISC */
+
+int cxflow[CXT_MAX+1]; /* See initflow() */
+
+#ifndef NOSHOW
+char * floname[] = { /* Flow control names */
+ "none", "xon/xoff", "rts/cts", "dtr/cd", "etx/ack", "string",
+ "xxx1", "xxx2", "dtr/cts", "keep", "auto"
+};
+int nfloname = (sizeof(floname) / sizeof(char *));
+
+char * cxname[] = { /* Connection type names */
+ "remote", "direct-serial", "modem", "tcp/ip", "x.25", "decnet",
+ "lat", "netbios", "named-pipe", "ssh", "pipe"
+};
+int ncxname = (sizeof(cxname) / sizeof(char *));
+#endif /* NOSHOW */
+
+int parity = DEFPAR, /* Parity specified, 0,'e','o',etc */
+ hwparity = 0, /* Hardware parity for serial port */
+ stopbits = -1, /* Stop bits for serial port */
+ clsondisc = CLSONDISC, /* Serial port close on disconnect */
+ autopar = 0, /* Automatic parity change flag */
+ sosi = 0, /* Shift-In/Out flag */
+ flow = 0, /* Flow control (see initflow()) */
+ autoflow = 1, /* Automatic flow control */
+ turn = 0, /* Line turnaround handshake flag */
+ turnch = XON, /* Line turnaround character */
+ duplex = 0, /* Duplex, full by default */
+ escape = DFESC, /* Escape character for connect */
+ ckdelay = DDELAY, /* Initial delay before sending */
+ tnlm = 0; /* Terminal newline mode */
+
+/* Networks for SET HOST */
+
+#ifdef BIGBUFOK
+#define MYHOSTL 1024
+#else
+#define MYHOSTL 100
+#endif /* BIGBUFOK */
+
+char myhost[MYHOSTL]; /* Local host name */
+int network = 0; /* Network vs serial connection */
+int inserver = 0; /* Running as an Internet server */
+int isguest = 0; /* User is anonymous */
+char * clienthost = NULL; /* Peer host name or address */
+int tcp_incoming = 0; /* Incoming TCP connection? */
+
+#ifdef NETCONN
+#ifdef TCPSOCKET
+int nettype = NET_TCPB; /* Default network type */
+#else
+#ifdef SUNX25
+int nettype = NET_SX25;
+#else
+#ifdef IBMX25
+int nettype = NET_IX25;
+#else
+#ifdef HPX25
+int nettype = NET_HX25;
+#else
+#ifdef STRATUSX25
+int nettype = NET_VX25;
+#else
+#ifdef DECNET
+int nettype = NET_DEC;
+#else
+#ifdef SUPERLAT
+int nettype = NET_SLAT;
+#else
+int nettype = NET_NONE;
+#endif /* SUPERLAT */
+#endif /* DECNET */
+#endif /* STRATUSX25 */
+#endif /* HPX25 */
+#endif /* IBMX25 */
+#endif /* SUNX25 */
+#endif /* TCPSOCKET */
+#else /* NETCONN */
+int nettype = NET_NONE;
+#endif /* NETCONN */
+
+#ifdef ANYX25
+int revcall = 0; /* X.25 reverse call not selected */
+int closgr = -1; /* X.25 closed user group */
+int cudata = 0; /* X.25 call user data not specified */
+char udata[MAXCUDATA]; /* X.25 call user data */
+
+#ifdef IBMX25
+/*
+ I was unable to find any pre-defined MAX values for x25 addresses - the
+ addresses that I've seen have been around 10-12 characters 32 is probably
+ enough, 64 is hopefully safe for everyone.
+*/
+ x25addr_t local_nua = {'\0'}; /* local x.25 address */
+ x25addr_t remote_nua = {'\0'}; /* remote x.25 address */
+ char x25name[32] = {'\0'}; /* x25 device name, sx25a0 or sx25a1 */
+ char x25dev[64] = "/dev/x25pkt"; /* x25 device in /dev */
+ int x25port = 0; /* port used for X.25 - AIX only */
+#endif /* IBMX25 */
+
+#ifndef IBMX25
+/*
+ This condition is unrelated to the above IBMX25 condition.
+ IBM X.25 doesn't have PAD support.
+*/
+ CHAR padparms[MAXPADPARMS+1]; /* X.3 parameters */
+#endif /* IBMX25 */
+#endif /* ANYX25 */
+
+/* Other items */
+
+int isinterrupted = 0; /* Used in exception handling */
+int what = W_INIT; /* What I am doing */
+int lastxfer = 0; /* Last transfer (send or receive) */
+
+extern int mdmtyp; /* Modem (/network) type */
+
+#ifdef NT
+extern int StartedFromDialer;
+#ifdef NTSIG
+extern int TlsIndex;
+#endif /* NTSIG */
+#ifdef NTASM
+unsigned long ESPToRestore; /* Ditto */
+#endif /* NTASM */
+#endif /* NT */
+
+#ifdef OS2PM
+int os2pm = 0; /* OS/2 Presentation Manager flag */
+#endif /* OS2PM */
+
+/* Terminal screen size, if known, -1 means unknown. */
+
+#ifdef OS2
+#include "ckocon.h"
+#ifdef KUI
+int tt_rows[VNUM] = {24,24,25,1}; /* Rows (height) */
+int tt_cols[VNUM] = {80,80,80,80}; /* Columns (width) */
+int cmd_rows = 24, cmd_cols = 80; /* Command/console screen dimensions */
+#else /* KUI */
+int tt_rows[VNUM] = {-1,24,25,1}; /* Rows (height) */
+int tt_cols[VNUM] = {-1,80,80,80}; /* Columns (width) */
+int cmd_rows = -1, cmd_cols = -1; /* Command/console screen dimensions */
+#endif /* KUI */
+int k95stdio = 0; /* Stdio threads */
+int tt_bell = XYB_AUD | XYB_SYS; /* BELL AUDIBLE (system sounds) */
+#else /* OS2 */
+int tt_rows = -1; /* Rows (height) */
+int tt_cols = -1; /* Columns (width) */
+int cmd_rows = 24, cmd_cols = 80; /* Command/console screen dimensions */
+int tt_bell = XYB_AUD; /* BELL ON */
+#endif /* OS2 */
+
+int tt_print = 0; /* Transparent print disabled */
+int tt_escape = 1; /* Escaping back is enabled */
+int tt_scroll = 1; /* Scrolling operations are enabled */
+
+int tn_exit = 0; /* Exit on disconnect */
+
+int exitonclose = 0; /* Exit on close */
+int exithangup = 1; /* Hangup on exit */
+int haveline = 0; /* SET LINE or SET HOST in effect */
+int tlevel = -1; /* Take-file command level */
+int hints = 1; /* Whether to give hints */
+
+#ifdef NOLOCAL
+int remonly = 1; /* Remote-mode-only advisory (-R) */
+int nolocal = 1; /* Remote-only strictly enforced */
+#else
+int remonly = 0;
+int nolocal = 0;
+int cx_status = 0; /* CONNECT return status */
+#endif /* NOLOCAL */
+
+#ifndef NOSPL
+extern int cmdlvl; /* Command level */
+extern int maclvl; /* Macro invocation level */
+#endif /* NOSPL */
+
+int protocol = PROTO_K; /* File transfer protocol = Kermit */
+
+#ifdef NEWDEFAULTS
+int prefixing = PX_CAU;
+#else
+int prefixing = PX_ALL;
+#endif /* NEWDEFAULTS */
+
+extern short ctlp[]; /* Control-prefix table */
+
+int carrier = CAR_AUT; /* Pay attention to carrier signal */
+int cdtimo = 0; /* Carrier wait timeout */
+int xitsta = GOOD_EXIT; /* Program exit status */
+
+#ifdef VMS /* Default filename collision action */
+int fncact = XYFX_X; /* REPLACE for VMS */
+#else
+int fncact = XYFX_B; /* BACKUP for everybody else */
+#endif /* VMS */
+
+int fncsav = -1; /* For saving & restoring the above */
+int bgset = -1; /* BACKGROUND mode set explicitly */
+
+int cmdint = 1; /* Interrupts are allowed */
+#ifdef UNIX
+int xsuspend = DFSUSP; /* Whether SUSPEND command, etc, */
+#else /* is to be allowed. */
+int xsuspend = 0;
+#endif /* UNIX */
+
+/* Statistics variables */
+
+long filcnt, /* Number of files in transaction */
+ filrej, /* Number of files rejected in transaction */
+ flci, /* Characters from line, current file */
+ flco, /* Chars to line, current file */
+ tlci, /* Chars from line in transaction */
+ tlco, /* Chars to line in transaction */
+ ffc, /* Chars to/from current file */
+ tfc, /* Chars to/from files in transaction */
+ cps = 0L, /* Chars/sec last transfer */
+ peakcps = 0L, /* Peak chars/sec last transfer */
+ ccu, /* Control chars unprefixed in transaction */
+ ccp, /* Control chars prefixed in transaction */
+ rptn; /* Repeated characters compressed */
+
+int tsecs = 0; /* Seconds for transaction */
+int fsecs = 0; /* Per-file timer */
+
+#ifdef GFTIMER
+CKFLOAT
+ fpfsecs = 0.0, /* Floating point per-file timer */
+ fptsecs = 0.0; /* and per-transaction timer */
+#endif /* GFTIMER */
+
+/* Flags */
+
+int deblog = 0, /* Debug log is open */
+ debok = 1, /* Debug log is not disabled */
+ debxlen = 54, /* Default length for debug strings */
+ debses = 0, /* Flag for DEBUG SESSION */
+ debtim = 0, /* Include timestamp in debug log */
+ pktlog = 0, /* Flag for packet logging */
+ seslog = 0, /* Session logging */
+ dialog = 0, /* DIAL logging */
+ tralog = 0, /* Transaction logging */
+ tlogfmt = 1, /* Transaction log format (verbose) */
+ tlogsep = (int)',', /* Transaction log field separator */
+ displa = 0, /* File transfer display on/off */
+ stdouf = 0, /* Flag for output to stdout */
+ stdinf = 0, /* Flag for input from stdin */
+ xflg = 0, /* Flag for X instead of F packet */
+ hcflg = 0, /* Doing Host command */
+ dest = DEST_D, /* Destination for packet data */
+ zchkod = 0, /* zchko() should work for dirs too? */
+ zchkid = 0, /* zchki() should work for dirs too? */
+
+/* If you change this, also see struct ptab above... */
+
+#ifdef OS2 /* Flag for file name conversion */
+ fncnv = XYFN_L, /* Default is Literal in OS/2, */
+ f_save = XYFN_L, /* (saved copy of same) */
+#else
+ fncnv = XYFN_C, /* elsewhere Convert them */
+ f_save = XYFN_C, /* (ditto) */
+#endif /* OS2 */
+
+ fnspath = PATH_OFF, /* Send file path */
+ fnrpath = PATH_AUTO, /* Receive file path */
+ fackpath = 1, /* Send back path in ACK to F */
+ binary = XYFT_B, /* Default file transfer mode */
+ b_save = XYFT_B, /* Saved file mode */
+ eofmethod = 0, /* EOF detection method (length) */
+
+#ifdef OS2
+ cursor_save = -1, /* Cursor state */
+#endif /* OS2 */
+
+ xfermode = XMODE_A, /* Transfer mode, manual or auto */
+ xfiletype = -1, /* Transfer only text (or binary) */
+ recursive = 0, /* Recursive directory traversal */
+ nolinks = 2, /* Don't follow symbolic links */
+ skipbup = 0, /* Skip backup files when sending */
+ sendmode = SM_SEND, /* Which type of SEND operation */
+ slostart = 1, /* Slow start (grow packet lengths) */
+ cmask = 0377, /* CONNECT (terminal) byte mask */
+ fmask = 0377, /* File byte mask */
+ ckwarn = 0, /* Flag for file warning */
+ quiet = 0, /* Be quiet during file transfer */
+ local = 0, /* 1 = local mode, 0 = remote mode */
+ cxtype = CXT_REMOTE, /* Connection type */
+ server = 0, /* Flag for I Am Server */
+ query = 0, /* Flag for Query active */
+ justone = 0, /* Server should do Just One command */
+ urserver = 0, /* Flag for You Are Server */
+ bye_active = 0, /* Flag for BYE command active */
+ diractive = 0, /* Flag for DIRECTORY command active */
+ cdactive = 0, /* Flag for CD command active */
+ cflg = 0, /* Connect before transaction */
+ cnflg = 0, /* Connect after transaction */
+ cxseen = 0, /* Flag for cancelling a file */
+ czseen = 0, /* Flag for cancelling file group */
+ fatalio = 0, /* Flag for fatal i/o error */
+ discard = 0, /* Flag for file to be discarded */
+ keep = SET_AUTO, /* Keep incomplete files = AUTO */
+ unkcs = 1, /* Keep file w/unknown character set */
+#ifdef VMS
+ filepeek = 0, /* Inspection of files */
+#else
+#ifdef datgeneral
+ filepeek = 0,
+#else
+ filepeek = 1,
+#endif /* datageneral */
+#endif /* VMS */
+ nakstate = 0, /* In a state where we can send NAKs */
+ dblchar = -1, /* Character to double when sending */
+ moving = 0, /* MOVE = send, then delete */
+ reliable = SET_AUTO, /* Nonzero if transport is reliable */
+ xreliable = -1,
+ setreliable = 0,
+ urclear = 0, /* Nonzero for clear channel to you */
+ clearrq = SET_AUTO, /* SET CLEARCHANEL value */
+ cleared = 0,
+ streaming = 0, /* Nonzero if streaming is active */
+ streamok = 0, /* Nonzero if streaming negotiated */
+ streamrq = SET_AUTO, /* SET STREAMING value */
+ streamed = -1; /* Whether we streamed last time */
+
+char * snd_move = NULL; /* Move file after sending it */
+char * snd_rename = NULL; /* Rename file after sending it */
+char * rcv_move = NULL; /* Move file after receiving it */
+char * rcv_rename = NULL; /* Rename file after receiving it */
+
+char * g_snd_move = NULL;
+char * g_snd_rename = NULL;
+char * g_rcv_move = NULL;
+char * g_rcv_rename = NULL;
+
+long sendstart = 0L; /* SEND start position */
+long calibrate = 0L; /* Nonzero if calibration run */
+
+#ifdef CK_TRIGGER
+char *tt_trigger[TRIGGERS] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL };
+CHAR *tt_trmatch[TRIGGERS] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL };
+char *triggerval = NULL;
+#endif /* CK_TRIGGER */
+
+int ckxlogging = 0; /* Flag for syslogging active */
+int ikdbopen = 0; /* Flag for IKSD database active */
+int dbinited = 0; /* Flag for IKSDB record init'd */
+#ifndef CKSYSLOG
+int ckxsyslog = 0; /* Logging level 0 */
+#else
+#ifdef SYSLOGLEVEL
+int ckxsyslog = SYSLOGLEVEL; /* Logging level specified */
+#else
+int ckxsyslog = SYSLG_DF; /* Default logging level */
+#endif /* SYSLOGLEVEL */
+#endif /* CKSYSLOG */
+
+#ifndef NOHELP
+#ifndef NOCMDL
+_PROTOTYP( VOID iniopthlp, (void) ); /* Command-line help initializer */
+#endif /* NOCMDL */
+#endif /* NOHELP */
+
+_PROTOTYP( VOID getexedir, (void) );
+_PROTOTYP( int putnothing, (char) );
+
+#ifdef IKSD
+_PROTOTYP( VOID doiksdinit, (void) );
+_PROTOTYP( VOID iksdinit, (void) );
+_PROTOTYP( VOID doiklog, (void) );
+_PROTOTYP( int dbinit, (void) );
+#endif /* IKSD */
+
+/* Variables passed from command parser to protocol module */
+
+#ifndef NOSPL
+#ifndef NOICP
+#ifdef CK_APC
+_PROTOTYP( VOID apconect, (void) );
+#endif /* CK_APC */
+#ifdef OS2
+extern int initvik;
+#endif /* OS2 */
+#endif /* NOICP */
+#endif /* NOSPL */
+char *clcmds = NULL; /* Pointer to command-line commands */
+
+#ifndef NOSETKEY
+extern KEY *keymap;
+extern MACRO *macrotab;
+#endif /* NOSETKEY */
+
+#ifndef NOPUSH
+int nopush = 0; /* PUSH enabled */
+#else
+int nopush = 1; /* PUSH disabled */
+#endif /* NOPUSH */
+
+CHAR sstate = (CHAR) 0; /* Starting state for automaton */
+CHAR zstate = (CHAR) 0; /* For remembering sstate */
+char * printername = NULL; /* NULL if printer not redirected */
+int printpipe = 0; /* For SET PRINTER */
+int noprinter = 0;
+
+#ifndef NOXFER
+char *cmarg = ""; /* Pointer to command data */
+char *cmarg2 = ""; /* Pointer to 2nd command data */
+char **cmlist; /* Pointer to file list in argv */
+
+#ifdef CK_AUTODL /* Autodownload */
+int autodl = 1; /* Enabled by default */
+#else
+int autodl = 0; /* (or if not implemented). */
+#endif /* CK_AUTODL */
+int adl_err = 1; /* 1 = stop on error */
+#ifdef KUI
+int adl_ask = 1; /* 1 = file dialog on autodownload */
+#else
+int adl_ask = 0; /* 0 = no file dialog */
+#endif /* KUI */
+#ifdef OS2 /* AUTODOWNLOAD parameters */
+int adl_kmode = ADL_PACK, /* Match Packet to signal download */
+ adl_zmode = ADL_PACK;
+char * adl_kstr = NULL; /* KERMIT Download String */
+char * adl_zstr = NULL; /* ZMODEM Download String */
+#endif /* OS2 */
+
+int remfile = 0, rempipe = 0, remappd = 0; /* REMOTE output redirection */
+char * remdest = NULL;
+
+#ifndef NOSERVER
+/*
+ Server services:
+ 0 = disabled
+ 1 = enabled in local mode
+ 2 = enabled in remote mode
+ 3 = enabled in both local and remote modes
+ only as initial (default) values.
+*/
+int en_xit = 2; /* EXIT */
+int en_cwd = 3; /* CD/CWD */
+int en_cpy = 3; /* COPY */
+int en_del = 2; /* DELETE */
+int en_mkd = 3; /* MKDIR */
+int en_rmd = 2; /* RMDIR */
+int en_dir = 3; /* DIRECTORY */
+int en_fin = 3; /* FINISH */
+int en_get = 3; /* GET */
+#ifndef NOPUSH
+int en_hos = 2; /* HOST enabled */
+#else
+int en_hos = 0; /* HOST disabled */
+#endif /* NOPUSH */
+int en_ren = 3; /* RENAME */
+int en_sen = 3; /* SEND */
+int en_set = 3; /* SET */
+int en_spa = 3; /* SPACE */
+int en_typ = 3; /* TYPE */
+int en_who = 3; /* WHO */
+#ifdef datageneral
+/* Data General AOS/VS can't do this */
+int en_bye = 0; /* BYE */
+#else
+int en_bye = 2; /* PCs in local mode... */
+#endif /* datageneral */
+int en_asg = 3; /* ASSIGN */
+int en_que = 3; /* QUERY */
+int en_ret = 2; /* RETRIEVE */
+int en_mai = 3; /* MAIL */
+int en_pri = 3; /* PRINT */
+int en_ena = 3; /* ENABLE */
+#else
+int en_xit = 0, en_cwd = 0, en_cpy = 0, en_del = 0, en_mkd = 0, en_rmd = 0,
+ en_dir = 0, en_fin = 0, en_get = 0, en_hos = 0, en_ren = 0, en_sen = 0,
+ en_set = 0, en_spa = 0, en_typ = 0, en_who = 0, en_bye = 0, en_asg = 0,
+ en_que = 0, en_ret = 0, en_mai = 0, en_pri = 0, en_ena = 0;
+#endif /* NOSERVER */
+#endif /* NOXFER */
+
+/* Miscellaneous */
+
+char **xargv; /* Global copies of argv */
+int xargc; /* and argc */
+int xargs; /* an immutable copy of argc */
+char *xarg0; /* and of argv[0] */
+char *pipedata; /* Pointer to -P (pipe) data */
+
+extern char *dftty; /* Default tty name from ck?tio.c */
+extern int dfloc; /* Default location: remote/local */
+extern int dfprty; /* Default parity */
+extern int dfflow; /* Default flow control */
+
+#ifdef TNCODE
+extern int tn_deb;
+#endif /* TNCODE */
+/*
+ Buffered file input and output buffers. See getpkt() in ckcfns.c
+ and zoutdump() in the system-dependent file i/o module (usually ck?fio.c).
+*/
+#ifndef DYNAMIC
+/* Now we allocate them dynamically, see getiobs() below. */
+char zinbuffer[INBUFSIZE], zoutbuffer[OBUFSIZE];
+#endif /* DYNAMIC */
+char *zinptr, *zoutptr;
+int zincnt, zoutcnt;
+int zobufsize = OBUFSIZE;
+int zofbuffer = 1;
+int zofblock = 1;
+
+#ifdef SESLIMIT
+int seslimit = 0;
+#endif /* SESLIMIT */
+
+#ifdef CK_AUTHENTICATION
+#include "ckuath.h"
+#endif /* CK_AUTHENTICATION */
+
+_PROTOTYP( int getiobs, (VOID) );
+
+/* M A I N -- C-Kermit main program */
+
+#include <signal.h>
+
+#ifndef NOCCTRAP
+#include <setjmp.h>
+#include "ckcsig.h"
+ckjmpbuf cmjbuf;
+#ifdef GEMDOS /* Special for Atari ST */
+cc_clean(); /* This can't be right? */
+#endif /* GEMDOS */
+#endif /* NOCCTRAP */
+
+#ifndef NOXFER
+/* Info associated with a system ID */
+
+struct sysdata sysidlist[] = { /* Add others as needed... */
+ { "0", "anonymous", 0, NUL, 0, 0, 0 },
+ { "A1", "Apple II", 0, NUL, 0, 0, 3 }, /* fix this */
+ { "A3", "Macintosh", 1, ':', 0, 2, 1 },
+ { "D7", "VMS", 0, ']', 1, 0, 0 },
+ { "DA", "RSTS/E", 0, ']', 1, 0, 3 }, /* (i think...) */
+ { "DB", "RT11", 0, NUL, 1, 0, 3 }, /* (maybe...) */
+ { "F3", "AOS/VS", 1, ':', 0, 0, 2 },
+ { "I1", "VM/CMS", 0, NUL, 0, 0, 0 },
+ { "I2", "MVS/TSO", 0, NUL, 0, 0, 0 },
+ { "I4", "MUSIC", 0, NUL, 0, 0, 0 },
+ { "I7", "CICS", 0, NUL, 0, 0, 0 },
+ { "I9", "MVS/ROSCOE", 0, NUL, 0, 0, 0 },
+ { "K2", "Atari ST", 1, '\\', 1, 0, 3 },
+ { "L3", "Amiga", 1, '/', 1, 0, 2 },
+ { "MV", "Stratus VOS", 1, '>', 0, 1, 0 },
+ { "N3", "Apollo Aegis", 1, '/', 0, 3, 2 },
+ { "U1", "UNIX", 1, '/', 0, 3, 2 },
+ { "U8", "MS-DOS", 1, '\\', 1, 0, 3 },
+ { "UD", "OS-9", 1, '/', 0, 3, 2 },
+ { "UN", "Windows-32", 1, '\\', 1, 2, 3 },
+ { "UO", "OS/2", 1, '\\', 1, 2, 3 }
+};
+static int nxxsysids = (sizeof(sysidlist) / sizeof(struct sysdata));
+
+/* Given a Kermit system ID code, return the associated name string */
+/* and some properties of the filenames... */
+
+char *
+getsysid(s) char * s; { /* Get system-type name */
+ int i;
+ if (!s) return("");
+ for (i = 0; i < nxxsysids; i++)
+ if (!strcmp(sysidlist[i].sid_code,s))
+ return(sysidlist[i].sid_name);
+ return(s);
+}
+
+int
+getsysix(s) char *s; { /* Get system-type index */
+ int i;
+ if (!s) return(-1);
+ for (i = 0; i < nxxsysids; i++)
+ if (!strcmp(sysidlist[i].sid_code,s))
+ return(i);
+ return(-1);
+}
+#endif /* NOXFER */
+
+/* Tell if a pathname is absolute (versus relative) */
+/* This should be parceled out to each of the ck*fio.c modules... */
+int
+isabsolute(path) char * path; {
+ int rc = 0;
+ int x;
+ if (!path)
+ return(0);
+ if (!*path)
+ return(0);
+ x = (int) strlen(path);
+ debug(F111,"isabsolute",path,x);
+#ifdef VMS
+ rc = 0;
+ x = ckindex("[",path,0,0,0); /* 1-based */
+ if (!x)
+ x = ckindex("<",path,0,0,0);
+ debug(F111,"isabsolute left bracket",path,x);
+ if (!x) {
+ x = ckindex(":",path,-1,1,1);
+ if (x)
+ debug(F111,"isabsolute logical",path,x);
+ }
+ if (x > 0)
+ if (path[x] != '.') /* 0-based */
+ rc = 1;
+#else
+#ifdef UNIX
+ if (*path == '/'
+#ifdef DTILDE
+ || *path == '~'
+#endif /* DTILDE */
+ )
+ rc = 1;
+#else
+#ifdef OS2
+ if (*path == '/' || *path == '\\')
+ rc = 1;
+ else if (isalpha(*path) && x > 2)
+ if (*(path+1) == ':' && (*(path +2) == '/' || *(path+2) == '\\'))
+ rc = 1;
+#else
+#ifdef AMIGA
+ if (*path == '/'
+#ifdef DTILDE
+ || *path == '~'
+#endif /* DTILDE */
+ )
+ rc = 1;
+#else
+#ifdef OSK
+ if (*path == '/'
+#ifdef DTILDE
+ || *path == '~'
+#endif /* DTILDE */
+ )
+ rc = 1;
+#else
+#ifdef datageneral
+ if (*path == ':')
+ rc = 1;
+#else
+#ifdef MAC
+ rc = 0; /* Fill in later... */
+#else
+#ifdef STRATUS
+ rc = 0; /* Fill in later... */
+#else
+#ifdef GEMDOS
+ if (*path == '/' || *path == '\\')
+ rc = 1;
+ else if (isalpha(*path) && x > 1)
+ if (*(path+1) == ':')
+ rc = 1;
+#endif /* GEMDOS */
+#endif /* STRATUS */
+#endif /* MAC */
+#endif /* datageneral */
+#endif /* OSK */
+#endif /* AMIGA */
+#endif /* OS2 */
+#endif /* UNIX */
+#endif /* VMS */
+ debug(F101,"isabsolute rc","",rc);
+ return(rc);
+}
+
+/* See if I have direct access to the keyboard */
+
+int
+is_a_tty(n) int n; {
+#ifdef UNIX
+ extern int ttfdflg;
+ if (ttfdflg > 0)
+ return(1);
+#endif /* UNIX */
+#ifdef KUI
+ return 1;
+#else /* KUI */
+#ifdef NT
+ if (isWin95())
+ return(1);
+ else
+ return(_isatty(n));
+#else
+#ifdef IKSD
+ if (inserver)
+ return(1);
+ else
+#endif /* IKSD */
+ return(isatty(n));
+#endif /* NT */
+#endif /* KUI */
+}
+
+#ifndef NOXFER
+VOID
+initxlist() {
+ extern char * sndexcept[], * rcvexcept[];
+ int i;
+ for (i = 0; i < NSNDEXCEPT; i++) {
+ sndexcept[i] = NULL;
+ rcvexcept[i] = NULL;
+ }
+}
+#endif /* NOXFER */
+
+/* Initialize flow control table */
+
+VOID
+initflow() { /* Default values for flow control */
+#ifdef VMS /* for each kind of connection. */
+ /* The VMS telnet terminal driver treats "none" as request to lose chars */
+ cxflow[CXT_REMOTE] = FLO_XONX; /* Remote mode... */
+#else
+#ifdef HPUX
+ /* Ditto for HP-UX */
+ cxflow[CXT_REMOTE] = FLO_XONX; /* Remote mode... */
+#else
+ /* The temptation is to make this one FLO_KEEP but don't!!! */
+ /* It totally wrecks binary-file transfer when coming in via Telnet. */
+ /* In UNIX at least... */
+ cxflow[CXT_REMOTE] = FLO_NONE;
+#endif /* HPUX */
+#endif /* VMS */
+
+#ifdef VMS
+ cxflow[CXT_DIRECT] = FLO_XONX; /* Direct serial connections... */
+#else
+ cxflow[CXT_DIRECT] = FLO_NONE;
+#endif /* VMS */
+
+#ifdef CK_RTSCTS
+ cxflow[CXT_MODEM] = FLO_RTSC; /* Modem connections... */
+#else
+#ifdef VMS
+ cxflow[CXT_MODEM] = FLO_XONX;
+#else
+ cxflow[CXT_MODEM] = FLO_NONE;
+#endif /* VMS */
+#endif /* CK_RTSCTS */
+
+#ifdef VMS
+ cxflow[CXT_TCPIP] = FLO_XONX; /* TCP/IP connections... */
+#else
+ cxflow[CXT_TCPIP] = FLO_NONE;
+#endif /* VMS */
+
+ cxflow[CXT_SSH] = FLO_NONE;
+ cxflow[CXT_X25] = FLO_NONE; /* Other kinds of networks... */
+ cxflow[CXT_DECNET] = FLO_XONX;
+ cxflow[CXT_LAT] = FLO_XONX;
+ cxflow[CXT_NETBIOS] = FLO_NONE;
+ cxflow[CXT_NPIPE] = FLO_NONE;
+ cxflow[CXT_PIPE] = FLO_NONE;
+ flow = cxflow[cxtype]; /* Initial flow setting. */
+ debug(F101,"initflow","",flow);
+}
+
+#ifndef NOXFER
+/* Initialize file transfer protocols */
+
+VOID
+initproto(y, upbstr, uptstr, srvstr, sndbstr, sndtstr, rcvbstr, rcvtstr)
+ int y;
+ char * upbstr, * uptstr, * srvstr, * sndbstr, * sndtstr, * rcvbstr,
+ * rcvtstr;
+/* initproto */ {
+
+ if (upbstr) /* Convert null strings */
+ if (!*upbstr) /* to null pointers */
+ upbstr = NULL;
+
+ if (uptstr) /* Convert null strings */
+ if (!*uptstr) /* to null pointers */
+ uptstr = NULL;
+
+ if (sndbstr)
+ if (!*sndbstr)
+ sndbstr = NULL;
+
+ if (sndtstr)
+ if (!*sndtstr)
+ sndtstr = NULL;
+
+ if (rcvbstr)
+ if (!*rcvbstr)
+ rcvbstr = NULL;
+
+ if (rcvtstr)
+ if (!*rcvtstr)
+ rcvtstr = NULL;
+
+ if (srvstr)
+ if (!*srvstr)
+ srvstr = NULL;
+
+ protocol = y; /* Set protocol */
+
+ if (ptab[protocol].rpktlen > -1)
+ urpsiz = ptab[protocol].rpktlen;
+ if (ptab[protocol].spktflg > -1)
+ spsizf = ptab[protocol].spktflg;
+ if (ptab[protocol].spktlen > -1) {
+ spsiz = ptab[protocol].spktlen;
+ debug(F101,"initproto spsiz","",spsiz);
+ if (spsizf) {
+ spsizr = spmax = spsiz;
+ debug(F101,"initproto spsizr","",spsizr);
+ }
+ }
+ if (ptab[protocol].winsize > -1)
+ wslotr = ptab[protocol].winsize;
+ if (ptab[protocol].prefix > -1)
+ prefixing = ptab[protocol].prefix;
+ if (ptab[protocol].fnca > -1)
+ fncact = ptab[protocol].fnca;
+ if (ptab[protocol].fncn > -1)
+ fncnv = ptab[protocol].fncn;
+ if (ptab[protocol].fnsp > -1)
+ fnspath = ptab[protocol].fnsp;
+ if (ptab[protocol].fnrp > -1)
+ fnrpath = ptab[protocol].fnrp;
+
+ makestr(&(ptab[protocol].h_b_init),upbstr);
+ makestr(&(ptab[protocol].h_t_init),uptstr);
+ makestr(&(ptab[protocol].h_x_init),srvstr);
+ makestr(&(ptab[protocol].p_b_scmd),sndbstr);
+ makestr(&(ptab[protocol].p_t_scmd),sndtstr);
+ makestr(&(ptab[protocol].p_b_rcmd),rcvbstr);
+ makestr(&(ptab[protocol].p_t_rcmd),rcvtstr);
+}
+#endif /* NOXFER */
+
+#ifndef NOCMDL
+VOID
+#ifdef CK_ANSIC
+docmdline(void * threadinfo)
+#else /* CK_ANSIC */
+docmdline(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef NTSIG
+ setint();
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug( F100, "docmdline called with threadinfo block", "", 0 );
+ } else
+ debug( F100, "docmdline threadinfo is NULL","",0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef NT
+#ifdef IKSD
+ if (inserver)
+ setntcreds();
+#endif /* IKSD */
+#endif /* NT */
+#endif /* CK_LOGIN */
+ proto(); /* Take any requested action, then */
+ if (!quiet) /* put cursor back at left margin, */
+ conoll("");
+#ifndef NOLOCAL
+ if (cnflg) { /* Re-connect if requested */
+ cnflg = 0;
+ doconect(0,0);
+ if (ttchk() < 0)
+ dologend();
+ }
+#endif /* NOLOCAL */
+
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+}
+
+void
+ikslogin() {
+ if (sstelnet
+#ifdef IKSD
+ || inserver /* Internet server */
+#endif /* IKSD */
+ ) {
+ char *s;
+ extern int fdispla; /* File-transfer display format */
+ extern char * ikprompt; /* IKSD prompt */
+
+#ifdef IKSD
+#ifdef CK_LOGIN
+ if (inserver) {
+ x_login = 1; /* Login required */
+ x_logged = 0; /* Not logged in yet */
+ cmsetp(ikprompt); /* Set up IKSD's prompt */
+#ifndef NOSERVER
+ en_mai = 0; /* MAIL is disabled */
+ en_who = 0; /* REMOTE WHO is disabled */
+ en_hos = 0; /* REMOTE HOST is disabled */
+ en_pri = 0; /* PRINT is disabled */
+#endif /* NOSERVER */
+ } else {
+ x_login = 0; /* Login not required */
+ x_logged = 1; /* Already logged in */
+ }
+#endif /* CK_LOGIN */
+#endif /* IKSD */
+ nolocal = 1; /* SET LINE/HOST not allowed */
+ fdispla = XYFD_N; /* No file-transfer display */
+#ifdef NETCONN
+ clienthost = ckgetpeer(); /* Get client's hostname */
+ debug(F110,"ikslogin clienthost",clienthost,0);
+#endif /* NETCONN */
+ ztime(&s); /* Get current date and time */
+
+#ifdef CK_LOGIN
+#ifdef CK_AUTHENTICATION
+ if (x_login) {
+ x_logged = ck_tn_auth_valid(); /* Did Telnet Auth succeed? */
+ debug(F111,"ikslogin","x_logged",x_logged);
+
+#ifdef NT
+ /* On Windows 9x, we do not have the ability in */
+ /* zvuser() at present to determine if the name */
+ /* approved in a Kerberos principal is really a */
+ /* an account in the Windows Access Control List */
+ if (isWin95() && x_logged == AUTH_VALID
+ && (ck_tn_authenticated() != AUTHTYPE_NTLM)
+#ifdef CK_SRP
+ && (ck_tn_authenticated() != AUTHTYPE_SRP)
+#endif /* CK_SRP */
+ ) {
+ auth_finished(AUTH_USER);
+ x_logged = AUTH_USER;
+ printf("WARNING:\r\n");
+ printf(
+" The Telnet authentication method used cannot provide for automated\r\n");
+ printf(
+" login to Windows 95 or Windows 98. A password must be entered\r\n");
+ printf(
+" locally to validate your userid. Telnet authentication (and encryption)\r\n"
+ );
+ printf(
+" can be used to validate the host (and protect the privacy of your password.)\
+\r\n"
+ );
+ }
+#endif /* NT */
+
+ if (x_logged == AUTH_VALID) {
+#ifdef CK_SSL
+ if ((ssl_active_flag || tls_active_flag) &&
+ (!TELOPT_U(TELOPT_AUTHENTICATION) ||
+ ck_tn_authenticated() == AUTHTYPE_NULL ||
+ ck_tn_authenticated() == AUTHTYPE_AUTO)
+ ) {
+#ifdef SSL_KRB5
+ if (tls_is_krb5(0)) {
+ printf("Authenticated using Kerberos 5\r\n");
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ extern char szUserNameAuthenticated[];
+ cksyslog(SYSLG_LI, 1, "AUTH_VALID",
+ "Kerberos 5",
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ } else
+#endif /* SSL_KRB5 */
+ {
+ printf("Authenticated using X.509 certificate\r\n");
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ extern char szUserNameAuthenticated[];
+ cksyslog(SYSLG_LI, 1, "AUTH_VALID",
+ "X.509 certificate",
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ }
+ } else
+#endif /* CK_SSL */
+ {
+ printf("Authenticated using %s\r\n",
+ AUTHTYPE_NAME(ck_tn_authenticated()));
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ extern char szUserNameAuthenticated[];
+ cksyslog(SYSLG_LI, 1, "AUTH_VALID",
+ AUTHTYPE_NAME(ck_tn_authenticated()),
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ }
+ zvuser(uidbuf);
+ if (zvpass("") == 0)
+ x_logged = 0;
+ } else if (x_logged == AUTH_USER && !strcmp(uidbuf,"anonymous")) {
+ extern char szUserNameAuthenticated[];
+ zvuser(uidbuf);
+ debug(F110,"szUserNameAuthenticated",
+ szUserNameAuthenticated,0);
+ if (zvpass(szUserNameAuthenticated) == 0) {
+ /* Anonymous login failed. Force a username prompt. */
+ x_logged = 0;
+ uidbuf[0] = '\0';
+ } else {
+#ifdef CK_SSL
+ if ((ssl_active_flag || tls_active_flag) &&
+ (!TELOPT_U(TELOPT_AUTHENTICATION) ||
+ ck_tn_authenticated() == AUTHTYPE_NULL ||
+ ck_tn_authenticated() == AUTHTYPE_AUTO)) {
+ printf("Authenticated using X.509 certificate\r\n");
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ extern char szUserNameAuthenticated[];
+ cksyslog(SYSLG_LI, 1, "AUTH_USER",
+ "X.509 certificate",
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ } else
+#endif /* CK_SSL */
+ {
+ printf("Authenticated using %s\r\n",
+ AUTHTYPE_NAME(ck_tn_authenticated())
+ );
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging) {
+ cksyslog(SYSLG_LI, 1, "AUTH_USER",
+ AUTHTYPE_NAME(ck_tn_authenticated()),
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ }
+ }
+ } else {
+#ifdef CKSYSLOG
+ if (ckxsyslog >= SYSLG_LI && ckxlogging &&
+ x_logged == AUTH_USER) {
+ extern char szUserNameAuthenticated[];
+ cksyslog(SYSLG_LI, 1, "AUTH_USER",
+ AUTHTYPE_NAME(ck_tn_authenticated()),
+ szUserNameAuthenticated
+ );
+ }
+#endif /* CKSYSLOG */
+ x_logged = 0;
+ if (!strcmp("(unknown)",uidbuf)
+#ifdef NT
+ || !stricmp("administrator",uidbuf)
+#ifdef UNIX
+ || !strcmp("root",uidbuf)
+#else
+#ifdef Plan9
+ || !strcmp("root",uidbuf)
+#else
+#ifdef OSK
+ || !strcmp("root",uidbuf)
+#endif /* OSK */
+#endif /* Plan9 */
+#endif /* UNIX */
+#endif /* NT */
+ )
+ uidbuf[0] = '\0';
+ }
+ }
+#endif /* CK_AUTHENTICATION */
+#endif /* CK_LOGIN */
+
+#ifdef IKSD
+ if (inserver)
+ printf("\r\nInternet Kermit Service ready at %s%s\r\n",s,versio);
+ else
+#endif /* IKSD */
+ printf("\r\nC-Kermit ready at %s%s\r\n",s,versio);
+ if (*myhost)
+ printf("%s\r\n", myhost);
+ printf("\r\n");
+ }
+#ifdef CK_LOGIN
+#ifdef IKSD
+ if (inserver) {
+ int i;
+ extern int arg_x; /* Flag for '-x' on command line */
+#ifndef NOSPL
+ extern struct mtab *mactab; /* For ON_LOGIN macro. */
+ extern int nmac;
+#endif /* NOSPL */
+
+ debug(F110,"MAIN clienthost",clienthost,0);
+ srvidl = timelimit = logintimo; /* For interactive login */
+ rtimer(); /* Reset timer */
+ for (i = 0; i < iks_retry && !x_logged; i++) { /* Count retries */
+ if (gtimer() > logintimo)
+ break;
+#ifdef TNCODE
+ tn_wait("login loop");
+ tn_push();
+#endif /* TNCODE */
+ debug(F101,"MAIN LOGIN try","",i);
+ what = W_NOTHING; /* Because proto() changes this */
+
+#ifdef IKS_OPTION
+ debug(F111,"MAIN LOGIN",
+ "TELOPT_SB(TELOPT_KERMIT).kermit.me_start",
+ TELOPT_SB(TELOPT_KERMIT).kermit.me_start
+ );
+ /* Kermit server negotiated */
+ if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start) {
+ debug(F101,"IKSD starting in server mode","",0);
+ arg_x = 1; /* Enter server mode */
+ sstate = 'x';
+#ifdef IKSDPOPBACK
+ justone = 1; /* Execute one command at a time. */
+#endif /* IKSDPOPBACK */
+ proto(); /* Enter protocol if requested. */
+#ifdef NTSIG
+ ck_ih();
+#endif /* NTSIG */
+ if (x_logged) /* Logged in */
+ break;
+ } else { /* Not in client/server mode */
+#endif /* IKS_OPTION */
+ debug(F101,"IKSD starting with Username prompt","",0);
+ x_logged = ckxlogin((CHAR *)uidbuf,NULL,NULL,1);
+ if (sstate) { /* Received a packet at prompt */
+#ifdef IKSDPOPBACK
+ justone = 1; /* Go handle it */
+#endif /* IKSDPOPBACK */
+ proto();
+ }
+ if (!x_logged) { /* In case we are at the prompt... */
+ printf("Access denied.\n");
+ uidbuf[0] = '\0'; /* Forget the name if we have one */
+ }
+#ifdef IKS_OPTION
+ }
+#endif /* IKS_OPTION */
+ }
+ srvidl = timelimit = iks_timo; /* Reset command timelimit */
+ debug(F101,"MAIN LOGIN","",x_logged);
+ if (!x_logged) { /* Logins failed. */
+ if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start)
+ errpkt((CHAR *)"Login Timeout");
+ msleep(500);
+ doexit(BAD_EXIT,0);
+ }
+ what = W_NOTHING; /* Stay in known state */
+#ifndef NOSERVER
+ if (isguest) {
+ en_pri = 0; /* No printing for anonymous users */
+ en_mai = 0; /* No email for anonymous users */
+ en_mkd = 0; /* Or directory creation */
+ en_rmd = 0; /* Or directory removal */
+ en_ena = 0; /* Or ENABLing DISABLEd items */
+ }
+#endif /* NOSERVER */
+
+#ifndef NOSPL
+/*
+ If a macro named "on_login" is defined, execute it. Also remove it from the
+ macro table so the user cannot see what it does. Execute it as part of the
+ iksd.conf file.
+*/
+ if (nmac) { /* Any macros defined? */
+ int k; /* Yes */
+ char * cmd = "on_login"; /* MSVC 2.x compiler error */
+ k = mlook(mactab,cmd,nmac); /* Look up "on_exit" */
+ if (k >= 0) { /* If found, */
+#ifdef IKSDCONF
+ int saved = iksdcf;
+ iksdcf = 0;
+#endif /* IKSDCONF */
+ if (dodo(k,"",0) > -1) /* set it up, */
+ parser(1); /* execute it */
+#ifdef IKSDCONF
+ iksdcf = saved;
+#endif /* IKSDCONF */
+ delmac(cmd,1); /* and delete it */
+ }
+ }
+#endif /* NOSPL */
+ } /* if (inserver) */
+#else /* CK_LOGIN */
+ if (inserver)
+ srvidl = timelimit = iks_timo; /* Set idle limits for IKS */
+#endif /* CK_LOGIN */
+#endif /* IKSD */
+}
+
+VOID
+#ifdef CK_ANSIC
+failcmdline(void * foo)
+#else /* CK_ANSIC */
+failcmdline(foo) VOID * foo;
+#endif /* CK_ANSIC */
+{
+#ifdef GEMDOS
+ cc_clean();
+#endif /* GEMDOS */
+#ifndef NOLOCAL
+ if (cnflg) doconect(0,0); /* connect again if requested. */
+ if (ttchk() < 0)
+ dologend();
+#endif /* NOLOCAL */
+}
+#endif /* NOCMDL */
+
+#ifndef NOICP
+VOID
+#ifdef CK_ANSIC
+dotakeini(void * threadinfo) /* Execute init file. */
+#else /* CK_ANSIC */
+dotakeini(threadinfo) VOID * threadinfo; /* Execute init file. */
+#endif /* CK_ANSIC */
+/* dotakeini */ {
+#ifdef NTSIG
+ setint();
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "dotakeini called with threadinfo block","", 0);
+ } else
+ debug(F100, "dotakeini - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef NT
+#ifdef IKSD
+ if (inserver)
+ setntcreds();
+#endif /* IKSD */
+#endif /* NT */
+#endif /* CK_LOGIN */
+ cmdini(); /* Sets tlevel */
+
+ debug(F111,"dotakeini","inserver",inserver);
+ debug(F111,"dotakeini","sstelnet",sstelnet);
+
+#ifdef COMMENT
+/* Wrong place for this... */
+#ifndef NOXFER
+#ifdef CK_FAST
+ dofast(); /* By now FAST defaults should be OK */
+#endif /* CK_FAST */
+#endif /* NOXFER */
+#endif /* COMMENT */
+
+ doinit(); /* Now do the initialization file */
+ debug(F101,"main executing init file","",tlevel);
+ while (tlevel > -1) {
+ sstate = (CHAR) parser(1); /* Execute one command at a time. */
+ if (sstate) proto(); /* Enter protocol if requested. */
+#ifdef NTSIG
+ ck_ih();
+#endif /* NTSIG */
+ }
+ debug(F101,"main exits init file","",tlevel);
+
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+}
+
+VOID
+#ifdef CK_ANSIC
+failtakeini(void * threadinfo)
+#else /* CK_ANSIC */
+failtakeini(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* failtakeini */ {
+#ifdef GEMDOS
+ cc_clean(); /* Atari: Clean up after ^C-trap. */
+#endif /* GEMDOS */
+ if (!cfilef) {
+ conoll("Interrupt during initialization or command-line processing.");
+ conoll("C-Kermit quitting...");
+ }
+ doexit(BAD_EXIT,-1); /* Exit with bad status. */
+}
+
+VOID
+#ifdef CK_ANSIC
+doicp(void * threadinfo)
+#else /* CK_ANSIC */
+doicp(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* doicp */ {
+#ifdef NTSIG
+ setint();
+ if (threadinfo) { /* Thread local storage... */
+ if (!TlsSetValue(TlsIndex,threadinfo))
+ debug(F101,"doicp TlsSetValue failed","",GetLastError());
+ debug(F101, "doicp a threadinfo block - TlsIndex", "", TlsIndex);
+ } else {
+ debug(F100, "doicp received a null threadinfo", "", 0);
+ }
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef NT
+#ifdef IKSD
+ if (inserver)
+ setntcreds();
+#endif /* IKSD */
+#endif /* NT */
+#endif /* CK_LOGIN */
+#ifdef MAC
+ while (1) {
+ extern char *lfiles; /* Fake pointer cast */
+
+ if (connected) {
+ debug(F100, "doicp: calling macparser", "", 0);
+ sstate = newparser(1, 1, 0L);
+
+ /* ignore null command state */
+ if (sstate == 'n')
+ sstate = '\0';
+
+ if (sstate)
+ proto();
+ } else {
+ /*
+ * process take files the finder gave us.
+ */
+ if ((tlevel == -1) && lfiles)
+ startlfile();
+
+ debug(F100, "doicp: calling parser", "", 0);
+ sstate = (CHAR) parser(0);
+ if (sstate == 'c') /* if MAC connect */
+ sstate = 0;
+ if (sstate)
+ proto();
+ }
+ }
+#else /* Not MAC */
+
+#ifndef NOSPL
+/*
+ If interactive commands were given on the command line (using the
+ -C "command, command, ..." option), assign them to a macro called
+ "cl_commands", then execute the macro and leave it defined for
+ subsequent re-execution if desired.
+*/
+ if (clcmds) { /* Check for -C commands */
+ int x;
+ x = addmac("cl_commands",clcmds); /* Put macro in table */
+ if (x > -1) { /* If successful, */
+ dodo(x,NULL,CF_CMDL); /* set up for macro execution */
+ while (maclvl > -1) { /* Loop getting macro commands. */
+ sstate = (CHAR) parser(1);
+ if (sstate) proto(); /* Enter protocol if requested. */
+#ifdef NTSIG
+ ck_ih();
+#endif /* NTSIG */
+ }
+ }
+ debug(F100,"doicp calling herald","",0);
+ herald();
+ }
+#endif /* NOSPL */
+ while(1) { /* Loop getting commands. */
+ sstate = (CHAR) parser(0);
+ if (sstate) proto(); /* Enter protocol if requested. */
+#ifdef NTSIG
+ ck_ih();
+#endif /* NTSIG */
+ }
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+#endif /* MAC */
+}
+
+VOID
+#ifdef CK_ANSIC
+failicp(void * threadinfo)
+#else /* CK_ANSIC */
+failicp(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+{
+#ifdef GEMDOS
+ cc_clean();
+#endif /* GEMDOS */
+ fixcmd(); /* Pop command stacks, etc. */
+ clcmds = NULL;
+ debug(F100,"ckcmai got interrupt","",0);
+}
+#endif /* NOICP */
+
+#ifndef NOICP
+VOID
+#ifdef CK_ANSIC
+docmdfile(void * threadinfo) /* Execute application file */
+#else /* CK_ANSIC */
+docmdfile(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* docmdfile */ {
+#ifdef NTSIG
+ concb((char)escape);
+ setint();
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ debug(F100, "docmdfile called with threadinfo block","", 0);
+ } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef IKSD
+#ifdef NT
+ if (inserver)
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* CK_LOGIN */
+ debug(F110,"main cmdfil",cmdfil,0);
+#ifndef NOSPL
+ addmac("\\%0",cmdfil);
+#endif /* NOSPL */
+ dotake(cmdfil); /* execute it */
+ while (tlevel > -1) { /* until it runs out. */
+ sstate = parser(1); /* Loop getting commands. */
+ if (sstate) proto(); /* Enter protocol if requested. */
+#ifdef NTSIG
+ ck_ih();
+#endif /* NTSIG */
+ }
+ cfilef = 1; /* Remember we did this */
+
+#ifdef NTSIG
+ ckThreadEnd(threadinfo);
+#endif /* NTSIG */
+ return;
+}
+
+VOID
+#ifdef CK_ANSIC
+failcmdfile(void * threadinfo)
+#else /* CK_ANSIC */
+failcmdfile(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* failcmdfile */ {
+#ifdef GEMDOS
+ cc_clean(); /* Atari: Clean up after ^C-trap. */
+#endif /* GEMDOS */
+ if (!cfilef) {
+ conoll("Interrupt during initialization or command-line processing.");
+ conoll("C-Kermit quitting...");
+ }
+ doexit(BAD_EXIT,-1); /* Exit with bad status. */
+}
+#endif /* NOICP */
+
+#ifndef NOXFER
+VOID
+setprefix(z) int z; { /* Initial control-char prefixing */
+#ifdef CK_SPEED
+ int i, val;
+
+ prefixing = z;
+ ptab[protocol].prefix = prefixing;
+ debug(F101,"setprefix","",prefixing);
+ switch (z) {
+ case PX_ALL: /* All */
+#ifdef COMMENT
+ /* Don't let Clear-Channel be dependent on prefixing */
+ clearrq = 0; /* Turn off clearchannel, fall thru */
+#endif /* COMMENT */
+ case PX_NON: /* None */
+ val = (z == PX_ALL) ? 1 : 0;
+ for (i =
+#ifdef UNPREFIXZERO
+ 0
+#else
+ 1
+#endif /* UNPREFIXZERO */
+ ; i < 32; i++)
+ ctlp[i] = val;
+ for (i = 127; i < 160; i++) ctlp[i] = val;
+ ctlp[(unsigned)255] = val;
+ if (z == PX_NON) { /* These are never safe */
+ if (network) { /* Assume network = telnet or rlogin */
+ ctlp[CR] = 1; /* Prefix CR because of NVT rules */
+ ctlp[XON] = ctlp[XOFF] = 1; /* Because of Telnet server */
+ ctlp[127] = ctlp[255] = 1; /* Telnet IAC */
+ ctlp[mystch] = ctlp[mystch+128] = 1; /* Kermit packet start */
+ } else {
+ ctlp[CR] = ctlp[255] = ctlp[mystch] = ctlp[mystch+128] = 1;
+ if (flow == FLO_XONX) /* Xon/Xoff forces prefixing */
+ ctlp[XON] = ctlp[XOFF] = ctlp[XON+128] = ctlp[XOFF+128] = 1;
+ }
+ }
+ break;
+
+ case PX_CAU: /* Cautious or Minimal */
+#ifdef COMMENT
+ /* Don't let CLEAR-CHANNEL be dependent on Prefixing */
+ clearrq = 0; /* Turn off clearchannel */
+#endif /* COMMENT */
+ case PX_WIL: /* Minimal ("wild") */
+ ctlp[0] = 1; /* Does not include 0 */
+ for (i = 1; i < 32; i++)
+ ctlp[i] = 0;
+ for (i = 127; i < 160; i++)
+ ctlp[i] = 0;
+ ctlp[mystch] = ctlp[mystch+128] = 1; /* Kermit start of packet */
+ if (seol != 13)
+ ctlp[seol] = ctlp[seol+128] = 1; /* Kermit end */
+ ctlp[13] = ctlp[141] = 1; /* In case of TELNET (NVT rules) */
+ ctlp[(unsigned)255] = 1; /* Ditto */
+
+ /* ^D, ^J, ^M, or ^U followed by tilde trigger Rlogin escape */
+
+ ctlp[4] = ctlp[4+128] = 1; /* In case of RLOGIN */
+ ctlp[10] = ctlp[10+128] = 1; /* In case of RLOGIN */
+ ctlp[21] = ctlp[21+128] = 1; /* In case of RLOGIN */
+
+ if (flow == FLO_XONX || /* Xon/Xoff forces prefixing these */
+ prefixing == PX_CAU || /* So does CAUTIOUS */
+ network) /* Networks too... */
+ ctlp[XON] = ctlp[XOFF] = ctlp[XON+128] = ctlp[XOFF+128] = 1;
+ if (prefixing == PX_CAU) { /* Cautious - add some more */
+#ifdef UNPREFIXZERO
+ ctlp[0] = 1;
+#endif /* UNPREFIXZERO */
+ ctlp[3] = ctlp[16] = 1; /* ^C, DLE */
+ ctlp[14] = ctlp[15] = 1; /* SO/SI */
+ ctlp[24] = ctlp[25] = 1; /* VMS might need these */
+ ctlp[26] = ctlp[26+128] = 1; /* UNIX suspend */
+ ctlp[28] = ctlp[29] = ctlp[30] = 1; /* Assorted esc chars */
+ ctlp[131] = ctlp[141] = ctlp[144] = 1; /* and 8-bit versions */
+ ctlp[(unsigned)255] = ctlp[156] = ctlp[157] = ctlp[158] = 1;
+ }
+ break;
+ }
+#endif /* CK_SPEED */
+}
+#endif /* NOXFER */
+
+VOID
+makever() { /* Make version string from pieces */
+ int x, y;
+#ifndef OS2
+#ifndef MAC
+ ck_s_xver = ck_s_ver; /* Fill in C-Kermit version number */
+ ck_l_xver = ck_l_ver; /* for UNIX, VMS, etc. */
+#endif /* MAC */
+#endif /* OS2 */
+ x = strlen(ck_s_name);
+ y = strlen(ck_s_xver);
+ if (y + x + 1 < CKVERLEN) {
+ ckmakmsg(versio,CKVERLEN,ck_s_name," ",ck_s_xver,NULL);
+ } else {
+ ckstrncpy(versio,"C-Kermit",CKVERLEN);
+ return;
+ }
+ x += y + 1;
+ if (*ck_s_who) {
+ y = strlen(ck_s_who);
+ if (CKVERLEN < x + y + 1)
+ return;
+ ckstrncat(versio,"-",CKVERLEN);
+ ckstrncat(versio,ck_s_who,CKVERLEN);
+ }
+ x += y + 1;
+ y = strlen(ck_s_test);
+ if (y > 0 && y + x + 1 < CKVERLEN) {
+ ckstrncat(versio," ",CKVERLEN);
+ ckstrncat(versio,ck_s_test,CKVERLEN);
+ x += y + 1;
+ y = strlen(ck_s_tver);
+ if (y > 0 && y + x + 1 < CKVERLEN) {
+ ckstrncat(versio,".",CKVERLEN);
+ ckstrncat(versio,ck_s_tver,CKVERLEN);
+ x += y + 1;
+ }
+ }
+ y = strlen(ck_s_date);
+ if (y > 0 && y + x + 2 < CKVERLEN) {
+ ckstrncat(versio,", ",CKVERLEN);
+ ckstrncat(versio,ck_s_date,CKVERLEN);
+ }
+ vernum = ck_l_ver;
+ xvernum = ck_l_xver;
+ debug(F110,"Kermit version",versio,0);
+}
+
+union ck_short shortbytes; /* For determining byte order */
+int byteorder = 0; /* 0 = Big Endian; 1 = Little Endian */
+int bigendian = 1;
+/* NOTE: MUST BE 0 or 1 - nothing else */
+
+#ifndef NOSPL
+#define SCRIPTLEN 10240
+#endif /* NOSPL */
+
+#ifdef NETCONN
+#ifndef NOCMDL
+#ifndef NOURL
+VOID
+dourl() {
+ int rc = 0;
+ char * port = NULL;
+ extern int ttnproto;
+ extern struct urldata g_url;
+
+#ifdef COMMENT
+ /* NOTE: debug() doesn't work yet - must use printf's */
+ printf("URL: %s\n",g_url.sav ? g_url.sav : "(none)");
+ printf("Type: %s\n",g_url.svc ? g_url.svc : "(none)");
+ printf("User: %s\n",g_url.usr ? g_url.usr : "(none)");
+ printf("Pass: %s\n",g_url.psw ? g_url.psw : "(none)");
+ printf("Host: %s\n",g_url.hos ? g_url.hos : "(none)");
+/* printf("Port: %s\n",g_url.por ? g_url.por : "(none)"); */
+ printf("Path: %s\n",g_url.pth ? g_url.pth : "(none)");
+#endif /* COMMENT */
+
+ if (!ckstrcmp(g_url.svc,"iksd",-1,0) ||
+ !ckstrcmp(g_url.svc,"kermit",-1,0)) {
+ extern char pwbuf[];
+ extern int pwflg;
+#ifdef OS2
+ extern int pwcrypt;
+#endif /* OS2 */
+
+ if (!g_url.hos) {
+ printf("?Incomplete IKSD URL\n");
+ doexit(BAD_EXIT,1);
+ }
+ if (!g_url.usr)
+ makestr(&g_url.usr,"anonymous");
+ if (!g_url.psw) {
+ char * tmpbuf = NULL;
+ if (!(tmpbuf = (char *)malloc(1024)))
+ fatal("dourl: out of memory");
+ if (!ckstrcmp(g_url.usr,"anonymous",-1,0)) {
+ ckmakmsg(tmpbuf,1024,uidbuf,"@",myhost,NULL);
+ makestr(&g_url.psw,tmpbuf);
+ } else {
+ readpass(" Password:",tmpbuf,1024);
+ makestr(&g_url.psw,tmpbuf);
+ }
+ free(tmpbuf);
+ }
+ port = "kermit";
+ ttnproto = NP_TELNET;
+ nettype = NET_TCPB;
+ mdmtyp = -nettype;
+ local = -1;
+ ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
+ if (g_url.psw) {
+ ckstrncpy(pwbuf,g_url.psw,PWBUFL);
+ pwflg = 1;
+#ifdef OS2
+ pwcrypt = 0;
+#endif /* OS2 */
+ }
+ ckmakmsg(ttname,
+ TTNAMLEN,
+ g_url.hos,
+ ":",
+ g_url.por ? g_url.por : port,
+ NULL
+ );
+ rc = ttopen(ttname,&local,mdmtyp,0);
+ if (rc > -1) {
+ network = 1;
+ exitonclose = 1;
+#ifdef CKLOGDIAL
+ dolognet();
+#endif /* CKLOGDIAL */
+ } else {
+ printf("?Connection failed: %s\n",g_url.sav);
+ doexit(BAD_EXIT,1);
+ }
+ /* Also need to check here for secure authentication already done */
+
+#ifdef NOSPL
+ cflg = 1;
+#else
+ {
+ char * script = NULL;
+ if (!(script = (char *)malloc(SCRIPTLEN)))
+ fatal("dourl: out of memory");
+ if (!g_url.pth) { /* Write the appropriate script */
+ cflg = 1;
+ ckmakxmsg(script,SCRIPTLEN,
+ "if not eq {\\v(authstate)} {user} ",
+ "if not eq {\\v(authstate)} {valid} { ",
+ "remote login ", /* No path */
+ g_url.usr, /* Just log in and CONNECT */
+ " ",
+ g_url.psw,
+ ", if fail exit 1 {IKSD login failed} }",
+ ", connect",
+ NULL,NULL,NULL,NULL);
+ /* printf("CLCMDS 1: %s\n",script); */
+ } else {
+ /* does the path specify a file or a directory? */
+ int len = strlen(g_url.pth);
+ if (ISDIRSEP(g_url.pth[len-1])) {
+ ckmakxmsg(script,SCRIPTLEN, /* Directory name given */
+ "if not eq {\\v(authstate)} {user} \
+if not eq {\\v(authstate)} {valid} { remote login ",
+ g_url.usr,
+ " ",
+ g_url.psw,
+ ", if fail exit 1 {IKSD login failed} }",
+ ", set macro error on",
+ ", set xfer displ brief",
+ ", set xfer bell off",
+ ", remote cd ",
+ g_url.pth,
+ ", lineout directory",
+ ", connect"
+ );
+ /* printf("CLCMDS 2: %s\n",script); */
+ } else {
+ ckmakxmsg(script,SCRIPTLEN, /* Path given, try to GET */
+ "if not eq {\\v(authstate)} {user} \
+if not eq {\\v(authstate)} {valid} { remote login ",
+ g_url.usr,
+ " ",
+ g_url.psw,
+ ", if fail exit 1 {IKSD login failed} }",
+ ", set xfer displ brief",
+ ", set xfer bell off",
+ ", get ",
+ g_url.pth,
+ ", .rc := \\v(status)",
+ ", if open connection bye",
+ ", exit \\m(rc)"
+ );
+ /* printf("CLCMDS 2: %s\n",script); */
+ }
+ }
+ clcmds = script; /* Make this our -C cmdline macro */
+ /* printf("HAVEURL=%d\n",haveurl); */
+ }
+#endif /* NOSPL */
+ } else {
+ if (ckstrcmp(g_url.svc,"telnet",-1,0) &&
+#ifdef SSHBUILTIN
+ ckstrcmp(g_url.svc,"ssh",-1,0) &&
+#endif /* SSHBUILTIN */
+ ckstrcmp(g_url.svc,"ftp",-1,0)) {
+ printf("?Sorry, %s URLs not supported\n",
+ g_url.svc ? g_url.svc : "");
+ doexit(BAD_EXIT,1);
+ }
+ }
+}
+#endif /* NOCMDL */
+#endif /* NETCONN */
+#endif /* NOURL */
+
+/*
+ main()...
+
+ If you get complaints about "main: return type is not blah",
+ define MAINTYPE on the CC command line, e.g. "CFLAGS=-DMAINTYPE=blah"
+ (where "blah" is int, long, or whatever).
+
+ If the complaint is "Attempt to return a value from a function of type void"
+ then add -DMAINISVOID.
+*/
+#ifndef MAINTYPE
+#ifndef MAINISVOID
+#define MAINTYPE int
+#endif /* MAINISVOID */
+#endif /* MAINTYPE */
+
+#ifdef MAINISVOID
+#ifndef MAINTYPE
+#define MAINTYPE void
+#endif /* MAINTYPE */
+#endif /* MAINISVOID */
+
+#ifdef aegis
+/* On the Apollo, intercept main to insert a cleanup handler */
+int
+ckcmai(argc,argv) int argc; char **argv;
+#else
+#ifdef MAC /* Macintosh */
+int
+main (void)
+#else
+#ifdef __GNUC__ /* GCC compiler */
+int
+main(argc,argv) int argc; char **argv;
+#else
+#ifdef __DECC /* DEC Alpha with DEC C compiler */
+#ifdef __ALPHA
+int
+main(argc,argv) int argc; char **argv;
+#else /* DEC C compiler, not Alpha */
+#define MAINISVOID
+VOID
+main(argc,argv) int argc; char **argv;
+#endif /* __ALPHA */
+#else
+#ifdef STRATUS /* Stratus VOS */
+int
+main(argc,argv) int argc; char **argv;
+#else /* K-95 */
+#ifdef OS2
+#ifdef KUI
+#define MAINISVOID
+void
+Main( int argc, char ** argv )
+#else /* KUI */
+#define MAINISVOID
+VOID
+main(argc,argv) int argc; char **argv;
+#endif /* KUI */
+#else /* Not K95 */
+MAINTYPE /* All others... */
+main(argc,argv) int argc; char **argv;
+#endif /* OS2 */
+#endif /* STRATUS */
+#endif /* __DECC */
+#endif /* __GNUC__ */
+#endif /* MAC */
+#endif /* aegis */
+
+/* main */ {
+
+ char *p;
+
+#ifndef NOSETKEY
+ int i;
+#endif /* NOSETKEY */
+
+#ifdef datageneral
+ short *pfha = 016000000036; /* Get around LANG_RT problem */
+ *pfha = (short) 0; /* No user protection fault handler */
+#endif /* datageneral */
+
+/* Do some initialization */
+
+#ifndef MAC
+ xargc = xargs = argc; /* Make global copies of argc */
+ xargv = argv; /* ...and argv. */
+ xarg0 = argv[0];
+#ifdef NT
+ setOSVer();
+#endif /* NT */
+ zstrip(argv[0],&p); /* Get name we were invoked with */
+ makestr(&myname,p);
+ if (!ckstrcmp(myname,"telnet",-1,0)) howcalled = I_AM_TELNET;
+#ifdef CK_KERBEROS
+ else if (!ckstrcmp(myname,"ktelnet",-1,0)) howcalled = I_AM_TELNET;
+#endif /* CK_KERBEROS */
+ else if (!ckstrcmp(myname,"rlogin",-1,0)) howcalled = I_AM_RLOGIN;
+ else if (!ckstrcmp(myname,"iksd",-1,0)) howcalled = I_AM_IKSD;
+#ifdef NEWFTP
+ else if (!ckstrcmp(myname,"ftp",-1,0)) howcalled = I_AM_FTP;
+#endif /* NEWFTP */
+#ifndef NOHTTP
+ else if (!ckstrcmp(myname,"http",-1,0)) howcalled = I_AM_HTTP;
+#endif /* NOHTTP */
+#ifdef OS2
+ else if (!ckstrcmp(myname,"telnet.exe",-1,0)) howcalled = I_AM_TELNET;
+#ifdef SSHBUILTIN
+ else if (!ckstrcmp(myname,"ssh",-1,0)) howcalled = I_AM_SSH;
+ else if (!ckstrcmp(myname,"ssh.exe",-1,0)) howcalled = I_AM_SSH;
+#endif /* SSHBUILTIN */
+#ifdef CK_KERBEROS
+ else if (!ckstrcmp(myname,"ktelnet.exe",-1,0)) howcalled = I_AM_TELNET;
+#endif /* CK_KERBEROS */
+ else if (!ckstrcmp(myname,"rlogin.exe",-1,0)) howcalled = I_AM_RLOGIN;
+#ifdef NT
+ else if (!ckstrcmp(myname,"iksdnt",-1,0)) howcalled = I_AM_IKSD;
+ else if (!ckstrcmp(myname,"iksdnt.exe",-1,0)) howcalled = I_AM_IKSD;
+#endif /* NT */
+#ifdef NEWFTP
+ else if (!ckstrcmp(myname,"ftp.exe",-1,0)) howcalled = I_AM_FTP;
+#endif /* NEWFTP */
+#ifndef NOHTTP
+ else if (!ckstrcmp(myname,"http.exe",-1,0)) howcalled = I_AM_HTTP;
+#endif /* NOHTTP */
+#endif /* OS2 */
+ else if (!ckstrcmp(myname,"kermit-sshsub",-1,0)) howcalled = I_AM_SSHSUB;
+
+#ifndef NOICP
+ cmdini(); /* Must come before prescan */
+ debug(F100,"main cmdini() done","",0);
+#endif /* NOICP */
+ prescan(0); /* Pre-Check for debugging, etc */
+#endif /* MAC */
+ debug(F101,"MAIN feol","",feol);
+ makever(); /* Put together version strings */
+#ifndef NOSETKEY /* Allocate & initialize the keymap */
+ /* This code has been moved to before sysinit() for K95G */
+ if (!(keymap = (KEY *) malloc(sizeof(KEY)*KMSIZE)))
+ fatal("main: no memory for keymap");
+ if (!(macrotab = (MACRO *) malloc(sizeof(MACRO)*KMSIZE)))
+ fatal("main: no memory for macrotab");
+ for (i = 0; i < KMSIZE; i++) {
+ keymap[i] = (KEY) i;
+ macrotab[i] = NULL;
+ }
+#endif /* NOSETKEY */
+
+ shortbytes.x_short = 0xABCD; /* Get Endianness */
+ if (shortbytes.x_char[0] == 0xCD) { /* 0 = Big Endian */
+ byteorder = 1; /* 1 = Little Endian */
+ bigendian = 0; /* (for clarity in programming) */
+ } else {
+ byteorder = 0; /* Big Endian */
+ bigendian = 1;
+ }
+ if (sysinit() < 0) /* System-dependent initialization. */
+ fatal("Can't initialize!");
+ else
+ initflg = 1; /* Remember we did. */
+ debug(F111,"ckcmai myname",myname,howcalled);
+
+#ifdef UNIX
+ getexedir(); /* Compute exedir variable */
+#endif /* UNIX */
+
+#ifdef CKSYSLOG
+#ifdef SYSLOGLEVEL
+/*
+ If built with -DSYSLOGLEVEL on cc command line, this means we always
+ do syslogging at the indicated level.
+*/
+ zsyslog(); /* Open syslog */
+#else /* SYSLOGLEVEL */
+#ifdef IKSD
+ if (inserver)
+ zsyslog(); /* Open syslog */
+#endif /* IKSD */
+#endif /* SYSLOGLEVEL */
+#endif /* CKSYSLOG */
+
+#ifdef CK_KERBEROS
+ ini_kerb(); /* Initialize Kerberos data */
+#endif /* CK_KERBEROS */
+#ifdef CK_SSL
+ ssl_once_init();
+#endif /* CK_SSL */
+#ifdef TNCODE
+ tn_set_modes(); /* Init Telnet Option tables */
+#endif /* TNCODE */
+
+#ifdef CK_TTGWSIZ /* Initialize screen dimensions */
+#ifdef OS2
+ ttgcwsz();
+#else /* OS2 */
+ if (ttgwsiz() > 0) {
+ if (tt_rows > 0 && tt_cols > 0) {
+ cmd_rows = tt_rows;
+ cmd_cols = tt_cols;
+ }
+ }
+#endif /* OS2 */
+#endif /* CK_TTGWSIZ */
+
+#ifndef OS2
+#ifdef TCPSOCKET
+#ifdef CK_SOCKS
+ SOCKSinit(argv[0]); /* Internet relay package... */
+#endif /* CK_SOCKS */
+#endif /* TCPSOCKET */
+#endif /* OS2 */
+
+ initflow(); /* Initialize flow-control table */
+
+#ifndef NOICP
+#ifdef CKFLOAT
+ initfloat(); /* Deduce floating-point precision */
+#endif /* CKFLOAT */
+#endif /* NOICP */
+
+#ifndef NOXFER
+ initxlist(); /* Init exception lists */
+
+#ifdef CK_XYZ /* Initialize protocols... */
+
+#ifdef XYZ_INTERNAL /* XYZMODEM are internal ... */
+
+#ifdef COMMENT
+ /* Can't do this for XMODEM because if filename contains a "C" etc... */
+ initproto(PROTO_X, "rx %s","rx %s", NULL, NULL, NULL, NULL, NULL);
+ initproto(PROTO_XC,"rc %s","rc %s", NULL, NULL, NULL, NULL, NULL);
+#else /* COMMENT */
+ initproto(PROTO_X, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ initproto(PROTO_XC,NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+#endif /* COMMENT */
+ initproto(PROTO_Y, "rb","rb", NULL, NULL, NULL, NULL, NULL);
+ initproto(PROTO_G, "rb","rb", NULL, NULL, NULL, NULL, NULL);
+ initproto(PROTO_Z, "rz","rz", NULL, NULL, NULL, NULL, NULL);
+ initproto(PROTO_K,"kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL);
+ /* Kermit Must be last */
+
+#else /* XYZMODEM are external protocols ... */
+
+ /* s1 s2 s3 s4 s5 s6 s7 */
+ initproto(PROTO_X, "rx %s","rx %s",NULL,"sx %s","sx -a %s","rx %s", "rx %s");
+ initproto(PROTO_XC,"rc %s","rc %s",NULL,"sx %s","sx -a %s","rc %s", "rc %s");
+ initproto(PROTO_Y, "rb", "rb", NULL,"sb %s","sb -a %s","rb", "rb" );
+ initproto(PROTO_G, "rb", "rb", NULL,"sb %s","sb -a %s","rb", "rb" );
+ initproto(PROTO_Z, "rz", "rz", NULL,"sz %s","sz -a %s","rz", "rz" );
+ initproto(PROTO_K, "kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL);
+ /* Kermit must be last */
+
+#endif /* XYZ_INTERNAL */
+
+#else /* No XYZMODEM support */
+
+ initproto(PROTO_K,"kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL);
+
+#endif /* CK_XYZ */
+#endif /* NOXFER */
+
+ connoi(); /* Console interrupts off */
+
+#ifndef NOXFER
+#ifdef OS2
+ /* Initialize Kermit and Zmodem Auto-Download Strings */
+ adl_kstr = strdup("KERMIT READY TO SEND...");
+ adl_zstr = strdup("rz\r");
+#endif /* OS2 */
+
+#ifdef PATTERNS
+ initpat(); /* Initialize filename patterns */
+#endif /* PATTERNS */
+#endif /* NOXFER */
+
+#ifndef NOCSETS
+ initcsets(); /* Initialize character sets */
+#endif /* NOCSETS */
+
+#ifndef NOICP
+#ifdef DFCDMSG
+ makestr(&cdmsgstr,DFCDMSG);
+ makelist(cdmsgstr,cdmsgfile,8); /* Initialize CD message filenames */
+#endif /* DFCDMSG */
+#endif /* NOICP */
+
+ sstate = 0; /* No default start state. */
+#ifdef DYNAMIC
+ if (getiobs() < 0)
+ fatal("Can't allocate i/o buffers!");
+#endif /* DYNAMIC */
+
+#ifndef NOSPL
+#ifndef NORANDOM
+ {
+ char stackdata[256];
+ unsigned int c = 1234, n;
+ /* try to make a random unsigned int to feed srand() */
+#ifndef VMS
+ /* time.h and MultiNet do not get along */
+ c = time(NULL);
+#endif /* VMS */
+ c *= getpid();
+ /* Referenced before set... DELIBERATELY */
+ for (n = 0; n < sizeof(stackdata); n++) /* IGNORE WARNING */
+ c += stackdata[n]; /* DELIBERATELY USED BEFORE SET */
+ srand((unsigned int)c);
+ }
+#endif /* NORANDOM */
+#endif /* NOSPL */
+
+ ckhost(myhost,MYHOSTL); /* Name of local host */
+ debug(F110,"main ckhost",myhost,0);
+#ifdef IKSD
+ if (!inserver) {
+#endif /* IKSD */
+ ckstrncpy(ttname,dftty,TTNAMLEN); /* Set up default tty name. */
+ local = nolocal ? 0 : dfloc; /* And whether it's local or remote. */
+ parity = dfprty; /* Set initial parity, */
+#ifndef NOXFER
+ myindex = getsysix(cksysid); /* System index */
+#endif /* NOXFER */
+ if (local) if (ttopen(ttname,&local,0,0) < 0) {
+#ifndef OS2
+ conol("Can't open device: ");
+ conoll(ttname);
+#endif /* OS2 */
+ local = 0;
+ ckstrncpy(ttname,CTTNAM,TTNAMLEN);
+ }
+ setflow(); /* Set appropriate flow control */
+ speed = ttgspd(); /* Get transmission speed. */
+#ifdef IKSD
+ }
+#endif /* IKSD */
+
+#ifdef ANYX25 /* All X.25 implementations */
+#ifndef IBMX25 /* except IBM have PAD support */
+ initpad(); /* Initialize X.25 PAD */
+#endif /* IBMX25 */
+#endif /* ANYX25 */
+
+#ifndef NOXFER
+ if (inibufs(SBSIZ,RBSIZ) < 0) /* Allocate packet buffers */
+ fatal("Can't allocate packet buffers!");
+#ifndef NOCKSPEED
+ setprefix(prefixing); /* Set up control char prefixing */
+#endif /* NOCKSPEED */
+#endif /* NOXFER */
+
+#ifndef NOICP
+ if (sstelnet
+#ifdef IKSD
+ || inserver
+#endif /* IKSD */
+ ) {
+ int on = 1, x = 0;
+ extern int ckxech, ttnet, ttnproto, cmdmsk;
+#ifdef SO_SNDBUF
+ extern int tcp_sendbuf;
+#endif
+#ifdef SO_RCVBUF
+ extern int tcp_recvbuf;
+#endif
+#ifdef SO_KEEPALIVE
+ extern int tcp_keepalive;
+#endif
+#ifdef SO_LINGER
+ extern int tcp_linger, tcp_linger_tmo;
+#endif /* SO_LINGER */
+#ifdef SO_DONTROUTE
+ extern int tcp_dontroute;
+#endif /* SO_DONTROUTE */
+#ifdef TCP_NODELAY
+ extern int tcp_nodelay;
+#endif /* TCP_NODELAY */
+#ifdef IKSD
+ extern int iklogopen;
+#endif /* IKSD */
+ extern int ttmdm;
+
+#ifdef UNIX
+ if (isatty(0))
+ fatal("Internet Kermit Service cannot be started at a terminal.");
+#endif /* UNIX */
+
+ reliable = xreliable = SET_ON; /* IKSD has reliable connection */
+#ifndef VMS
+ flow = 0; /* No flow control needed */
+#endif /* VMS */
+ bgset = 0; /* Not in background */
+ nopush = 1; /* No external processes */
+ parity = 0; /* 8 bits ... */
+ cmdmsk = 0xff; /* all the way */
+ cmask = 0xff;
+
+#ifdef IKSD
+ if (inserver) { /* If IKSD */
+ doiksdinit(); /* Execute IKSD configuration file */
+ while (tlevel > -1)
+ parser(1); /* (Ignore any file-xfer commands) */
+ iksdcf = 1; /* IKSD c.f. has been processed */
+ }
+ if (!iklogopen) (VOID) doiklog(); /* Open Kermit-specific log */
+#endif /* IKSD */
+
+#ifdef UNIX
+ setbuf(stdout,NULL); /* Don't buffer the output */
+ ckstrncpy(ttname,"0",TTNAMLEN); /* not "/dev/tty"... */
+#endif /* UNIX */
+ local = 0; /* We are in remote mode */
+ ckxech = 1; /* We will echo */
+#ifdef OS2
+ nettype = NET_TCPB; /* So ttopen() treats the connection */
+ mdmtyp = -nettype; /* as a network */
+#endif /* OS2 */
+ debug(F100,"main about to call ttopen() inserver","",0);
+ if (ttopen(ttname,&local,mdmtyp,0) < 0) { /* Open comm channel */
+ fatal("can't initialize i/o");
+ }
+#ifdef OS2
+ local = 0;
+ network = 1; /* Does use networking code */
+#else /* OS2 */
+ network = 0; /* Does not use networking code */
+#endif /* OS2 */
+ ttmdm = -1; /* Does not use a modem */
+ sstelnet = 1; /* Do server-side Telnet negotations */
+ debug(F111,"MAIN","sstelnet",sstelnet);
+ ttnet = NET_TCPB; /* Network type is TCP sockets */
+ ttnproto = NP_TELNET; /* Netword protocol is Telnet */
+#ifdef IKSDB
+ dbinit(); /* Initialize database record */
+#endif /* IKSDB */
+#ifndef OS2
+#ifdef CK_AUTHENTICATION
+ /* Before initializating Telnet/Rlogin negotiations, init Kerberos */
+ ck_auth_init(ckgetpeer(),"","",0);
+#endif /* CK_AUTHENTICATION */
+
+#ifdef NON_BLOCK_IO
+ on = 1;
+ x = socket_ioctl(0,FIONBIO,&on);
+ debug(F101,"main FIONBIO","",x);
+#endif /* NON_BLOCK_IO */
+#ifdef SO_OOBINLINE
+ on = 1;
+ x = setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on));
+ debug(F101,"main SO_OOBINLINE","",x);
+#endif /* SO_OOBINLINE */
+
+#ifndef NOTCPOPTS
+#ifndef datageneral
+#ifdef SOL_SOCKET
+#ifdef TCP_NODELAY
+ no_delay(0,tcp_nodelay);
+#endif /* TCP_NODELAY */
+#ifdef SO_KEEPALIVE
+ keepalive(0,tcp_keepalive);
+#endif /* SO_KEEPALIVE */
+#ifdef SO_LINGER
+ ck_linger(0,tcp_linger, tcp_linger_tmo);
+#endif /* SO_LINGER */
+#ifdef SO_DONTROUTE
+ dontroute(0,tcp_dontroute);
+#endif /* SO_DONTROUTE */
+#ifdef SO_SNDBUF
+ sendbuf(0,tcp_sendbuf);
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ recvbuf(0,tcp_recvbuf);
+#endif /* SO_RCVBUF */
+#endif /* SOL_SOCKET */
+#endif /* datageneral */
+#endif /* NOTCPOPTS */
+
+#ifdef CK_SSL
+ if (ck_ssleay_is_installed()) {
+ if (!ssl_tn_init(SSL_SERVER)) {
+ if (bio_err != NULL) {
+ BIO_printf(bio_err,"do_ssleay_init() failed\r\n");
+ ERR_print_errors(bio_err);
+ } else {
+ fflush(stderr);
+ fprintf(stderr,"do_ssleay_init() failed\r\n");
+ ERR_print_errors_fp(stderr);
+ }
+ switch (ttnproto) {
+ case NP_SSL:
+ case NP_TLS:
+ case NP_SSL_TELNET:
+ case NP_TLS_TELNET:
+ doexit(BAD_EXIT,1);
+ }
+ /* otherwise we will continue to accept the connection */
+ /* without SSL or TLS support unless required. */
+ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ } else {
+ if ( ck_ssl_incoming(0) < 0 ) {
+ doexit(BAD_EXIT,1);
+ }
+ }
+ }
+#endif /* CK_SSL */
+
+#ifdef TNCODE
+ tn_ini(); /* Start Telnet negotiation now */
+#endif /* TNCODE */
+#endif /* OS2 */
+ }
+ debug(F101,"main argc after prescan()","",argc);
+
+ /* Now process any relevant environment variables */
+
+#ifndef NODIAL
+ getdialenv(); /* Dialing */
+#ifdef NETCONN
+ ndinit(); /* Initialize network directory info */
+ getnetenv(); /* Network directories */
+#endif /* NETCONN */
+#endif /* NODIAL */
+
+#ifndef NOXFER
+#ifdef CK_FAST
+ dofast(); /* By now FAST defaults should be OK */
+#endif /* CK_FAST */
+#endif /* NOXFER */
+
+#ifndef NOCMDL
+ ikslogin(); /* IKSD Login and other stuff */
+#ifdef IKSD
+#ifdef NT
+ if ( inserver )
+ setntcreds();
+#endif /* NT */
+#endif /* IKSD */
+#endif /* NOCMDL */
+
+ if (howcalled == I_AM_SSHSUB) {
+ reliable = 1; /* We say the connection is reliable */
+ xreliable = 1; /* And that we said it was */
+ setreliable = 1; /* And pretend the "user" did too */
+ xfinish = 1; /* For REMOTE HELP response */
+ mdmtyp = 0; /* For ttopen() */
+ ckstrncpy(ttname,"0",TTNAMLEN+1); /* Use file descriptor 0 */
+ local = 0; /* And force remote mode */
+ ttopen(ttname,&local,mdmtyp,0); /* Open the "connection" */
+ sstate = 'x'; /* Initial state is Server */
+ proto(); /* Enter protocol */
+ doexit(GOOD_EXIT,xitsta); /* Exit when done */
+ }
+ debug(F111,"howcalled",myname,howcalled);
+
+#ifdef NOCCTRAP
+ dotakeini(0);
+#else /* NOCCTRAP */
+ debug(F100,"main about to cc_execute","",0);
+ setint();
+ cc_execute( ckjaddr(cmjbuf), dotakeini, failtakeini );
+#endif /* NOCCTRAP */
+
+ debug(F111,"main 2 cfilef",cmdfil,cfilef);
+ if (cmdfil[0]) { /* If we got one (see prescan())... */
+#ifdef NOCCTRAP
+ docmdfile(0); /* execute it. */
+#else /* NOCCTRAP */
+ setint();
+ cc_execute( ckjaddr(cmjbuf), docmdfile, failcmdfile );
+#endif /* NOCCTRAP */
+ }
+#ifndef OS2 /* Preserve name so we can delete it */
+ *cmdfil = '\0'; /* Done, nullify the file name */
+#endif /* OS2 */
+#endif /* NOICP */
+
+#ifndef NOCMDL
+/* Look for a UNIX-style command line... */
+
+ what = W_NOTHING;
+
+ debug(F101,"main argc","",argc);
+#ifndef NOHELP
+ iniopthlp(); /* Initialize cmdline arg help */
+#endif /* NOHELP */
+ if (
+#ifdef COMMENT
+ !cfilef &&
+#endif /* COMMENT */
+ argc > 1) { /* Command line arguments? */
+ sstate = (CHAR) cmdlin(); /* Yes, parse. */
+#ifdef NETCONN
+#ifndef NOURL
+ if (haveurl) { /* Was a URL given? */
+ dourl(); /* if so, do it. */
+ }
+#endif /* NOURL */
+#endif /* NETCONN */
+#ifndef NOXFER
+ zstate = sstate; /* Remember sstate around protocol */
+ debug(F101,"main zstate","",zstate);
+#endif /* NOXFER */
+
+#ifndef NOLOCAL
+ if (cflg) { /* Connect first if requested */
+ doconect(0,0);
+ if (ttchk() < 0)
+ dologend();
+ cflg = 0;
+ }
+#endif /* NOLOCAL */
+
+#ifndef NOXFER
+ if (sstate) {
+#ifndef NOLOCAL
+ if (displa) concb((char)escape); /* (for console "interrupts") */
+#endif /* NOLOCAL */
+#ifdef NOCCTRAP
+ docmdline(1);
+#else /* NOCCTRAP */
+ setint();
+ cc_execute( ckjaddr(cmjbuf), docmdline, failcmdline );
+#endif /* NOCCTRAP */
+ }
+#endif /* NOXFER */
+
+#ifndef NOICP
+/*
+ If a command-line action argument was given and -S ("stay") was not given,
+ exit now.
+*/
+ if ((cflg || cnflg || zstate) && !stayflg)
+#endif /* NOICP */
+ doexit(GOOD_EXIT,xitsta); /* Exit with good status */
+
+#ifndef NOLOCAL
+#ifndef NOICP
+ if (local) {
+#ifdef NETCONN
+ if ((cflg || cnflg) && tn_exit && ttchk() < 0)
+ doexit(GOOD_EXIT,xitsta); /* Exit with good status */
+#endif /* NETCONN */
+ if (exitonclose && !network &&
+ (carrier != CAR_OFF && (ttgmdm() & BM_DCD) == 0))
+ doexit(GOOD_EXIT,xitsta); /* Exit with good status */
+ if (exitonclose && network && ttchk() < 0)
+ doexit(GOOD_EXIT,xitsta); /* Exit with good status */
+ }
+#endif /* NOICP */
+#endif /* NOLOCAL */
+ }
+#endif /* NOCMDL */
+
+#ifdef NOICP /* No interactive command parser */
+#ifndef NOCMDL
+ else {
+
+ /* Command-line-only version */
+ fatal("?No command-line options given - type 'kermit -h' for help");
+ }
+#else /* Neither one! */
+ sstate = 'x';
+ justone = 0;
+ proto(); /* So go into server mode */
+ doexit(GOOD_EXIT,xitsta); /* exit with good status */
+
+#endif /* NOCMDL */
+#else /* not NOICP */
+/*
+ If no action requested on command line, or if -S ("stay") was included,
+ enter the interactive command parser.
+*/
+ if (!clcmds)
+ herald(); /* Display program herald. */
+
+#ifdef NOCCTRAP
+ debug(F100,"main NOCCTRAP setting interrupt trap","",0);
+ setint(); /* Set up command interrupt traps */
+ doicp(NULL);
+#else /* NOCCTRAP */
+ while (1) {
+ debug(F100,"main setting interrupt trap","",0);
+ setint(); /* Set up command interrupt traps */
+ if (!cc_execute(ckjaddr(cmjbuf), doicp, failicp))
+ break;
+ }
+#endif /* NOCCTRAP */
+#endif /* NOICP */
+#ifndef MAINISVOID
+ return(1);
+#endif /* MAINISVOID */
+}
+
+#ifdef DYNAMIC
+/* Allocate file i/o buffers */
+
+char *zinbuffer = NULL, *zoutbuffer = NULL;
+
+int
+getiobs() {
+ zinbuffer = (char *)malloc(INBUFSIZE);
+ if (!zinbuffer) return(-1);
+ zoutbuffer = (char *)malloc(zobufsize);
+ debug(F101,"zoutbuffer malloc","",zobufsize);
+ if (!zoutbuffer) return(-1);
+ debug(F100,"getiobs ok","",0);
+ return(0);
+}
+#endif /* DYNAMIC */
diff --git a/ckermit-8.0.211/ckcmdb.c b/ckermit-8.0.211/ckcmdb.c
new file mode 100644
index 0000000..de5cebc
--- /dev/null
+++ b/ckermit-8.0.211/ckcmdb.c
@@ -0,0 +1,387 @@
+/*
+ C K C M D B . C -- malloc debugger.
+*/
+
+/*
+ Author: Howie Kaye, Columbia University Center for Computing Activities.
+
+ Copyright (C) 1985, 1999,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+/* Use the real ones in this module! */
+#ifdef malloc
+#undef malloc
+#endif /* malloc */
+#ifdef calloc
+#undef calloc
+#endif /* calloc */
+#ifdef realloc
+#undef realloc
+#endif /* realloc */
+#ifdef free
+#undef free
+#endif /* free */
+
+#include "ckcsym.h"
+#include <stdio.h>
+#include "ckcdeb.h"
+
+#ifdef COHERENT
+_PROTOTYP ( FILE * fdopen, (int, char *) );
+#endif /* COHERENT */
+
+/*
+ memdebug:
+ variable to control memory debugging.
+ if memdebug == 1, then action is always taken.
+ if memdebug == 0, then no action is taken.
+ if memdebug == -1, then the user is asked (works well with gdb).
+*/
+int memdebug = -1;
+int disabled = 0;
+int inited = 0;
+/*
+ To use this package, compile your program with:
+ -Dmalloc=dmalloc -Dfree=dfree =Dcalloc=dcalloc ... -DMDEBUG
+ and then link it with ckcmdb.c.
+*/
+#ifdef MDEBUG
+
+#ifndef M_SIZE_T
+#ifdef NEXT
+#define M_SIZE_T size_t
+#else
+#ifdef SUNOS41
+#define M_SIZE_T unsigned
+#else
+#define M_SIZE_T int
+#endif /* SUNOS41 */
+#endif /* NEXT */
+#endif /* M_SIZE_T */
+
+#ifdef CK_ANSIC
+_PROTOTYP( void free, (void *) );
+_PROTOTYP( void * malloc, (size_t) );
+_PROTOTYP( void * realloc, (void *, size_t) );
+#else
+_PROTOTYP( VOID free, (char *) );
+_PROTOTYP( char * malloc, (M_SIZE_T) );
+_PROTOTYP( char * realloc, (char *, M_SIZE_T) );
+#endif /* NEXT */
+
+_PROTOTYP( VOID m_insert, (char *) );
+_PROTOTYP( int m_delete, (char *) );
+
+_PROTOTYP( char * dmalloc, (int) );
+_PROTOTYP( char * dcalloc, (int, int) );
+_PROTOTYP( char * drealloc, (char *, int) );
+
+_PROTOTYP( char *set_range_check, (char *, int) );
+_PROTOTYP( char *check_range, (char *) );
+_PROTOTYP( static char *maybe_check_range, (char *) );
+
+_PROTOTYP( static VOID maybe_quit, (char *) );
+_PROTOTYP( static int ask, (char *) );
+
+#ifndef min
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#endif /* min */
+#define RANGE "ABCDEFGHIJKLMNOP"
+#define INTSIZE sizeof(int)
+#define LONGSIZE sizeof(long)
+#define RSIZE sizeof(RANGE)
+#define RFRONT min((RSIZE/2),LONGSIZE)
+#define RBACK min((RSIZE-RFRONT),LONGSIZE)
+
+char *
+dmalloc(size) int size; {
+ char *cp;
+
+ cp = malloc(size + RSIZE + INTSIZE);
+ if (cp) {
+ cp = set_range_check(cp, size);
+ m_insert(cp);
+ }
+ return(cp);
+}
+
+char *
+dcalloc(nelem, elsize) int nelem, elsize; {
+ char *cp;
+
+ cp = dmalloc(nelem * elsize);
+ if (cp)
+ memset(cp, 0, nelem * elsize);
+ return(cp);
+}
+
+char *
+drealloc(bp,size) char *bp; int size; {
+ char *cp;
+
+ if (bp == NULL) {
+ maybe_quit("Freeing NULL pointer");
+ } else {
+ m_delete(bp);
+ cp = check_range(bp);
+ }
+ cp = realloc(cp, size + RSIZE + INTSIZE);
+ if (cp) {
+ cp = set_range_check(cp, size);
+ m_insert(cp);
+ }
+ return(cp);
+}
+
+VOID
+dfree(cp) char *cp; {
+ if (cp == NULL)
+ maybe_quit("Freeing NULL pointer");
+ else {
+ switch(m_delete(cp)) {
+ case 0:
+ cp = maybe_check_range(cp);
+ break;
+ case 1:
+ cp = check_range(cp);
+ break;
+ case 2:
+ break;
+ }
+ }
+#ifndef CK_ANSIC
+ return(free(cp));
+#endif /* CK_ANSIC */
+}
+
+char *
+set_range_check(cp,size) char *cp; int size; {
+ register int i;
+ int tmp = size;
+
+ for(i = 0; i < INTSIZE; i++) { /* set the size in the string */
+ cp[i] = tmp & 0xff;
+ tmp >>= 8;
+ }
+ cp += INTSIZE; /* skip the size */
+
+ for(i = 0; i < RFRONT; i++) /* set the front of the range check */
+ cp[i] = RANGE[i]; /* string */
+
+ cp += RFRONT; /* skip the front range check */
+
+ for(i = 0; i < RBACK; i++) /* set the back odf the range check */
+ cp[i+size] = RANGE[i+RFRONT];
+
+ return(cp);
+}
+
+/*
+ Put calls to this routine in your code any place where you want to
+ check whether you've copied too many characters into a malloc'd space.
+*/
+char *
+check_range(cp) char *cp; {
+ register char *bp = cp - RFRONT - INTSIZE;
+ char *xp = bp;
+ register int i;
+ int size = 0;
+
+ for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */
+ size <<= 8;
+ size |= bp[INTSIZE-i-1] & 0xff;
+ }
+ bp += INTSIZE;
+
+ for(i = 0; i < RFRONT; i++) /* check front range check */
+ if (bp[i] != RANGE[i]) {
+ maybe_quit("leftside malloc buffer overrun");
+ break;
+ }
+ bp += RFRONT; /* skip front range check */
+
+ for(i = 0; i < RBACK; i++) /* check back range check */
+ if (bp[i+size] != RANGE[i+RFRONT]) {
+ maybe_quit("rightside malloc buffer overrun");
+ break;
+ }
+ return(xp);
+}
+
+static char *
+maybe_check_range(cp) char *cp; {
+ register char *bp = cp - RFRONT - INTSIZE;
+ char *xp = bp;
+ register int i;
+ int size = 0;
+
+ for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */
+ size <<= 8;
+ size |= bp[INTSIZE-i-1] & 0xff;
+ }
+ bp += INTSIZE;
+
+ for(i = 0; i < RFRONT; i++) /* check front range check */
+ if (bp[i] != RANGE[i]) {
+ return(cp);
+ }
+ bp += RFRONT; /* skip front range check */
+
+ for(i = 0; i < RBACK; i++) /* check back range check */
+ if (bp[i+size] != RANGE[i+RFRONT]) {
+ fprintf(stderr,"rightside malloc buffer overrun\n");
+ abort();
+ break;
+ }
+ return(xp);
+}
+
+#define BUCKETS 10000
+char *m_used[BUCKETS];
+char *m_used2[BUCKETS];
+
+VOID
+m_insert(cp) register char *cp; {
+ register int i;
+
+ if (disabled)
+ return;
+
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used[i] == 0) {
+ m_used[i] = cp;
+ return;
+ }
+ disabled ++;
+}
+
+static VOID
+m_insert2(cp) register char *cp; {
+ register int i;
+
+ if (disabled)
+ return;
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used2[i] == 0) {
+ m_used2[i] = cp;
+ return;
+ }
+ disabled ++;
+}
+
+int
+m_delete(cp) register char *cp; {
+ register int i;
+
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used[i] == cp) {
+ m_used[i] = 0;
+ return(1);
+ }
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used2[i] == cp) {
+ m_used2[i] = 0;
+ return(2);
+ }
+ if (disabled)
+ return(0);
+
+ maybe_quit("Freeing unmalloc'ed pointer");
+ return(0);
+}
+
+VOID
+m_init() {
+ register int i;
+
+ inited = 1;
+ disabled = 0;
+#ifdef NEXT
+ malloc_debug(2+4+8+16);
+#endif /* NEXT */
+
+ for(i = 0; i < BUCKETS; i++)
+ m_used[i] = 0;
+}
+
+VOID
+m_done() {
+ register int i,j=0;
+
+ if (disabled)
+ return;
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used[i] != 0) {
+ if (memdebug) {
+ if (j == 0)
+ fprintf(stderr,"unfree'ed buffers, indices: ");
+ fprintf(stderr,"%d, ", i);
+ j++;
+ }
+ }
+ if (j)
+ fprintf(stderr,"\n");
+ for(i = 0; i < BUCKETS; i++)
+ if (m_used2[i] != 0) {
+ if (memdebug) {
+ if (j == 0)
+ fprintf(stderr,"unfree'ed registered buffers, indices: ");
+ fprintf(stderr,"%d, ", i);
+ j++;
+ }
+ }
+ if (j)
+ fprintf(stderr,"\n");
+ if (j)
+ maybe_quit("Unfree'ed malloc buffers");
+}
+
+VOID
+m_checkranges() {
+ int i;
+
+ for ( i = 0; i < BUCKETS; i++)
+ if (m_used[i])
+ check_range(m_used[i]);
+}
+
+static VOID
+maybe_quit(str) char *str; {
+ debug(F100,"mdebug maybe_quit","",0);
+ if (memdebug == 0)
+ return;
+ fprintf(stderr,"%s\n",str);
+ if (memdebug == 1)
+ abort();
+ if (memdebug == -1)
+ if (ask("Quit? "))
+ abort();
+}
+
+static int
+ask(str) char *str; {
+ char buf[100];
+ FILE *in;
+ int fd;
+
+ fd = dup(fileno(stdin));
+ in = fdopen(fd, "r");
+ while(1) {
+ fprintf(stderr,str);
+ fflush(stderr);
+ if (fgets(buf, 99, in) == NULL) /* EOF? */
+ return(0);
+ if (buf[0] == 'n' || buf[0] == 'N') {
+ fclose(in);
+ return(0);
+ }
+ if (buf[0] == 'y' || buf[0] == 'Y') {
+ fclose(in);
+ return(1);
+ }
+ fprintf(stderr,"please answer y/n.\n");
+ }
+}
+#endif /* MDEBUG */
diff --git a/ckermit-8.0.211/ckcnet.c b/ckermit-8.0.211/ckcnet.c
new file mode 100644
index 0000000..3bae53e
--- /dev/null
+++ b/ckermit-8.0.211/ckcnet.c
@@ -0,0 +1,14205 @@
+char *cknetv = "Network support, 8.0.283, 7 Feb 2004";
+
+/* C K C N E T -- Network support */
+
+/*
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+
+/*
+ REMINDER: Any changes made to this file that other modules depend must
+ also be made to cklnet.c (for VOS) until such time as cklnet.c and this
+ module are merged back together.
+
+ NOTE TO CONTRIBUTORS: This file, and all the other shared (ckc and cku)
+ C-Kermit source files, must be compatible with C preprocessors that support
+ only #ifdef, #else, #endif, #define, and #undef. Please do not use #if,
+ logical operators, or other preprocessor features in this module. Also,
+ don't use any ANSI C constructs except within #ifdef CK_ANSIC..#endif.
+
+ Authors:
+
+ Frank da Cruz (fdc@columbia.edu),
+ Columbia University Academic Information Systems, New York City.
+ Jeffrey E Altman (jaltman@secure-endpoints.com) -- Primary
+ maintainer/developer since about 1996.
+ netopen() routine for TCP/IP originally by Ken Yap, Rochester University
+ (ken@cs.rochester.edu) (no longer at that address).
+ Missing pieces for Excelan sockets library from William Bader.
+ Telnet protocol by Frank da Cruz and Jeffrey Altman.
+ Rlogin protocol by Jeffrey E Altman.
+ SSL support adapted by Jeffrey E Altman from work done by
+ Tim Hudson <tjh@cryptosoft.com> +61 7 32781581
+ TLS support by Jeffrey E Altman.
+ HTTP support by Jeffrey E Altman.
+ TGV MultiNet code by Frank da Cruz.
+ MultiNet code adapted to WIN/TCP by Ray Hunter of TWG.
+ MultiNet code adapted to DEC TCP/IP by Lee Tibbert of DEC and Frank da Cruz.
+ TCP/IP support adapted to IBM TCP/IP 1.2.1,2.0 for OS/2 by Kai Uwe Rommel.
+ CMU-OpenVMS/IP modifications by Mike O'Malley, Digital (DEC).
+ X.25 support by Marcello Frutig, Catholic University,
+ Rio de Janeiro, Brazil (frutig@rnp.impa.br) with fixes from
+ Stefaan Eeckels, Eurokom, Luxembourg.
+ David Lane added support for Stratus VOS X.25 1996.
+ Stephen Riehm added support for IBM AIX X.25 in April 1998.
+ Other contributions as indicated in the code.
+*/
+#define CKCNET_C
+#include "ckcsym.h"
+#include "ckcdeb.h"
+#include "ckcker.h"
+#include "ckcasc.h"
+#ifdef I386IX /* Has to come before ckcnet.h in */
+#include <errno.h> /* this version, but after in others */
+#endif /* I386IX */
+#include "ckcnet.h" /* which includes ckctel.h */
+#ifdef CK_SSL
+#include "ck_ssl.h"
+#endif /* CK_SSL */
+
+#ifdef CK_DNS_SRV
+#ifdef OS2
+#ifdef NT
+#include <wshelper.h>
+#else /* NT */
+/* !Error OS/2 does not support DNS Service Records. */
+#endif /* NT */
+#else /* OS2 */
+#include <arpa/inet.h>
+#ifdef USE_NAMESER_COMPAT
+#include <arpa/nameser_compat.h>
+#endif /* USE_NAMESER_COMPAT */
+#include <arpa/nameser.h>
+#include <resolv.h>
+#ifndef PS2AIX10
+#ifndef BSD4
+#ifndef I386IX
+#ifndef RTAIX
+#include <netdb.h>
+#endif /* RTAIX */
+#endif /* I386IX */
+#endif /* BSD4 */
+#endif /* PS2AIX10 */
+#endif /* OS2 */
+#ifndef T_SRV
+#define T_SRV 33
+#endif /* T_SRV */
+#ifndef T_TXT
+#define T_TXT 16
+#endif /* T_TXT */
+
+/* for old Unixes and friends ... */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif /* MAXHOSTNAMELEN */
+
+#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
+#endif /* CK_DNS_SRV */
+
+#ifdef NONET
+#ifdef TCPIPLIB
+#undef TCPIPLIB
+#endif /* TCPIPLIB */
+#endif /* NONET */
+
+#ifndef NOMHHOST
+#ifdef datageneral
+#define NOMHHOST
+#else
+#ifdef HPUX5WINTCP
+#define NOMHHOST
+#endif /* HPUX5WINTCP */
+#endif /* datageneral */
+#endif /* NOMHHOST */
+
+#ifdef INADDRX
+ struct in_addr inaddrx;
+#endif /* INADDRX */
+
+int ttnet = NET_NONE; /* Network type */
+int ttnproto = NP_DEFAULT; /* Network virtual terminal protocol */
+
+/* 0 = don't lowercase username for Rlogin/Telnet protocol */
+/* nonzero = do lowercase it. Add a SET command if necessary... */
+#ifdef VMS
+int ck_lcname = 1;
+#else
+int ck_lcname = 0;
+#endif /* VMS */
+
+extern int /* External variables */
+ duplex, debses, seslog, sessft, wasclosed,
+ ttyfd, quiet, msgflg, what, nettype, ttmdm;
+#ifdef IKSD
+extern int inserver;
+#endif /* IKSD */
+
+char myipaddr[20] = { '\0' }; /* Global copy of my IP address */
+
+#ifdef NETCONN
+/* Don't need any of this if there is no network support. */
+
+/*
+ NETLEBUF is (must be) defined for those platforms that call this
+ module to do network i/o (e.g. netinc(), nettchk(), etc) rather
+ than doing it themselves (ttinc(), ttchk(), etc). In this case
+ the Telnet local-echo buffers and routines are defined and referenced
+ here, rather than in the ck?tio.c module.
+*/
+#ifdef NETLEBUF
+#define LEBUFSIZ 4096
+int ttpush = -1, le_data = 0; /* These are seen from outside */
+static CHAR le_buf[LEBUFSIZ]; /* These are used internally */
+static int le_start = 0, le_end = 0;
+int tt_push_inited = 0;
+#endif /* NETLEBUF */
+
+#ifdef CK_SOCKS /* SOCKS Internet relay package */
+#ifdef CK_SOCKS5 /* SOCKS 5 */
+#define accept SOCKSaccept
+#define bind SOCKSbind
+#define connect SOCKSconnect
+#define getsockname SOCKSgetsockname
+#define listen SOCKSlisten
+#else /* Not SOCKS 5 */
+#define accept Raccept
+#define bind Rbind
+#define connect Rconnect
+#define getsockname Rgetsockname
+#define listen Rlisten
+#endif /* CK_SOCKS5 */
+#endif /* CK_SOCKS */
+
+#ifdef DEC_TCPIP
+#include <time.h>
+#include <inet.h>
+#endif /* DEC_TCPIP */
+
+/* Also see ckcnet.h -- hmmm, why don't we always include inet.h? */
+
+#ifdef HPUX
+#ifndef HPUX7 /* HPUX 7.00 doesn't have it */
+#include <arpa/inet.h> /* For inet_ntoa() prototype */
+#endif /* HPUX7 */
+#endif /* HPUX */
+
+#ifdef CMU_TCPIP
+#include <time.h>
+#endif /* CMU_TCPIP */
+
+#ifndef NODCLTIMEVAL
+#ifdef DCLTIMEVAL /* UnixWare 7 */
+struct timeval { /* And define these ourselves. */
+ long tv_sec; /* (see comments in ckutio.c) */
+ long tv_usec;
+};
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+#endif /* DCLTIMEVAL */
+#endif /* NODCLTIMEVAL */
+
+#ifdef WINTCP
+
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/time.h>
+/*
+ The WIN/TCP code path is the same as that for MultiNet.
+ Only the routine names have changed ...
+*/
+#define socket_read netread
+#define socket_ioctl ioctl
+#define socket_write netwrite
+#define socket_close netclose
+
+#ifdef OLD_TWG /* some routines have evolved */
+ extern int vmserrno, uerrno;
+#define socket_errno uerrno
+#define socket_perror perror /* which uses errno, not uerrno! */
+#else
+#define socket_errno errno
+#define socket_perror win$perror
+#endif /* OLD_TWG */
+
+#else /* Not WINTCP */
+
+#ifdef OSF13
+#ifdef CK_ANSIC
+#ifdef _NO_PROTO
+#undef _NO_PROTO
+#endif /* _NO_PROTO */
+#endif /* CK_ANSIC */
+#endif /* OSF13 */
+
+#ifndef I386IX
+#include <errno.h> /* Already included above */
+#endif /* I386IX */
+
+#include <signal.h> /* Everybody needs this */
+
+#ifdef ZILOG /* Zilog has different name for this */
+#include <setret.h>
+#else
+#include <setjmp.h>
+#endif /* ZILOG */
+
+#endif /* WINTCP */
+
+#ifdef datageneral /* Data General AOS/VS */
+#include <:usr:include:vs_tcp_errno.h>
+#include <:usr:include:sys:vs_tcp_types.h>
+#ifdef SELECT
+/*
+ NOTE: This can be compiled and linked OK with SELECT defined
+ but it doesn't work at all. Anybody who cares and knows how
+ to fix it, feel free.
+*/
+#include <:usr:include:sys:vs_tcp_time.h>
+#endif /* SELECT */
+#include <:usr:include:sys:socket.h>
+#include <:usr:include:netinet:in.h>
+#include <:usr:include:netdb.h>
+#endif /* datageneral */
+
+#ifndef socket_errno
+#define socket_errno errno
+#endif /* socket_errno */
+
+#ifdef TNCODE
+extern int tn_deb;
+#endif /* TNCODE */
+
+int tcp_rdns = /* Reverse DNS lookup */
+#ifdef DEC_TCPIP_OLD
+ SET_OFF /* Doesn't seem to work in UCX */
+#else
+ SET_AUTO
+#endif /* DEC_TCPIP */
+ ;
+#ifdef CK_DNS_SRV
+int tcp_dns_srv = SET_OFF;
+#endif /* CK_DNS_SRV */
+
+_PROTOTYP( char * cmcvtdate, (char *, int) );
+
+#ifdef RLOGCODE
+_PROTOTYP( int rlog_ctrl, (CHAR *, int) );
+_PROTOTYP( static int rlog_oob, (CHAR *, int) );
+#ifndef TCPIPLIB
+_PROTOTYP( static SIGTYP rlogoobh, ( int ) );
+#endif /* TCPIPLIB */
+_PROTOTYP( static int rlog_ini, (CHAR *, int,
+ struct sockaddr_in *,
+ struct sockaddr_in *) );
+int rlog_mode = RL_COOKED;
+int rlog_stopped = 0;
+int rlog_inband = 0;
+#endif /* RLOGCODE */
+
+#ifndef NOICP
+extern int doconx; /* CONNECT-class command active */
+#endif /* NOICP */
+
+#ifdef IBMX25
+/* This variable should probably be generalised for true client/server
+ * support - ie: the fd of the listening server, accepted calls should
+ * be forked or at least handled via a second fd (for IBM's X.25 -
+ * ttyfd always holds the active fd - ie the server becomes inactive
+ * as long as a client is connected, and becomes active again when the
+ * connection is closed)
+ */
+int x25serverfd = 0; /* extern in ckcnet.h */
+int x25seqno = 0; /* Connection sequence number */
+int x25lastmsg = -1; /* A cheapskate's state table */
+
+#define X25_CLOSED 0 /* Default state: no connection, no STREAM */
+#define X25_SETUP 1 /* X.25 has been set up (no connection) */
+#define X25_CONNECTED 2 /* X.25 connection has been established */
+int x25_state = X25_CLOSED; /* Default state */
+#endif /* IBMX25 */
+
+#ifndef DEBUG
+#define deblog 0
+#endif /* DEBUG */
+
+#ifdef CK_NAWS /* Negotiate About Window Size */
+#ifdef RLOGCODE
+_PROTOTYP( int rlog_naws, (void) );
+#endif /* RLOGCODE */
+#endif /* CK_NAWS */
+
+#ifdef OS2 /* For terminal type name string */
+#include "ckuusr.h"
+#ifndef NT
+#include <os2.h>
+#undef COMMENT
+#endif /* NT */
+#include "ckocon.h"
+extern int tt_type, max_tt;
+extern struct tt_info_rec tt_info[];
+extern char ttname[];
+#else
+#ifdef CK_AUTHENTICATION
+#include "ckuusr.h"
+#endif /* CK_AUTHENTICATION */
+#endif /* OS2 */
+
+#ifdef NT
+extern int winsock_version;
+#endif /* NT */
+
+#ifdef CK_AUTHENTICATION
+#include "ckuath.h"
+#endif /* CK_AUTHENTICATION */
+
+#include "ckcsig.h"
+
+#ifndef OS2 /* For timeout longjumps */
+static ckjmpbuf njbuf;
+#endif /* OS2 */
+
+#define NAMECPYL 1024 /* Local copy of hostname */
+char namecopy[NAMECPYL]; /* Referenced by ckctel.c */
+char namecopy2[NAMECPYL]; /* Referenced by ckctel.c */
+#ifndef NOHTTP
+char http_host_port[NAMECPYL]; /* orig host/port necessary for http */
+char http_ip[20] = { '\0' }; /* ip address of host */
+char http_port = 0;
+int http_ssl = 0;
+char * http_agent = 0;
+int httpfd = -1; /* socket for http connections */
+int http_code = 0;
+#define HTTPBUFLEN 1024
+char http_reply_str[HTTPBUFLEN] = "";
+#endif /* NOHTTP */
+
+char ipaddr[20] = { '\0' }; /* Global copy of IP address */
+unsigned long myxipaddr = 0L; /* Ditto as a number */
+#endif /* NETCONN */
+
+char *tcp_address = NULL; /* Preferred IP Address */
+extern char uidbuf[]; /* User ID buffer */
+extern char pwbuf[]; /* Password buffer */
+
+#ifndef NOHTTP
+char * tcp_http_proxy = NULL; /* Name[:port] of http proxy server */
+int tcp_http_proxy_errno = 0;
+char * tcp_http_proxy_user = NULL;
+char * tcp_http_proxy_pwd = NULL;
+char * tcp_http_proxy_agent = NULL;
+#define HTTPCPYL 1024
+static char proxycopy[HTTPCPYL];
+#endif /* NOHTTP */
+
+#ifdef OS2
+extern int tt_rows[], tt_cols[];
+extern int tt_status[VNUM];
+#else /* OS2 */
+extern int tt_rows, tt_cols; /* Everybody has this */
+#endif /* OS2 */
+
+extern int cmd_cols, cmd_rows;
+
+#ifdef STREAMING /* Use blocking writes for streaming */
+extern int streaming;
+#endif /* STREAMING */
+
+#ifdef NT
+extern int WSASafeToCancel;
+int win95selectbug = 0; /* For TCP/IP stacks whose select() */
+/* always fails on write requests such as Cisco and Quarterdeck */
+#define stricmp _stricmp
+#endif /* NT */
+
+#ifndef NOTCPOPTS
+
+/* Skip all this if NOTCPOPTS specified. */
+
+#ifdef SOL_SOCKET
+
+#ifdef TCP_NODELAY
+int tcp_nodelay = 0; /* Nagle algorithm TCP_NODELAY */
+#endif /* TCP_NODELAY */
+
+#ifdef SO_DONTROUTE
+int tcp_dontroute = 0;
+#endif /* SO_DONTROUTE */
+
+#ifdef SO_LINGER
+int tcp_linger = 0; /* SO_LINGER */
+int tcp_linger_tmo = 0; /* SO_LINGER timeout */
+#endif /* SO_LINGER */
+
+#ifdef HPUX /* But the data structures */
+#ifndef HPUX8 /* needed for linger are not */
+#ifndef HPUX9 /* defined in HP-UX versions */
+#ifndef HPUX10 /* prior to 8.00. */
+#ifdef SO_LINGER
+#undef SO_LINGER
+#endif /* SO_LINGER */
+#endif /* HPUX10 */
+#endif /* HPUX9 */
+#endif /* HPUX8 */
+#endif /* HPUX */
+
+#ifndef SO_OOBINLINE /* Hopefully only HP-UX 7.0 */
+#define SO_OOBINLINE 0x0100
+#endif /* SO_OOBINLINE */
+
+#ifndef TCPSNDBUFSIZ
+#ifdef VMS
+#ifdef __alpha
+#define TCPSNDBUFSIZ 16384
+#endif /* __alpha */
+#endif /* VMS */
+#endif /* TCPSNDBUFSIZ */
+
+#ifndef TCPSNDBUFSIZ
+#define TCPSNDBUFSIZ -1
+#endif /* TCPSNDBUFSIZ */
+
+#ifdef SO_SNDBUF
+int tcp_sendbuf = TCPSNDBUFSIZ;
+#endif /* SO_SNDBUF */
+
+#ifdef SO_RCVBUF
+int tcp_recvbuf = -1;
+#endif /* SO_RCVBUF */
+
+#ifdef SO_KEEPALIVE
+int tcp_keepalive = 1;
+#endif /* SO_KEEPALIVE */
+
+#endif /* SOL_SOCKET */
+#endif /* NOTCPOPTS */
+
+#ifndef NETCONN
+/*
+ Network support not defined.
+ Dummy functions here in case #ifdef's forgotten elsewhere.
+*/
+int /* Open network connection */
+netopen(name, lcl, nett) char *name; int *lcl, nett; {
+ return(-1);
+}
+int /* Close network connection */
+netclos() {
+ return(-1);
+}
+int /* Check network input buffer */
+nettchk() {
+ return(-1);
+}
+int /* Flush network input buffer */
+netflui() {
+ return(-1);
+}
+int /* Send network BREAK */
+netbreak() {
+ return(-1);
+}
+int /* Input character from network */
+netinc(timo) int timo; {
+ return(-1);
+}
+int /* Output character to network */
+#ifdef CK_ANSIC
+nettoc(CHAR c)
+#else
+nettoc(c) CHAR c;
+#endif /* CK_ANSIC */
+/* nettoc */ {
+ return(-1);
+}
+int
+nettol(s,n) CHAR *s; int n; {
+ return(-1);
+}
+
+#else /* NETCONN is defined (much of this module...) */
+
+#ifdef NETLEBUF
+VOID
+le_init() { /* LocalEchoInit() */
+ int i;
+ for (i = 0; i < LEBUFSIZ; i++)
+ le_buf[i] = '\0';
+ le_start = 0;
+ le_end = 0;
+ le_data = 0;
+ tt_push_inited = 1;
+}
+
+VOID
+le_clean() { /* LocalEchoCleanup() */
+ le_init();
+ return;
+}
+
+int
+le_inbuf() {
+ int rc = 0;
+ if (le_start != le_end) {
+ rc = (le_end -
+ le_start +
+ LEBUFSIZ) % LEBUFSIZ;
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+le_putchar(CHAR ch)
+#else
+le_putchar(ch) CHAR ch;
+#endif /* CK_ANSIC */
+/* le_putchar */ {
+ if ((le_start - le_end + LEBUFSIZ)%LEBUFSIZ == 1) {
+ debug(F110,"le_putchar","buffer is full",0);
+ return(-1);
+ }
+ le_buf[le_end++] = ch;
+ if (le_end == LEBUFSIZ)
+ le_end = 0;
+ le_data = 1;
+ return(0);
+}
+
+int
+#ifdef CK_ANSIC
+le_puts(CHAR * s, int n)
+#else
+le_puts(s,n) CHAR * s; int n;
+#endif /* CK_ANSIC */
+/* le_puts */ {
+ int rc = 0;
+ int i = 0;
+ CHAR * p = (CHAR *)"le_puts";
+ hexdump(p,s,n);
+ for (i = 0; i < n; i++)
+ rc = le_putchar((char)s[i]);
+ debug(F101,"le_puts","",rc);
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+le_putstr(CHAR * s)
+#else
+le_putstr(s) CHAR * s;
+#endif /* CK_ANSIC */
+/* le_puts */ {
+ CHAR * p;
+ int rc = 0;
+ p = (CHAR *)"le_putstr";
+ hexdump(p,s,(int)strlen((char *)s));
+ for (p = s; *p && !rc; p++)
+ rc = le_putchar(*p);
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+le_getchar(CHAR * pch)
+#else /* CK_ANSIC */
+le_getchar(pch) CHAR * pch;
+#endif /* CK_ANSIC */
+/* le_gatchar */ {
+ int rc = 0;
+ if (le_start != le_end) {
+ *pch = le_buf[le_start];
+ le_buf[le_start] = 0;
+ le_start++;
+
+ if (le_start == LEBUFSIZ)
+ le_start = 0;
+
+ if (le_start == le_end) {
+ le_data = 0;
+ }
+ rc++;
+ } else {
+ *pch = 0;
+ }
+ return(rc);
+}
+#endif /* NETLEBUF */
+
+#ifdef VMS
+/*
+ In edit 190, we moved tn_ini() to be called from within netopen().
+ But tn_ini() calls ttol(), and ttol() checks to see if it's a net
+ connection, but the flag for that isn't set until after netopen()
+ is finished. Since, in this module, we are always doing network
+ output anyway, we just call nettol() directly, instead of going thru
+ ttol(). Only needed for VMS, since UNIX, AOS/VS, and VOS can handle
+ net connections just like regular connections in ttol(), and OS/2
+ has a special routine for this.
+*/
+#define ttol nettol
+#endif /* VMS */
+
+int tcpsrfd = -1;
+
+#ifdef CK_KERBEROS
+
+char * krb5_d_principal = NULL; /* Default principal */
+char * krb5_d_instance = NULL; /* Default instance */
+char * krb5_d_realm = NULL; /* Default realm */
+char * krb5_d_cc = NULL; /* Default credentials cache */
+char * krb5_d_srv = NULL; /* Default Service */
+int krb5_d_lifetime = 600; /* Default lifetime (10 hours) */
+int krb5_d_forwardable = 0; /* creds not forwardable */
+int krb5_d_proxiable = 0; /* creds not proxiable */
+int krb5_d_renewable = 0; /* creds not renewable (0 min) */
+int krb5_autoget = 1; /* Autoget TGTs */
+int krb5_autodel = 0; /* Auto delete TGTs */
+int krb5_d_getk4 = 0; /* K5 Kinit gets K4 TGTs */
+int krb5_checkaddrs = 1; /* Check TGT Addrs */
+int krb5_d_no_addresses = 0; /* Do not include IP Addresses */
+char * krb5_d_addrs[KRB5_NUM_OF_ADDRS+1]={NULL,NULL}; /* Addrs to include */
+int krb5_errno = 0; /* Last K5 errno */
+char * krb5_errmsg = NULL; /* Last K5 errmsg */
+char * k5_keytab = NULL;
+
+char * krb4_d_principal = NULL; /* Default principal */
+char * krb4_d_realm = NULL; /* Default realm */
+char * krb4_d_srv = NULL; /* Default Service */
+int krb4_d_lifetime = 600; /* Default lifetime (10 hours) */
+int krb4_d_preauth = 1; /* Use preauth requests */
+char * krb4_d_instance = NULL; /* Default instance */
+int krb4_autoget = 1; /* Autoget TGTs */
+int krb4_autodel = 0; /* Auto delete TGTs */
+int krb4_checkaddrs = 1; /* Check TGT Addrs */
+char * k4_keytab = NULL;
+
+int krb4_errno = 0; /* Last K4 errno */
+char * krb4_errmsg = NULL; /* Last K4 errmsg */
+
+struct krb_op_data krb_op = { /* Operational data structure */
+ 0, NULL /* (version, cachefile) */
+};
+
+struct krb4_init_data krb4_init = { /* Kerberos 4 INIT data structure */
+ 0, NULL, NULL, NULL, NULL
+};
+
+struct krb5_init_data krb5_init = { /* Kerberos 5 INIT data structure */
+ 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0,
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+ 0
+};
+
+struct krb5_list_cred_data krb5_lc = { /* List Credentials data structure */
+ 0, 0, 0
+};
+
+int krb_action = -1; /* Kerberos action to perform */
+
+#ifndef CK_AUTHENTICATION
+char *
+ck_krb4_getrealm() {
+ return("");
+}
+char *
+ck_krb5_getrealm(cc) char * cc; {
+ return("");
+}
+char *
+ck_krb4_getprincipal() {
+ return("");
+}
+char *
+ck_krb5_getprincipal(cc) char * cc; {
+ return("");
+}
+#endif /* CK_AUTHENTICATION */
+
+/* I N I _ K E R B -- Initialize Kerberos data */
+
+VOID
+ini_kerb() {
+ int i;
+
+ krb_action = -1; /* No action specified */
+
+ krb_op.version = 0; /* Kerberos version (none) */
+ krb_op.cache = NULL; /* Cache file (none) */
+
+/* Kerberos 5 */
+
+ krb5_init.forwardable = krb5_d_forwardable; /* Init switch values... */
+ krb5_init.proxiable = krb5_d_proxiable;
+ krb5_init.lifetime = krb5_d_lifetime;
+ krb5_init.renew = 0;
+ krb5_init.renewable = krb5_d_renewable;
+ krb5_init.validate = 0;
+ krb5_init.no_addresses = krb5_d_no_addresses;
+ krb5_init.getk4 = krb5_d_getk4;
+ if (krb5_init.postdate) {
+ free(krb5_init.postdate);
+ krb5_init.postdate = NULL;
+ }
+ if (krb5_init.service) {
+ free(krb5_init.service);
+ krb5_init.service = NULL;
+ }
+ if (!krb5_d_cc || !krb5_d_cc[0]) { /* Set default cache */
+ char * p;
+ p = ck_krb5_get_cc_name();
+ makestr(&krb5_d_cc,(p && p[0]) ? p : NULL);
+ }
+ if (!krb5_d_realm || !krb5_d_realm[0]) { /* Set default realm */
+ char * p;
+ p = ck_krb5_getrealm(krb5_d_cc);
+ makestr(&krb5_d_realm,(p && p[0]) ? p : NULL);
+ }
+ makestr(&krb5_init.instance,krb5_d_instance);
+ makestr(&krb5_init.realm,krb5_d_realm); /* Set realm from default */
+ if (krb5_init.password) {
+ memset(krb5_init.password,0xFF,strlen(krb5_init.password));
+ free(krb5_init.password);
+ krb5_init.password = NULL;
+ }
+ if (!krb5_d_principal) { /* Default principal */
+ /* a Null principal indicates the user should be prompted */
+ char * p = ck_krb5_getprincipal(krb5_d_cc);
+ if (!p || !(*p))
+ p = (char *)uidbuf; /* Principal = user */
+ makestr(&krb5_d_principal,(p && p[0]) ? p : NULL);
+ }
+ makestr(&krb5_init.principal,krb5_d_principal);
+ for (i = 0; i <= KRB5_NUM_OF_ADDRS; i++) {
+ if (krb5_init.addrs[i])
+ free(krb5_init.addrs[i]);
+ krb5_init.addrs[i] = NULL;
+ }
+ for (i = 0; i <= KRB5_NUM_OF_ADDRS && krb5_d_addrs[i]; i++) {
+ makestr(&krb5_init.addrs[i],krb5_d_addrs[i]);
+ }
+
+ /* Kerberos 4 */
+
+ krb4_init.lifetime = krb4_d_lifetime;
+ krb4_init.preauth = krb4_d_preauth;
+ makestr(&krb4_init.instance,krb4_d_instance);
+ if (!krb4_d_realm || !krb4_d_realm[0]) {/* Set default realm */
+ char * p;
+ p = ck_krb4_getrealm();
+ makestr(&krb4_d_realm,(p && p[0]) ? p : NULL);
+ }
+ makestr(&krb4_init.realm,krb4_d_realm);
+ if (krb4_init.password) {
+ memset(krb4_init.password,0xFF,strlen(krb4_init.password));
+ free(krb4_init.password);
+ krb4_init.password = NULL;
+ }
+ if (!krb4_d_principal) { /* Default principal */
+ /* a Null principal indicates the user should be prompted */
+ char * p = ck_krb4_getprincipal();
+ if (!p || !(*p))
+ p = (char *)uidbuf; /* Principal = user */
+ makestr(&(krb4_d_principal),(p && p[0]) ? p : NULL);
+ }
+ makestr(&(krb4_init.principal),krb4_d_principal);
+}
+
+/* D O A U T H -- AUTHENTICATE action routine */
+
+int
+doauth(cx) int cx; { /* AUTHENTICATE action routine */
+ int rc = 0; /* Return code */
+
+#ifdef CK_AUTHENTICATION
+#ifdef OS2
+ if (!ck_security_loaddll()) /* Load various DLLs */
+ return(rc);
+#endif /* OS2 */
+ if (krb_op.version == 4) { /* Version = 4 */
+#ifdef COMMENT
+ sho_auth(AUTHTYPE_KERBEROS_V4);
+#endif /* COMMENT */
+ if (!ck_krb4_is_installed()) {
+ printf("?Kerberos 4 is not installed\n");
+ return(0);
+ }
+ switch (krb_action) { /* Perform V4 functions */
+ case KRB_A_IN: /* INIT */
+ rc |= !(ck_krb4_initTGT(&krb_op,&krb4_init) < 0);
+ break;
+ case KRB_A_DE: /* DESTROY */
+ rc |= !(ck_krb4_destroy(&krb_op) < 0);
+ break;
+ case KRB_A_LC: /* LIST-CREDENTIALS */
+ rc |= !(ck_krb4_list_creds(&krb_op) < 0);
+ break;
+ }
+ }
+ if (krb_op.version == 5) { /* V5 functions */
+#ifdef COMMENT
+ sho_auth(AUTHTYPE_KERBEROS_V5);
+#endif /* COMMENT */
+ if (!ck_krb5_is_installed()) {
+ printf("?Kerberos 5 is not installed\n");
+ return(0);
+ }
+ switch (krb_action) {
+ case KRB_A_IN: /* INIT */
+ rc |= !(ck_krb5_initTGT(&krb_op,&krb5_init,
+ krb5_init.getk4 ? &krb4_init : 0) < 0);
+ break;
+ case KRB_A_DE: /* DESTROY */
+ rc |= !(ck_krb5_destroy(&krb_op) < 0);
+ break;
+ case KRB_A_LC: /* LIST-CREDENTIALS */
+ if (krb_op.version == 0)
+ printf("\n");
+ rc |= !(ck_krb5_list_creds(&krb_op,&krb5_lc) < 0);
+ break;
+ }
+ }
+#else
+#ifndef NOICP
+#ifndef NOSHOW
+ rc = sho_auth(0); /* Show all */
+#endif /* NOSHOW */
+#endif /* NOICP */
+#endif /* CK_AUTHENTICATION */
+ return(rc);
+}
+#endif /* CK_KERBEROS */
+
+#ifdef TCPSOCKET
+#ifndef OS2
+#ifndef NOLISTEN /* For incoming connections */
+
+#ifndef INADDR_ANY
+#define INADDR_ANY 0
+#endif /* INADDR_ANY */
+
+_PROTOTYP( int ttbufr, ( VOID ) );
+_PROTOTYP( int tcpsrv_open, (char *, int *, int, int ) );
+
+static unsigned short tcpsrv_port = 0;
+
+#endif /* NOLISTEN */
+#endif /* OS2 */
+
+static char svcbuf[80]; /* TCP service string */
+static int svcnum = 0; /* TCP port number */
+
+#endif /* TCPSOCKET */
+
+/*
+ TCPIPLIB means use separate socket calls for i/o, while on UNIX the
+ normal file system calls are used for TCP/IP sockets too.
+ Means "DEC_TCPIP or MULTINET or WINTCP or OS2 or BEBOX" (see ckcnet.h),
+*/
+
+#ifdef TCPIPLIB
+
+/* For buffered network reads... */
+/*
+ If the buffering code is written right, it shouldn't matter
+ how long this buffer is.
+*/
+#ifdef OS2
+#ifdef NT
+#define TTIBUFL 64240 /* 44 * 1460 (MSS) */
+#else
+#define TTIBUFL 32120 /* 22 * 1460 (MSS) */
+#endif /* NT */
+#else /* OS2 */
+#define TTIBUFL 8191 /* Let's use 8K. */
+#endif /* OS2 */
+
+CHAR ttibuf[TTIBUFL+1];
+
+/*
+ select() is used in preference to alarm()/signal(), but different systems
+ use different forms of select()...
+*/
+#ifndef NOSELECT /* Option to override BSDSELECT */
+#ifdef BELLV10
+/*
+ Note: Although BELLV10 does have TCP/IP support, and does use the unique
+ form of select() that is evident in this module (and in ckutio.c), it does
+ not have a sockets library and so we can't build Kermit TCP/IP support for
+ it. For this, somebody would have to write TCP/IP streams code.
+*/
+#define BELLSELECT
+#ifndef FD_SETSIZE
+#define FD_SETSIZE 128
+#endif /* FD_SETSIZE */
+#else
+#ifdef WINTCP /* VMS with Wollongong WIN/TCP */
+#ifndef OLD_TWG /* TWG 3.2 has only select(read) */
+#define BSDSELECT
+#endif /* OLD_TWG */
+#else
+#ifdef CMU_TCPIP /* LIBCMU can do select */
+#define BSDSELECT
+#else
+#ifdef DEC_TCPIP
+#define BSDSELECT
+#else
+#ifdef OS2 /* OS/2 with TCP/IP */
+#ifdef NT
+#define BSDSELECT
+#else /* NT */
+#define IBMSELECT
+#endif /* NT */
+#endif /* OS2 */
+#endif /* DEC_TCPIP */
+#endif /* CMU_TCPIP */
+#endif /* WINTCP */
+#endif /* BELLV10 */
+#endif /* NOSELECT */
+/*
+ Others (TGV, TCPware, ...) use alarm()/signal(). The BSDSELECT case does not
+ compile at all; the IBMSELECT case compiles and links but crashes at runtime.
+ NOTE: If any of these can be converted to select(), they should be for two
+ reasons: (1) It's a lot faster; (2) certain sockets libraries do not like
+ their socket_read() calls to be interrupted; subsequent socket_read()'s tend
+ to fail with EBUSY. This happened in the UCX case before it was converted
+ to use select().
+*/
+#ifndef OS2
+#ifndef VMS
+static /* These are used in CKVTIO.C */
+#endif /* VMS */ /* And in CKONET.C */
+#endif /* OS2 */
+int
+ ttibp = 0,
+ ttibn = 0;
+/*
+ Read bytes from network into internal buffer ttibuf[].
+ To be called when input buffer is empty, i.e. when ttibn == 0.
+
+ Other network reading routines, like ttinc, ttinl, ttxin, should check the
+ internal buffer first, and call this routine for a refill if necessary.
+
+ Returns -1 on error, 0 if nothing happens. When data is read successfully,
+ returns number of bytes read, and sets global ttibn to that number and
+ ttibp (the buffer pointer) to zero.
+*/
+_PROTOTYP( int ttbufr, ( VOID ) );
+int
+ttbufr() { /* TT Buffer Read */
+ int count;
+
+ if (ttnet != NET_TCPB) /* First make sure current net is */
+ return(-1); /* TCP/IP; if not, do nothing. */
+
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#endif /* OS2 */
+
+ if (ttibn > 0) { /* Our internal buffer is not empty, */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(ttibn); /* so keep using it. */
+ }
+
+ if (ttyfd == -1) { /* No connection, error */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+
+ ttibp = 0; /* Else reset pointer to beginning */
+
+#ifdef WINTCP
+ count = 512; /* This works for WIN/TCP */
+#else
+#ifdef DEC_TCPIP
+ count = 512; /* UCX */
+#else
+#ifdef OS2
+ count = TTIBUFL;
+#else /* Multinet, etc. */
+ count = ttchk(); /* Check network input buffer, */
+ if (ttibn > 0) { /* which can put a char there! */
+ debug(F111,"ttbufr","ttchk() returns",count);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(ttibn);
+ }
+ if (count < 0) { /* Read error - connection closed */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ else if (count > TTIBUFL) /* Too many to read */
+ count = TTIBUFL;
+ else if (count == 0) /* None, so force blocking read */
+ count = 1;
+#endif /* OS2 */
+#endif /* DEC_TCPIP */
+#endif /* WINTCP */
+ debug(F101,"ttbufr count 1","",count);
+
+#ifdef CK_SSL
+ if (ssl_active_flag || tls_active_flag) {
+ int error;
+ ssl_read:
+ if (ssl_active_flag)
+ count = SSL_read(ssl_con, ttibuf, count);
+ else
+ count = SSL_read(tls_con, ttibuf, count);
+ error = SSL_get_error(ssl_active_flag?ssl_con:tls_con,count);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ debug(F111,"ttbufr SSL_ERROR_NONE","count",count);
+ if (count > 0) {
+ ttibp = 0; /* Reset buffer pointer. */
+ ttibn = count;
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(ttibn); /* Return buffer count. */
+ } else if (count < 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else {
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"ttbufr SSL_ERROR_WANT_WRITE","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"ttbufr SSL_ERROR_WANT_READ","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( count == 0 ) { /* EOF */
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"ttbufr SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+#endif /* NT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(rc);
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"ttbufr SSL_ERROR_WANT_X509_LOOKUP","",0);
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_SSL:
+ if (bio_err!=NULL) {
+ int len;
+ extern char ssl_err[];
+ BIO_printf(bio_err,"ttbufr SSL_ERROR_SSL\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ debug(F110,"ttbufr SSL_ERROR_SSL",ssl_err,0);
+ if (ssl_debug_flag)
+ printf(ssl_err);
+ } else if (ssl_debug_flag) {
+ debug(F100,"ttbufr SSL_ERROR_SSL","",0);
+ fflush(stderr);
+ fprintf(stderr,"ttbufr SSL_ERROR_SSL\n");
+ ERR_print_errors_fp(stderr);
+ }
+#ifdef COMMENT
+ netclos();
+#endif /* COMMENT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"ttbufr SSL_ERROR_ZERO_RETURN","",0);
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ default:
+ debug(F100,"ttbufr SSL_ERROR_?????","",0);
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ }
+#endif /* CK_SSL */
+
+#ifdef COMMENT
+/*
+ This is for nonblocking reads, which we don't do any more. This code didn't
+ work anyway, in the sense that a broken connection was never sensed.
+*/
+ if ((count = socket_read(ttyfd,&ttibuf[ttibp+ttibn],count)) < 1) {
+ if (count == -1 && socket_errno == EWOULDBLOCK) {
+ debug(F100,"ttbufr finds nothing","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(0);
+ } else {
+ debug(F101,"ttbufr socket_read error","",socket_errno);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ }
+
+ } else if (count == 0) {
+ debug(F100,"ttbufr socket eof","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ }
+#else /* COMMENT */
+
+/* This is for blocking reads */
+
+#ifndef VMS
+#ifdef SO_OOBINLINE
+ {
+ int outofband = 0;
+#ifdef BELLSELECT
+ if (select(128, NULL, NULL, efds, 0) > 0 && FD_ISSET(ttyfd, efds))
+ outofband = 1;
+#else
+#ifdef BSDSELECT
+ fd_set efds;
+ struct timeval tv;
+ FD_ZERO(&efds);
+ FD_SET(ttyfd, &efds);
+ tv.tv_sec = tv.tv_usec = 0L;
+ debug(F100,"Out-of-Band BSDSELECT","",0);
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+ if (select(FD_SETSIZE, NULL, NULL, &efds, &tv) > 0 &&
+ FD_ISSET(ttyfd, &efds))
+ outofband = 1;
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* !BSDSELECT */
+#ifdef IBMSELECT
+/* Is used by OS/2 ... */
+/* ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set */
+/* and timeval stuff since this is the only place where it is used. */
+ int socket = ttyfd;
+ debug(F100,"Out-of-Band IBMSELECT","",0);
+ if ((select(&socket, 0, 0, 1, 0L) == 1) && (socket == ttyfd))
+ outofband = 1;
+#else /* !IBMSELECT */
+/*
+ If we can't use select(), then we use the regular alarm()/signal()
+ timeout mechanism.
+*/
+ debug(F101,"Out-of-Band data not supported","",0);
+ outofband = 0;
+
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+#endif /* BELLSELECT */
+ if (outofband) {
+ /* Get the Urgent Data */
+ /* if OOBINLINE is disabled this should be only a single byte */
+ /* MS Winsock has a bug in Windows 95. Extra bytes are delivered */
+ /* That were never sent. */
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#endif /* OS2 */
+ count = socket_recv(ttyfd,&ttibuf[ttibp+ttibn],count,MSG_OOB);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ if (count <= 0) {
+ int s_errno = socket_errno;
+ debug(F101, "ttbufr socket_recv MSG_OOB","",count);
+ debug(F101, "ttbufr socket_errno","",s_errno);
+#ifdef OS2ONLY
+ if (count < 0 && (s_errno == 0 || s_errno == 23)) {
+ /* These appear in OS/2 - don't know why */
+ /* ignore it and read as normal data */
+ /* and break, then we will attempt to read */
+ /* the port using normal read() techniques */
+ debug(F100,"ttbufr handing as in-band data","",0);
+ count = 1;
+ } else {
+ netclos(); /* *** *** */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+#else /* OS2ONLY */
+ netclos(); /* *** *** */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+#endif /* OS2ONLY */
+ } else { /* we got out-of-band data */
+ hexdump("ttbufr out-of-band chars",&ttibuf[ttibp+ttibn],count);
+#ifdef BETADEBUG
+ bleep(BP_NOTE);
+#endif /* BETADEBUG */
+#ifdef RLOGCODE /* blah */
+ if (ttnproto == NP_RLOGIN ||
+ ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN ||
+ ((ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) &&
+ !rlog_inband)
+ )
+ {
+ /*
+ When urgent data is read with MSG_OOB and not OOBINLINE
+ then urgent data and normal data are not mixed. So
+ treat the entire buffer as urgent data.
+ */
+ rlog_oob(&ttibuf[ttibp+ttibn], count);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return ttbufr();
+ } else
+#endif /* RLOGCODE */ /* blah */
+#ifdef COMMENT
+ /*
+ I haven't written this yet, nor do I know what it should do
+ */
+ if (ttnproto == NP_TELNET) {
+ tn_oob();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return 0;
+ } else
+#endif /* COMMENT */
+ {
+ /* For any protocols we don't have a special out-of-band */
+ /* handler for, just put the bytes in the normal buffer */
+ /* and return */
+
+ ttibp += 0; /* Reset buffer pointer. */
+ ttibn += count;
+#ifdef DEBUG
+ /* Got some bytes. */
+ debug(F101,"ttbufr count 2","",count);
+ if (count > 0)
+ ttibuf[ttibp+ttibn] = '\0';
+ debug(F111,"ttbufr ttibuf",ttibuf,ttibp);
+#endif /* DEBUG */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(ttibn); /* Return buffer count. */
+ }
+ }
+ }
+ }
+#endif /* SO_OOBINLINE */
+#endif /* VMS */
+
+ count = socket_read(ttyfd,&ttibuf[ttibp+ttibn],count);
+ if (count <= 0) {
+ int s_errno = socket_errno;
+ debug(F101,"ttbufr socket_read","",count);
+ debug(F101,"ttbufr socket_errno","",s_errno);
+#ifdef OS2
+ if (count == 0 || os2socketerror(s_errno) < 0) {
+ netclos();
+ ReleaseTCPIPMutex();
+ return(-2);
+ }
+ ReleaseTCPIPMutex();
+ return(-1);
+#else /* OS2 */
+ netclos(); /* *** *** */
+ return(-2);
+#endif /* OS2 */
+ }
+#endif /* COMMENT */ /* (blocking vs nonblock reads...) */
+ else {
+ ttibp = 0; /* Reset buffer pointer. */
+ ttibn += count;
+#ifdef DEBUG
+ debug(F101,"ttbufr count 2","",count); /* Got some bytes. */
+ if (count > 0)
+ ttibuf[ttibp+ttibn] = '\0';
+ debug(F111,"ttbufr ttibuf",&ttibuf[ttibp],ttibn);
+#endif /* DEBUG */
+
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(ttibn); /* Return buffer count. */
+ }
+}
+#endif /* TCPIPLIB */
+
+#ifndef IBMSELECT
+#ifndef BELLSELECT
+#ifndef BSDSELECT /* Non-TCPIPLIB case */
+#ifdef SELECT
+#define BSDSELECT
+#endif /* SELECT */
+#endif /* BSDSELECT */
+#endif /* BELLSELECT */
+#endif /* IBMSELECT */
+
+#define TELNET_PORT 23 /* Should do lookup, but it won't change */
+#define RLOGIN_PORT 513
+#define KERMIT_PORT 1649
+#define KLOGIN_PORT 543
+#define EKLOGIN_PORT 2105
+
+#ifndef NONET
+/*
+ C-Kermit network open/close functions for BSD-sockets.
+ Much of this code shared by SunLink X.25, which also uses the socket library.
+*/
+
+/* N E T O P N -- Open a network connection. */
+/*
+ Call with:
+ name of host (or host:service),
+ lcl - local-mode flag to be set if this function succeeds,
+ network type - value defined in ckunet.h.
+*/
+#ifdef TCPSOCKET
+struct hostent *
+#ifdef CK_ANSIC
+ck_copyhostent(struct hostent * h)
+#else /* CK_ANSIC */
+ck_copyhostent(h) struct hostent * h;
+#endif /* CK_ANSIC */
+{
+ /*
+ * The hostent structure is dynamic in nature.
+ * struct hostent {
+ * char * h_name;
+ * char * * h_aliases;
+ * short h_addrtype;
+ * short h_length;
+ * char * * h_addr_list;
+ * #define h_addr h_addr_list[0]
+ */
+#define HOSTENTCNT 5
+ static struct hostent hosts[HOSTENTCNT] = {{NULL,NULL,0,0,NULL},
+ {NULL,NULL,0,0,NULL},
+ {NULL,NULL,0,0,NULL},
+ {NULL,NULL,0,0,NULL},
+ {NULL,NULL,0,0,NULL}};
+ static int next = 0;
+ int i,cnt;
+ char ** pp;
+
+ if ( h == NULL )
+ return(NULL);
+
+ if (next == HOSTENTCNT)
+ next = 0;
+
+ if ( hosts[next].h_name ) {
+ free(hosts[next].h_name);
+ hosts[next].h_name = NULL;
+ }
+ if ( hosts[next].h_aliases ) {
+ pp = hosts[next].h_aliases;
+ while ( *pp ) {
+ free(*pp);
+ pp++;
+ }
+ free(hosts[next].h_aliases);
+ }
+#ifdef HADDRLIST
+ if ( hosts[next].h_addr_list ) {
+ pp = hosts[next].h_addr_list;
+ while ( *pp ) {
+ free(*pp);
+ pp++;
+ }
+ free(hosts[next].h_addr_list);
+ }
+#endif /* HADDRLIST */
+
+ makestr(&hosts[next].h_name,h->h_name);
+ if (h->h_aliases) {
+ for ( cnt=0,pp=h->h_aliases; pp && *pp; pp++,cnt++) ;
+ /* The following can give warnings in non-ANSI builds */
+ hosts[next].h_aliases = (char **) malloc(sizeof(char *) * (cnt+1));
+ for ( i=0; i<cnt; i++) {
+ hosts[next].h_aliases[i] = NULL;
+ makestr(&hosts[next].h_aliases[i],h->h_aliases[i]);
+ }
+ hosts[next].h_aliases[i] = NULL;
+ } else
+ hosts[next].h_aliases = NULL;
+
+ hosts[next].h_addrtype = h->h_addrtype;
+ hosts[next].h_length = h->h_length;
+
+#ifdef HADDRLIST
+#ifdef h_addr
+ if (h->h_addr_list) {
+ for ( cnt=0,pp=h->h_addr_list; pp && *pp; pp++,cnt++) ;
+ /* The following can give warnings non-ANSI builds */
+ hosts[next].h_addr_list = (char **) malloc(sizeof(char *) * (cnt+1));
+ for ( i=0; i<cnt; i++) {
+ hosts[next].h_addr_list[i] = malloc(h->h_length);
+ bcopy(h->h_addr_list[i],hosts[next].h_addr_list[i],h->h_length);
+ }
+ hosts[next].h_addr_list[i] = NULL;
+ } else
+ hosts[next].h_addr_list = NULL;
+#else
+ bcopy(h->h_addr, &hosts[next].h_addr, h->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(h->h_addr, &hosts[next].h_addr, h->h_length);
+#endif /* HADDRLIST */
+
+ return(&hosts[next++]);
+}
+
+#ifdef EXCELAN
+/*
+ Most other BSD sockets implementations define these in header files
+ and libraries.
+*/
+struct servent {
+ unsigned short s_port;
+};
+
+struct hostent {
+ short h_addrtype;
+ struct in_addr h_addr;
+ int h_length;
+};
+
+struct servent *
+getservbyname(service, connection) char *service,*connection; {
+ static struct servent servrec;
+ int port;
+
+ port = 0;
+ if (strcmp(service, "telnet") == 0) port = 23;
+ else if (strcmp(service, "smtp") == 0) port = 25;
+ else port = atoi(service);
+
+ debug(F101,"getservbyname return port ","",port);
+
+ if (port > 0) {
+ servrec.s_port = htons(port);
+ return(&servrec);
+ }
+ return((struct servent *) NULL);
+}
+
+struct hostent *
+gethostbyname(hostname) char *hostname; {
+ return((struct hostent *) NULL);
+}
+
+unsigned long
+inet_addr(name) char *name; {
+ unsigned long addr;
+
+ addr = rhost(&name);
+ debug(F111,"inet_addr ",name,(int)addr);
+ return(addr);
+}
+
+char *
+inet_ntoa(in) struct in_addr in; {
+ static char name[80];
+ ckmakxmsg(name, ckuitoa(in.s_net),".",ckuitoa(in.s_host),".",
+ ckuitoa(in.s_lh),".", ckuitoa(in.s_impno));
+ return(name);
+}
+#else
+#ifdef DEC_TCPIP /* UCX */
+
+int ucx_port_bug = 0; /* Explained below */
+
+#ifndef __DECC /* VAXC or GCC */
+
+#define getservbyname my_getservbyname
+
+#ifdef CK_ANSIC
+globalref int (*C$$GA_UCX_GETSERVBYNAME)();
+extern void C$$TRANSLATE();
+extern void C$$SOCK_TRANSLATE();
+#else
+globalref int (*C$$GA_UCX_GETSERVBYNAME)();
+extern VOID C$$TRANSLATE();
+extern VOID C$$SOCK_TRANSLATE();
+#endif /* CK_ANSIC */
+
+struct servent *
+my_getservbyname (service, proto) char *service, *proto; {
+ static struct servent sent;
+ struct iosb {
+ union {
+ unsigned long status;
+ unsigned short st[2];
+ } sb;
+ unsigned long spare;
+ } s;
+ struct {
+ struct iosb *s;
+ char *serv;
+ char *prot;
+ } par;
+ unsigned long e;
+ char sbuf[30], pbuf[30];
+ char *p;
+
+ debug(F111,"UCX getservbyname",service,(int)C$$GA_UCX_GETSERVBYNAME);
+
+ p = sbuf;
+ ckstrncpy(p, service, 29);
+ while (*p = toupper(*p), *p++) {}
+ p = pbuf;
+ ckstrncpy(p, proto, 29);
+ while (*p = toupper(*p), *p++) {}
+
+ par.s = &s;
+
+ par.serv = "";
+ par.prot = "";
+ /* reset file pointer or something like that!?!? */
+ e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s);
+ par.serv = sbuf;
+ par.prot = pbuf; /* that is don't care */
+ e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s);
+ if ((long)e == -1L)
+ return NULL;
+ if ((e & 1) == 0L) {
+ C$$TRANSLATE(e);
+ return NULL;
+ }
+ if ((s.sb.st[0] & 1) == 0) {
+ C$$SOCK_TRANSLATE(&s.sb.st[0]);
+ return NULL;
+ }
+/*
+ sent.s_port is supposed to be returned by UCX in network byte order.
+ However, UCX 2.0 through 2.0C did not do this; 2.0D and later do it.
+ But there is no way of knowing which UCX version, so we have a user-settable
+ runtime variable. Note: UCX 2.0 was only for the VAX.
+*/
+ debug(F101,"UCX getservbyname port","",sent.s_port);
+ debug(F101,"UCX getservbyname ntohs(port)","",ntohs(sent.s_port));
+ if (ucx_port_bug) {
+ sent.s_port = htons(sent.s_port);
+ debug(F100,"UCX-PORT-BUG ON: swapping bytes","",0);
+ debug(F101,"UCX swapped port","",sent.s_port);
+ debug(F101,"UCX swapped ntohs(port)","",ntohs(sent.s_port));
+ }
+ return &sent;
+}
+#endif /* __DECC */
+#endif /* DEC_TCPIP */
+#endif /* EXCELAN */
+#endif /* TCPSOCKET */
+
+#ifndef NOTCPOPTS
+#ifndef datageneral
+int
+ck_linger(sock, onoff, timo) int sock; int onoff; int timo; {
+/*
+ The following, from William Bader, turns off the socket linger parameter,
+ which makes a close() block until all data is sent. "I don't think that
+ disabling linger can ever cause kermit to lose data, but you telnet to a
+ flaky server (or to our modem server when the modem is in use), disabling
+ linger prevents kermit from hanging on the close if you try to exit."
+
+ Modified by Jeff Altman to be generally useful.
+*/
+#ifdef SOL_SOCKET
+#ifdef SO_LINGER
+ struct linger set_linger_opt;
+ struct linger get_linger_opt;
+ SOCKOPT_T x;
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB &&
+ nettype != NET_SSH || ttmdm >= 0) {
+ tcp_linger = onoff;
+ tcp_linger_tmo = timo;
+ return(1);
+ }
+ x = sizeof(get_linger_opt);
+ if (getsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (char *)&get_linger_opt, &x)) {
+ debug(F111,"TCP ck_linger can't get SO_LINGER",ck_errstr(),errno);
+ } else if (x != sizeof(get_linger_opt)) {
+#ifdef OS2
+ struct _linger16 {
+ short s_linger;
+ short s_onoff;
+ } get_linger_opt16, set_linger_opt16;
+ if ( x == sizeof(get_linger_opt16) ) {
+ debug(F111,"TCP setlinger warning: SO_LINGER","len is 16-bit",x);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_LINGER,
+ (char *)&get_linger_opt16, &x)
+ ) {
+ debug(F111,
+ "TCP ck_linger can't get SO_LINGER",ck_errstr(),errno);
+ } else if (get_linger_opt16.s_onoff != onoff ||
+ get_linger_opt16.s_linger != timo)
+ {
+ set_linger_opt16.s_onoff = onoff;
+ set_linger_opt16.s_linger = timo;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_LINGER,
+ (char *)&set_linger_opt16,
+ sizeof(set_linger_opt16))
+ ) {
+ debug(F111,
+ "TCP ck_linger can't set SO_LINGER",
+ ck_errstr(),
+ errno
+ );
+ tcp_linger = get_linger_opt16.s_onoff;
+ tcp_linger_tmo = get_linger_opt16.s_linger;
+ } else {
+ debug(F101,
+ "TCP ck_linger new SO_LINGER","",
+ set_linger_opt16.s_onoff);
+ tcp_linger = set_linger_opt16.s_onoff;
+ tcp_linger_tmo = set_linger_opt16.s_linger;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP ck_linger SO_LINGER unchanged","",
+ get_linger_opt16.s_onoff);
+ tcp_linger = get_linger_opt16.s_onoff;
+ tcp_linger_tmo = get_linger_opt16.s_linger;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP ck_linger error: SO_LINGER","len",x);
+ debug(F111,"TCP ck_linger SO_LINGER",
+ "expected len",sizeof(get_linger_opt));
+ debug(F111,"TCP ck_linger SO_LINGER","linger_opt.l_onoff",
+ get_linger_opt.l_onoff);
+ debug(F111,"TCP linger SO_LINGER","linger_opt.l_linger",
+ get_linger_opt.l_linger);
+ } else if (get_linger_opt.l_onoff != onoff ||
+ get_linger_opt.l_linger != timo) {
+ set_linger_opt.l_onoff = onoff;
+ set_linger_opt.l_linger = timo;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_LINGER,
+ (char *)&set_linger_opt,
+ sizeof(set_linger_opt))) {
+ debug(F111,"TCP ck_linger can't set SO_LINGER",ck_errstr(),errno);
+ tcp_linger = get_linger_opt.l_onoff;
+ tcp_linger_tmo = get_linger_opt.l_linger;
+ } else {
+ debug(F101,
+ "TCP ck_linger new SO_LINGER",
+ "",
+ set_linger_opt.l_onoff
+ );
+ tcp_linger = set_linger_opt.l_onoff;
+ tcp_linger_tmo = set_linger_opt.l_linger;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP ck_linger SO_LINGER unchanged","",
+ get_linger_opt.l_onoff);
+ tcp_linger = get_linger_opt.l_onoff;
+ tcp_linger_tmo = get_linger_opt.l_linger;
+ return 1;
+ }
+#else
+ debug(F100,"TCP ck_linger SO_LINGER not defined","",0);
+#endif /* SO_LINGER */
+#else
+ debug(F100,"TCP ck_linger SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return(0);
+}
+
+int
+sendbuf(sock,size) int sock; int size; {
+/*
+ The following, from William Bader, allows changing of socket buffer sizes,
+ in case that might affect performance.
+
+ Modified by Jeff Altman to be generally useful.
+*/
+#ifdef SOL_SOCKET
+#ifdef SO_SNDBUF
+ int i, j;
+ SOCKOPT_T x;
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
+ || ttmdm >= 0) {
+ tcp_sendbuf = size;
+ return 1;
+ }
+ x = sizeof(i);
+ if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&i, &x)) {
+ debug(F111,"TCP sendbuf can't get SO_SNDBUF",ck_errstr(),errno);
+ } else if (x != sizeof(i)) {
+#ifdef OS2
+ short i16,j16;
+ if (x == sizeof(i16)) {
+ debug(F111,"TCP sendbuf warning: SO_SNDBUF","len is 16-bit",x);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_SNDBUF,
+ (char *)&i16, &x)
+ ) {
+ debug(F111,"TCP sendbuf can't get SO_SNDBUF",
+ ck_errstr(),errno);
+ } else if (size <= 0) {
+ tcp_sendbuf = i16;
+ debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i16);
+ return 1;
+ } else if (i16 != size) {
+ j16 = size;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_SNDBUF,
+ (char *)&j16,
+ sizeof(j16))
+ ) {
+ debug(F111,"TCP sendbuf can't set SO_SNDBUF",
+ ck_errstr(),errno);
+ } else {
+ debug(F101,"TCP sendbuf old SO_SNDBUF","",i16);
+ debug(F101,"TCP sendbuf new SO_SNDBUF","",j16);
+ tcp_sendbuf = size;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i16);
+ tcp_sendbuf = size;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP sendbuf error: SO_SNDBUF","len",x);
+ debug(F111,"TCP sendbuf SO_SNDBUF","expected len",sizeof(i));
+ debug(F111,"TCP sendbuf SO_SNDBUF","i",i);
+ } else if (size <= 0) {
+ tcp_sendbuf = i;
+ debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i);
+ return 1;
+ } else if (i != size) {
+ j = size;
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&j, sizeof(j))) {
+ debug(F111,"TCP sendbuf can't set SO_SNDBUF",ck_errstr(),errno);
+ tcp_sendbuf = i;
+ } else {
+ debug(F101,"TCP sendbuf old SO_SNDBUF","",i);
+ debug(F101,"TCP sendbuf new SO_SNDBUF","",j);
+ tcp_sendbuf = size;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i);
+ tcp_sendbuf = size;
+ return 1;
+ }
+#else
+ debug(F100,"TCP sendbuf SO_SNDBUF not defined","",0);
+#endif /* SO_SNDBUF */
+#else
+ debug(F100,"TCP sendbuf SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return(0);
+}
+
+int
+recvbuf(sock,size) int sock; int size; {
+/*
+ The following, from William Bader, allows changing of socket buffer sizes,
+ in case that might affect performance.
+
+ Modified by Jeff Altman to be generally useful.
+*/
+#ifdef SOL_SOCKET
+#ifdef SO_RCVBUF
+ int i, j;
+ SOCKOPT_T x;
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB &&
+ nettype != NET_SSH || ttmdm >= 0) {
+ tcp_recvbuf = size;
+ return(1);
+ }
+ x = sizeof(i);
+ if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&i, &x)) {
+ debug(F111,"TCP recvbuf can't get SO_RCVBUF",ck_errstr(),errno);
+ } else if (x != sizeof(i)) {
+#ifdef OS2
+ short i16,j16;
+ if ( x == sizeof(i16) ) {
+ debug(F111,"TCP recvbuf warning: SO_RCVBUF","len is 16-bit",x);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_RCVBUF,
+ (char *)&i16, &x)
+ ) {
+ debug(F111,"TCP recvbuf can't get SO_RCVBUF",
+ ck_errstr(),errno);
+ } else if (size <= 0) {
+ tcp_recvbuf = i16;
+ debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i16);
+ return 1;
+ } else if (i16 != size) {
+ j16 = size;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j16,
+ sizeof(j16))) {
+ debug(F111,"TCP recvbuf can' set SO_RCVBUF",
+ ck_errstr(),errno);
+ } else {
+ debug(F101,"TCP recvbuf old SO_RCVBUF","",i16);
+ debug(F101,"TCP recvbuf new SO_RCVBUF","",j16);
+ tcp_recvbuf = size;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i16);
+ tcp_recvbuf = size;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP recvbuf error: SO_RCVBUF","len",x);
+ debug(F111,"TCP recvbuf SO_RCVBUF","expected len",sizeof(i));
+ debug(F111,"TCP recvbuf SO_RCVBUF","i",i);
+ } else if (size <= 0) {
+ tcp_recvbuf = i;
+ debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i);
+ return 1;
+ } else if (i != size) {
+ j = size;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j, sizeof(j))) {
+ debug(F111,"TCP recvbuf can't set SO_RCVBUF",ck_errstr(),errno);
+ tcp_recvbuf = i;
+ } else {
+ debug(F101,"TCP recvbuf old SO_RCVBUF","",i);
+ debug(F101,"TCP recvbuf new SO_RCVBUF","",j);
+ tcp_recvbuf = size;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i);
+ tcp_recvbuf = size;
+ return 1;
+ }
+#else
+ debug(F100,"TCP recvbuf SO_RCVBUF not defined","",0);
+#endif /* SO_RCVBUF */
+#else
+ debug(F100,"TCP recvbuf SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return 0;
+}
+
+int
+keepalive(sock,onoff) int sock; int onoff; {
+#ifdef SOL_SOCKET
+#ifdef SO_KEEPALIVE
+ int get_keepalive_opt;
+ int set_keepalive_opt;
+ SOCKOPT_T x;
+
+ debug(F111,"TCP keepalive","sock",sock);
+ debug(F111,"TCP keepalive","nettype",nettype);
+ debug(F111,"TCP keepalive","ttmdm",ttmdm);
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
+ || ttmdm >= 0) {
+ tcp_keepalive = onoff;
+ return 1;
+ }
+ x = sizeof(get_keepalive_opt);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_KEEPALIVE, (char *)&get_keepalive_opt, &x)) {
+ debug(F111,"TCP keepalive can't get SO_KEEPALIVE",ck_errstr(),errno);
+ } else if (x != sizeof(get_keepalive_opt)) {
+#ifdef OS2
+ short get_keepalive_opt16;
+ short set_keepalive_opt16;
+ if (x == sizeof(get_keepalive_opt16)) {
+ debug(F111,"TCP keepalive warning: SO_KEEPALIVE",
+ "len is 16-bit",x);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&get_keepalive_opt16, &x)
+ ) {
+ debug(F111,
+ "TCP keepalive can't get SO_KEEPALIVE",
+ ck_errstr(),
+ errno
+ );
+ } else if (get_keepalive_opt16 != onoff) {
+ set_keepalive_opt16 = onoff;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_KEEPALIVE,
+ (char *)&set_keepalive_opt16,
+ sizeof(set_keepalive_opt16))
+ ) {
+ debug(F111,
+ "TCP keepalive can't clear SO_KEEPALIVE",
+ ck_errstr(),
+ errno
+ );
+ tcp_keepalive = get_keepalive_opt16;
+ } else {
+ debug(F101,
+ "TCP keepalive new SO_KEEPALIVE","",
+ set_keepalive_opt16);
+ tcp_keepalive = set_keepalive_opt16;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP keepalive SO_KEEPALIVE unchanged","",
+ get_keepalive_opt16);
+ tcp_keepalive = onoff;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP keepalive error: SO_KEEPALIVE","len",x);
+ debug(F111,
+ "TCP keepalive SO_KEEPALIVE",
+ "expected len",
+ sizeof(get_keepalive_opt)
+ );
+ debug(F111,
+ "TCP keepalive SO_KEEPALIVE",
+ "keepalive_opt",
+ get_keepalive_opt
+ );
+ } else if (get_keepalive_opt != onoff) {
+ set_keepalive_opt = onoff;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_KEEPALIVE,
+ (char *)&set_keepalive_opt,
+ sizeof(set_keepalive_opt))
+ ) {
+ debug(F111,
+ "TCP keepalive can't clear SO_KEEPALIVE",
+ ck_errstr(),
+ errno
+ );
+ tcp_keepalive = get_keepalive_opt;
+ } else {
+ debug(F101,
+ "TCP keepalive new SO_KEEPALIVE",
+ "",
+ set_keepalive_opt
+ );
+ tcp_keepalive = onoff;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP keepalive SO_KEEPALIVE unchanged",
+ "",
+ get_keepalive_opt
+ );
+ tcp_keepalive = onoff;
+ return 1;
+ }
+#else
+ debug(F100,"TCP keepalive SO_KEEPALIVE not defined","",0);
+#endif /* SO_KEEPALIVE */
+#else
+ debug(F100,"TCP keepalive SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return(0);
+}
+
+int
+dontroute(sock,onoff) int sock; int onoff; {
+#ifdef SOL_SOCKET
+#ifdef SO_DONTROUTE
+ int get_dontroute_opt;
+ int set_dontroute_opt;
+ SOCKOPT_T x;
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
+ || ttmdm >= 0) {
+ tcp_dontroute = onoff;
+ return 1;
+ }
+ x = sizeof(get_dontroute_opt);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_DONTROUTE, (char *)&get_dontroute_opt, &x)) {
+ debug(F111,"TCP dontroute can't get SO_DONTROUTE",ck_errstr(),errno);
+ } else if (x != sizeof(get_dontroute_opt)) {
+#ifdef OS2
+ short get_dontroute_opt16;
+ short set_dontroute_opt16;
+ if (x == sizeof(get_dontroute_opt16)) {
+ debug(F111,"TCP dontroute warning: SO_DONTROUTE",
+ "len is 16-bit",x);
+ if (getsockopt(sock,
+ SOL_SOCKET, SO_DONTROUTE,
+ (char *)&get_dontroute_opt16, &x)
+ ) {
+ debug(F111,
+ "TCP dontroute can't get SO_DONTROUTE",
+ ck_errstr(),
+ errno
+ );
+ } else if (get_dontroute_opt16 != onoff) {
+ set_dontroute_opt16 = onoff;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_DONTROUTE,
+ (char *)&set_dontroute_opt16,
+ sizeof(set_dontroute_opt16))
+ ) {
+ debug(F111,
+ "TCP dontroute can't clear SO_DONTROUTE",
+ ck_errstr(),
+ errno
+ );
+ tcp_dontroute = get_dontroute_opt16;
+ } else {
+ debug(F101,
+ "TCP dontroute new SO_DONTROUTE","",
+ set_dontroute_opt16);
+ tcp_dontroute = set_dontroute_opt16;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP dontroute SO_DONTROUTE unchanged","",
+ get_dontroute_opt16);
+ tcp_dontroute = onoff;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP dontroute error: SO_DONTROUTE","len",x);
+ debug(F111,
+ "TCP dontroute SO_DONTROUTE",
+ "expected len",
+ sizeof(get_dontroute_opt)
+ );
+ debug(F111,
+ "TCP dontroute SO_DONTROUTE",
+ "dontroute_opt",
+ get_dontroute_opt
+ );
+ } else if (get_dontroute_opt != onoff) {
+ set_dontroute_opt = onoff;
+ if (setsockopt(sock,
+ SOL_SOCKET,
+ SO_DONTROUTE,
+ (char *)&set_dontroute_opt,
+ sizeof(set_dontroute_opt))
+ ) {
+ debug(F111,
+ "TCP dontroute can't clear SO_DONTROUTE",
+ ck_errstr(),
+ errno
+ );
+ tcp_dontroute = get_dontroute_opt;
+ } else {
+ debug(F101,
+ "TCP dontroute new SO_DONTROUTE",
+ "",
+ set_dontroute_opt
+ );
+ tcp_dontroute = onoff;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP dontroute SO_DONTROUTE unchanged",
+ "",
+ get_dontroute_opt
+ );
+ tcp_dontroute = onoff;
+ return 1;
+ }
+#else
+ debug(F100,"TCP dontroute SO_DONTROUTE not defined","",0);
+#endif /* SO_DONTROUTE */
+#else
+ debug(F100,"TCP dontroute SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return(0);
+}
+
+int
+no_delay(sock,onoff) int sock; int onoff; {
+#ifdef SOL_SOCKET
+#ifdef TCP_NODELAY
+ int get_nodelay_opt;
+ int set_nodelay_opt;
+ SOCKOPT_T x;
+
+#ifdef IKSD
+ if (!inserver)
+#endif /* IKSD */
+ if (sock == -1 ||
+ nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH
+ || ttmdm >= 0) {
+ tcp_nodelay = onoff;
+ return(1);
+ }
+ x = sizeof(get_nodelay_opt);
+ if (getsockopt(sock,IPPROTO_TCP,TCP_NODELAY,
+ (char *)&get_nodelay_opt,&x)) {
+ debug(F111,
+ "TCP no_delay can't get TCP_NODELAY",
+ ck_errstr(),
+ errno);
+ } else if (x != sizeof(get_nodelay_opt)) {
+#ifdef OS2
+ short get_nodelay_opt16;
+ short set_nodelay_opt16;
+ if (x == sizeof(get_nodelay_opt16)) {
+ debug(F111,"TCP no_delay warning: TCP_NODELAY","len is 16-bit",x);
+ if (getsockopt(sock,
+ IPPROTO_TCP, TCP_NODELAY,
+ (char *)&get_nodelay_opt16, &x)
+ ) {
+ debug(F111,
+ "TCP no_delay can't get TCP_NODELAY",
+ ck_errstr(),
+ errno);
+ } else if (get_nodelay_opt16 != onoff) {
+ set_nodelay_opt16 = onoff;
+ if (setsockopt(sock,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (char *)&set_nodelay_opt16,
+ sizeof(set_nodelay_opt16))
+ ) {
+ debug(F111,
+ "TCP no_delay can't clear TCP_NODELAY",
+ ck_errstr(),
+ errno);
+ tcp_nodelay = get_nodelay_opt16;
+ } else {
+ debug(F101,
+ "TCP no_delay new TCP_NODELAY",
+ "",
+ set_nodelay_opt16);
+ tcp_nodelay = onoff;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP no_delay TCP_NODELAY unchanged","",
+ get_nodelay_opt16);
+ tcp_nodelay = onoff;
+ return 1;
+ }
+ return(0);
+ }
+#endif /* OS2 */
+ debug(F111,"TCP no_delay error: TCP_NODELAY","len",x);
+ debug(F111,"TCP no_delay TCP_NODELAY","expected len",
+ sizeof(get_nodelay_opt));
+ debug(F111,"TCP no_delay TCP_NODELAY","nodelay_opt",get_nodelay_opt);
+ } else if (get_nodelay_opt != onoff) {
+ set_nodelay_opt = onoff;
+ if (setsockopt(sock,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (char *)&set_nodelay_opt,
+ sizeof(set_nodelay_opt))) {
+ debug(F111,
+ "TCP no_delay can't clear TCP_NODELAY",
+ ck_errstr(),
+ errno
+ );
+ tcp_nodelay = get_nodelay_opt;
+ } else {
+ debug(F101,"TCP no_delay new TCP_NODELAY","",set_nodelay_opt);
+ tcp_nodelay = onoff;
+ return 1;
+ }
+ } else {
+ debug(F101,"TCP no_delay TCP_NODELAY unchanged","",get_nodelay_opt);
+ tcp_nodelay = onoff;
+ return(1);
+ }
+#else
+ debug(F100,"TCP no_delay TCP_NODELAY not defined","",0);
+#endif /* TCP_NODELAY */
+#else
+ debug(F100,"TCP no_delay SO_SOCKET not defined","",0);
+#endif /* SOL_SOCKET */
+ return 0;
+}
+#endif /* datageneral */
+#endif /* NOTCPOPTS */
+
+#ifdef SUNX25
+#ifndef X25_WR_FACILITY
+/* For Solaris 2.3 / SunLink 8.x - see comments in ckcnet.h */
+void
+bzero(s,n) char *s; int n; {
+ memset(s,0,n);
+}
+#endif /* X25_WR_FACILITY */
+#endif /* SUNX25 */
+
+#ifdef TCPSOCKET
+#ifndef OS2
+#ifndef NOLISTEN
+
+#ifdef BSDSELECT
+#ifndef VMS
+#ifndef BELLV10
+#ifndef datageneral
+#ifdef hp9000s500 /* HP-9000/500 HP-U 5.21 */
+#include <time.h>
+#else
+
+/****** THIS SECTION ADDED BY STEVE RANCE - OS9 NETWORK SERVER
+* ------------------------------------------------------
+*
+* Due to OS9's Lack of a select() call, the following seems to be
+* enough to fool the rest of the code into compiling. The only
+* effect that I can see is using control L to refresh the status
+* display gets qued up until some network packets arrive.
+*
+* This solution is by no means elegant but works enough to be
+* a (the) solution.
+*
+* Also with the defines I had specified in my makefile I had to
+* have an #endif right at the end of the file when compiling.
+* I did not bother speding time to find out why.
+*
+* COPTS = -to=osk -d=OSK -d=TCPSOCKET -d=SELECT -d=VOID=void -d=SIG_V \
+* -d=DYNAMIC -d=PARSENSE -d=KANJI -d=MYCURSES -d=ZFCDAT \
+* -d=CK_APC -d=CK_REDIR -d=RENAME -d=CK_TTYFD -d=NOOLDMODEMS \
+* -d=CK_ANSIC -d=CK_XYZ -tp=68040d -l=netdb.l -l=socklib.l \
+* -l=termlib.l -l=math.l -l=sys_clib.l
+*
+* stever@ozemail.com.au
+*/
+
+#ifdef OSK
+#define BSDSELECT /* switch on BSD select code */
+#define FD_SETSIZE 32 /* Max # of paths in OS9 */
+#define FD_ZERO(p) ((*p)=0)
+#define FD_SET(n,b) ((*b)|=(1<<(n)))
+#define FD_ISSET(n,b) 1 /* always say data is ready */
+#define select(a,b,c,d,e) 1 /* always say 1 path has data */
+typedef int fd_set; /* keep BSD Code Happy */
+struct timeval {int tv_sec,tv_usec;}; /* keep BSD Code Happy */
+
+/****** END OF OS9 MODS FROM STEVE RANCE **************************/
+#endif /* OSK */
+
+#include <sys/time.h>
+#endif /* hp9000s500 */
+#endif /* datageneral */
+#endif /* BELLV10 */
+#endif /* VMS */
+#ifdef SELECT_H
+#include <sys/select.h>
+#endif /* SELECT_H */
+#endif /* BSDSELECT */
+
+#ifdef SELECT
+#ifdef CK_SCOV5
+#include <sys/select.h>
+#endif /* CK_SCOV5 */
+#endif /* SELECT */
+
+#ifdef NOTUSED
+/* T C P S O C K E T _ O P E N -- Open a preexisting socket number */
+
+int
+tcpsocket_open(name,lcl,nett,timo) char * name; int * lcl; int nett; int timo {
+ int on = 1;
+ static struct servent *service, servrec;
+ static struct hostent *host;
+ static struct sockaddr_in saddr;
+ static
+#ifdef UCX50
+ unsigned
+#endif /* UCX50 */
+ int saddrlen;
+#ifdef BSDSELECT
+ fd_set rfds;
+ struct timeval tv;
+#else
+#ifdef BELLSELECT
+ fd_set rfds;
+#else
+ fd_set rfds;
+ fd_set rfds;
+ struct timeval {
+ long tv_sec;
+ long tv_usec;
+ } tv;
+#endif /* BELLSELECT */
+#endif /* BSDSELECT */
+
+ debug(F101,"tcpsocket_open nett","",nett);
+ *ipaddr = '\0';
+
+ if (nett != NET_TCPB)
+ return(-1); /* BSD socket support */
+
+ netclos(); /* Close any previous connection. */
+ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */
+ if (ttnproto != NP_TCPRAW)
+ ttnproto = NP_NONE; /* No protocol selected yet. */
+ debug(F110,"tcpsocket_open namecopy",namecopy,0);
+
+ /* Assign the socket number to ttyfd and then fill in tcp structures */
+ ttyfd = atoi(&name[1]);
+ debug(F111,"tcpsocket_open","ttyfd",ttyfd);
+
+#ifndef NOTCPOPTS
+#ifdef SOL_SOCKET
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+
+#ifndef datageneral
+#ifdef TCP_NODELAY
+ no_delay(ttyfd,tcp_nodelay);
+#endif /* TCP_NODELAY */
+#ifdef SO_KEEPALIVE
+ keepalive(ttyfd,tcp_keepalive);
+#endif /* SO_KEEPALIVE */
+#ifdef SO_LINGER
+ ck_linger(ttyfd,tcp_linger, tcp_linger_tmo);
+#endif /* SO_LINGER */
+#ifdef SO_SNDBUF
+ sendbuf(ttyfd,tcp_sendbuf);
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ recvbuf(ttyfd,tcp_recvbuf);
+#endif /* SO_RCVBUF */
+#endif /* datageneral */
+#endif /* SOL_SOCKET */
+#endif /* NOTCPOPTS */
+
+#ifdef NT_TCP_OVERLAPPED
+ OverlappedWriteInit();
+ OverlappedReadInit();
+#endif /* NT_TCP_OVERLAPPED */
+
+
+ /* Get the name of the host we are connected to */
+
+ saddrlen = sizeof(saddr);
+ getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen);
+
+ ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),20);
+
+ if (tcp_rdns == SET_ON
+#ifdef CK_KERBEROS
+ || tcp_rdns == SET_AUTO &&
+ (ck_krb5_is_installed() || ck_krb4_is_installed())
+#endif /* CK_KERBEROS */
+#ifndef NOHTTP
+ && (tcp_http_proxy == NULL)
+#endif /* NOHTTP */
+#ifdef CK_SSL
+ && !(ssl_only_flag || tls_only_flag)
+#endif /* CK_SSL */
+ ) { /* Reverse DNS */
+ if (!quiet) {
+ printf(" Reverse DNS Lookup... ");
+ fflush(stdout);
+ }
+ host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET);
+ debug(F110,"tcpsocket_open gethostbyaddr",host ? "OK" : "FAILED",0);
+ if (host) {
+ host = ck_copyhostent(host);
+ debug(F100,"tcpsocket_open gethostbyaddr != NULL","",0);
+ if (!quiet) {
+ printf("(OK)\n");
+ fflush(stdout);
+ }
+ ckstrncpy(name, host->h_name, 80);
+ ckstrncat(name, ":", 80);
+ ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)), 80);
+ if (!quiet
+#ifndef NOICP
+ && !doconx
+#endif /* NOICP */
+ )
+ printf("%s connected on port %d\n",
+ host->h_name,
+ ntohs(saddr.sin_port)
+ );
+ } else if (!quiet)
+ printf("Failed\n");
+ } else if (!quiet)
+ printf("(OK)\n");
+
+ if (tcp_rdns != SET_ON || !host) {
+ ckstrncpy(name,ipaddr,80);
+ ckstrncat(name,":",80);
+ ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80);
+ if (!quiet
+#ifdef NOICP
+ && !doconx
+#endif /* NOICP */
+ )
+ printf("%s connected on port %d\n",ipaddr,ntohs(saddr.sin_port));
+ }
+ if (!quiet) fflush(stdout);
+ ttnet = nett; /* TCP/IP (sockets) network */
+
+#ifdef RLOGCODE
+ if (ntohs(saddr.sin_port) == 513)
+ ttnproto = NP_LOGIN;
+ else
+#endif /* RLOGCODE */
+ /* Assume the service is TELNET. */
+ if (ttnproto != NP_TCPRAW)
+ ttnproto = NP_TELNET; /* Yes, set global flag. */
+#ifdef CK_SECURITY
+ /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
+ ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ?
+ host->h_name : ipaddr,
+ ipaddr,
+ uidbuf,
+ ttyfd
+ );
+#endif /* CK_SECURITY */
+ if (tn_ini() < 0) /* Start/Reset TELNET negotiations */
+ if (ttchk() < 0) /* Did it fail due to connect loss? */
+ return(-1);
+
+ if (*lcl < 0) *lcl = 1; /* Local mode. */
+
+ return(0); /* Done. */
+}
+#endif /* NOTUSED */
+
+/* T C P S R V _ O P E N -- Open a TCP/IP Server connection */
+/*
+ Calling conventions same as ttopen(), except third argument is network
+ type rather than modem type.
+*/
+int
+tcpsrv_open(name,lcl,nett,timo) char * name; int * lcl; int nett; int timo; {
+ char *p;
+ int i, x;
+ SOCKOPT_T on = 1;
+ int ready_to_accept = 0;
+ static struct servent *service, *service2, servrec;
+ static struct hostent *host;
+ static struct sockaddr_in saddr;
+ struct sockaddr_in l_addr;
+ GSOCKNAME_T l_slen;
+#ifdef UCX50
+ static u_int saddrlen;
+#else
+ static SOCKOPT_T saddrlen;
+#endif /* UCX50 */
+
+#ifdef BSDSELECT
+ fd_set rfds;
+ struct timeval tv;
+#else
+#ifdef BELLSELCT
+ fd_set rfds;
+#else
+ fd_set rfds;
+ struct timeval {
+ long tv_sec;
+ long tv_usec;
+ } tv;
+#endif /* BELLSELECT */
+#endif /* BSDSELECT */
+#ifdef CK_SSL
+ int ssl_failed = 0;
+#endif /* CK_SSL */
+
+ debug(F101,"tcpsrv_open nett","",nett);
+ *ipaddr = '\0';
+
+ if (nett != NET_TCPB)
+ return(-1); /* BSD socket support */
+
+ netclos(); /* Close any previous connection. */
+ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */
+#ifdef COMMENT
+ /* Don't do this. */
+ if (ttnproto != NP_TCPRAW)
+ ttnproto = NP_NONE; /* No protocol selected yet. */
+#endif /* COMMENT */
+ debug(F110,"tcpsrv_open namecopy",namecopy,0);
+
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':')
+ p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ *p++ = '\0'; /* Get service name or number */
+ } else { /* Otherwise use kermit */
+ p = "kermit";
+ }
+ debug(F110,"tcpsrv_open service requested",p,0);
+ if (isdigit(*p)) { /* Use socket number without lookup */
+ service = &servrec;
+ service->s_port = htons((unsigned short)atoi(p));
+ } else { /* Otherwise lookup the service name */
+ service = getservbyname(p, "tcp");
+ }
+ if (!service && !strcmp("kermit",p)) { /* Use Kermit service port */
+ service = &servrec;
+ service->s_port = htons(1649);
+ }
+#ifdef RLOGCODE
+ if (service && !strcmp("login",p) && service->s_port != htons(513)) {
+ fprintf(stderr,
+ " Warning: login service on port %d instead of port 513\n",
+ ntohs(service->s_port));
+ fprintf(stderr, " Edit SERVICES file if RLOGIN fails to connect.\n");
+ debug(F101,"tcpsrv_open login on port","",ntohs(service->s_port));
+ }
+#endif /* RLOGCODE */
+ if (!service) {
+ fprintf(stderr, "Cannot find port for service: %s\n", p);
+ debug(F111,"tcpsrv_open can't get service",p,errno);
+ errno = 0; /* rather than mislead */
+ return(-1);
+ }
+
+ /* If we currently have a listen active but port has changed then close */
+
+ debug(F101,"tcpsrv_open checking previous connection","",tcpsrfd);
+ debug(F101,"tcpsrv_open previous tcpsrv_port","",tcpsrv_port);
+ if (tcpsrfd != -1 &&
+ tcpsrv_port != ntohs((unsigned short)service->s_port)) {
+ debug(F100,"tcpsrv_open closing previous connection","",0);
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ tcpsrfd = -1;
+ }
+ debug(F100,"tcpsrv_open tcpsrfd","",tcpsrfd);
+ if (tcpsrfd == -1) {
+
+ /* Set up socket structure and get host address */
+
+ bzero((char *)&saddr, sizeof(saddr));
+ debug(F100,"tcpsrv_open bzero ok","",0);
+ saddr.sin_family = AF_INET;
+ if (tcp_address) {
+#ifdef INADDRX
+ inaddrx = inet_addr(tcp_address);
+ saddr.sin_addr.s_addr = *(unsigned long *)&inaddrx;
+#else
+ saddr.sin_addr.s_addr = inet_addr(tcp_address);
+#endif /* INADDRX */
+ } else
+ saddr.sin_addr.s_addr = INADDR_ANY;
+
+ /* Get a file descriptor for the connection. */
+
+ saddr.sin_port = service->s_port;
+ ipaddr[0] = '\0';
+
+ debug(F100,"tcpsrv_open calling socket","",0);
+ if ((tcpsrfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("TCP socket error");
+ debug(F101,"tcpsrv_open socket error","",errno);
+ return (-1);
+ }
+ errno = 0;
+
+ /* Specify the Port may be reused */
+
+ debug(F100,"tcpsrv_open calling setsockopt","",0);
+ x = setsockopt(tcpsrfd,
+ SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof on);
+ debug(F101,"tcpsrv_open setsockopt","",x);
+
+ /* Now bind to the socket */
+ printf("\nBinding socket to port %d ...\n",
+ ntohs((unsigned short)service->s_port));
+ if (bind(tcpsrfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ i = errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ ttyfd = -1;
+ wasclosed = 1;
+ errno = i; /* and report this error */
+ debug(F101,"tcpsrv_open bind errno","",errno);
+ printf("?Unable to bind to socket (errno = %d)\n",errno);
+ return(-1);
+ }
+ debug(F100,"tcpsrv_open bind OK","",0);
+ printf("Listening ...\n");
+ if (listen(tcpsrfd, 15) < 0) {
+ i = errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ ttyfd = -1;
+ wasclosed = 1;
+ errno = i; /* And report this error */
+ debug(F101,"tcpsrv_open listen errno","",errno);
+ return(-1);
+ }
+ debug(F100,"tcpsrv_open listen OK","",0);
+ tcpsrv_port = ntohs((unsigned short)service->s_port);
+ }
+
+#ifdef CK_SSL
+ if (ck_ssleay_is_installed()) {
+ if (!ssl_tn_init(SSL_SERVER)) {
+ ssl_failed = 1;
+ if (bio_err!=NULL) {
+ BIO_printf(bio_err,"do_ssleay_init() failed\n");
+ ERR_print_errors(bio_err);
+ } else {
+ fflush(stderr);
+ fprintf(stderr,"do_ssleay_init() failed\n");
+ ERR_print_errors_fp(stderr);
+ }
+ if (tls_only_flag || ssl_only_flag) {
+#ifdef TCPIPLIB
+ socket_close(ttyfd);
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(ttyfd);
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ return(-1);
+ }
+ /* we will continue to accept the connection */
+ /* without SSL or TLS support unless required. */
+ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ }
+ }
+#endif /* CK_SSL */
+
+ printf("\nWaiting to Accept a TCP/IP connection on port %d ...\n",
+ ntohs((unsigned short)service->s_port));
+ saddrlen = sizeof(saddr);
+
+#ifdef BSDSELECT
+ tv.tv_sec = tv.tv_usec = 0L;
+ if (timo < 0)
+ tv.tv_usec = (long) -timo * 10000L;
+ else
+ tv.tv_sec = timo;
+ debug(F101,"tcpsrv_open BSDSELECT","",timo);
+#else
+ debug(F101,"tcpsrv_open not BSDSELECT","",timo);
+#endif /* BSDSELECT */
+
+ if (timo) {
+ while (!ready_to_accept) {
+#ifdef BSDSELECT
+ FD_ZERO(&rfds);
+ FD_SET(tcpsrfd, &rfds);
+ ready_to_accept =
+ ((select(FD_SETSIZE,
+#ifdef HPUX
+#ifdef HPUX1010
+ (fd_set *)
+#else
+
+ (int *)
+#endif /* HPUX1010 */
+#else
+#ifdef __DECC
+ (fd_set *)
+#endif /* __DECC */
+#endif /* HPUX */
+ &rfds, NULL, NULL, &tv) > 0) &&
+ FD_ISSET(tcpsrfd, &rfds));
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+#define ck_sleepint 250
+ ready_to_accept =
+ (select(&tcpsrfd, 1, 0, 0,
+ timo < 0 ? -timo :
+ (timo > 0 ? timo * 1000L : ck_sleepint)) == 1
+ );
+#else
+#ifdef BELLSELECT
+ FD_ZERO(rfds);
+ FD_SET(tcpsrfd, rfds);
+ ready_to_accept =
+ ((select(128, rfds, NULL, NULL, timo < 0 ? -timo :
+ (timo > 0 ? timo * 1000L)) > 0) &&
+ FD_ISSET(tcpsrfd, rfds));
+#else
+/* Try this - what's the worst that can happen... */
+
+ FD_ZERO(&rfds);
+ FD_SET(tcpsrfd, &rfds);
+ ready_to_accept =
+ ((select(FD_SETSIZE,
+ (fd_set *) &rfds, NULL, NULL, &tv) > 0) &&
+ FD_ISSET(tcpsrfd, &rfds));
+
+#endif /* BELLSELECT */
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ }
+ }
+ if (ready_to_accept || timo == 0) {
+ if ((ttyfd = accept(tcpsrfd,
+ (struct sockaddr *)&saddr,&saddrlen)) < 0) {
+ i = errno; /* save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ errno = i; /* and report this error */
+ debug(F101,"tcpsrv_open accept errno","",errno);
+ return(-1);
+ }
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+
+#ifndef NOTCPOPTS
+#ifndef datageneral
+#ifdef SOL_SOCKET
+#ifdef TCP_NODELAY
+ no_delay(ttyfd,tcp_nodelay);
+ debug(F101,"tcpsrv_open no_delay","",tcp_nodelay);
+#endif /* TCP_NODELAY */
+#ifdef SO_KEEPALIVE
+ keepalive(ttyfd,tcp_keepalive);
+ debug(F101,"tcpsrv_open keepalive","",tcp_keepalive);
+#endif /* SO_KEEPALIVE */
+#ifdef SO_LINGER
+ ck_linger(ttyfd,tcp_linger, tcp_linger_tmo);
+ debug(F101,"tcpsrv_open linger","",tcp_linger_tmo);
+#endif /* SO_LINGER */
+#ifdef SO_SNDBUF
+ sendbuf(ttyfd,tcp_sendbuf);
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ recvbuf(ttyfd,tcp_recvbuf);
+#endif /* SO_RCVBUF */
+#endif /* SOL_SOCKET */
+#endif /* datageneral */
+#endif /* NOTCPOPTS */
+
+ ttnet = nett; /* TCP/IP (sockets) network */
+ tcp_incoming = 1; /* This is an incoming connection */
+ sstelnet = 1; /* Do server-side Telnet protocol */
+
+ /* See if the service is TELNET. */
+ x = (unsigned short)service->s_port;
+ service2 = getservbyname("telnet", "tcp");
+ if (service2 && x == service2->s_port) {
+ if (ttnproto != NP_TCPRAW) /* Yes and if raw port not requested */
+ ttnproto = NP_TELNET; /* Set protocol to TELNET. */
+ }
+
+ ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),20);
+ if (tcp_rdns) {
+ if (!quiet) {
+ printf(" Reverse DNS Lookup... ");
+ fflush(stdout);
+ }
+ if (host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET)) {
+ host = ck_copyhostent(host);
+ debug(F100,"tcpsrv_open gethostbyaddr != NULL","",0);
+ if (!quiet) {
+ printf("(OK)\n");
+ fflush(stdout);
+ }
+ name[0] = '*';
+ ckstrncpy(&name[1],host->h_name,78);
+ strncat(name,":",80-strlen(name));
+ strncat(name,p,80-strlen(name));
+ if (!quiet
+#ifndef NOICP
+ && !doconx
+#endif /* NOICP */
+ )
+ printf("%s connected on port %s\n",host->h_name,p);
+ } else {
+ if (!quiet) printf("Failed.\n");
+ }
+ } else if (!quiet) printf("(OK)\n");
+
+ if (!tcp_rdns || !host) {
+ ckstrncpy(name,ipaddr,80);
+ ckstrncat(name,":",80);
+ ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80);
+ if (!quiet
+#ifndef NOICP
+ && !doconx
+#endif /* NOICP */
+ )
+ printf("%s connected on port %d\n",ipaddr,ntohs(saddr.sin_port));
+ }
+ if (!quiet) fflush(stdout);
+
+#ifdef CK_SECURITY
+ /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
+ ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ?
+ (char *)host->h_name : ipaddr,
+ ipaddr,
+ uidbuf,
+ ttyfd
+ );
+#endif /* CK_SECURITY */
+
+#ifdef CK_SSL
+ if (ck_ssleay_is_installed() && !ssl_failed) {
+ if (ck_ssl_incoming(ttyfd) < 0) {
+#ifdef TCPIPLIB
+ socket_close(ttyfd);
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(ttyfd);
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ return(-1);
+ }
+ }
+#endif /* CK_SSL */
+
+#ifndef datageneral
+ /* Find out our own IP address. */
+ l_slen = sizeof(l_addr);
+ bzero((char *)&l_addr, l_slen);
+#ifndef EXCELAN
+ if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) {
+ char * s = (char *)inet_ntoa(l_addr.sin_addr);
+ ckstrncpy(myipaddr, s,20);
+ debug(F110,"getsockname",myipaddr,0);
+ }
+#endif /* EXCELAN */
+#endif /* datageneral */
+
+ if (tn_ini() < 0) /* Start TELNET negotiations. */
+ if (ttchk() < 0) { /* Disconnected? */
+ i = errno; /* save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ errno = i; /* and report this error */
+ debug(F101,"tcpsrv_open accept errno","",errno);
+ return(-1);
+ }
+ debug(F101,"tcpsrv_open service","",x);
+ if (*lcl < 0) /* Set local mode. */
+ *lcl = 1;
+
+#ifdef CK_KERBEROS
+#ifdef KRB5_U2U
+ if ( ttnproto == NP_K5U2U ) {
+ if (k5_user_to_user_server_auth() != 0) {
+ i = errno; /* save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ errno = i; /* and report this error */
+ debug(F101,"tcpsrv_open accept errno","",errno);
+ return(-1);
+ }
+ }
+#endif /* KRB5_U2U */
+#endif /* CK_KERBEROS */
+ return(0); /* Done. */
+ } else {
+ i = errno; /* save error code */
+#ifdef TCPIPLIB
+ socket_close(tcpsrfd);
+#else /* TCPIPLIB */
+ close(tcpsrfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ tcpsrfd = -1;
+ tcpsrv_port = 0;
+ errno = i; /* and report this error */
+ debug(F101,"tcpsrv_open accept errno","",errno);
+ return(-1);
+ }
+}
+#endif /* NOLISTEN */
+#endif /* OS2 */
+#endif /* TCPSOCKET */
+#endif /* NONET */
+
+#ifdef TCPSOCKET
+char *
+ckname2addr(name) char * name;
+{
+#ifdef HPUX5
+ return("");
+#else
+ struct hostent *host;
+
+ if (name == NULL || *name == '\0')
+ return("");
+
+ host = gethostbyname(name);
+ if ( host ) {
+ host = ck_copyhostent(host);
+ return(inet_ntoa(*((struct in_addr *) host->h_addr)));
+ }
+ return("");
+#endif /* HPUX5 */
+}
+
+char *
+ckaddr2name(addr) char * addr;
+{
+#ifdef HPUX5
+ return("");
+#else
+ struct hostent *host;
+ struct in_addr sin_addr;
+
+ if (addr == NULL || *addr == '\0')
+ return("");
+
+ sin_addr.s_addr = inet_addr(addr);
+ host = gethostbyaddr((char *)&sin_addr,4,AF_INET);
+ if (host) {
+ host = ck_copyhostent(host);
+ return((char *)host->h_name);
+ }
+ return("");
+#endif /* HPUX5 */
+}
+#endif /* TCPSOCKET */
+
+unsigned long peerxipaddr = 0L;
+
+char *
+ckgetpeer() {
+#ifdef TCPSOCKET
+ static char namebuf[256];
+ static struct hostent *host;
+ static struct sockaddr_in saddr;
+#ifdef PTX
+ static size_t saddrlen;
+#else
+#ifdef AIX42
+ /* It's size_t in 4.2 but int in 4.1 and earlier. */
+ /* Note: the 4.2 man page lies; believe socket.h. */
+ static size_t saddrlen;
+#else
+#ifdef UNIXWARE
+ static size_t saddrlen;
+#else /* UNIXWARE */
+#ifdef DEC_TCPIP
+ static unsigned int saddrlen;
+#else
+ static int saddrlen;
+#endif /* VMS */
+#endif /* UNIXWARE */
+#endif /* AIX42 */
+#endif /* PTX */
+ saddrlen = sizeof(saddr);
+ if (getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen) < 0) {
+ debug(F111,"ckgetpeer failure",ckitoa(ttyfd),errno);
+ return(NULL);
+ }
+ host = gethostbyaddr((char *)&saddr.sin_addr,4,AF_INET);
+ if (host) {
+ host = ck_copyhostent(host);
+ ckstrncpy(namebuf,(char *)host->h_name,80);
+ } else {
+ ckstrncpy(namebuf,(char *)inet_ntoa(saddr.sin_addr),80);
+ }
+ peerxipaddr = ntohl(saddr.sin_addr.s_addr);
+ debug(F111,"ckgetpeer",namebuf,peerxipaddr);
+ return(namebuf);
+#else
+ return(NULL);
+#endif /* TCPSOCKET */
+}
+
+/* Get fully qualified IP hostname */
+
+#ifndef NONET
+char *
+#ifdef CK_ANSIC
+ckgetfqhostname(char * name)
+#else
+ckgetfqhostname(name) char * name;
+#endif /* CK_ANSIC */
+{
+#ifdef NOCKGETFQHOST
+
+ return(name);
+
+#else /* If the following code dumps core, define NOCKGETFQHOST and rebuild. */
+
+ static char namebuf[256];
+ struct hostent *host=NULL;
+ struct sockaddr_in r_addr;
+ int i;
+
+ debug(F110,"ckgetfqhn()",name,0);
+
+ ckstrncpy(namebuf,name,256);
+ namebuf[255] = '\0';
+ i = ckindex(":",namebuf,0,0,0);
+ if (i)
+ namebuf[i-1] = '\0';
+
+ bzero((char *)&r_addr, sizeof(r_addr));
+
+ host = gethostbyname(namebuf);
+ if (host) {
+ host = ck_copyhostent(host);
+ debug(F100,"ckgetfqhn() gethostbyname != NULL","",0);
+ r_addr.sin_family = host->h_addrtype;
+#ifdef HADDRLIST
+#ifdef h_addr
+ /* This is for trying multiple IP addresses - see <netdb.h> */
+ if (!(host->h_addr_list))
+ goto exit_func;
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&r_addr.sin_addr,
+ host->h_length
+ );
+#else
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* HADDRLIST */
+#ifndef EXCELAN
+ debug(F111,"BCOPY","host->h_addr",host->h_addr);
+#endif /* EXCELAN */
+ debug(F111,"BCOPY"," (caddr_t)&r_addr.sin_addr",
+ (caddr_t)&r_addr.sin_addr);
+ debug(F111,"BCOPY","host->h_length",host->h_length);
+
+#ifdef NT
+ /* Windows 95/98 requires a 1 second wait between calls to Microsoft */
+ /* provided DNS functions. Otherwise, the TTL of the DNS response */
+ /* is ignored. */
+ if (isWin95())
+ sleep(1);
+#endif /* NT */
+ host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET);
+ if (host) {
+ host = ck_copyhostent(host);
+ debug(F100,"ckgetfqhn() gethostbyaddr != NULL","",0);
+ ckstrncpy(namebuf, host->h_name, 256);
+ }
+ }
+
+#ifdef HADDRLIST
+#ifdef h_addr
+ exit_func:
+#endif /* h_addr */
+#endif /* HADDRLIST */
+
+ if (i > 0)
+ strncat(namebuf,&name[i-1],256-strlen(namebuf)-strlen(&name[i-1]));
+ debug(F110,"ckgetfqhn()",namebuf,0);
+ return(namebuf);
+#endif /* NOCKGETFQHOST */
+}
+
+VOID
+#ifdef CK_ANSIC
+setnproto(char * p)
+#else
+setnproto(p) char * p;
+#endif /* CK_ANSIC */
+{
+ if (!isdigit(*p)) {
+ if (!strcmp("kermit",p))
+ ttnproto = NP_KERMIT;
+ else if (!strcmp("telnet",p))
+ ttnproto = NP_TELNET;
+ else if (!strcmp("http",p))
+ ttnproto = NP_TCPRAW;
+#ifdef RLOGCODE
+ else if (!strcmp("login",p))
+ ttnproto = NP_RLOGIN;
+#endif /* RLOGCODE */
+#ifdef CK_SSL
+ /* Commonly used SSL ports (might not be in services file) */
+ else if (!strcmp("https",p)) {
+ ttnproto = NP_SSL;
+ ssl_only_flag = 1;
+ } else if (!strcmp("ssl-telnet",p)) {
+ ttnproto = NP_TELNET;
+ ssl_only_flag = 1;
+ } else if (!strcmp("telnets",p)) {
+ ttnproto = NP_TELNET;
+ ssl_only_flag = 1;
+ }
+#endif /* CK_SSL */
+#ifdef CK_KERBEROS
+#ifdef RLOGCODE
+ else if (!strcmp("klogin",p)) {
+ if (ck_krb5_is_installed())
+ ttnproto = NP_K5LOGIN;
+ else if (ck_krb4_is_installed())
+ ttnproto = NP_K4LOGIN;
+ else
+ ttnproto = NP_RLOGIN;
+ } else if (!strcmp("eklogin",p)) {
+ if (ck_krb5_is_installed())
+ ttnproto = NP_EK5LOGIN;
+ else if (ck_krb4_is_installed())
+ ttnproto = NP_EK4LOGIN;
+ else
+ ttnproto = NP_RLOGIN;
+ }
+#endif /* RLOGCODE */
+#endif /* CK_KERBEROS */
+ else
+ ttnproto = NP_NONE;
+ } else {
+ switch (atoi(p)) {
+ case 23: /* Telnet */
+ ttnproto = NP_TELNET;
+ break;
+ case 513:
+ ttnproto = NP_RLOGIN;
+ break;
+ case 1649:
+ ttnproto = NP_KERMIT;
+ break;
+#ifdef CK_SSL
+ case 443:
+ ttnproto = NP_SSL;
+ ssl_only_flag = 1;
+ break;
+ case 151:
+ case 992:
+ ttnproto = NP_TELNET;
+ ssl_only_flag = 1;
+ break;
+#endif /* CK_SSL */
+#ifdef CK_KERBEROS
+ case 543:
+ if (ck_krb5_is_installed())
+ ttnproto = NP_K5LOGIN;
+ else if (ck_krb4_is_installed())
+ ttnproto = NP_K4LOGIN;
+ else
+ ttnproto = NP_RLOGIN;
+ break;
+ case 2105:
+ if (ck_krb5_is_installed())
+ ttnproto = NP_EK5LOGIN;
+ else if (ck_krb4_is_installed())
+ ttnproto = NP_EK4LOGIN;
+ else
+ ttnproto = NP_RLOGIN;
+ break;
+#endif /* CK_KERBEROS */
+ case 80: /* HTTP */
+ ttnproto = NP_TCPRAW;
+ break;
+ default:
+ ttnproto = NP_NONE;
+ break;
+ }
+ }
+}
+
+/* ckgetservice() is used to determine the port number for a given */
+/* service taking into account the use of DNS SRV records. */
+
+static struct servent servrec;
+static struct servent *
+ckgetservice(hostname, servicename, ip, iplen)
+ char *hostname; char * servicename; char * ip; int iplen;
+{
+ struct servent * service = NULL;
+#ifdef CK_DNS_SRV
+ struct sockaddr * dns_addrs = NULL;
+ int dns_naddrs = 0;
+#endif /* CK_DNS_SRV */
+
+ if (isdigit(*servicename)) { /* Use socket number without lookup */
+ service = &servrec;
+ service->s_port = htons((unsigned short)atoi(servicename));
+ } else { /* Otherwise lookup the service name */
+#ifdef CK_DNS_SRV
+ if (tcp_dns_srv && !quiet) {
+ printf(" DNS SRV Lookup... ");
+ fflush(stdout);
+ }
+ if (tcp_dns_srv &&
+ locate_srv_dns(hostname,
+ servicename,
+ "tcp",
+ &dns_addrs,
+ &dns_naddrs
+ )
+ ) {
+ /* Use the first one. Eventually we should cycle through all */
+ /* the returned IP addresses and port numbers. */
+ struct sockaddr_in *sin = NULL;
+#ifdef BETADEBUG
+ int i;
+ printf("\r\n");
+ for ( i=0;i<dns_naddrs;i++ ) {
+ sin = (struct sockaddr_in *) &dns_addrs[i];
+ printf("dns_addrs[%d] = %s %d\r\n", i,
+ (char *)inet_ntoa(sin->sin_addr),
+ ntohs(sin->sin_port));
+ }
+#endif /* BETADEBUG */
+ sin = (struct sockaddr_in *) &dns_addrs[0];
+ if ( ip && iplen > 0 )
+ ckstrncpy(ip,(char *)inet_ntoa(sin->sin_addr),iplen);
+ service = &servrec;
+ service->s_port = sin->sin_port;
+
+ free(dns_addrs);
+ dns_addrs = NULL;
+ dns_naddrs = 0;
+ } else
+#endif /* CK_DNS_SRV */
+ service = getservbyname(servicename, "tcp");
+ }
+ if (!service) {
+ if (!ckstrcmp("kermit",servicename,-1,0)) { /* Kermit service port */
+ service = &servrec;
+ service->s_port = htons(1649);
+ } else if (!ckstrcmp("telnet",servicename,-1,0)) { /* Telnet port */
+ service = &servrec;
+ service->s_port = htons(23);
+ } else if (!ckstrcmp("http",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(80);
+ }
+#ifdef RLOGCODE
+ else if (!ckstrcmp("login",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(513);
+ }
+#endif /* RLOGCODE */
+#ifdef CK_SSL
+ /* Commonly used SSL ports (might not be in services file) */
+ else if (!ckstrcmp("https",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(443);
+ } else if (!ckstrcmp("ssl-telnet",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(151);
+ } else if (!ckstrcmp("telnets",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(992);
+ }
+#endif /* CK_SSL */
+#ifdef CK_KERBEROS
+#ifdef RLOGCODE
+ else if (!ckstrcmp("klogin",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(543);
+ } else if (!ckstrcmp("eklogin",servicename,-1,0)) {
+ service = &servrec;
+ service->s_port = htons(2105);
+ }
+#endif /* RLOGCODE */
+#endif /* CK_KERBEROS */
+ }
+ return(service);
+}
+
+/* N E T O P E N -- Open a network connection */
+/*
+ Calling conventions same as ttopen(), except third argument is network
+ type rather than modem type. Designed to be called from within ttopen.
+*/
+
+int
+netopen(name, lcl, nett) char *name; int *lcl, nett; {
+ char *p;
+ int i, x, dns = 0;
+#ifdef TCPSOCKET
+ int isconnect = 0;
+#ifdef SO_OOBINLINE
+ int on = 1;
+#endif /* SO_OOBINLINE */
+ struct servent *service=NULL;
+ struct hostent *host=NULL;
+ struct sockaddr_in r_addr;
+ struct sockaddr_in sin;
+ struct sockaddr_in l_addr;
+ GSOCKNAME_T l_slen;
+#ifdef EXCELAN
+ struct sockaddr_in send_socket;
+#endif /* EXCELAN */
+
+#ifdef INADDRX
+/* inet_addr() is of type struct in_addr */
+#ifdef datageneral
+ extern struct in_addr inet_addr();
+#else
+#ifdef HPUX5WINTCP
+ extern struct in_addr inet_addr();
+#endif /* HPUX5WINTCP */
+#endif /* datageneral */
+ struct in_addr iax;
+#else
+#ifdef INADDR_NONE
+ struct in_addr iax;
+#else /* INADDR_NONE */
+ long iax;
+#endif /* INADDR_NONE */
+#endif /* INADDRX */
+#endif /* TCPSOCKET */
+
+#ifdef COMMENT
+/* This causes big trouble */
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif /* INADDR_NONE */
+#endif /* COMMENT */
+
+#ifdef SUNX25 /* Code for SunLink X.25 support */
+#define X29PID 1 /* X.29 Protocol ID */
+_PROTOTYP(SIGTYP x25oobh, (int) );
+ CONN_DB x25host;
+#ifndef X25_WR_FACILITY
+ FACILITY x25facil;
+#else
+ FACILITY_DB x25facil;
+#endif /* X25_WR_FACILITY */
+ static int needh = 1;
+ PID_T pid;
+ extern int linkid, lcn, x25ver;
+#endif /* SUNX25 */
+#ifdef ANYX25
+ extern int revcall, closgr, cudata;
+ extern char udata[];
+#endif /* ANYX25 */
+
+#ifdef IBMX25 /* Variables for IBM X25 */
+ extern int x25port; /* Logical port to use */
+ extern x25addr_t local_nua; /* Local X.25 address */
+ extern x25addr_t remote_nua; /* Remote X.25 address */
+ extern char x25name[]; /* X25 device name (sx25a0) */
+ extern char x25dev[]; /* X25 device file /dev/x25pkt */
+ ulong bind_flags = 0; /* Flags for binding the X25 stream */
+ ulong token = 0; /* Temporary return code */
+#endif /* IBMX25 */
+
+ debug(F101,"netopen nett","",nett);
+ *ipaddr = '\0'; /* Initialize IP address string */
+
+#ifdef SUNX25
+ if (nett == NET_SX25) { /* If network type is X.25 */
+ netclos(); /* Close any previous net connection */
+ ttnproto = NP_NONE; /* No protocol selected yet */
+
+ /* Set up host structure */
+ bzero((char *)&x25host,sizeof(x25host));
+ if ((x25host.hostlen = pkx121(name,x25host.host)) < 0) {
+ fprintf (stderr,"Invalid X.121 host address %s\n",name);
+ errno = 0;
+ return (-1);
+ }
+ x25host.datalen = X29PIDLEN;
+ x25host.data[0] = X29PID;
+
+ /* Set call user data if specified */
+ if (cudata) {
+ ckstrncpy((char *)x25host.data+X29PIDLEN,udata,(int)strlen(udata));
+ x25host.datalen += (int)strlen(udata);
+ }
+
+ /* Open SunLink X.25 socket */
+ if (!quiet && *name) {
+ printf(" Trying %s... ", name);
+ fflush(stdout);
+ }
+ if ((ttyfd = socket(AF_X25, SOCK_STREAM, 0)) < 0) {
+ debug(F101,"netopen socket error","",errno);
+ perror ("X.25 socket error");
+ return (-1);
+ }
+
+ /* Setting X.25 out-of-band data handler */
+ pid = getpid();
+ if (ioctl(ttyfd,SIOCSPGRP,&pid)) {
+ perror("X.25 set process group id error");
+ return(-1);
+ }
+ (VOID) signal(SIGURG,x25oobh);
+
+ /* Set reverse charge call and closed user group if requested */
+ bzero ((char *)&x25facil,sizeof(x25facil));
+
+#ifndef X25_WR_FACILITY
+/* New SunLink (7.0 or 8.0, not sure which)... */
+ x25facil.type = T_REVERSE_CHARGE; /* Reverse Charge */
+ x25facil.f_reverse_charge = revcall ? 1 : 0;
+ if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) {
+ perror ("Setting X.25 reverse charge");
+ return (-1);
+ }
+ if (closgr > -1) { /* Closed User Group (Outgoing) */
+ bzero ((char *)&x25facil,sizeof(x25facil));
+ x25facil.type = T_CUG;
+ x25facil.f_cug_req = CUG_REQ_ACS;
+ x25facil.f_cug_index = closgr;
+ if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) {
+ perror ("Setting X.25 closed user group");
+ return (-1);
+ }
+ }
+#else
+/* Old SunLink 6.0 (or 7.0?)... */
+ if (revcall) x25facil.reverse_charge = revcall;
+ if (closgr > -1) {
+ x25facil.cug_req = 1;
+ x25facil.cug_index = closgr;
+ }
+ if (ioctl(ttyfd,X25_WR_FACILITY,&x25facil) < 0) {
+ perror ("Setting X.25 facilities");
+ return (-1);
+ }
+#endif /* X25_WR_FACILITY */
+
+ /* Need X.25 header with bits Q and M */
+ if (ioctl (ttyfd,X25_HEADER,&needh) < 0) {
+ perror ("Setting X.25 header");
+ return (-1);
+ }
+
+ /* Connects to remote host via SunLink X.25 */
+ if (connect(ttyfd,(struct sockaddr *)&x25host,sizeof(x25host)) < 0) {
+ i = errno;
+ debug(F101,"netopen connect errno","",i);
+ if (i) {
+ perror("netopen x25 connect");
+ x25diag();
+ }
+ (VOID) netclos();
+ ttyfd = -1;
+ wasclosed = 1;
+ ttnproto = NP_NONE;
+ errno = i;
+ return (-1);
+ }
+
+ /* Get X.25 link identification used for the connection */
+ if (ioctl(ttyfd,X25_GET_LINK,&linkid) < 0) {
+ perror ("Getting X.25 link id");
+ return (-1);
+ }
+
+ /* Get X.25 logical channel number used for the connection */
+ if (ioctl(ttyfd,X25_RD_LCGN,&lcn) < 0) {
+ perror ("Getting X.25 lcn");
+ return (-1);
+ }
+
+ /* Get SunLink X.25 version */
+ if (ioctl(ttyfd,X25_VERSION,&x25ver) < 0) {
+ perror ("Getting SunLink X.25 version");
+ return (-1);
+ }
+ ttnet = nett; /* Sunlink X.25 network */
+ ttnproto = NP_X3; /* PAD X.3, X.28, X.29 protocol */
+ if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */
+ return(0);
+ } else /* Note that SUNX25 support can coexist with TCP/IP support. */
+#endif /* SUNX25 */
+
+#ifdef IBMX25
+ /* riehm */
+ if (nett == NET_IX25) { /* IBM AIX X.25 */
+ netclos(); /* Close any previous net connection */
+ ttnproto = NP_NONE; /* No protocol selected yet */
+
+ /* find out who we are - this is not so easy on AIX */
+ /* riehm: need to write the code that finds this out
+ * automatically, or at least allow it to be configured
+ * somehow
+ */
+ if (!local_nua[0] && !x25local_nua(local_nua)) {
+ return(-1);
+ }
+
+ /* Initialise the X25 API (once per process? once per connection?) */
+
+ debug(F110, "Opening ", x25dev, 0 );
+ /* set O_NDELAY to allow polling? */
+ if ((ttyfd = open(x25dev, O_RDWR)) < 0) {
+ perror ("X.25 device open error");
+ debug(F101,"netopen: device open error","",errno);
+ return (-1);
+ }
+
+ /* push the NPI onto the STREAM */
+ if (ioctl(ttyfd,I_PUSH,"npi") < 0 ) {
+ close(ttyfd);
+ ttyfd = -1;
+ wasclosed = 1;
+ perror( "kermit: netopen(): couldn't push npi on the X25 stream" );
+ debug(F101,"netopen: can't push npi on the X25 stream","",errno);
+ return (-1);
+ }
+
+ /* set up server mode - bind the x25 port and wait for
+ * incoming connections
+ */
+ if (name[0] == '*') { /* Server */
+ /* set up a server - see the warning in x25bind() */
+ bind_flags |= TOKEN_REQUEST;
+
+ /* bind kermit to the local X25 address */
+ token = x25bind(ttyfd,
+ local_nua,
+ udata,
+ (int)strlen( udata ),
+ 1,
+ x25port,
+ bind_flags
+ );
+ if (token < 0) {
+ debug(F100,"netopen: couldn't bind to local X25 address","",0);
+ netclos();
+ return(-1);
+ }
+ /* Currently not connected to a remote host */
+
+ remote_nua[0] = '\0';
+
+ /* store the fd so that incoming calls can have their own fd
+ * This is almost support for a true server (ie: a'la ftpd)
+ * but we're not quite there yet.
+ * used in netclos()
+ */
+ x25serverfd = ttyfd;
+ /*
+ * wait for an incoming call
+ * this should happen in the "server" command and not in
+ * the "set host *" command.
+ */
+ if ((ttyfd = x25getcall(ttyfd)) < 0) {
+ netclos();
+ return(-1);
+ }
+ } else { /* Client */
+ /* Bind kermit to the local X25 address */
+ token = x25bind(
+ ttyfd,
+ local_nua,
+ (char *)NULL,
+ 0,
+ 0,
+ x25port,
+ bind_flags
+ );
+ if (token < 0) {
+ debug(F100,"netopen: couldn't bind to local X25 address","",0);
+ netclos();
+ return(-1);
+ }
+/* riehm: this should be done via the CONNECT command, not HOST! */
+ {
+ x25serverfd = 0;
+ /* call the remote host */
+ /* name == address of remote host as char* */
+ if (x25call(ttyfd, name, udata) < 0 ) {
+ debug(F100,
+ "netopen: couldn't connect to remote X25 address",
+ "", 0);
+ netclos();
+ return(-1);
+ }
+ strcpy(remote_nua, name);
+ }
+ }
+ ttnet = nett; /* AIX X.25 network */
+ if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */
+ return(0);
+
+ } else /* Note that IBMX25 support can coexist with TCP/IP support. */
+#endif /* IBMX25 */
+
+/* Add support for other networks here. */
+
+ if (nett != NET_TCPB) return(-1); /* BSD socket support */
+
+#ifdef TCPSOCKET
+ netclos(); /* Close any previous connection. */
+ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */
+ debug(F110,"netopen namecopy",namecopy,0);
+
+#ifndef NOLISTEN
+ if (name[0] == '*')
+ return(tcpsrv_open(name, lcl, nett, 0));
+#endif /* NOLISTEN */
+
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ debug(F110,"netopen name has colon",namecopy,0);
+ *p++ = '\0'; /* Get service name or number */
+#ifdef CK_URL
+ /*
+ Here we have to check for various popular syntaxes:
+ host:port (our original syntax)
+ URL such as telnet:host or telnet://host/
+ Or even telnet://user:password@host:port/path/
+ Or a malformed URL such as generated by Netscape 4.0 like:
+ telnet:telnet or telnet::host.
+ */
+
+ /*
+ * REPLACE THIS CODE WITH urlparse() but not on the day of the
+ * C-Kermit 8.0 RELEASE.
+ */
+
+ if (*p == ':') /* a second colon */
+ *p++ = '\0'; /* get rid of that one too */
+ while (*p == '/') *p++ = '\0'; /* and slashes */
+ x = strlen(p); /* Length of remainder */
+ if (p[x-1] == '/') /* If there is a trailing slash */
+ p[x-1] = '\0'; /* remove it. */
+ debug(F110,"netopen namecopy after stripping",namecopy,0);
+ debug(F110,"netopen p after stripping",p,0);
+ service = getservbyname(namecopy,"tcp");
+ if (service ||
+#ifdef RLOGCODE
+ !ckstrcmp("rlogin",namecopy,NAMECPYL,0) ||
+#endif /* RLOGCODE */
+#ifdef CK_SSL
+ !ckstrcmp("telnets",namecopy,NAMECPYL,0) ||
+#endif /* CK_SSL */
+ !ckstrcmp("iksd",namecopy,NAMECPYL,0)
+ ) {
+ char temphost[256], tempservice[80], temppath[256];
+ char * q = p, *r = p, *w = p;
+ int uidfound=0;
+ extern char pwbuf[];
+ extern int pwflg, pwcrypt;
+
+ if (ttnproto == NP_DEFAULT)
+ setnproto(namecopy);
+
+ /* Check for userid and possibly password */
+ while (*p != '\0' && *p != '@')
+ p++; /* look for @ */
+ if (*p == '@') {
+ /* found username and perhaps password */
+ debug(F110,"netopen namecopy found @","",0);
+ *p = '\0';
+ p++;
+ while (*w != '\0' && *w != ':')
+ w++;
+ if (*w == ':')
+ *w++ = '\0';
+ /* r now points to username, save it and the password */
+ debug(F110,"netopen namecopy username",r,0);
+ debug(F110,"netopen namecopy password",w,0);
+ uidfound=1;
+ if ( strcmp(uidbuf,r) || *w )
+ ckstrncpy(pwbuf,w,PWBUFL+1);
+ ckstrncpy(uidbuf,r,UIDBUFLEN);
+ pwflg = 1;
+ pwcrypt = 0;
+ q = p; /* Host after user and pwd */
+ } else {
+ p = q; /* No username or password */
+ }
+ /* Now we must look for the optional port. */
+ debug(F110,"netopen x p",p,0);
+ debug(F110,"netopen x q",q,0);
+
+ /* Look for the port/service or a file/directory path */
+ while (*p != '\0' && *p != ':' && *p != '/')
+ p++;
+ if (*p == ':') {
+ debug(F110,"netopen found port",q,0);
+ *p++ = '\0'; /* Found a port name or number */
+ r = p;
+
+ /* Look for the end of port/service or a file/directory path */
+ while (*p != '\0' && *p != '/')
+ p++;
+ if (*p == '/')
+ *p++ = '\0';
+
+ debug(F110,"netopen port",r,0);
+ ckstrncpy(tempservice,r,80);
+ ckstrncpy(temphost,q,256);
+ ckstrncpy(temppath,p,256);
+ ckstrncpy(namecopy,temphost,NAMECPYL);
+ debug(F110,"netopen tempservice",tempservice,0);
+ debug(F110,"netopen temphost",temphost,0);
+ debug(F110,"netopen temppath",temppath,0);
+
+ /* move port/service to a buffer that won't go away */
+ x = strlen(namecopy);
+ p = namecopy + x + 1;
+ ckstrncpy(p, tempservice, NAMECPYL - x);
+ } else {
+ /* Handle a path if we found one */
+ if (*p == '/')
+ *p++ = '\0';
+ ckstrncpy(temppath,p,256);
+
+ /* We didn't find another port, but if q is a service */
+ /* then assume that namecopy is actually a host. */
+ if (getservbyname(q,"tcp")) {
+ p = q;
+ } else {
+#ifdef RLOGCODE
+ /* rlogin is not a valid service */
+ if (!ckstrcmp("rlogin",namecopy,6,0)) {
+ ckstrncpy(namecopy,"login",NAMECPYL);
+ }
+#endif /* RLOGCODE */
+ /* iksd is not a valid service */
+ if (!ckstrcmp("iksd",namecopy,6,0)) {
+ ckstrncpy(namecopy,"kermit",NAMECPYL);
+ }
+ /* Reconstruct namecopy */
+ ckstrncpy(tempservice,namecopy,80);
+ ckstrncpy(temphost,q,256);
+ ckstrncpy(namecopy,temphost,NAMECPYL);
+ debug(F110,"netopen tempservice",tempservice,0);
+ debug(F110,"netopen temphost",temphost,0);
+ debug(F110,"netopen temppath",temppath,0);
+
+ /* move port/service to a buffer that won't go away */
+ x = strlen(namecopy);
+ p = namecopy + x + 1;
+ ckstrncpy(p, tempservice, NAMECPYL - x - 1);
+ }
+ }
+ debug(F110,"netopen URL result: host",namecopy,0);
+ debug(F110,"netopen URL result: service",p,0);
+ debug(F110,"netopen URL result: path",temppath,0);
+
+#ifdef IKS_GET
+ /* If we have set a path specified, we need to try to GET it */
+ /* But we have another problem, we have to login first. How */
+ /* do we specify that a login must be done before the GET? */
+ /* The user's name if specified is in 'userid' and the */
+ /* password if any is in 'pwbuf'. */
+ if ( temppath[0] ) {
+ extern int action;
+ extern char * cmarg;
+
+ if ( !uidfound ) {
+ /* If no userid was specified as part of the URL but
+ * a path was specified, then we
+ * set the user name to anonymous and the password
+ * to the current userid.
+ */
+ ckstrncpy(pwbuf,uidbuf,PWBUFL);
+ ckstrncat(pwbuf,"@",PWBUFL);
+ pwflg = 1;
+ pwcrypt = 0;
+ ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
+ }
+
+ /*
+ * If a file path was specified we perform the GET
+ * operation and then terminate the connection.
+ *
+ * If a directory was given instead of a file, then
+ * we should REMOTE CD to the directory and list its
+ * contents. But how do we tell the difference?
+ */
+ makestr(&cmarg,temppath);
+ action = 'r';
+ }
+#endif /* IKS_GET */
+ }
+#endif /* CK_URL */
+ } else { /* Otherwise use telnet */
+ p = "telnet";
+ }
+/*
+ By the time we get here, namecopy[] should hold the null-terminated
+ hostname or address, and p should point to the service name or number.
+*/
+ debug(F110,"netopen host",namecopy,0);
+ debug(F110,"netopen service requested",p,0);
+
+ /* Use the service port to set the default protocol type if necessary */
+ if (ttnproto == NP_DEFAULT)
+ setnproto(p);
+
+ ckstrncpy(namecopy2,namecopy,NAMECPYL);
+ service = ckgetservice(namecopy,p,namecopy,NAMECPYL);
+ if (!service) {
+ fprintf(stderr, "Can't find port for service %s\n", p);
+#ifdef TGVORWIN
+ debug(F101,"netopen can't get service","",socket_errno);
+#else
+ debug(F101,"netopen can't get service","",errno);
+#endif /* TGVORWIN */
+ errno = 0; /* (rather than mislead) */
+ return(-1);
+ } else {
+ if (!ckstrcmp(namecopy,namecopy2,-1,0))
+ namecopy2[0] = '\0';
+ ckstrncpy(svcbuf,ckuitoa(ntohs(service->s_port)),sizeof(svcbuf));
+ debug(F110,"netopen service ok",svcbuf,0);
+ }
+
+#ifdef RLOGCODE
+ if (service && !strcmp("login",p) && service->s_port != htons(513)) {
+ fprintf(stderr,
+ " Warning: login service on port %d instead of port 513\n",
+ ntohs(service->s_port)
+ );
+ fprintf(stderr, " Edit SERVICES file if RLOGIN fails to connect.\n");
+ debug(F101,"tcpsrv_open login on port","",ntohs(service->s_port));
+ }
+#endif /* RLOGCODE */
+
+#ifndef NOHTTP
+ /* For HTTP connections we must preserve the original hostname and */
+ /* service requested so we can include them in the Host header. */
+ ckmakmsg(http_host_port,sizeof(http_host_port),namecopy,":",
+ ckitoa(ntohs(service->s_port)),NULL);
+
+ /* 'namecopy' contains the name of the host to which we want to connect */
+ /* 'svcbuf' contains the service name */
+ /* 'service->s_port' contains the port number in network byte order */
+
+ /* If we are using an http proxy, we need to create a buffer containing */
+ /* hostname:port-number */
+ /* to pass to the http_connect() function. Then we need to replace */
+ /* 'namecopy' with the name of the proxy server and the service->s_port */
+ /* with the port number of the proxy (default port 80). */
+
+ if ( tcp_http_proxy ) {
+ ckmakmsg(proxycopy,sizeof(proxycopy),namecopy,":",
+ ckuitoa(ntohs(service->s_port)),NULL);
+ ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);
+
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ debug(F110,"netopen name has colon",namecopy,0);
+ *p++ = '\0'; /* Get service name or number */
+ } else {
+ strcpy(++p,"http");
+ }
+
+ service = ckgetservice(namecopy,p,namecopy,NAMECPYL);
+ if (!service) {
+ fprintf(stderr, "Can't find port for service %s\n", p);
+#ifdef TGVORWIN
+ debug(F101,"netopen can't get service for proxy","",socket_errno);
+#else
+ debug(F101,"netopen can't get service for proxy","",errno);
+#endif /* TGVORWIN */
+ errno = 0; /* (rather than mislead) */
+ return(-1);
+ }
+ ckstrncpy(p,ckuitoa(ntohs(service->s_port)),NAMECPYL-(p-namecopy));
+
+ }
+#endif /* NOHTTP */
+
+ /* Set up socket structure and get host address */
+
+ bzero((char *)&r_addr, sizeof(r_addr));
+ debug(F100,"netopen bzero ok","",0);
+/*
+ NOTE: Originally the inet_addr() check was #ifdef NT, but is enabled for
+ all as of 20 Sep 97, to allow people to "set host" to a specific numeric IP
+ address without going through the multihomed host sequence and winding up
+ at a different place than the one requested.
+*/
+#ifdef INADDR_NONE
+ debug(F101,"netopen INADDR_NONE defined","",INADDR_NONE);
+#else /* INADDR_NONE */
+ debug(F100,"netopen INADDR_NONE not defined","",0);
+#endif /* INADDR_NONE */
+#ifdef INADDRX
+ debug(F100,"netopen INADDRX defined","",0);
+#else /* INADDRX */
+ debug(F100,"netopen INADDRX not defined","",0);
+#endif /* INADDRX */
+
+#ifndef NOMHHOST
+#ifdef INADDRX
+ iax = inet_addr(namecopy);
+ debug(F111,"netopen inet_addr",namecopy,iax.s_addr);
+#else /* INADDRX */
+#ifdef INADDR_NONE
+ iax.s_addr = inet_addr(namecopy);
+ debug(F111,"netopen inet_addr",namecopy,iax.s_addr);
+#else /* INADDR_NONE */
+#ifndef datageneral
+ iax = (unsigned int) inet_addr(namecopy);
+#else
+ iax = -1L;
+#endif /* datageneral */
+ debug(F111,"netopen inet_addr",namecopy,iax);
+#endif /* INADDR_NONE */
+#endif /* INADDRX */
+
+ dns = 0;
+ if (
+#ifdef INADDR_NONE
+/* This might give warnings on 64-bit platforms but they should be harmless */
+/* because INADDR_NONE should be all 1's anyway, thus the OR part is */
+/* probably superfluous -- not sure why it's even there, maybe it should be */
+/* removed. */
+ iax.s_addr == INADDR_NONE || iax.s_addr == (unsigned long) -1L
+#else /* INADDR_NONE */
+ iax < 0
+#endif /* INADDR_NONE */
+ ) {
+ if (!quiet) {
+ printf(" DNS Lookup... ");
+ fflush(stdout);
+ }
+ if ((host = gethostbyname(namecopy)) != NULL) {
+ debug(F100,"netopen gethostbyname != NULL","",0);
+ host = ck_copyhostent(host);
+ dns = 1; /* Remember we performed dns lookup */
+ r_addr.sin_family = host->h_addrtype;
+ if (tcp_rdns && host->h_name && host->h_name[0]
+#ifndef NOHTTP
+ && (tcp_http_proxy == NULL)
+#endif /* NOHTTP */
+ ) { /* Copying into our argument? */
+ ckstrncpy(name,host->h_name,80); /* Bad Bad Bad */
+ if ( (80-strlen(name)) > (strlen(svcbuf)+1) ) {
+ strncat(name,":",80-strlen(name));
+ strncat(name,svcbuf,80-strlen(name));
+ }
+ }
+
+#ifdef HADDRLIST
+#ifdef h_addr
+ /* This is for trying multiple IP addresses - see <netdb.h> */
+ if (!(host->h_addr_list))
+ return(-1);
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&r_addr.sin_addr,
+ host->h_length
+ );
+#else
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* HADDRLIST */
+#ifndef EXCELAN
+ debug(F111,"BCOPY","host->h_addr",host->h_addr);
+#endif /* EXCELAN */
+ debug(F111,"BCOPY"," (caddr_t)&r_addr.sin_addr",
+ (caddr_t)&r_addr.sin_addr);
+ debug(F111,"BCOPY"," r_addr.sin_addr.s_addr",
+ r_addr.sin_addr.s_addr);
+ debug(F111,"BCOPY","host->h_length",host->h_length);
+ }
+ }
+#endif /* NOMHHOST */
+
+ if (!dns) {
+#ifdef INADDRX
+/* inet_addr() is of type struct in_addr */
+ struct in_addr ina;
+ unsigned long uu;
+ debug(F100,"netopen gethostbyname == NULL: INADDRX","",0);
+ ina = inet_addr(namecopy);
+ uu = *(unsigned int *)&ina;
+#else /* Not INADDRX */
+/* inet_addr() is unsigned long */
+ unsigned long uu;
+ debug(F100,"netopen gethostbyname == NULL: Not INADDRX","",0);
+ uu = inet_addr(namecopy);
+#endif /* INADDRX */
+ debug(F101,"netopen uu","",uu);
+ if (
+#ifdef INADDR_NONE
+ !(uu == INADDR_NONE || uu == (unsigned int) -1L)
+#else /* INADDR_NONE */
+ uu != ((unsigned long)-1)
+#endif /* INADDR_NONE */
+ ) {
+ r_addr.sin_addr.s_addr = uu;
+ r_addr.sin_family = AF_INET;
+ } else {
+#ifdef VMS
+ fprintf(stdout, "\r\n"); /* complete any previous message */
+#endif /* VMS */
+ fprintf(stderr, "Can't get address for %s\n", namecopy);
+#ifdef TGVORWIN
+ debug(F101,"netopen can't get address","",socket_errno);
+#else
+ debug(F101,"netopen can't get address","",errno);
+#endif /* TGVORWIN */
+ errno = 0; /* Rather than mislead */
+ return(-1);
+ }
+ }
+
+ /* Get a file descriptor for the connection. */
+
+ r_addr.sin_port = service->s_port;
+ ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20);
+ debug(F110,"netopen trying",ipaddr,0);
+ if (!quiet && *ipaddr) {
+ printf(" Trying %s... ", ipaddr);
+ fflush(stdout);
+ }
+
+ /* Loop to try additional IP addresses, if any. */
+
+ do {
+#ifdef EXCELAN
+ send_socket.sin_family = AF_INET;
+ send_socket.sin_addr.s_addr = 0;
+ send_socket.sin_port = 0;
+ if ((ttyfd = socket(SOCK_STREAM, (struct sockproto *)0,
+ &send_socket, SO_REUSEADDR)) < 0)
+#else /* EXCELAN */
+#ifdef NT
+#ifdef COMMENT_X
+ /*
+ Must make sure that all sockets are opened in
+ Non-overlapped mode since we use the standard
+ C RTL functions to read and write data.
+ But it doesn't seem to work as planned.
+ */
+ {
+ int optionValue = SO_SYNCHRONOUS_NONALERT;
+ if (setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+ (char *) &optionValue, sizeof(optionValue))
+ != NO_ERROR)
+ return(-1);
+ }
+#endif /* COMMENT */
+#endif /* NT */
+
+ if ((ttyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+#endif /* EXCELAN */
+ {
+#ifdef EXCELAN
+ experror("TCP socket error");
+#else
+#ifdef VMS
+ fprintf(stdout, "\r\n"); /* complete any previous stdout */
+#endif /* VMS */
+#ifdef TGVORWIN
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ socket_perror("TCP socket error");
+ debug(F101,"netopen socket error","",socket_errno);
+#else
+ perror("TCP socket error");
+ debug(F101,"netopen socket error","",errno);
+#endif /* TGVORWIN */
+#endif /* EXCELAN */
+ return (-1);
+ }
+ errno = 0;
+
+#ifdef RLOGCODE
+ /* Not part of the RLOGIN RFC, but the BSD implementation */
+ /* requires that the client port be a priviliged port (<1024) */
+ /* on a Unix system this would require SuperUser permissions */
+ /* thereby saying that the root of the Unix system has given */
+ /* permission for this connection to be created */
+ if (service->s_port == htons((unsigned short)RLOGIN_PORT)) {
+ static unsigned short lport = 1024; /* max reserved port */
+#ifdef OS2
+ int s_errno;
+#endif /* OS2 */
+
+ lport--; /* Make sure we do not reuse a port */
+ if (lport == 512)
+ lport = 1023;
+
+ sin.sin_family = AF_INET;
+ if (tcp_address) {
+#ifdef INADDRX
+ inaddrx = inet_addr(tcp_address);
+ sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
+#else
+ sin.sin_addr.s_addr = inet_addr(tcp_address);
+#endif /* INADDRX */
+ } else
+ sin.sin_addr.s_addr = INADDR_ANY;
+ while (1) {
+ sin.sin_port = htons(lport);
+ if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
+ break;
+#ifdef OS2
+ s_errno = socket_errno;
+ if (s_errno && /* OS2 bind fails with 0, if already in use */
+#ifdef NT
+ s_errno != WSAEADDRINUSE
+#else
+ s_errno != SOCEADDRINUSE &&
+ s_errno != (SOCEADDRINUSE - SOCBASEERR)
+#endif /* NT */
+ )
+#else /* OS2 */
+#ifdef TGVORWIN
+ if (socket_errno != EADDRINUSE)
+#else
+ if (errno != EADDRINUSE)
+#endif /* TGVORWIN */
+#endif /* OS2 */
+ {
+#ifdef COMMENT
+ printf("\nBind failed with errno %d for port %d.\n",
+#ifdef OS2
+ s_errno
+#else
+#ifdef TGVORWIN
+ socket_errno
+#else
+ errno
+#endif /* TGVORWIN */
+#endif /* OS2 */
+ , lport
+ );
+#ifdef OS2
+ debug(F101,"rlogin bind failed","",s_errno);
+#else
+#ifdef TGVORWIN
+ debug(F101,"rlogin bind failed","",socket_errno);
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ socket_perror("rlogin bind");
+#else
+ debug(F101,"rlogin bind failed","",errno);
+ perror("rlogin bind");
+#endif /* TGVORWIN */
+#endif /* OS2 */
+#else /* COMMENT */
+#ifdef OS2
+ debug(F101,"rlogin bind s_errno","",s_errno);
+ perror("rlogin bind");
+#else
+#ifdef VMS
+ printf("\r\n"); /* complete any previous message */
+#endif /* VMS */
+#ifdef TGVORWIN
+ debug(F101,"rlogin bind socket_errno","",socket_errno);
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ socket_perror("rlogin bind");
+#else
+ debug(F101,"rlogin bind errno","",errno);
+ perror("rlogin bind");
+#endif /* TGVORWIN */
+#endif /* OS2 */
+ debug(F101,"rlogin local port","",lport);
+#endif /* COMMENT */
+ netclos();
+ return -1;
+ }
+ lport--;
+ if (lport == 512 /* lowest reserved port to use */ ) {
+ printf("\nNo reserved ports available.\n");
+ netclos();
+ return -1;
+ }
+ }
+ debug(F101,"rlogin lport","",lport);
+ ttnproto = NP_RLOGIN;
+ } else
+#endif /* RLOGCODE */
+
+ /* If a specific TCP address on the local host is desired we */
+ /* must bind it to the socket. */
+#ifndef datageneral
+ if (tcp_address) {
+ int s_errno;
+
+ debug(F110,"netopen binding socket to",tcp_address,0);
+ bzero((char *)&sin,sizeof(sin));
+ sin.sin_family = AF_INET;
+#ifdef INADDRX
+ inaddrx = inet_addr(tcp_address);
+ sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
+#else
+ sin.sin_addr.s_addr = inet_addr(tcp_address);
+#endif /* INADDRX */
+ sin.sin_port = 0;
+ if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ s_errno = socket_errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(ttyfd);
+#else /* TCPIPLIB */
+ close(ttyfd);
+#endif /* TCPIPLIB */
+ ttyfd = -1;
+ wasclosed = 1;
+ errno = s_errno; /* and report this error */
+ debug(F101,"netopen bind errno","",errno);
+ return(-1);
+ }
+ }
+#endif /* datageneral */
+
+/* Now connect to the socket on the other end. */
+
+#ifdef EXCELAN
+ if (connect(ttyfd, &r_addr) < 0)
+#else
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+ if (connect(ttyfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0)
+#endif /* EXCELAN */
+ {
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#ifdef OS2
+ i = socket_errno;
+#else /* OS2 */
+#ifdef TGVORWIN
+ i = socket_errno;
+#else
+ i = errno; /* Save error code */
+#endif /* TGVORWIN */
+#endif /* OS2 */
+#ifdef RLOGCODE
+ if (
+#ifdef OS2
+ i && /* OS2 bind fails with 0, if already in use */
+#ifdef NT
+ i == WSAEADDRINUSE
+#else
+ (i == SOCEADDRINUSE ||
+ i == (SOCEADDRINUSE - SOCBASEERR))
+#endif /* NT */
+#else /* OS2 */
+#ifdef TGVORWIN
+ socket_errno == EADDRINUSE
+#else
+ errno == EADDRINUSE
+#endif /* TGVORWIN */
+#endif /* OS2 */
+ && ttnproto == NP_RLOGIN) {
+#ifdef TCPIPLIB
+ socket_close(ttyfd); /* Close it. */
+#else
+ close(ttyfd);
+#endif /* TCPIPLIB */
+ continue; /* Try a different lport */
+ }
+#endif /* RLOGCODE */
+#ifdef HADDRLIST
+#ifdef h_addr
+ if (host && host->h_addr_list && host->h_addr_list[1]) {
+ perror("");
+ host->h_addr_list++;
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&r_addr.sin_addr,
+ host->h_length);
+
+ ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20);
+ debug(F110,"netopen h_addr_list",ipaddr,0);
+ if (!quiet && *ipaddr) {
+ printf(" Trying %s... ", ipaddr);
+ fflush(stdout);
+ }
+#ifdef TCPIPLIB
+ socket_close(ttyfd); /* Close it. */
+#else
+ close(ttyfd);
+#endif /* TCPIPLIB */
+ continue;
+ }
+#endif /* h_addr */
+#endif /* HADDRLIST */
+ netclos();
+ ttyfd = -1;
+ wasclosed = 1;
+ ttnproto = NP_NONE;
+ errno = i; /* And report this error */
+#ifdef EXCELAN
+ if (errno) experror("netopen connect");
+#else
+#ifdef TGVORWIN
+ debug(F101,"netopen connect error","",socket_errno);
+ /* if (errno) socket_perror("netopen connect"); */
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ if (!quiet)
+ socket_perror("netopen connect");
+#else /* TGVORWIN */
+ debug(F101,"netopen connect errno","",errno);
+#ifdef VMS
+ if (!quiet)
+ perror("\r\nFailed");
+#else
+ if (!quiet)
+ perror("Failed");
+#endif /* VMS */
+#ifdef DEC_TCPIP
+ if (!quiet)
+ perror("netopen connect");
+#endif /* DEC_TCPIP */
+#ifdef CMU_TCPIP
+ if (!quiet)
+ perror("netopen connect");
+#endif /* CMU_TCPIP */
+#endif /* TGVORWIN */
+#endif /* EXCELAN */
+ return(-1);
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+ isconnect = 1;
+ } while (!isconnect);
+
+#ifdef NON_BLOCK_IO
+ on = 1;
+ x = socket_ioctl(ttyfd,FIONBIO,&on);
+ debug(F101,"netopen FIONBIO","",x);
+#endif /* NON_BLOCK_IO */
+
+#ifdef NT_TCP_OVERLAPPED
+ OverlappedWriteInit();
+ OverlappedReadInit();
+#endif /* NT_TCP_OVERLAPPED */
+
+ ttnet = nett; /* TCP/IP (sockets) network */
+
+#ifndef NOHTTP
+ /* We have succeeded in connecting to the HTTP PROXY. So now we */
+ /* need to attempt to connect through the proxy to the actual host */
+ /* If that is successful, we have to pretend that we made a direct */
+ /* connection to the actual host. */
+
+ if ( tcp_http_proxy ) {
+#ifdef OS2
+ char * agent = "Kermit 95"; /* Default user agent */
+#else
+ char * agent = "C-Kermit";
+#endif /* OS2 */
+
+ if (http_connect(ttyfd,
+ tcp_http_proxy_agent ? tcp_http_proxy_agent : agent,
+ NULL,
+ tcp_http_proxy_user,
+ tcp_http_proxy_pwd,
+ 0,
+ proxycopy
+ ) < 0) {
+ netclos();
+ return(-1);
+ }
+
+ ckstrncpy(namecopy,proxycopy,NAMECPYL);
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ *p = '\0';
+ }
+#endif /* NOHTTP */
+
+ /* Jeff - Does this next block of code that set's the protocol */
+ /* need to be here anymore? 5/10/2000 */
+
+ /* There are certain magic port numbers that when used require */
+ /* the use of specific protocols. Check this now before we */
+ /* set the SO_OOBINLINE state or we might get it wrong. */
+ x = ntohs((unsigned short)service->s_port);
+ svcnum = x;
+ /* See if the service is TELNET. */
+ if (x == TELNET_PORT) {
+ /* Yes, so if raw port not requested */
+ if (ttnproto != NP_TCPRAW && ttnproto != NP_NONE)
+ ttnproto = NP_TELNET; /* Select TELNET protocol. */
+ }
+#ifdef RLOGCODE
+ else if (x == RLOGIN_PORT) {
+ ttnproto = NP_RLOGIN;
+ }
+#ifdef CK_KERBEROS
+ /* There is no good way to do this. If the user didn't tell */
+ /* which one to use up front. We may guess wrong if the user */
+ /* has both Kerberos versions installed and valid TGTs for each */
+ else if (x == KLOGIN_PORT &&
+ ttnproto != NP_K4LOGIN &&
+ ttnproto != NP_K5LOGIN) {
+ if (ck_krb5_is_installed() &&
+ ck_krb5_is_tgt_valid())
+ ttnproto = NP_K5LOGIN;
+ else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid())
+ ttnproto = NP_K4LOGIN;
+ else
+ ttnproto = NP_K4LOGIN;
+ } else if (x == EKLOGIN_PORT &&
+ ttnproto != NP_EK4LOGIN &&
+ ttnproto != NP_EK5LOGIN) {
+ if (ck_krb5_is_installed() && ck_krb5_is_tgt_valid())
+ ttnproto = NP_EK5LOGIN;
+ else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid())
+ ttnproto = NP_EK4LOGIN;
+ else
+ ttnproto = NP_EK4LOGIN;
+ }
+#endif /* CK_KERBEROS */
+#endif /* RLOGCODE */
+#ifdef IKS_OPTION
+ else if (x == KERMIT_PORT) { /* IKS uses Telnet protocol */
+ if (ttnproto == NP_NONE)
+ ttnproto = NP_KERMIT;
+ }
+#endif /* IKS_OPTION */
+
+#ifdef SO_OOBINLINE
+/*
+ The symbol SO_OOBINLINE is not known to Ultrix 2.0.
+ It means "leave out of band data inline". The normal value is 0x0100,
+ but don't try this on systems where the symbol is undefined.
+*/
+/*
+ Note from Jeff Altman: 12/13/95
+ In implementing rlogin protocol I have come to the conclusion that it is
+ a really bad idea to read out-of-band data inline.
+ At least Windows and OS/2 does not handle this well.
+ And if you need to know that data is out-of-band, then it becomes
+ absolutely pointless.
+
+ Therefore, at least on OS2 and Windows (NT) I have changed the value of
+ on to 0, so that out-of-band data stays out-of-band.
+
+ 12/18/95
+ Actually, OOB data should be read inline when possible. Especially with
+ protocols that don't care about the Urgent flag. This is true with Telnet.
+ With Rlogin, you need to be able to catch OOB data. However, the best
+ way to do this is to set a signal handler on SIGURG. This isn't possible
+ on OS/2 and Windows. But it is in UNIX. We will also need OOB data for
+ FTP so better create a general mechanism.
+
+ The reason for making OOB data be inline is that the standard ttinc/ttoc
+ calls can be used for reading that data on UNIX systems. If we didn't
+ have the OOBINLINE option set then we would have to use recv(,MSG_OOB)
+ to read it.
+*/
+#ifdef RLOGCODE
+#ifdef TCPIPLIB
+ if (ttnproto == NP_RLOGIN
+#ifdef CK_KERBEROS
+ || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
+ || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
+#endif /* CK_KERBEROS */
+ )
+ on = 0;
+#else /* TCPIPLIB */
+ if (ttnproto == NP_RLOGIN
+#ifdef CK_KERBEROS
+ || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
+ || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
+#endif /* CK_KERBEROS */
+ ) {
+ debug(F100,"Installing rlogoobh on SIGURG","",0);
+ signal(SIGURG, rlogoobh);
+ on = 0;
+ } else {
+ debug(F100,"Ignoring SIGURG","",0);
+ signal(SIGURG, SIG_DFL);
+ }
+#endif /* TCPIPLIB */
+#endif /* RLOGCODE */
+
+#ifdef datageneral
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef BSD43
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OSF1
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef POSIX
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef MOTSV88R4
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef SOLARIS
+/*
+ Maybe this applies to all SVR4 versions, but the other (else) way has been
+ compiling and working fine on all the others, so best not to change it.
+*/
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OSK
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OS2
+ {
+ int rc;
+ rc = setsockopt(ttyfd,
+ SOL_SOCKET,
+ SO_OOBINLINE,
+ (char *) &on,
+ sizeof on
+ );
+ debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc);
+ }
+#else
+#ifdef VMS /* or, at least, VMS with gcc */
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef CLIX
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* CLIX */
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* OSK */
+#endif /* SOLARIS */
+#endif /* MOTSV88R4 */
+#endif /* POSIX */
+#endif /* BSD43 */
+#endif /* OSF1 */
+#endif /* datageneral */
+#endif /* SO_OOBINLINE */
+
+#ifndef NOTCPOPTS
+#ifndef datageneral
+#ifdef SOL_SOCKET
+#ifdef TCP_NODELAY
+ no_delay(ttyfd,tcp_nodelay);
+#endif /* TCP_NODELAY */
+#ifdef SO_KEEPALIVE
+ keepalive(ttyfd,tcp_keepalive);
+#endif /* SO_KEEPALIVE */
+#ifdef SO_LINGER
+ ck_linger(ttyfd,tcp_linger, tcp_linger_tmo);
+#endif /* SO_LINGER */
+#ifdef SO_SNDBUF
+ sendbuf(ttyfd,tcp_sendbuf);
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ recvbuf(ttyfd,tcp_recvbuf);
+#endif /* SO_RCVBUF */
+#endif /* SOL_SOCKET */
+#endif /* datageneral */
+#endif /* NOTCPOPTS */
+
+#ifndef datageneral
+ /* Find out our own IP address. */
+ /* We need the l_addr structure for [E]KLOGIN. */
+ l_slen = sizeof(l_addr);
+ bzero((char *)&l_addr, l_slen);
+#ifndef EXCELAN
+ if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) {
+ char * s = (char *)inet_ntoa(l_addr.sin_addr);
+ ckstrncpy(myipaddr, s, 20);
+ debug(F110,"getsockname",myipaddr,0);
+ }
+#endif /* EXCELAN */
+#endif /* datageneral */
+
+/*
+ This is really only needed for Kerberos IV but is useful information in any
+ case. If we connect to a name that is really a pool, we need to get the
+ name of the machine we are actually connecting to for K4 to authenticate
+ properly. This way we also update the names properly.
+
+ However, it is a security hole when used with insecure DNS.
+
+ Note: This does not work on Windows 95 or Windows NT 3.5x. This is because
+ of the Microsoft implementation of gethostbyaddr() in both Winsock 1.1
+ and Winsock 2.0 on those platforms. Their algorithm is:
+
+ 1. Check the HOSTENT cache.
+ 2. Check the HOSTS file at %SystemRoot%\System32\DRIVERS\ETC.
+ 3. Do a DNS query if the DNS server is configured for name resolution.
+ 4. Do an additional NetBIOS remote adapter status to an IP address for its
+ NetBIOS name table. This step is specific only to the Windows NT version
+ 3.51 implementation.
+
+ The problem is the use of the HOSTENT cache. It means that gethostbyaddr()
+ can not be used to resolve the real name of machine if it was originally
+ accessed by an alias used to represent a cluster.
+*/
+ if ((tcp_rdns && dns || tcp_rdns == SET_ON
+#ifdef CK_KERBEROS
+ || tcp_rdns == SET_AUTO &&
+ (ck_krb5_is_installed() || ck_krb4_is_installed())
+#endif /* CK_KERBEROS */
+ )
+#ifndef NOHTTP
+ && (tcp_http_proxy == NULL)
+#endif /* NOHTTP */
+#ifdef CK_SSL
+ && !(ssl_only_flag || tls_only_flag)
+#endif /* CK_SSL */
+ ) {
+#ifdef NT
+ if (isWin95())
+ sleep(1);
+#endif /* NT */
+ if (!quiet) {
+ printf(" Reverse DNS Lookup... ");
+ fflush(stdout);
+ }
+ if (host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET)) {
+ char * s;
+ host = ck_copyhostent(host);
+ debug(F100,"netopen gethostbyname != NULL","",0);
+ if (!quiet) {
+ printf("(OK)\n");
+ fflush(stdout);
+ }
+ s = host->h_name;
+ if (!s) { /* This can happen... */
+ debug(F100,"netopen host->h_name is NULL","",0);
+ s = "";
+ }
+ /* Something is wrong with inet_ntoa() on HPUX 10.xx */
+ /* The compiler says "Integral value implicitly converted to */
+ /* pointer in assignment." The prototype is right there */
+ /* in <arpa/inet.h> so what's the problem? */
+ /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */
+ if (!*s) { /* No name so substitute the address */
+ debug(F100,"netopen host->h_name is empty","",0);
+ s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */
+ if (!s) /* Trust No 1 */
+ s = "";
+ if (*s) { /* If it worked, use this string */
+ ckstrncpy(ipaddr,s,20);
+ }
+ s = ipaddr; /* Otherwise stick with the IP */
+ if (!*s) /* or failing that */
+ s = namecopy; /* the name we were called with. */
+ }
+ if (*s) { /* Copying into our argument? */
+ ckstrncpy(name,s,80); /* Bad Bad Bad */
+ if ( (80-strlen(name)) > (strlen(svcbuf)+1) ) {
+ strncat(name,":",80-strlen(name));
+ strncat(name,svcbuf,80-strlen(name));
+ }
+ }
+ if (!quiet && *s
+#ifndef NOICP
+ && !doconx
+#endif /* NOICP */
+ ) {
+ printf(" %s connected on port %s\n",s,p);
+#ifdef BETADEBUG
+ /* This is simply for testing the DNS entries */
+ if (host->h_aliases) {
+ char ** a = host->h_aliases;
+ while (*a) {
+ printf(" alias => %s\n",*a);
+ a++;
+ }
+ }
+#endif /* BETADEBUG */
+ }
+ } else {
+ if (!quiet) printf("Failed.\n");
+ }
+ } else if (!quiet) printf("(OK)\n");
+ if (!quiet) fflush(stdout);
+
+ /* This should already have been done but just in case */
+ ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20);
+
+#ifdef CK_SECURITY
+
+ /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */
+#ifndef NOHTTP
+ if (tcp_http_proxy) {
+ for (i=strlen(proxycopy); i >= 0 ; i--)
+ if ( proxycopy[i] == ':' )
+ proxycopy[i] = '\0';
+ }
+#endif /* NOHTTP */
+ ck_auth_init(
+#ifndef NOHTTP
+ tcp_http_proxy ? proxycopy :
+#endif /* NOHTTP */
+ (tcp_rdns && host && host->h_name && host->h_name[0]) ?
+ (char *)host->h_name : (namecopy2[0] ? namecopy2 :
+ (namecopy[0] ? namecopy : ipaddr)),
+ ipaddr,
+ uidbuf,
+ ttyfd
+ );
+#endif /* CK_SECURITY */
+#ifdef CK_SSL
+ if (ck_ssleay_is_installed()) {
+ if (!ssl_tn_init(SSL_CLIENT)) {
+ debug(F100,"netopen ssl_tn_init() failed","",0);
+ if (bio_err!=NULL) {
+ BIO_printf(bio_err,"ssl_tn_init() failed\n");
+ ERR_print_errors(bio_err);
+ } else {
+ fflush(stderr);
+ fprintf(stderr,"ssl_tn_init() failed\n");
+ ERR_print_errors_fp(stderr);
+ }
+ if (tls_only_flag || ssl_only_flag) {
+ debug(F100,"netopen ssl/tls required","",0);
+ netclos();
+ return(-1);
+ }
+
+ /* we will continue to accept the connection */
+ /* without SSL or TLS support unless required. */
+ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU )
+ TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF;
+ } else if ( ck_ssl_outgoing(ttyfd) < 0 ) {
+ debug(F100,"ck_ssl_outgoing() failed","",0);
+ netclos();
+ return(-1);
+ }
+ }
+#endif /* CK_SSL */
+
+#ifdef RLOGCODE
+ if (ttnproto == NP_RLOGIN
+#ifdef CK_KERBEROS
+ || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN
+ || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN
+#endif /* CK_KERBEROS */
+ ) { /* Similar deal for rlogin */
+ if (rlog_ini(((tcp_rdns && host && host->h_name && host->h_name[0]) ?
+ (CHAR *)host->h_name : (CHAR *)ipaddr),
+ service->s_port,
+ &l_addr,&r_addr
+ ) < 0) {
+ debug(F100,"rlogin initialization failed","",0);
+ netclos();
+ return(-1);
+ }
+ } else
+#endif /* RLOGCODE */
+ if (tn_ini() < 0) { /* Start Telnet negotiations. */
+ netclos();
+ return(-1); /* Gone, so open failed. */
+ }
+ if (ttchk() < 0) {
+ netclos();
+ return(-1);
+ }
+#ifdef CK_KERBEROS
+#ifdef KRB5_U2U
+ if ( ttnproto == NP_K5U2U ) {
+ if (k5_user_to_user_client_auth()) {
+ netclos();
+ return(-1);
+ }
+ }
+#endif /* KRB5_U2U */
+#endif /* CK_KERBEROS */
+
+ debug(F101,"netopen service","",svcnum);
+ debug(F110,"netopen name",name,0);
+
+ if (lcl) if (*lcl < 0) /* Local mode. */
+ *lcl = 1;
+#endif /* TCPSOCKET */
+ return(0); /* Done. */
+}
+
+/* N E T C L O S -- Close current network connection. */
+
+#ifndef NOLOCAL
+_PROTOTYP(VOID slrestor,(VOID));
+#ifdef CK_SSL
+int tls_norestore = 0;
+#endif /* CK_SSL */
+#endif /* NOLOCAL */
+
+int
+netclos() {
+ static int close_in_progress = 0;
+ int x = 0, y, z;
+ debug(F101,"netclos","",ttyfd);
+
+#ifdef NETLEBUF
+ if (!tt_push_inited)
+ le_init();
+#endif /* NETLEBUF */
+
+ if (ttyfd == -1) /* Was open? */
+ return(0); /* Wasn't. */
+
+ if (close_in_progress)
+ return(0);
+ close_in_progress = 1; /* Remember */
+
+#ifndef NOLOCAL
+ /* This function call should not be here since this is a direct call */
+ /* from an I/O routine to a user interface level function. However, */
+ /* the reality is that we do not have pure interfaces. If we ever */
+ /* decide to clean this up the UI level should assign this function */
+ /* via a pointer assignment. - Jeff 9/10/1999 */
+#ifdef CK_SSL
+ if (!tls_norestore)
+#endif /* CK_SSL */
+ slrestor();
+#endif /* NOLOCAL */
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#else /* OS2 */
+ if (ttyfd > -1) /* Was. */
+#endif /* OS2 */
+ {
+#ifdef VMS
+ y = 1; /* Turn on nonblocking reads */
+ z = socket_ioctl(ttyfd,FIONBIO,&y);
+ debug(F111,"netclos FIONBIO","on",z);
+#endif /* VMS */
+#ifdef TNCODE
+ if (ttnproto == NP_TELNET) {
+ if ( !TELOPT_ME(TELOPT_LOGOUT) ) {
+ /* Send LOGOUT option before close */
+ if (tn_sopt(DO,TELOPT_LOGOUT) >= 0) {
+ TELOPT_UNANSWERED_DO(TELOPT_LOGOUT) = 1;
+ /* It would be nice to call tn_wait but we can't */
+ }
+ }
+ tn_push(); /* Place any waiting data into input*/
+ }
+#endif /* TNCODE */
+#ifdef CK_SSL
+ if (ssl_active_flag) {
+ if (ssl_debug_flag)
+ BIO_printf(bio_err,"calling SSL_shutdown\n");
+ SSL_shutdown(ssl_con);
+ ssl_active_flag = 0;
+ }
+ if (tls_active_flag) {
+ if (ssl_debug_flag)
+ BIO_printf(bio_err,"calling SSL_shutdown\n");
+ SSL_shutdown(tls_con);
+ tls_active_flag = 0;
+ }
+#endif /* CK_SSL */
+#ifdef VMS
+ ck_cancio(); /* Cancel any outstanding reads. */
+#endif /* VMS */
+#ifdef TCPIPLIB
+ x = socket_close(ttyfd); /* Close it. */
+#else
+#ifndef OS2
+#ifdef IBMX25
+ if (ttnet == NET_IX25) {
+ /* riehm: should send a disc_req - but only if link is still OK */
+ x = x25clear();
+ close(ttyfd);
+ if (x25serverfd) {
+ /* we were the passive client of a server, now we
+ * go back to being the normal client.
+ * I hope that kermit can cope with the logic that
+ * there can still be a connection after netclos
+ * has been called.
+ */
+ ttyfd = x25serverfd;
+ x25serverfd = 0;
+ /*
+ * need to close the server connection too - because
+ * all file descriptors connected to the NPI have the
+ * same status.
+ *
+ * The problem is that any waiting connections get
+ * lost, the client doesn't realise, and hangs.
+ */
+ netclos();
+ }
+ x25_state = X25_CLOSED; /* riehm: dead code? */
+ } else
+#endif /* IBMX25 */
+ x = close(ttyfd);
+#endif /* OS2 */
+#endif /* TCPIPLIB */
+ }
+ ttyfd = -1; /* Mark it as closed. */
+ wasclosed = 1;
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+#ifdef TNCODE
+#ifdef CK_FORWARD_X
+ fwdx_close_all(); /* Shut down any Forward X sockets */
+#endif /* CK_FORWARD_X */
+ tn_reset(); /* The Reset Telnet Option table. */
+ debug(F100,"netclose setting tn_init = 0","",0);
+ tn_init = 0; /* Remember about telnet protocol... */
+ sstelnet = 0; /* Client-side Telnet */
+#endif /* TNCODE */
+ *ipaddr = '\0'; /* Zero the IP address string */
+ tcp_incoming = 0; /* No longer incoming */
+ /* Don't reset ttnproto so that we can remember which protocol is in use */
+
+#ifdef TCPIPLIB
+/*
+ Empty the internal buffers so they won't be used as invalid input on
+ the next connect attempt (rlogin).
+*/
+ ttibp = 0;
+ ttibn = 0;
+#endif /* TCPIPLIB */
+#ifdef CK_KERBEROS
+ /* If we are automatically destroying Kerberos credentials on Close */
+ /* do it now. */
+#ifdef KRB4
+ if (krb4_autodel == KRB_DEL_CL) {
+ extern struct krb_op_data krb_op;
+ krb_op.version = 4;
+ krb_op.cache = NULL;
+ ck_krb4_destroy(&krb_op);
+ }
+#endif /* KRB4 */
+#ifdef KRB5
+ if (krb5_autodel == KRB_DEL_CL) {
+ extern struct krb_op_data krb_op;
+ extern char * krb5_d_cc;
+ krb_op.version = 5;
+ krb_op.cache = krb5_d_cc;
+ ck_krb5_destroy(&krb_op);
+ }
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+ close_in_progress = 0; /* Remember we are done. */
+ return(x);
+}
+
+#ifdef OS2
+int
+os2socketerror( int s_errno ) {
+#ifdef OS2ONLY
+ if (s_errno > 0 && s_errno <= SOCBASEERR) {
+ /* in OS/2, there is a problem with threading in that
+ * the value of errno is not thread safe. It can be
+ * set to a value from a previous library call and if
+ * it was not cleared it will appear here. Only treat
+ * valid socket error codes as errors in this function.
+ */
+ debug(F100,"os2socketerror errno.h","",0);
+ socket_errno = 0;
+ return(0);
+ }
+#endif /* OS2ONLY */
+
+ switch (s_errno) {
+ case 0: /* NO ERROR */
+ debug(F100,"os2socketerror NOERROR","",0);
+ return(0);
+#ifdef NT
+ case WSAECONNRESET:
+#else /* NT */
+ case SOCECONNRESET:
+ case SOCECONNRESET - SOCBASEERR:
+#endif /* NT */
+ debug(F100,"os2socketerror ECONRESET","",0);
+ tn_debug("ECONRESET");
+ netclos(); /* *** *** */
+ return(-1); /* Connection is broken. */
+#ifdef NT
+ case WSAECONNABORTED:
+#else /* NT */
+ case SOCECONNABORTED:
+ case SOCECONNABORTED - SOCBASEERR:
+#endif /* NT */
+ debug(F100,"os2socketerror ECONNABORTED","",0);
+ tn_debug("ECONNABORTED");
+ netclos(); /* *** *** */
+ return(-1); /* Connection is broken. */
+#ifdef NT
+ case WSAENETRESET:
+#else /* NT */
+ case SOCENETRESET:
+ case SOCENETRESET - SOCBASEERR:
+#endif /* NT */
+ debug(F100,"os2socketerror ENETRESET","",0);
+ tn_debug("ENETRESET");
+ netclos(); /* *** *** */
+ return(-1); /* Connection is broken. */
+#ifdef NT
+ case WSAENOTCONN:
+#else /* NT */
+ case SOCENOTCONN:
+ case SOCENOTCONN - SOCBASEERR:
+#endif /* NT */
+ debug(F100,"os2socketerror ENOTCONN","",0);
+ tn_debug("ENOTCONN");
+ netclos(); /* *** *** */
+ return(-1); /* Connection is broken. */
+#ifdef NT
+ case WSAESHUTDOWN:
+ debug(F100,"os2socketerror ESHUTDOWN","",0);
+ tn_debug("ESHUTDOWN");
+ netclos(); /* *** *** */
+ return(-1); /* Connection is broken. */
+#endif /* NT */
+#ifdef NT
+ case WSAEWOULDBLOCK:
+#else
+ case SOCEWOULDBLOCK:
+ case SOCEWOULDBLOCK - SOCBASEERR:
+#endif /* NT */
+ debug(F100,"os2socketerror EWOULDBLOCK","",0);
+ return(0);
+#ifdef NT
+ case ERROR_IO_INCOMPLETE:
+ case ERROR_IO_PENDING:
+ case ERROR_OPERATION_ABORTED:
+ return(0);
+#endif /* NT */
+ default:
+ return(-2);
+ }
+ return(0);
+}
+#endif /* OS2 */
+
+/* N E T T C H K -- Check if network up, and how many bytes can be read */
+/*
+ Returns number of bytes waiting, or -1 if connection has been dropped.
+*/
+int /* Check how many bytes are ready */
+nettchk() { /* for reading from network */
+#ifdef TCPIPLIB
+ long count = 0;
+ int x = 0, z;
+ long y;
+ char c;
+ int rc;
+#ifdef NT
+ extern int ionoblock; /* For Overlapped I/O */
+#endif /* NT */
+
+ debug(F101,"nettchk entry ttibn","",ttibn);
+ debug(F101,"nettchk entry ttibp","",ttibp);
+
+#ifdef NETLEBUF
+ {
+ int n = 0;
+ if (ttpush >= 0)
+ n++;
+ n += le_inbuf();
+ if (n > 0)
+ return(n);
+ }
+#endif /* NETLEBUF */
+
+#ifndef OS2
+#ifndef BEBOX
+ socket_errno = 0; /* This is a function call in NT, and BeOS */
+#endif /* BEBOX */
+#endif /* OS2 */
+
+ if (ttyfd == -1) {
+ debug(F100,"nettchk socket is closed","",0);
+ return(-1);
+ }
+/*
+ Note: this socket_ioctl() call does NOT return an error if the
+ connection has been broken. (At least not in MultiNet.)
+*/
+#ifdef COMMENT
+/* Another trick that can be tried here is something like this: */
+
+ if (ttnet == NET_TCPB) {
+ char dummy;
+ x = read(ttyfd,&dummy,0); /* Try to read nothing */
+ if (x < 0) { /* "Connection reset by peer" */
+ perror("TCP/IP"); /* or somesuch... */
+ ttclos(0); /* Close our end too. */
+ return(-1);
+ }
+ }
+#endif /* COMMENT */
+
+
+#ifdef CK_SSL
+ if (ssl_active_flag) {
+#ifndef IKSDONLY
+#ifdef OS2
+ if ( IsConnectMode() ) {
+ debug(F101,"nettchk (ssl_active_flag) returns","",count);
+ return(0);
+ }
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ count = SSL_pending(ssl_con);
+ if (count < 0) {
+ debug(F111,"nettchk","SSL_pending error",count);
+ netclos();
+ return(-1);
+ }
+ if ( count > 0 )
+ return(count); /* Don't perform a read */
+ } else if (tls_active_flag) {
+#ifndef IKSDONLY
+#ifdef OS2
+ if ( IsConnectMode() ) {
+ debug(F101,"nettchk (tls_active_flag) returns","",count);
+ return(0);
+ }
+#endif /* OS2 */
+#endif /* IKSDONLY */
+ count = SSL_pending(tls_con);
+ if (count < 0) {
+ debug(F111,"nettchk","TLS_pending error",count);
+ netclos();
+ return(-1);
+ }
+ if ( count > 0 )
+ return(count); /* Don't perform a read */
+ } else
+#endif /* CK_SSL */
+
+ if (socket_ioctl(ttyfd,FIONREAD,
+#ifdef COMMENT
+ /* Now we've changed the ioctl(..,..,x) prototype for DECC to (void *) */
+#ifdef __DECC
+ /* NOTE: "&count" might need to be "(char *)&count" in some settings. */
+ /* Cast needed for DECC 4.1 & later? */
+ /* Maybe, but __DECC_VER only exists in 5.0 and later */
+ (char *)
+#endif /* __DECC */
+#endif /* COMMENT */
+ &count
+ ) < 0) {
+ debug(F101,"nettchk socket_ioctl error","",socket_errno);
+ /* If the connection is gone, the connection is gone. */
+ netclos();
+#ifdef NT_TCP_OVERLAPPED
+ /* Is there anything in the overlapped I/O buffers? */
+ count += OverlappedDataWaiting();
+#endif /* NT_TCP_OVERLAPPED */
+ count += ttibn;
+ return(count>0?count:-1);
+ }
+ debug(F101,"nettchk count","",count);
+#ifdef NT_TCP_OVERLAPPED
+ /* Is there anything in the overlapped I/O buffers? */
+ count += OverlappedDataWaiting();
+ debug(F101,"nettchk count w/overlapped","",count);
+#endif /* NT_TCP_OVERLAPPED */
+
+#ifdef OS2
+#ifndef IKSDONLY
+ if ( IsConnectMode() ) {
+ debug(F101,"nettchk (FIONREAD) returns","",count);
+ return(count);
+ }
+#endif /* IKSDONLY */
+#endif /* OS2 */
+
+/* For the sake of efficiency, if there is still data in the ttibuf */
+/* do not go to the bother of checking to see of the connection is */
+/* still valid. The handle is still good, so just return the count */
+/* of the bytes that we already have left to process. */
+#ifdef OS2
+ if ( count > 0 || ttibn > 0 ) {
+ count+=ttibn;
+ debug(F101,"nettchk (count+ttibn > 0) returns","",count);
+ return(count);
+ } else {
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+ if ( ttibn == 0 )
+ ttibp = 0; /* reset for next read */
+ }
+#else /* OS2 */
+ if ( count > 0 || ttibn > 0 ) {
+ debug(F101,"nettchk returns","",count+ttibn);
+ return(count+ttibn);
+ }
+ ttibn = ttibp = 0;
+#endif /* OS2 */
+
+/*
+ The following code works well in most settings, but messes things up in
+ others, including CMU/Tek TCP/IP and UCX 2.0, where it somehow manages to
+ make it impossible to ever make a new connection to the same host again with
+ CONNECT, once it has been logged out from the first time. Not even if you
+ HANGUP first, or SET HOST<CR>, or SET LINE<CR>. Reportedly, however, it
+ does work OK in later releases of UCX. But there is no way we can
+ accommodate both old and new -- we might have static linking or dynamic
+ linking, etc etc. If we have static, I only have access to 2.0, where this
+ doesn't work, etc etc blah blah.
+
+ In the following lines, we define a symbol NOCOUNT for builds where we want
+ to omit this code. By default, it is omitted for CMU/Tek. You can force
+ omission of it for other combinations by defining NOCOUNT in CFLAGS. You
+ can force inclusion of this code, even for CMU/Tek, by including NONOCOUNT
+ in CFLAGS.
+*/
+#ifdef NONOCOUNT
+#ifdef NOCOUNT
+#undef NOCOUNT
+#endif /* NOCOUNT */
+#else
+#ifndef NOCOUNT
+#ifdef CMU_TCPIP
+#define NOCOUNT
+#endif /* CMU_TCPIP */
+#endif /* NOCOUNT */
+#endif /* NONOCOUNT */
+
+
+ /* From this point forward we have a possible race condition in K95
+ * due to its use of multiple threads. Therefore, we must ensure
+ * that only one thread attempt to read/write from the socket at a
+ * time. Otherwise, it is possible for a buffer to be overwritten.
+ */
+ /* we know now that count >= 0 and that ttibn == 0 */
+
+ if (count == 0
+#ifdef RLOGCODE
+#ifdef CK_KERBEROS
+ && ttnproto != NP_EK4LOGIN && ttnproto != NP_EK5LOGIN
+#endif /* CK_KERBEROS */
+#endif /* RLOGCODE */
+ ) {
+ int s_errno = 0;
+#ifndef NOCOUNT
+/*
+ Here we need to tell the difference between a 0 count on an active
+ connection, and a 0 count because the remote end of the socket broke the
+ connection. There is no mechanism in TGV MultiNet (or WIN/TCP?) to query
+ the status of the connection, so we have to do a read. -1 means there was
+ no data available (socket_errno == EWOULDBLOCK), 0 means the connection is
+ down. But if, by chance, we actually get a character, we have to put it
+ where it won't be lost.
+*/
+#ifndef NON_BLOCK_IO
+#ifdef OS2
+#ifdef CK_SSL
+ RequestSSLMutex(SEM_INDEFINITE_WAIT);
+#endif /* CK_SSL */
+#endif /* OS2 */
+ y = 1; /* Turn on nonblocking reads */
+ z = socket_ioctl(ttyfd,FIONBIO,&y);
+ debug(F111,"nettchk FIONBIO","on",z);
+#ifdef OS2
+#ifdef CK_SSL
+ ReleaseSSLMutex();
+#endif /* CK_SSL */
+#endif /* OS2 */
+#endif /* NON_BLOCK_IO */
+#ifdef NT_TCP_OVERLAPPED
+ ionoblock = 1; /* For Overlapped I/O */
+#endif /* NT_TCP_OVERLAPPED */
+#ifdef CK_SSL
+ if ( ssl_active_flag || tls_active_flag ) {
+#ifdef OS2
+ ssl_read:
+ x = SSL_read( ssl_active_flag?ssl_con:tls_con,
+ &ttibuf[ttibp+ttibn],
+ TTIBUFL-ttibp-ttibn );
+ switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,x)) {
+ case SSL_ERROR_NONE:
+ debug(F111,"nettchk SSL_ERROR_NONE","x",x);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"nettchk SSL_ERROR_WANT_WRITE","",0);
+ x = -1;
+ break;
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"nettchk SSL_ERROR_WANT_READ","",0);
+ x = -1;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if ( x == 0 ) { /* EOF */
+ netclos();
+ rc = -1;
+ goto nettchk_return;
+ } else {
+#ifdef NT
+ int gle = GetLastError();
+#endif /* NT */
+#ifndef NON_BLOCK_IO
+#ifdef OS2
+#ifdef CK_SSL
+ RequestSSLMutex(SEM_INDEFINITE_WAIT);
+#endif /* CK_SSL */
+#endif /* OS2 */
+ y = 0; /* Turn off nonblocking reads */
+ z = socket_ioctl(ttyfd,FIONBIO,&y);
+ debug(F111,"nettchk FIONBIO","off",z);
+#ifdef OS2
+#ifdef CK_SSL
+ ReleaseSSLMutex();
+#endif /* CK_SSL */
+#endif /* OS2 */
+#endif /* NON_BLOCK_IO */
+#ifdef NT_TCP_OVERLAPPED
+ ionoblock = 0; /* For Overlapped I/O */
+#endif /* NT_TCP_OVERLAPPED */
+#ifdef NT
+ debug(F111,"nettchk SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+ goto nettchk_return;
+#endif /* NT */
+ break;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"nettchk SSL_ERROR_WANT_X509_LOOKUP","",0);
+ break;
+ case SSL_ERROR_SSL:
+ if (bio_err!=NULL) {
+ int len;
+ extern char ssl_err[];
+ BIO_printf(bio_err,"nettchk() SSL_ERROR_SSL\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ debug(F110,"nettchk SSL_ERROR_SSL",ssl_err,0);
+ if (ssl_debug_flag)
+ printf(ssl_err);
+ } else if (ssl_debug_flag) {
+ debug(F100,"nettchk SSL_ERROR_SSL","",0);
+ fflush(stderr);
+ fprintf(stderr,"nettchk() SSL_ERROR_SSL\n");
+ ERR_print_errors_fp(stderr);
+ }
+#ifdef COMMENT
+ netclos();
+ rc = -1;
+ goto nettchk_return;
+#else
+ x = -1;
+ break;
+#endif
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"nettchk SSL_ERROR_ZERO_RETURN","",0);
+ netclos();
+ rc = -1;
+ goto nettchk_return;
+ default:
+ debug(F100,"nettchk SSL_ERROR_?????","",0);
+ netclos();
+ rc = -1;
+ goto nettchk_return;
+ }
+#else /* OS2 */
+ /* Do not block */
+ x = -1;
+#endif /* OS2 */
+ } else
+#endif /* CK_SSL */
+ {
+#ifdef OS2
+ x = socket_read(ttyfd,&ttibuf[ttibp+ttibn],
+ TTIBUFL-ttibp-ttibn); /* Returns -1 if no data */
+#else /* OS2 */
+ x = socket_read(ttyfd,&c,1); /* Returns -1 if no data */
+#endif /* OS2 */
+ }
+ s_errno = socket_errno; /* socket_errno may be a function */
+ debug(F101,"nettchk socket_read","",x);
+
+#ifndef NON_BLOCK_IO
+#ifdef OS2
+#ifdef CK_SSL
+ RequestSSLMutex(SEM_INDEFINITE_WAIT);
+#endif /* CK_SSL */
+#endif /* OS2 */
+ y = 0; /* Turn off nonblocking reads */
+ z = socket_ioctl(ttyfd,FIONBIO,&y);
+ debug(F111,"nettchk FIONBIO","off",z);
+#ifdef OS2
+#ifdef CK_SSL
+ ReleaseSSLMutex();
+#endif /* CK_SSL */
+#endif /* OS2 */
+#endif /* NON_BLOCK_IO */
+#ifdef NT_TCP_OVERLAPPED
+ ionoblock = 0; /* For Overlapped I/O */
+#endif /* NT_TCP_OVERLAPPED */
+
+ if (x == -1) {
+ debug(F101,"nettchk socket_read errno","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0) {
+ rc = -1;
+ goto nettchk_return;
+ }
+#endif /* OS2 */
+ } else if (x == 0) {
+ debug(F100,"nettchk connection closed","",0);
+ netclos(); /* *** *** */
+ rc = -1;
+ goto nettchk_return;
+ }
+ if (x >= 1) { /* Oops, actually got a byte? */
+#ifdef OS2
+ /* In OS/2 we read directly into ttibuf[] */
+ hexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x);
+ ttibn += x;
+#else /* OS2 */
+#ifdef CK_SSL
+ if ( ssl_active_flag || tls_active_flag ) {
+ hexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x);
+ ttibn += x;
+ } else
+#endif /* CK_SSL */
+ {
+ debug(F101,"nettchk socket_read char","",c);
+ debug(F101,"nettchk ttibp","",ttibp);
+ debug(F101,"nettchk ttibn","",ttibn);
+/*
+ In the case of Overlapped I/O the character would have come from
+ the beginning of the buffer, so put it back.
+*/
+ if (ttibp > 0) {
+ ttibp--;
+ ttibuf[ttibp] = c;
+ ttibn++;
+ } else {
+ ttibuf[ttibp+ttibn] = c;
+ ttibn++;
+ }
+ }
+#endif /* OS2 */
+ }
+#else /* NOCOUNT */
+ if (ttnet == NET_TCPB) {
+ char dummy;
+ x = read(ttyfd,&dummy,0); /* Try to read nothing */
+ if (x < 0) { /* "Connection reset by peer" */
+ perror("TCP/IP"); /* or somesuch... */
+ ttclos(0); /* Close our end too. */
+ rc = -1;
+ goto nettchk_return;
+ }
+ }
+#endif /* NOCOUNT */
+ }
+#ifdef CK_KERBEROS
+#ifdef KRB4
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK4LOGIN)
+ count += krb4_des_avail(ttyfd);
+#endif /* RLOGCODE */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK5LOGIN)
+ count += krb5_des_avail(ttyfd);
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+ if (ttnproto == NP_K5U2U)
+ count += krb5_u2u_avail(ttyfd);
+#endif /* KRB5_U2U */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+ debug(F101,"nettchk returns","",count+ttibn);
+ rc = count + ttibn;
+
+ nettchk_return:
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(rc);
+
+#else /* Not TCPIPLIB */
+/*
+ UNIX just uses ttchk(), in which the ioctl() calls on the file descriptor
+ seem to work OK.
+*/
+ return(ttchk());
+#endif /* TCPIPLIB */
+/*
+ But what about X.25?
+*/
+}
+
+#ifndef OS2
+VOID
+nettout(i) int i; { /* Catch the alarm interrupts */
+ debug(F100,"nettout caught timeout","",0);
+ ttimoff();
+ cklongjmp(njbuf, -1);
+}
+#endif /* !OS2 */
+
+#ifdef TCPIPLIB
+
+VOID
+#ifdef CK_ANSIC
+donetinc(void * threadinfo)
+#else /* CK_ANSIC */
+donetinc(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* donetinc */ {
+#ifdef NTSIG
+ extern int TlsIndex;
+ setint();
+ if (threadinfo) { /* Thread local storage... */
+ TlsSetValue(TlsIndex,threadinfo);
+ }
+#endif /* NTSIG */
+#ifdef CK_LOGIN
+#ifdef NT
+#ifdef IKSD
+ if (inserver)
+ setntcreds();
+#endif /* IKSD */
+#endif /* NT */
+#endif /* CK_LOGIN */
+ while (1) {
+ if (ttbufr() < 0) /* Keep trying to refill it. */
+ break; /* Till we get an error. */
+ if (ttibn > 0) /* Or we get a character. */
+ break;
+ }
+}
+#endif /* TCPIPLIB */
+
+VOID
+#ifdef CK_ANSIC
+failnetinc(void * threadinfo)
+#else /* CK_ANSIC */
+failnetinc(threadinfo) VOID * threadinfo;
+#endif /* CK_ANSIC */
+/* failnetinc */ {
+ ; /* Nothing to do on an error */
+}
+
+/* N E T X I N -- Input block of characters from network */
+
+int
+netxin(n,buf) int n; CHAR * buf; {
+ int len, i, j;
+#ifdef TCPIPLIB
+ int rc;
+#endif /* TCPIPLIB */
+
+ if (ttyfd == -1) {
+ debug(F100,"netxin socket is closed","",0);
+ return(-2);
+ }
+#ifdef CK_KERBEROS
+#ifdef KRB4
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK4LOGIN) {
+ if ((len = krb4_des_read(ttyfd,buf,n)) < 0)
+ return(-1);
+ else
+ return(len);
+ }
+#endif /* RLOGCODE */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK5LOGIN) {
+ if ((len = krb5_des_read(ttyfd,buf,n,0)) < 0)
+ return(-1);
+ else
+ return(len);
+ }
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+ if (ttnproto == NP_K5U2U) {
+ if ((len = krb5_u2u_read(ttyfd,buf,n)) < 0)
+ return(-1);
+ else
+ return(len);
+ }
+#endif /* KRB5_U2U */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+#ifdef TCPIPLIB
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#endif /* OS2 */
+ if (ttibn == 0)
+ if ((rc = ttbufr()) <= 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(rc);
+ }
+
+ if (ttibn <= n) {
+ len = ttibn;
+ memcpy(buf,&ttibuf[ttibp],len); /* safe */
+ ttibp += len;
+ ttibn = 0;
+ } else {
+ memcpy(buf,&ttibuf[ttibp],n); /* safe */
+ ttibp += n;
+ ttibn -= n;
+ len = n;
+ }
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+#else /* TCPIPLIB */
+ for (i = 0; i < n; i++) {
+ if ((j = netinc(0)) < 0) {
+ if (j < -1)
+ return(j);
+ else
+ break;
+ }
+ buf[i] = j;
+ }
+ len = i;
+#endif /* TCPIPLIB */
+
+#ifdef COMMENT
+#ifdef CK_ENCRYPTION
+ /* This would be great if it worked. But what if the buffer we read */
+ /* contains a telnet negotiation that changes the state of the */
+ /* encryption. If so, we would be either decrypting unencrypted text */
+ /* or not decrypting encrypted text. So we must move this call to */
+ /* all functions that call ttxin(). In OS2 that means os2_netxin() */
+ /* where the Telnet Negotiations are handled. */
+ if (u_encrypt)
+ ck_tn_decrypt(buf,len);
+#endif /* CK_ENCRYPTION */
+#endif /* COMMENT */
+
+ return(len);
+}
+
+/* N E T I N C -- Input character from network */
+
+#ifdef NETLEBUF
+#define LEBUF
+#endif /* NETLEBUF */
+#ifdef TTLEBUF
+#define LEBUF
+#endif /* TTLEBUF */
+#ifndef LEBUF
+#ifdef OS2
+#define LEBUF
+#endif /* OS2 */
+#endif /* LEBUF */
+
+int
+netinc(timo) int timo; {
+#ifdef TCPIPLIB
+ int x; unsigned char c; /* The locals. */
+
+#ifdef NETLEBUF
+ if (ttpush >= 0) {
+ debug(F111,"netinc","ttpush",ttpush);
+ c = ttpush;
+ ttpush = -1;
+ return(c);
+ }
+ if (le_data) {
+ if (le_getchar((CHAR *)&c) > 0) {
+ debug(F111,"netinc le_getchar","c",c);
+ return(c);
+ }
+ }
+#endif /* NETLEBUF */
+
+ if (ttyfd == -1) {
+ debug(F100,"netinc socket is closed","",0);
+ return(-2);
+ }
+
+#ifdef CK_KERBEROS
+#ifdef KRB4
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK4LOGIN) {
+ if ((x = krb4_des_read(ttyfd,&c,1)) == 0)
+ return(-1);
+ else if (x < 0)
+ return(-2);
+ else
+ return(c);
+ }
+#endif /* RLOGCODE */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK5LOGIN) {
+ if ((x = krb5_des_read(ttyfd,&c,1,0)) == 0)
+ return(-1);
+ else if (x < 0)
+ return(-2);
+ else
+ return(c);
+ }
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+ if (ttnproto == NP_K5U2U) {
+ if ((x = krb5_u2u_read(ttyfd,&c,1)) == 0)
+ return(-1);
+ else if (x < 0)
+ return(-2);
+ else
+ return(c);
+ }
+#endif /* KRB5_U2U */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#endif /* OS2 */
+ if (ttibn > 0) { /* Something in internal buffer? */
+#ifdef COMMENT
+ debug(F100,"netinc char in buf","",0); /* Yes. */
+#endif /* COMMENT */
+ x = 0; /* Success. */
+ } else { /* Else must read from network. */
+ x = -1; /* Assume failure. */
+#ifdef DEBUG
+ debug(F101,"netinc goes to net, timo","",timo);
+#endif /* DEBUG */
+#ifdef CK_SSL
+ /*
+ * In the case of OpenSSL, it is possible that there is still
+ * data waiting in the SSL session buffers that has not yet
+ * been read by Kermit. If this is the case we must process
+ * it without calling select() because select() will not return
+ * with an indication that there is data to be read from the
+ * socket. If there is no data pending in the SSL session
+ * buffers then fall through to the select() code and wait for
+ * some data to arrive.
+ */
+ if (ssl_active_flag) {
+ x = SSL_pending(ssl_con);
+ if (x < 0) {
+ debug(F111,"netinc","SSL_pending error",x);
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else if ( x > 0 ) {
+ if ( ttbufr() >= 0 ) {
+ x = netinc(timo);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(x);
+ }
+ }
+ x = -1;
+ } else if (tls_active_flag) {
+ x = SSL_pending(tls_con);
+ if (x < 0) {
+ debug(F111,"netinc","TLS_pending error",x);
+ netclos();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else if ( x > 0 ) {
+ if ( ttbufr() >= 0 ) {
+ x = netinc(timo);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(x);
+ }
+ }
+ x = -1;
+ }
+#endif /* CK_SSL */
+#ifndef LEBUF
+ if (timo == 0) { /* Untimed case. */
+ while (1) { /* Wait forever if necessary. */
+ if (ttbufr() < 0) /* Refill buffer. */
+ break; /* Error, fail. */
+ if (ttibn > 0) { /* Success. */
+ x = 0;
+ break;
+ }
+ }
+ } else /* Timed case... */
+#endif /* LEBUF */
+ {
+#ifdef NT_TCP_OVERLAPPED
+ /* This code is for use on NT when we are using */
+ /* Overlapped I/O to handle reads. In the case */
+ /* of outstanding reads select() doesn't work */
+
+ if (WaitForOverlappedReadData(timo)) {
+ while (1) {
+ if (ttbufr() < 0) /* Keep trying to refill it. */
+ break; /* Till we get an error. */
+ if (ttibn > 0) { /* Or we get a character. */
+ x = 0;
+ break;
+ }
+ }
+ }
+#else /* NT_TCP_OVERLAPPED */
+#ifdef BSDSELECT
+ fd_set rfds;
+ struct timeval tv;
+ int timeout = timo < 0 ? -timo : 1000 * timo;
+ debug(F101,"netinc BSDSELECT","",timo);
+
+ for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
+ int rc;
+ debug(F111,"netinc","timeout",timeout);
+ /* Don't move select() initialization out of the loop. */
+ FD_ZERO(&rfds);
+ FD_SET(ttyfd, &rfds);
+ tv.tv_sec = tv.tv_usec = 0L;
+ if (timo)
+ tv.tv_usec = (long) 100000L;
+ else
+ tv.tv_sec = 30;
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+ rc = select(FD_SETSIZE,
+#ifndef __DECC
+ (fd_set *)
+#endif /* __DECC */
+ &rfds, NULL, NULL, &tv);
+ if (rc < 0) {
+ int s_errno = socket_errno;
+ debug(F111,"netinc","select",rc);
+ debug(F111,"netinc","socket_errno",s_errno);
+ if (s_errno) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ }
+ }
+ debug(F111,"netinc","select",rc);
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+ if (!FD_ISSET(ttyfd, &rfds)) {
+#ifdef LEBUF
+ if (le_inbuf() > 0) {
+ timeout = -1;
+ break;
+ }
+#endif /* LEBUF */
+ /* If waiting forever we have no way of knowing if the */
+ /* socket closed so try writing a 0-length TCP packet */
+ /* which should force an error if the socket is closed */
+ if (!timo) {
+ if ((rc = socket_write(ttyfd,"",0)) < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"netinc socket_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0) {
+ ReleaseTCPIPMutex();
+ return(-2);
+ }
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1); /* Call it an i/o error */
+ }
+ }
+ continue;
+ }
+ while (1) {
+ if (ttbufr() < 0) { /* Keep trying to refill it. */
+ timeout = -1;
+ break; /* Till we get an error. */
+ }
+ if (ttibn > 0) { /* Or we get a character. */
+ x = 0;
+ timeout = -1;
+ break;
+ }
+ }
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* !BSDSELECT */
+#ifdef IBMSELECT
+/*
+ Was used by OS/2, currently not used, but might come in handy some day...
+ ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set
+ and timeval stuff since this is the only place where it is used.
+*/
+ int socket = ttyfd;
+ int timeout = timo < 0 ? -timo : 1000 * timo;
+
+ debug(F101,"netinc IBMSELECT","",timo);
+ for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
+ if (select(&socket, 1, 0, 0, 100L) == 1) {
+ while (1) {
+ if (ttbufr() < 0) { /* Keep trying to refill it. */
+ timeout = -1;
+ break; /* Till we get an error. */
+ }
+ if (ttibn > 0) { /* Or we get a character. */
+ x = 0;
+ timeout = -1;
+ break;
+ }
+ }
+ }
+#ifdef LEBUF
+ else if (le_inbuf() > 0) {
+ timeout = -1;
+ break;
+ }
+#endif /* LEBUF */
+ }
+#else /* !IBMSELECT */
+#ifdef WINSOCK
+ /* Actually, under WinSock we have a better mechanism than select() */
+ /* for setting timeouts (SO_RCVTIMEO, SO_SNDTIMEO) */
+ SOCKET socket = ttyfd;
+ debug(F101,"netinc NTSELECT","",timo);
+ if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timo,
+ sizeof(timo)) == NO_ERROR)
+ while (1) {
+ if (ttbufr() < 0) /* Keep trying to refill it. */
+ break; /* Till we get an error. */
+ if (ttibn > 0) { /* Or we get a character. */
+ x = 0;
+ break;
+ }
+ }
+#else /* WINSOCK */
+/*
+ If we can't use select(), then we use the regular alarm()/signal()
+ timeout mechanism.
+*/
+ debug(F101,"netinc alarm","",timo);
+ x = alrm_execute(ckjaddr(njbuf),timo,nettout,donetinc,failnetinc);
+ ttimoff(); /* Timer off. */
+#endif /* WINSOCK */
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+#endif /* NT_TCP_OVERLAPPED */
+ }
+ }
+
+#ifdef LEBUF
+ if (le_inbuf() > 0) { /* If data was inserted into the */
+ if (le_getchar((CHAR *)&c) > 0) {/* Local Echo buffer while the */
+#ifdef OS2 /* was taking place do not mix */
+ ReleaseTCPIPMutex(); /* the le data with the net data */
+#endif /* OS2 */
+ return(c);
+ }
+ }
+#endif /* LEBUF */
+ if (x < 0) { /* Return -1 if we failed. */
+ debug(F100,"netinc timed out","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else { /* Otherwise */
+ c = ttibuf[ttibp]; /* Return the first char in ttibuf[] */
+ if (deblog) {
+#ifndef COMMENT
+ debug(F101,"netinc returning","",c);
+#endif /* COMMENT */
+ if (c == 0) {
+ debug(F101,"netinc 0 ttibn","",ttibn);
+ debug(F101,"netinc 0 ttibp","",ttibp);
+#ifdef BETADEBUG
+ {
+#ifdef OS2
+ extern int tt_type_mode;
+ if ( !ISVTNT(tt_type_mode) )
+#endif /* OS2 */
+ hexdump("netinc &ttbuf[ttibp]",&ttibuf[ttibp],ttibn);
+ }
+#endif /* BETADEBUG */
+ }
+ }
+ ttibp++;
+ ttibn--;
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+#ifdef CK_ENCRYPTION
+ if (TELOPT_U(TELOPT_ENCRYPTION))
+ ck_tn_decrypt(&c,1);
+#endif /* CK_ENCRYPTION */
+ return(c);
+ }
+#else /* Not using TCPIPLIB */
+ return(-1);
+#endif /* TCPIPLIB */
+}
+
+/* N E T T O L -- Output a string of bytes to the network */
+/*
+ Call with s = pointer to string, n = length.
+ Returns number of bytes actually written on success, or
+ -1 on i/o error, -2 if called improperly.
+*/
+
+int
+nettol(s,n) CHAR *s; int n; {
+#ifdef TCPIPLIB
+ int count = 0;
+ int len = n;
+ int try = 0;
+
+ if (ttyfd == -1) {
+ debug(F100,"nettol socket is closed","",0);
+ return -1;
+ }
+ debug(F101,"nettol TCPIPLIB ttnet","",ttnet);
+#ifdef COMMENT
+ hexdump("nettol",s,n);
+#endif /* COMMENT */
+
+#ifdef CK_KERBEROS
+#ifdef KRB4
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK4LOGIN) {
+ return(krb4_des_write(ttyfd,s,n));
+ }
+#endif /* RLOGCODE */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK5LOGIN) {
+ return(krb5_des_write(ttyfd,s,n,0));
+ }
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+ if (ttnproto == NP_K5U2U) {
+ return(krb5_u2u_write(ttyfd,s,n));
+ }
+#endif /* KRB5_U2U */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+#ifdef CK_ENCRYPTION
+ if (TELOPT_ME(TELOPT_ENCRYPTION))
+ ck_tn_encrypt(s,n);
+#endif /* CK_ENCRYPTION */
+
+#ifdef CK_SSL
+ if (ssl_active_flag || tls_active_flag) {
+ int error, r;
+ /* Write using SSL */
+ ssl_retry:
+ if (ssl_active_flag)
+ r = SSL_write(ssl_con, s, len /* >1024?1024:len */);
+ else
+ r = SSL_write(tls_con, s, len /* >1024?1024:len */);
+ switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,r)) {
+ case SSL_ERROR_NONE:
+ debug(F111,"nettol","SSL_write",r);
+ if ( r == len )
+ return(n);
+ s += r;
+ len -= r;
+ goto ssl_retry;
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"nettol SSL_ERROR_WANT_WRITE","",0);
+ return(-1);
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"nettol SSL_ERROR_WANT_READ","",0);
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( r == 0 ) { /* EOF */
+ netclos();
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"nettol SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+#endif /* NT */
+ return(rc);
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"nettol SSL_ERROR_WANT_X509_LOOKUP","",0);
+ netclos();
+ return(-2);
+ case SSL_ERROR_SSL:
+ debug(F100,"nettol SSL_ERROR_SSL","",0);
+ if (bio_err!=NULL) {
+ int len;
+ extern char ssl_err[];
+ BIO_printf(bio_err,"nettol() SSL_ERROR_SSL\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ debug(F110,"nettol SSL_ERROR_SSL",ssl_err,0);
+ if (ssl_debug_flag)
+ printf(ssl_err);
+ } else if (ssl_debug_flag) {
+ debug(F100,"nettol SSL_ERROR_SSL","",0);
+ fflush(stderr);
+ fprintf(stderr,"nettol() SSL_ERROR_SSL\n");
+ ERR_print_errors_fp(stderr);
+ }
+#ifdef COMMENT
+ netclos();
+ return(-2);
+#else
+ return(-1);
+#endif
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"nettol SSL_ERROR_ZERO_RETURN","",0);
+ netclos();
+ return(-2);
+ default:
+ debug(F100,"nettol SSL_ERROR_?????","",0);
+ netclos();
+ return(-2);
+ }
+ }
+#endif /* CK_SSL */
+
+ nettol_retry:
+ try++; /* Increase the try counter */
+
+ if (ttnet == NET_TCPB) {
+#ifdef BSDSELECT
+ fd_set wfds;
+ struct timeval tv;
+
+ debug(F101,"nettol BSDSELECT","",0);
+ tv.tv_usec = 0L;
+ tv.tv_sec=30;
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+#ifdef STREAMING
+ do_select:
+#endif /* STREAMING */
+ FD_ZERO(&wfds);
+ FD_SET(ttyfd, &wfds);
+ if (select(FD_SETSIZE, NULL,
+#ifdef __DECC
+#ifndef __DECC_VER
+ (int *)
+#endif /* __DECC_VER */
+#endif /* __DECC */
+ &wfds, NULL, &tv) < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"nettol select failed","",s_errno);
+#ifdef BETADEBUG
+ printf("nettol select failed: %d\n", s_errno);
+#endif /* BETADEBUG */
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+ if (!FD_ISSET(ttyfd, &wfds)) {
+#ifdef STREAMING
+ if (streaming)
+ goto do_select;
+#endif /* STREAMING */
+ debug(F111,"nettol","!FD_ISSET",ttyfd);
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ {
+ int tries = 0;
+ debug(F101,"nettol IBMSELECT","",0);
+ while (select(&ttyfd, 0, 1, 0, 1000) != 1) {
+ int count;
+ if (tries++ >= 60) {
+ /* if after 60 seconds we can't get permission to write */
+ debug(F101,"nettol select failed","",socket_errno);
+ return(-1);
+ }
+ if ((count = nettchk()) < 0) {
+ debug(F111,"nettol","nettchk()",count);
+ return(count);
+ }
+ }
+ }
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ if ((count = socket_write(ttyfd,s,n)) < 0) {
+ int s_errno = socket_errno; /* maybe a function */
+ debug(F101,"nettol socket_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-2);
+#endif /* OS2 */
+ return(-1); /* Call it an i/o error */
+ }
+ if (count < n) {
+ debug(F111,"nettol socket_write",s,count);
+ if (try > 25) {
+ /* don't try more than 25 times */
+ debug(F100,"nettol tried more than 25 times","",0);
+ return(-1);
+ }
+ if (count > 0) {
+ s += count;
+ n -= count;
+ }
+ debug(F111,"nettol retry",s,n);
+ goto nettol_retry;
+ } else {
+ debug(F111,"nettol socket_write",s,count);
+ return(len); /* success - return total length */
+ }
+ } else
+ return(-2);
+#else
+ debug(F100,"nettol TCPIPLIB not defined","",0);
+ return(-2);
+#endif /* TCPIPLIB */
+}
+
+/* N E T T O C -- Output character to network */
+/*
+ Call with character to be transmitted.
+ Returns 0 if transmission was successful, or
+ -1 upon i/o error, or -2 if called improperly.
+*/
+int
+#ifdef CK_ANSIC
+nettoc(CHAR c)
+#else
+nettoc(c) CHAR c;
+#endif /* CK_ANSIC */
+/* nettoc */ {
+#ifdef UNIX
+ return(ttoc(c));
+#else
+#ifdef TCPIPLIB
+ unsigned char cc;
+ if (ttyfd == -1) {
+ debug(F100,"nettoc socket is closed","",0);
+ return -1;
+ }
+ cc = c;
+ debug(F101,"nettoc cc","",cc);
+
+#ifdef CK_KERBEROS
+#ifdef KRB4
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK4LOGIN) {
+ return(krb4_des_write(ttyfd,&cc,1)==1?0:-1);
+ }
+#endif /* RLOGCODE */
+#endif /* KRB4 */
+#ifdef KRB5
+#ifdef RLOGCODE
+ if (ttnproto == NP_EK5LOGIN) {
+ return(krb5_des_write(ttyfd,&cc,1,0)==1?0:-1);
+ }
+#endif /* RLOGCODE */
+#ifdef KRB5_U2U
+ if (ttnproto == NP_K5U2U) {
+ return(krb5_u2u_write(ttyfd,&cc,1)==1?0:-1);
+ }
+#endif /* KRB5_U2U */
+#endif /* KRB5 */
+#endif /* CK_KERBEROS */
+
+#ifdef CK_ENCRYPTION
+ if ( TELOPT_ME(TELOPT_ENCRYPTION) )
+ ck_tn_encrypt(&cc,1);
+#endif /* CK_ENCRYPTION */
+#ifdef CK_SSL
+ if (ssl_active_flag || tls_active_flag) {
+ int len, error;
+ /* Write using SSL */
+ ssl_retry:
+ if (ssl_active_flag)
+ len = SSL_write(ssl_con, &cc, 1);
+ else
+ len = SSL_write(tls_con, &cc, 1);
+ switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,len)) {
+ case SSL_ERROR_NONE:
+ debug(F111,"nettoc","SSL_write",len);
+ return(len == 1 ? 0 : -1);
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( len == 0 ) { /* EOF */
+ netclos();
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"nettoc SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+#endif /* NT */
+ return(rc);
+ }
+ case SSL_ERROR_SSL:
+ if (bio_err!=NULL) {
+ int len;
+ extern char ssl_err[];
+ BIO_printf(bio_err,"nettoc() SSL_ERROR_SSL\n");
+ ERR_print_errors(bio_err);
+ len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ);
+ ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0';
+ debug(F110,"nettoc SSL_ERROR_SSL",ssl_err,0);
+ if (ssl_debug_flag)
+ printf(ssl_err);
+ } else if (ssl_debug_flag) {
+ debug(F100,"nettoc SSL_ERROR_SSL","",0);
+ fflush(stderr);
+ fprintf(stderr,"nettoc() SSL_ERROR_SSL\n");
+ ERR_print_errors_fp(stderr);
+ }
+ return(-1);
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ netclos();
+ return(-2);
+ }
+ }
+#endif /* CK_SSL */
+ if (ttnet == NET_TCPB) {
+#ifdef BSDSELECT
+ fd_set wfds;
+ struct timeval tv;
+
+ debug(F101,"nettoc BSDSELECT","",0);
+ tv.tv_usec = 0L;
+ tv.tv_sec = 30;
+
+#ifdef STREAMING
+ do_select:
+#endif /* STREAMING */
+
+ FD_ZERO(&wfds);
+ FD_SET(ttyfd, &wfds);
+ if (select(FD_SETSIZE, NULL,
+#ifdef __DECC
+#ifndef __DECC_VER
+ (int *)
+#endif /* __DECC_VER */
+#endif /* __DECC */
+ &wfds, NULL, &tv) < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"nettoc select failed","",s_errno);
+#ifdef BETADEBUG
+ printf("nettoc select failed: %d\n", s_errno);
+#endif /* BETADEBUG */
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+ if (!FD_ISSET(ttyfd, &wfds)) {
+#ifdef STREAMING
+ if (streaming)
+ goto do_select;
+#endif /* STREAMING */
+ debug(F111,"nettoc","!FD_ISSET",ttyfd);
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ {
+ int tries = 0;
+ while (select(&ttyfd, 0, 1, 0, 1000) != 1) {
+ int count;
+ if (tries++ >= 60) {
+ /* if after 60 seconds we can't get permission to write */
+ debug(F101,"nettoc select failed","",socket_errno);
+ return(-1);
+ }
+ if ((count = nettchk()) < 0) {
+ debug(F111,"nettoc","nettchk()",count);
+ return(count);
+ }
+ }
+ }
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ if (socket_write(ttyfd,&cc,1) < 1) {
+ int s_errno = socket_errno; /* maybe a function */
+ debug(F101,"nettoc socket_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-2);
+#endif /* OS2 */
+ return(-1);
+ }
+ debug(F101,"nettoc socket_write","", cc);
+ return(0);
+ } else return(-2);
+#else
+ return(-2);
+#endif /* TCPIPLIB */
+#endif /* UNIX */
+}
+
+/* N E T F L U I -- Flush network input buffer */
+
+#ifdef TNCODE
+static int
+#ifdef CK_ANSIC
+netgetc(int timo) /* Input function to point to... */
+#else /* CK_ANSIC */
+netgetc(timo) int timo;
+#endif /* CK_ANSIC */
+{ /* ...in the tn_doop() call */
+#ifdef TCPIPLIB
+ return netinc(timo);
+#else /* TCPIPLIB */
+ return ttinc(timo);
+#endif /* TCPIPLIB */
+}
+#endif /* TNCODE */
+
+int
+netflui() {
+ int n;
+ int ch;
+#ifdef NETLEBUF
+ ttpush = -1; /* Clear the peek-ahead char */
+ while (le_data && (le_inbuf() > 0)) {
+ CHAR ch = '\0';
+ if (le_getchar(&ch) > 0) {
+ debug(F101,"ttflui le_inbuf ch","",ch);
+ }
+ }
+#endif /* NETLEBUF */
+
+#ifdef TCPIPLIB
+#ifdef OS2
+ RequestTCPIPMutex(SEM_INDEFINITE_WAIT);
+#endif /* OS2 */
+#ifdef TNCODE
+ if (ttnproto == NP_TELNET) {
+ /* Netflui must process Telnet negotiations or get out of sync */
+ if ((n = nettchk()) <= 0)
+ goto exit_flui;
+ while (n-- > 0) {
+ ch = netinc(1);
+ if (ch == IAC) {
+ extern int duplex; /* this really shouldn't be here but ... */
+ int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetc);
+ if (tx == 1) duplex = 1;
+ else if (tx == 2) duplex = 0;
+ n = nettchk();
+ }
+ }
+ } else
+#endif /* TNCODE */
+ {
+ ttibuf[ttibp+ttibn] = '\0';
+ debug(F111,"netflui 1",ttibuf,ttibn);
+#ifdef CK_ENCRYPTION
+ if (TELOPT_U(TELOPT_ENCRYPTION)) {
+ ck_tn_decrypt(&ttibuf[ttibp],ttibn);
+ }
+#endif /* CK_ENCRYPTION */
+ ttibn = ttibp = 0; /* Flush internal buffer *FIRST* */
+ if (ttyfd < 1)
+ goto exit_flui;
+ if ((n = nettchk()) > 0) { /* Now see what's waiting on the net */
+ if (n > TTIBUFL) n = TTIBUFL; /* and sponge it up */
+ debug(F101,"netflui 2","",n); /* ... */
+ n = socket_read(ttyfd,ttibuf,n); /* into our buffer */
+ if (n >= 0) ttibuf[n] = '\0';
+ debug(F111,"netflui 3",ttibuf,n);
+#ifdef CK_ENCRYPTION
+ if (TELOPT_U(TELOPT_ENCRYPTION)) {
+ ck_tn_decrypt(&ttibuf[ttibp],n);
+ }
+#endif /* CK_ENCRYPTION */
+ ttibuf[0] = '\0';
+ }
+ }
+#else /* !TCPIPLIB */
+ if (ttyfd < 1)
+ goto exit_flui;
+#ifdef TNCODE
+ if (ttnproto == NP_TELNET) {
+ if ((n = ttchk()) <= 0)
+ goto exit_flui;
+ while (n-- >= 0) {
+ /* Netflui must process Telnet negotiations or get out of sync */
+ ch = ttinc(1);
+ if (ch == IAC) {
+ extern int duplex; /* this really shouldn't be here but ... */
+ int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetc);
+ if (tx == 1) duplex = 1;
+ else if (tx == 2) duplex = 0;
+ n = ttchk();
+ }
+ };
+ } else
+#endif /* TNCODE */
+ if ((n = ttchk()) > 0) {
+ debug(F101,"netflui non-TCPIPLIB","",n);
+ while ((n--) && ttinc(1) > -1) /* Don't worry, ttinc() is buffered */
+ ; /* and it handles the decryption... */
+ }
+#endif /* TCPIPLIB */
+ exit_flui:
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(0);
+}
+
+#ifdef CK_KERBEROS
+/* The following two functions are required for encrypted rlogin */
+/* They are called with nettoc() or nettol() are transmitting */
+/* encrypted data. They call a function to encrypt the data */
+/* and that function needs to be able to write to/read from the */
+/* network in an unimpeded manner. Hence, these two simple fns. */
+int
+net_write(fd, buf, len)
+ int fd;
+ register const char *buf;
+ int len;
+{
+ int cc;
+ register int wrlen = len;
+ do {
+#ifdef TCPIPLIB
+ cc = socket_write(fd, buf, wrlen);
+#else
+ cc = write(fd,buf,wrlen);
+#endif /* TCPIPLIB */
+ if (cc < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"net_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-1);
+ else
+ continue;
+#else /* OS2 */
+ if (errno == EINTR)
+ continue;
+ return(-1);
+#endif /* OS2 */
+ }
+ else {
+ buf += cc;
+ wrlen -= cc;
+ }
+ } while (wrlen > 0);
+ return(len);
+}
+int
+net_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ register int len;
+{
+ int cc, len2 = 0;
+
+ do {
+#ifdef TCPIPLIB
+ cc = socket_read(fd, buf, len);
+#else
+ cc = read(fd,buf,len);
+#endif
+ if (cc < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"net_read error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-1);
+#endif /* OS2 */
+ return(cc); /* errno is already set */
+ }
+ else if (cc == 0) {
+ netclos();
+ return(len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return(len2);
+}
+#endif /* CK_KERBEROS */
+#endif /* NONET */
+
+/* getlocalipaddr() attempts to resolve an IP Address for the local machine.
+ * If the host is multi-homed it returns only one address.
+ *
+ * Two techniques are used.
+ * (1) get the local host name and perform a DNS lookup, then take
+ * the first entry;
+ * (2) open a UDP socket, use it to connect to a fictitious host (it's OK,
+ * no data is sent), then retrieve the local address from the socket.
+ * Note: the second technique won't work on Microsoft systems. See
+ * Article ID: Q129065 PRB: Getsockname() Returns IP Address 0.0.0.0 for UDP
+ */
+
+/* Technique number one cannot work reliably if the machine is a laptop
+ * and the hostname is associated with a physical adapter which is not
+ * installed and a PPP connection is being used instead. This is because
+ * the hostname DNS lookup will succeed for the physical adapter even though
+ * it would be impossible to use it. In NT4 SP4, the gethostbyname()
+ * when given the result of gethostname() returns not the real DNS entries
+ * for that name+domain. Instead it returns all of the static and dynamic
+ * IP addresses assigned to any physical or virtual adapter defined in the
+ * system regardless of whether or not it is installed. The order of the
+ * addresses is fixed according to the binding order in the NT registry.
+ */
+
+/*
+ * It appears that calling gethostbyname(NULL) is more reliable than
+ * calling gethostbyname(gethostname()) on Windows. So on Windows we will
+ * only call gethostbyname(NULL).
+ */
+
+int
+getlocalipaddr() {
+#ifndef datageneral
+ struct sockaddr_in l_sa;
+ struct sockaddr_in r_sa;
+ GSOCKNAME_T slen = sizeof(struct sockaddr_in);
+ int sock;
+ int rc;
+ struct in_addr laddr;
+
+ /* if still not resolved, then try second strategy */
+ /* This second strategy does not work on Windows */
+
+ memset(&l_sa,0,slen);
+ memset(&r_sa,0,slen);
+
+ /* get a UDP socket */
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock != -1) {
+ /* connect to arbirary port and address (NOT loopback) */
+ r_sa.sin_family = AF_INET;
+ r_sa.sin_port = htons(IPPORT_ECHO);
+
+ /* The following is an "illegal conversion" in AOS/VS */
+ /* (and who knows where else) */
+
+#ifdef INADDRX
+ inaddrx = inet_addr("128.127.50.1");
+ r_sa.sin_addr.s_addr = *(unsigned long *)&inaddrx;
+#else
+ r_sa.sin_addr.s_addr = inet_addr("128.127.50.1");
+#endif /* INADDRX */
+ rc = connect(sock, (struct sockaddr *) &r_sa, sizeof(struct sockaddr));
+ if (!rc) { /* get local address */
+ getsockname(sock,(struct sockaddr *)&l_sa,&slen);
+#ifdef TCPIPLIB
+ socket_close(sock); /* We're done with the socket */
+#else
+ close(sock);
+#endif /* TCPIPLIB */
+ if (l_sa.sin_addr.s_addr != INADDR_ANY) {
+ myxipaddr = ntohl(l_sa.sin_addr.s_addr);
+ ckstrncpy(myipaddr,(char *)inet_ntoa(l_sa.sin_addr),20);
+ debug(F110,"getlocalipaddr setting buf to",myipaddr,0);
+ return(0);
+ }
+ }
+ }
+ return getlocalipaddrs(myipaddr,sizeof(myipaddr),0);
+#else /* datageneral */
+ return(-1);
+#endif /* datageneral */
+}
+
+int
+getlocalipaddrs(buf,bufsz,index)
+ char * buf;
+ int bufsz;
+ int index;
+/* getlocalipaddrs */ {
+#ifndef datageneral
+ char localhost[256];
+ struct hostent * host=NULL;
+ struct sockaddr_in l_sa;
+ struct sockaddr_in r_sa;
+ GSOCKNAME_T slen = sizeof(struct sockaddr_in);
+ int rc;
+#ifdef COMMENT
+ int sock;
+ char messageBuf[60];
+ struct in_addr laddr;
+#endif /* COMMENT */
+
+ memset(&l_sa,0,slen);
+ memset(&r_sa,0,slen);
+
+ /* init local address (to zero) */
+ l_sa.sin_addr.s_addr = INADDR_ANY;
+
+#ifdef CKGHNLHOST
+ rc = gethostname(localhost, 256);
+ debug(F110,"getlocalipaddrs localhost",localhost,0);
+#else
+ /* This doesn't work on some platforms, e.g. Solaris */
+ rc = 0;
+ localhost[0] = '\0';
+#ifdef NT
+ if ( winsock_version < 20 ) {
+ rc = gethostname(localhost, 256);
+ debug(F110,"getlocalipaddrs localhost",localhost,0);
+ }
+#endif /* NT */
+#endif /* CKGHNLHOST */
+ if (!rc) {
+ /* resolve host name for local address */
+ debug(F110,"getlocalipaddrs","calling gethostbyname()",0);
+ host = gethostbyname(localhost);
+ debug(F111,"getlocalipaddrs","gethostbyname() returned",host);
+ if (host) {
+#ifdef HADDRLIST
+ host = ck_copyhostent(host);
+ if ( index < 0 || index > 63 || !host->h_addr_list[index] ) {
+ buf[0] = '\0';
+ return(-1);
+ }
+ l_sa.sin_addr.s_addr =
+ *((unsigned long *) (host->h_addr_list[index]));
+ ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),20);
+ debug(F110,"getlocalipaddrs setting buf to",buf,0);
+
+#ifdef COMMENT
+ /* This is for reporting multiple IP Address */
+ while (host->h_addr_list && host->h_addr_list[0]) {
+ l_sa.sin_addr.s_addr =
+ *((unsigned long *) (host->h_addr_list[0]));
+ ckstrncpy(messageBuf,
+ (char *)inet_ntoa(l_sa.sin_addr),60);
+ if (tcp_address) {
+ if (!strcmp(messageBuf,tcp_address))
+ ckstrncpy(myipaddr,tcp_address,20);
+ }
+ debug(F110,"getlocalipaddrs ip address list", messageBuf, 0);
+ host->h_addr_list++;
+ }
+#endif /* COMMENT */
+#else /* HADDRLIST */
+ if (index != 0) {
+ buf[0] = '\0';
+ return(-1);
+ }
+ l_sa.sin_addr.s_addr = *((unsigned long *) (host->h_addr));
+ ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),bufsz);
+ debug(F110,"getlocalipaddrs setting buf to",buf,0);
+#endif /* HADDRLIST */
+ return(0);
+ } else debug(F110,
+ "getlocalipaddrs: gethostbyname() failed",
+ localhost,
+ 0
+ );
+ }
+#endif /* datageneral */
+ return(-1);
+}
+
+#ifdef RLOGCODE /* TCP/IP RLOGIN protocol support code */
+int
+rlog_naws() {
+ struct rlog_naws {
+ unsigned char id[4];
+ unsigned short rows, cols, ypix, xpix;
+ } nawsbuf;
+
+ if (ttnet != NET_TCPB)
+ return 0;
+ if (ttnproto != NP_RLOGIN
+#ifdef CK_KERBEROS
+ && ttnproto != NP_K4LOGIN
+ && ttnproto != NP_EK4LOGIN
+ && ttnproto != NP_K5LOGIN
+ && ttnproto != NP_EK5LOGIN
+#endif /* CK_KERBEROS */
+ )
+ return 0;
+ if (!TELOPT_ME(TELOPT_NAWS))
+ return 0;
+
+ debug(F100,"rlogin Window Size sent","",0);
+
+ nawsbuf.id[0] = nawsbuf.id[1] = 0377;
+ nawsbuf.id[2] = nawsbuf.id[3] = 's';
+#ifdef OS2
+ nawsbuf.rows = htons((unsigned short) (VscrnGetHeight(VTERM)
+ -(tt_status[VTERM]?1:0)));
+ nawsbuf.cols = htons((unsigned short) VscrnGetWidth(VTERM));
+#else /* OS2 */
+ nawsbuf.rows = htons((unsigned short) tt_rows);
+ nawsbuf.cols = htons((unsigned short) tt_cols);
+#endif /* OS2 */
+ nawsbuf.ypix = htons(0); /* y pixels */
+
+ nawsbuf.xpix = htons(0); /* x pixels */
+ if (ttol((CHAR *)(&nawsbuf), sizeof(nawsbuf)) < 0)
+ return(-1);
+ return(0);
+}
+
+#ifdef OS2ORUNIX
+#define RLOGOUTBUF
+#endif /* OS2 */
+static int
+#ifdef CK_ANSIC
+rlog_ini(CHAR * hostname, int port,
+ struct sockaddr_in * l_addr, struct sockaddr_in * r_addr)
+#else /* CK_ANSIC */
+rlog_ini(hostname, port, l_addr, r_addr)
+ CHAR * hostname;
+ int port;
+ struct sockaddr_in * l_addr;
+ struct sockaddr_in * r_addr;
+#endif /* CK_ANSIC */
+/* rlog_ini */ {
+
+#ifdef RLOGOUTBUF
+ char outbuf[512];
+ int outbytes=0;
+#endif /* RLOGOUTBUF */
+ int flag = 0;
+#define TERMLEN 16
+#define CONSPDLEN 16
+ CHAR localuser[UIDBUFLEN+1];
+ CHAR remoteuser[UIDBUFLEN+1];
+ int userlen = 0;
+ CHAR term_speed[TERMLEN+CONSPDLEN+1];
+#ifdef CONGSPD
+ long conspd = -1L;
+#endif /* CONGSPD */
+#ifdef OS2
+ extern int tt_type, max_tt;
+ extern struct tt_info_rec tt_info[];
+#endif /* OS2 */
+ int i, n;
+
+ int rc = 0;
+ tn_reset(); /* This call will reset all of the Telnet */
+ /* options and then quit. We need to do */
+ /* this since we use the Telnet options */
+ /* to hold various state information */
+ duplex = 0; /* Rlogin is always remote echo */
+ rlog_inband = 0;
+
+#ifdef CK_TTGWSIZ
+/*
+ But compute the values anyway before the first read since the out-
+ of-band NAWS request would arrive before the first data byte (NULL).
+*/
+#ifdef OS2
+ /* Console terminal screen rows and columns */
+ debug(F101,"rlog_ini tt_rows 1","",VscrnGetHeight(VTERM)
+ -(tt_status[VTERM]?1:0));
+ debug(F101,"rlog_ini tt_cols 1","",VscrnGetWidth(VTERM));
+ /* Not known yet */
+ if (VscrnGetWidth(VTERM) < 0 ||
+ VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) < 0) {
+ ttgwsiz(); /* Try to get screen dimensions */
+ }
+ debug(F101,
+ "rlog_ini tt_rows 2",
+ "",
+ VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0)
+ );
+ debug(F101,"rlog_ini tt_cols 2","",VscrnGetWidth(VTERM));
+#else /* OS2 */
+ debug(F101,"rlog_ini tt_rows 1","",tt_rows);
+ debug(F101,"rlog_ini tt_cols 1","",tt_cols);
+ if (tt_rows < 0 || tt_cols < 0) { /* Not known yet */
+ ttgwsiz(); /* Try to find out */
+ }
+ debug(F101,"rlog_ini tt_rows 2","",tt_rows);
+ debug(F101,"rlog_ini tt_cols 2","",tt_cols);
+#endif /* OS2 */
+#endif /* CK_TTGWSIZ */
+
+ ttflui(); /* Start by flushing the buffers */
+
+ rlog_mode = RL_COOKED;
+
+ /* Determine the user's local username ... */
+
+ localuser[0] = '\0';
+#ifdef NT
+ {
+ char localuid[UIDBUFLEN+1];
+ ckstrncpy((char *)localuser,(char *)GetLocalUser(),UIDBUFLEN);
+ }
+
+ if ( !localuser[0] )
+#endif /* NT */
+ {
+ char * user = getenv("USER");
+ if (!user)
+ user = "";
+ userlen = strlen(user);
+ debug(F111,"rlogin getenv(USER)",user,userlen);
+ ckstrncpy((char *)localuser,user,UIDBUFLEN);
+ debug(F110,"rlog_ini localuser 1",localuser,0);
+ }
+ if ( !localuser[0] )
+ strcpy((char *)localuser,"unknown");
+ else if (ck_lcname) {
+ cklower((char *)localuser);
+ debug(F110,"rlog_ini localuser 2",localuser,0);
+ }
+
+ /* And the username to login with */
+ if (uidbuf[0]) {
+ ckstrncpy((char *)remoteuser,uidbuf,UIDBUFLEN);
+ debug(F110,"rlog_ini remoteuser 1",remoteuser,0);
+ } else if (localuser[0]) {
+ ckstrncpy((char *)remoteuser,(char *)localuser,UIDBUFLEN);
+ debug(F110,"rlog_ini remoteuser 2",remoteuser,0);
+ } else {
+ remoteuser[0] = '\0';
+ debug(F110,"rlog_ini remoteuser 3",remoteuser,0);
+ }
+ if (ck_lcname)
+ cklower((char *)remoteuser);
+ debug(F110,"rlog_ini remoteuser 4",remoteuser,0);
+
+ /* The command to issue is the terminal type and speed */
+ term_speed[0] = '\0';
+ if (tn_term) { /* SET TELNET TERMINAL-TYPE value */
+ if (*tn_term) { /* (if any) takes precedence. */
+ ckstrncpy((char *)term_speed, tn_term, TERMLEN);
+ flag = 1;
+ }
+ } else { /* Otherwise the local terminal type */
+#ifdef OS2
+ /* In terminal-emulating versions, it's the SET TERM TYPE value */
+ ckstrncpy(term_speed, (tt_type >= 0 && tt_type <= max_tt) ?
+ tt_info[tt_type].x_name : "network", TERMLEN);
+#else
+ /* In the others, we just look at the TERM environment variable */
+ {
+ char *p = getenv("TERM");
+ if (p)
+ ckstrncpy((char *)term_speed,p,TERMLEN);
+ else
+ term_speed[0] = '\0';
+#ifdef VMS
+ for (p = (char *) term_speed; *p; p++) {
+ if (*p == '-' && (!strcmp(p,"-80") || !strcmp(p,"-132")))
+ break;
+ else if (isupper(*p))
+ *p = tolower(*p);
+ }
+ *p = '\0';
+#endif /* VMS */
+ }
+#endif /* OS2 */
+ }
+ n = strlen((char *)term_speed);
+ if (n > 0) { /* We have a terminal type */
+ if (!flag) { /* If not user-specified */
+ for (i = 0; i < n; i++) /* then lowercase it. */
+ if (isupper(term_speed[i]))
+ term_speed[i] = tolower(term_speed[i]);
+ }
+ debug(F110,"rlog_ini term_speed 1",term_speed,0);
+
+#ifdef CONGSPD
+ /* conspd() is not yet defined in all ck*tio.c modules */
+ conspd = congspd();
+ if (conspd > 0L) {
+ ckstrncat((char *)term_speed,"/",sizeof(term_speed));
+ ckstrncat((char *)term_speed,ckltoa(conspd),sizeof(term_speed));
+ } else
+#endif /* CONGSPD */
+ ckstrncat((char *)term_speed,"/19200",sizeof(term_speed));
+ debug(F110,"rlog_ini term_speed 2",term_speed,0);
+ } else {
+ term_speed[0] = '\0';
+ debug(F110,"rlog_ini term_speed 3",term_speed,0);
+ }
+
+#ifdef CK_KERBEROS
+ if (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN ||
+ ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) {
+ int kver, encrypt, rc;
+ switch (ttnproto) {
+ case NP_K4LOGIN:
+ kver = 4;
+ encrypt = 0;
+ break;
+ case NP_EK4LOGIN:
+ kver = 4;
+ encrypt = 1;
+ break;
+ case NP_K5LOGIN:
+ kver = 5;
+ encrypt = 0;
+ break;
+ case NP_EK5LOGIN:
+ kver = 5;
+ encrypt = 1;
+ break;
+ default:
+ kver = 0;
+ encrypt = 0;
+ }
+ rc = ck_krb_rlogin(hostname, port,
+ localuser, remoteuser, term_speed,
+ l_addr, r_addr, kver, encrypt);
+ if (!rc) { /* success */
+ TELOPT_ME(TELOPT_NAWS) = 1;
+ rc = rlog_naws();
+ }
+ return(rc);
+ } else
+#endif /* CK_KERBEROS */
+ if (ttnproto == NP_RLOGIN) {
+#ifdef RLOGOUTBUF
+ /*
+ * The rcmds start the connection with a series of init data:
+ *
+ * a port number upon which client is listening for stderr data
+ * the user's name on the client machine
+ * the user's name on the server machine
+ * the terminal_type/speed or command to execute
+ */
+ outbuf[outbytes++] = 0;
+ strcpy((char *)outbuf+outbytes,(char *)localuser);
+ outbytes += strlen((char *)localuser) + 1;
+ strcpy((char *)outbuf+outbytes,(char *)remoteuser);
+ outbytes += strlen((char *)remoteuser) + 1;
+ strcpy((char *)outbuf+outbytes,(char *)term_speed);
+ outbytes += strlen((char *)term_speed) + 1;
+ rc = ttol((CHAR *)outbuf,outbytes);
+#else /* RLOGOUTBUF */
+ ttoc(0); /* Send an initial NUL as wake-up */
+ /* Send each variable with the trailing NUL */
+ rc = ttol(localuser,strlen((char *)localuser)+1);
+ if (rc > 0)
+ rc = ttol(remoteuser,strlen((char *)remoteuser)+1);
+ if (rc > 0)
+ rc = ttol(term_speed,strlen((char *)term_speed)+1);
+#endif /* RLOGOUTBUF */
+
+ /* Now we are supposed to get back a single NUL as confirmation */
+ errno = 0;
+ rc = ttinc(60);
+ debug(F101,"rlogin first ttinc","",rc);
+ if (rc > 0) {
+ debug(F101,"rlogin ttinc 1","",rc);
+ printf(
+ "Rlogin protocol error - 0x%x received instead of 0x00\n", rc);
+ return(-1);
+ } else if (rc < 0) {
+ debug(F101,"rlogin ttinc errno","",errno);
+ /* printf("Network error: %d\n", errno); */
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/* two control messages are defined:
+
+ a double flag byte of 'o' indicates a one-byte message which is
+ identical to what was once carried out of band.
+
+ a double flag byte of 'q' indicates a zero-byte message. This
+ message is interpreted as two \377 data bytes. This is just a
+ quote rule so that binary data from the server does not confuse the
+ client. */
+
+int
+rlog_ctrl(cp, n)
+ unsigned char *cp;
+ int n;
+{
+ if ((n >= 5) && (cp[2] == 'o') && (cp[3] == 'o')) {
+ if (rlog_oob(&cp[4],1))
+ return(-5);
+ return(5);
+ } else if ((n >= 4) && (cp[2] == 'q') && (cp[3] == 'q')) {
+ /* this is somewhat of a hack */
+ cp[2] = '\377';
+ cp[3] = '\377';
+ return(2);
+ }
+ return(0);
+}
+
+static int
+rlog_oob(oobdata, count) CHAR * oobdata; int count; {
+ int i;
+ int flush = 0;
+
+ debug(F111,"rlogin out_of_band","count",count);
+
+ for (i = 0; i<count; i++) {
+ debug(F101,"rlogin out_of_band","",oobdata[i]);
+ if (oobdata[i] & 0x01)
+ continue;
+
+ if (oobdata[i] & 0x02) { /* Flush Buffered Data not yet displayed */
+ debug(F101,"rlogin Flush Buffered Data command","",oobdata[i]);
+
+ /* Only flush the data if in fact we are in a mode that won't */
+ /* get out of sync. Ie, not when we are in protocol mode. */
+ switch ( what ) {
+ case W_NOTHING:
+ case W_CONNECT:
+ case W_COMMAND:
+ if ( rlog_inband )
+ flush = 1;
+ else
+ ttflui();
+ break;
+ }
+ }
+ if (oobdata[i] & 0x10) { /* Switch to RAW mode */
+ debug(F101,"rlogin Raw Mode command","",oobdata[i]);
+ rlog_mode = RL_RAW;
+ }
+
+ if (oobdata[i] & 0x20) { /* Switch to COOKED mode */
+ debug(F101,"rlogin Cooked Mode command","",oobdata[i]);
+ rlog_mode = RL_COOKED;
+ }
+ if (oobdata[i] & 0x80)
+ { /* Send Window Size Info */
+ debug(F101,"rlogin Window Size command","",oobdata[i]);
+ /* Remember to send WS Info when Window Size changes */
+ if ( !TELOPT_ME(TELOPT_NAWS) ) {
+ TELOPT_ME(TELOPT_NAWS) = 1;
+ rlog_naws();
+ }
+ }
+ }
+ return(flush);
+}
+#ifndef TCPIPLIB
+static SIGTYP
+rlogoobh(sig) int sig; {
+#ifdef SOLARIS
+ char /* Or should it be char for all? */
+#else
+ CHAR
+#endif /* SOLARIS */
+ oobdata;
+
+ /* int count = 0; */ /* (not used) */
+
+ while (recv(ttyfd, &oobdata, 1, MSG_OOB) < 0) {
+ /*
+ * We need to do some special processing here.
+ * Just in case the socket is blocked for input
+ *
+ */
+ switch (errno) {
+ case EWOULDBLOCK:
+ break;
+ default:
+ return;
+ }
+ }
+ debug(F101,"rlogin out_of_band","",oobdata);
+ if (oobdata == 0x02) { /* Flush Buffered Data not yet displayed */
+ debug(F101,"rlogin Flush Buffered Data command","",oobdata);
+ netflui();
+ }
+ if (oobdata & 0x10) { /* Switch to raw mode */
+ debug(F101,"rlogin Raw Mode command","",oobdata);
+ rlog_mode = RL_RAW;
+ }
+ if (oobdata & 0x20) { /* Switch to cooked mode */
+ debug(F101,"rlogin Cooked Mode command","",oobdata);
+ rlog_mode = RL_COOKED;
+ }
+ if (oobdata & 0x80) { /* Send Window Size Info */
+ debug(F101,"rlogin Window Size command","",oobdata);
+ /* Remember to send WS Info when Window Size changes */
+ if ( !TELOPT_ME(TELOPT_NAWS) ) {
+ TELOPT_ME(TELOPT_NAWS) = 1;
+ rlog_naws();
+ }
+ }
+}
+#endif /* TCPIPLIB */
+#endif /* RLOGCODE */
+
+/* Send network BREAK */
+/*
+ Returns -1 on error, 0 if nothing happens, 1 if BREAK sent successfully.
+*/
+int
+netbreak() {
+ CHAR buf[3];
+ if (ttnet == NET_TCPB) {
+ if (ttnproto == NP_TELNET) {
+#ifdef TNCODE
+ buf[0] = (CHAR) IAC; buf[1] = (CHAR) BREAK; buf[2] = (CHAR) 0;
+ if (
+#ifdef OS2
+ nettol((char *) buf, 2)
+#else
+ ttol(buf, 2)
+#endif /* OS2 */
+ < 2)
+ return(-1);
+ if (tn_deb || debses || deblog) {
+ extern char tn_msg[];
+ ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET SENT ",TELCMD(BREAK),
+ NULL,NULL);
+ debug(F101,tn_msg,"",BREAK);
+ if (debses || tn_deb) tn_debug(tn_msg);
+ }
+ return(1);
+#else
+ debug(F100,"netbreak no TNCODE","",0);
+ return(0);
+#endif /* TNCODE */
+ }
+ /* Insert other TCP/IP protocols here */
+ }
+ /* Insert other networks here */
+ return(0);
+}
+#endif /* NETCONN */
+
+
+#ifdef NETCONN
+#ifdef SUNX25
+/*
+ SunLink X.25 support by Marcello Frutig, Catholic University,
+ Rio de Janeiro, Brazil, 1990.
+*/
+
+/* PAD X.3, X.28 and X.29 support */
+
+static CHAR x29err[MAXPADPARMS+3] = { X29_ERROR, INVALID_PAD_PARM, '\0' };
+
+/* Initialize PAD */
+
+extern CHAR padparms[];
+
+VOID
+initpad() {
+ padparms[PAD_BREAK_CHARACTER] = 0; /* Break character */
+ padparms[PAD_ESCAPE] = 1; /* Escape permitted */
+ padparms[PAD_ECHO] = 1; /* Kermit PAD does echo */
+ padparms[PAD_DATA_FORWARD_CHAR] = 2; /* forward character CR */
+ padparms[PAD_DATA_FORWARD_TIMEOUT] = 0; /* no timeout forward condition */
+ padparms[PAD_FLOW_CONTROL_BY_PAD] = 0; /* not used */
+ padparms[PAD_SUPPRESSION_OF_SIGNALS] = 1; /* allow PAD service signals */
+ padparms[PAD_BREAK_ACTION] = 21; /* brk action: INT pk + brk ind*/
+ padparms[PAD_SUPPRESSION_OF_DATA] = 0; /* no supression of user data */
+ padparms[PAD_PADDING_AFTER_CR] = 0; /* no padding after CR */
+ padparms[PAD_LINE_FOLDING] = 0; /* no line fold */
+ padparms[PAD_LINE_SPEED] = 0; /* line speed - don't care */
+ padparms[PAD_FLOW_CONTROL_BY_USER] = 0; /* flow cont of PAD - not used */
+ padparms[PAD_LF_AFTER_CR] = 0; /* no LF insertion after CR */
+ padparms[PAD_PADDING_AFTER_LF] = 0; /* no padding after LF */
+ padparms[PAD_EDITING] = 1; /* can edit */
+ padparms[PAD_CHAR_DELETE_CHAR] = 8; /* character delete character */
+ padparms[PAD_BUFFER_DELETE_CHAR] = 21; /* buffer delete character */
+ padparms[PAD_BUFFER_DISPLAY_CHAR] = 18; /* buffer display character */
+}
+
+/* Set PAD parameters */
+
+VOID
+setpad(s,n) CHAR *s; int n; {
+ int i;
+ CHAR *ps = s;
+
+ if (n < 1) {
+ initpad();
+ } else {
+ for (i = 0; i < n; i++) {
+ if (*ps > MAXPADPARMS)
+ x29err[i+2] = *ps;
+ else
+ padparms[*ps] = *(ps+1);
+ ps += 2;
+ }
+ }
+}
+
+/* Read PAD parameters */
+
+VOID
+readpad(s,n,r) CHAR *s; int n; CHAR *r; {
+ int i;
+ CHAR *ps = s;
+ CHAR *pr = r;
+
+ *pr++ = X29_PARAMETER_INDICATION;
+ if (n > 0) {
+ for (i = 0; i < n; i++, ps++) {
+ if (*ps > MAXPADPARMS) {
+ x29err[i+2] = *ps++;
+ } else {
+ *pr++ = *ps;
+ *pr++ = padparms[*ps++];
+ }
+ }
+ } else {
+ for (i = 1; i < MAXPADPARMS; i++) {
+ *pr++ = i;
+ *pr++ = padparms[i];
+ }
+ }
+}
+
+int
+qbitpkt(s,n) CHAR *s; int n; {
+ CHAR *ps = s;
+ int x29cmd = *ps;
+ CHAR *psa = s+1;
+ CHAR x29resp[(MAXPADPARMS*2)+1];
+
+ switch (x29cmd) {
+
+ case X29_SET_PARMS:
+ setpad (ps+1,n/2);
+ if ((int)strlen((char *)x29err) > 2) {
+ ttol(x29err,(int)strlen((char *)x29err));
+ x29err[2] = '\0';
+ }
+ return (-2);
+ case X29_READ_PARMS:
+ readpad (ps+1,n/2,x29resp);
+ setqbit ();
+ ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1));
+ if ((int)strlen((char *)x29err) > 2) {
+ ttol(x29err,(int)strlen((char *)x29err));
+ x29err[2] = '\0';
+ }
+ resetqbit();
+ break;
+ case X29_SET_AND_READ_PARMS:
+ setpad (ps+1,n/2);
+ readpad (ps+1,n/2,x29resp);
+ setqbit();
+ ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1));
+ if ((int)strlen((char *)x29err) > 2) {
+ ttol (x29err,(int)strlen((char *)x29err));
+ x29err [2] = '\0';
+ }
+ resetqbit();
+ return (-2);
+ case X29_INVITATION_TO_CLEAR:
+ (VOID) x25clear();
+ return (-1);
+ case X29_INDICATION_OF_BREAK:
+ break;
+ }
+ return (0);
+}
+
+/* PAD break action processor */
+
+VOID
+breakact() {
+ extern char x25obuf[MAXOX25];
+ extern int obufl;
+ extern int active;
+ extern unsigned char tosend;
+ static CHAR indbrk[3] = {
+ X29_INDICATION_OF_BREAK,
+ PAD_SUPPRESSION_OF_DATA,
+ 1
+ };
+ CHAR intudat, cause, diag;
+
+ if (x25stat() < 0) return; /* Ignore if no virtual call established */
+
+ if (padparms[PAD_BREAK_ACTION] != 0) /* Forward condition */
+ if (ttol((CHAR *)x25obuf,obufl) < 0) {
+ perror ("\r\nCan't send characters");
+ active = 0;
+ } else {
+ bzero (x25obuf,sizeof(x25obuf));
+ obufl = 0;
+ tosend = 0;
+ };
+
+ switch (padparms[PAD_BREAK_ACTION]) {
+
+ case 0 : break; /* do nothing */
+ case 1 : /* send interrupt packet with interrupt user data field = 1 */
+ intudat = 1;
+ x25intr (intudat);
+ break;
+ case 2 : /* send reset packet with cause and diag = 0 */
+ cause = diag = 0;
+ x25reset (cause,diag);
+ break;
+ case 5 : /* send interrupt packet with interrupt user data field = 0 */
+ intudat = 0;
+ x25intr (intudat);
+ setqbit ();
+ /* send indication of break without a parameter field */
+ ttoc(X29_INDICATION_OF_BREAK);
+ resetqbit ();
+ break;
+ case 8 : active = 0; /* leave data transfer */
+ conol ("\r\n");
+ break;
+ case 21: /* send interrupt packet with interrupt user data field = 0 */
+ intudat = 0;
+ x25intr (intudat);
+ setpad (indbrk+1,2); /* set pad to discard input */
+ setqbit ();
+ /* send indication of break with parameter field */
+ ttol (indbrk,sizeof(indbrk));
+ resetqbit ();
+ break;
+ }
+}
+
+/* X.25 support functions */
+
+X25_CAUSE_DIAG diag;
+
+/*
+ Convert a null-terminated string representing an X.121 address
+ to a packed BCD form.
+*/
+int
+pkx121(str,bcd) char *str; CHAR *bcd; {
+ int i, j;
+ u_char c;
+
+ i = j = 0;
+ while (str[i]) {
+ if (i >= 15 || str [i] < '0' || str [i] > '9')
+ return (-1);
+ c = str [i] - '0';
+ if (i & 1)
+ bcd [j++] |= c;
+ else
+ bcd [j] = c << 4;
+ i++;
+ }
+ return (i);
+}
+
+/* Reads and prints X.25 diagnostic */
+
+int
+x25diag () {
+ int i;
+
+ bzero ((char *)&diag,sizeof(diag));
+ if (ioctl(ttyfd,X25_RD_CAUSE_DIAG,&diag)) {
+ perror ("Reading X.25 diagnostic");
+ return(-1);
+ }
+ if (diag.datalen > 0) {
+ printf ("X.25 Diagnostic :");
+ for (i = 0; i < (int)diag.datalen; i++)
+ printf(" %02h",diag.data[i])+
+ printf ("\r\n");
+ }
+ return(0);
+}
+
+/* X.25 Out-of-Band Signal Handler */
+
+SIGTYP
+x25oobh(foo) int foo; {
+ int oobtype;
+ u_char oobdata;
+ int t;
+
+ (VOID) signal(SIGURG,x25oobh);
+ do {
+ if (ioctl(ttyfd,X25_OOB_TYPE,&oobtype)) {
+ perror ("Getting signal type");
+ return;
+ }
+ switch (oobtype) {
+ case INT_DATA:
+ if (recv(ttyfd,(char *)&oobdata,1,MSG_OOB) < 0) {
+ perror ("Receiving X.25 interrupt data");
+ return;
+ }
+ t = oobdata;
+ printf ("\r\nInterrupt received, data = %d\r\n", t);
+ break;
+ case VC_RESET:
+ printf ("\r\nVirtual circuit reset\r\n");
+ x25diag ();
+ break;
+ case N_RESETS:
+ printf ("\r\nReset timeout\r\n");
+ break;
+ case N_CLEARS:
+ printf ("\r\nClear timeout\r\n");
+ break;
+ case MSG_TOO_LONG:
+ printf ("\r\nMessage discarded, too long\r\n");
+ break;
+ default:
+ if (oobtype) printf("\r\nUnknown oob type %d\r\n",oobtype);
+ break;
+ }
+ } while (oobtype);
+}
+
+/* Send a X.25 interrupt packet */
+
+int
+#ifdef CK_ANSIC
+x25intr(char intr)
+#else
+x25intr(intr) char intr;
+#endif /* CK_ANSIC */
+/* x25intr */ {
+ if (send(ttyfd,&intr,1,MSG_OOB) < 0) return(-1);
+ debug(F100,"X.25 intr","",0);
+ return(0);
+}
+
+/* Reset X.25 virtual circuit */
+int
+#ifdef CK_ANSIC
+x25reset(char cause, char diagn)
+#else
+x25reset(cause, diagn) char cause; char diagn;
+#endif /* CK_ANSIC */
+/* x25reset */ {
+ bzero ((char *)&diag,sizeof(diag));
+ diag.flags = 0;
+ diag.datalen = 2;
+ diag.data[0] = cause;
+ diag.data[1] = diagn;
+ if (ioctl(ttyfd,X25_WR_CAUSE_DIAG,&diag) < 0)
+ return(-1);
+ debug(F100,"X.25 reset","",0);
+ return(0);
+}
+
+/* Clear X.25 virtual circuit */
+int
+x25clear() {
+ int i;
+ debug(F100,"X.25 clear","",0);
+ bzero ((char *)&diag,sizeof(diag));
+ diag.flags = (1 << DIAG_TYPE);
+ diag.datalen = 2;
+ diag.data[0] = 0;
+ diag.data[1] = 0;
+ ioctl (ttyfd,X25_WR_CAUSE_DIAG,&diag); /* Send Clear Request */
+ return(ttclos(0)); /* Close socket */
+}
+
+/* X.25 status */
+int
+x25stat() {
+ if (ttyfd == -1) return (-1);
+ return(0);
+}
+
+/* Set Q_BIT on */
+VOID
+setqbit() {
+ static int qbiton = 1 << Q_BIT;
+ ioctl (ttyfd,X25_SEND_TYPE,&qbiton);
+}
+
+/* Set Q_BIT off */
+VOID
+resetqbit() {
+ static int qbitoff = 0;
+ ioctl (ttyfd,X25_SEND_TYPE,&qbitoff);
+}
+
+/* Read n characters from X.25 circuit into buf */
+
+int
+x25xin(n,buf) int n; CHAR *buf; {
+ register int x, c;
+ int qpkt;
+
+ do {
+ x = read(ttyfd,buf,n);
+ if (buf[0] & (1 << Q_BIT)) { /* If Q_BIT packet, process it */
+ /* If return -1 : invitation to clear; -2 : PAD changes */
+ if ((c=qbitpkt(buf+1,x-2)) < 0) return(c);
+ qpkt = 1;
+ } else qpkt = 0;
+ } while (qpkt);
+
+#ifdef COMMENT /* Disabled by Stephen Riehm 19.12.97 */
+ /* BUG!
+ * if buf[] is full, then this null lands in nirvana!
+ * I was unable to find any code which needs a trailing null in buf[]
+ */
+ if (x > 0) buf[x] = '\0';
+#endif /* COMMENT */
+ if (x < 1) x = -1;
+ debug(F101,"x25xin x","",x);
+
+ return(x);
+}
+
+#ifdef COMMENT /* NO LONGER NEEDED! */
+/* X.25 read a line */
+
+int
+#ifdef PARSENSE
+#ifdef CK_ANSIC
+x25inl(CHAR *dest, int max,int timo, CHAR eol, CHAR start)
+#else
+x25inl(dest,max,timo,eol,start) int max,timo; CHAR *dest, eol, start;
+#endif /* CK_ANSIC */
+#else /* not PARSENSE */
+#ifdef CK_ANSIC
+x25inl(CHAR *dest, int max,int timo, CHAR eol)
+#else
+x25inl(dest,max,timo,eol) int max,timo; CHAR *dest, eol;
+#endif /* __SDTC__ */
+#endif /*PARSENSE */
+ /* x25inl */ {
+ CHAR *pdest;
+ int pktype, goteol, rest, n;
+ int i, flag = 0;
+ extern int ttprty, ttpflg;
+ int ttpmsk;
+
+ ttpmsk = (ttprty) ? 0177 : 0377; /* Set parity stripping mask */
+
+ debug(F101,"x25inl max","",max);
+ debug(F101,"x25inl eol","",eol);
+ pdest = dest;
+ rest = max;
+ goteol = 0;
+ do {
+ n = read(ttyfd,pdest,rest);
+ n--;
+ pktype = *pdest & 0x7f;
+ switch (pktype) {
+ case 1 << Q_BIT:
+ if (qbitpkt(pdest+1,--n) < 0) return(-2);
+ break;
+ default:
+ if (flag == 0) { /* if not in packet, search start */
+ for (i = 1; (i < n) &&
+ !(flag = ((dest[i] & 0x7f) == start));
+ i++);
+ if (flag == 0) { /* not found, discard junk */
+ debug(F101,"x25inl skipping","",n);
+ continue;
+ } else { /* found, discard junk before start */
+ int k;
+ n = n - i + 1;
+ for (k = 1; k <= n; k++, i++) dest[k] = dest[i];
+ }
+ }
+ for (i = 0; (i < n) && /* search for eol */
+ !(goteol=(((*pdest = *(pdest+1)&ttpmsk)&0x7f)== eol));
+ i++,pdest++);
+ *pdest = '\0';
+ rest -= n;
+ }
+ } while ((rest > 0) && (!goteol));
+
+ if (goteol) {
+ n = max - rest;
+ debug (F111,"x25inl X.25 got",(char *) dest,n);
+ if (timo) ttimoff();
+ if (ttpflg++ == 0 && ttprty == 0) {
+ if ((ttprty = parchk(dest,start,n)) > 0) {
+ int j;
+ debug(F101,"x25inl senses parity","",ttprty);
+ debug(F110,"x25inl packet before",(char *)dest,0);
+ ttpmsk = 0x7f;
+ for (j = 0; j < n; j++)
+ dest[j] &= 0x7f; /* Strip parity from packet */
+ debug(F110,"x25inl packet after ",dest,0);
+ } else {
+ debug(F101,"parchk","",ttprty);
+ if (ttprty < 0) { ttprty = 0; n = -1; }
+ }
+ }
+ ttimoff();
+ return(n);
+ }
+ ttimoff();
+ return(-1);
+}
+#endif /* COMMENT */
+#endif /* SUNX25 */
+
+#ifdef IBMX25
+/*
+ * IBM X25 support - using the NPI streams interface
+ * written by Stephen Riehm, pc-plus, Munich Germany
+ */
+
+/* riehm: missing functions / TODO list */
+
+/*
+ x25intr() - Send an interrupt packet
+*/
+
+/* return an error message depending on packet type */
+char *
+x25err(n) int n; {
+ static char buf[30];
+ switch (n) {
+ case NBADADDR: return "invalid address";
+ case NBADOPT: return "invalid options";
+ case NACCESS: return "no permission";
+ case NNOADDR: return "unable to allocate address";
+ case NOUTSTATE: return "invalid state";
+ case NBADSEQ: return "invalid sequence number";
+ case NSYSERR: return "system error";
+ case NBADDATA: return "invalid data size";
+ case NBADFLAG: return "invalid flag";
+ case NNOTSUPPORT: return "unsupported primitive";
+ case NBOUND: return "address in use";
+ case NBADQOSPARAM: return "bad QOS parameters";
+ case NBADQOSTYPE: return "bad QOS type";
+ case NBADTOKEN: return "bad token value";
+ case NNOPROTOID: return "protocol id could not be allocated";
+ case NODDCUD: return "odd length call user data";
+ default:
+ ckmakmsg(buf,sizeof(buf),"Unknown NPI error ",ckitoa(n),NULL,NULL);
+ return buf;
+ }
+}
+
+/* turn a meaningless primitive number into a meaningful primitive name */
+char *
+x25prim(n) int n; {
+ static char buf[30];
+ switch(n) {
+ case N_BIND_ACK: return "N_BIND_ACK";
+ case N_BIND_REQ: return "N_BIND_REQ";
+ case N_CONN_CON: return "N_CONN_CON";
+ case N_CONN_IND: return "N_CONN_IND";
+ case N_CONN_REQ: return "N_CONN_REQ";
+ case N_CONN_RES: return "N_CONN_RES";
+ case N_DATACK_IND: return "N_DATAACK_IND";
+ case N_DATACK_REQ: return "N_DATAACK_REQ";
+ case N_DATA_IND: return "N_DATA_IND";
+ case N_DATA_REQ: return "N_DATA_REQ";
+ case N_DISCON_IND: return "N_DISCON_IND";
+ case N_DISCON_REQ: return "N_DISCON_REQ";
+ case N_ERROR_ACK: return "N_ERROR_ACK";
+ case N_EXDATA_IND: return "N_EXDATA_IND";
+ case N_EXDATA_REQ: return "N_EXDATA_REQ";
+ case N_INFO_ACK: return "N_INFO_ACK";
+ case N_INFO_REQ: return "N_INFO_REQ";
+ case N_OK_ACK: return "N_OK_ACK";
+ case N_OPTMGMT_REQ: return "N_OPTMGMT_REQ";
+ case N_RESET_CON: return "N_RESET_CON";
+ case N_RESET_IND: return "N_RESET_IND";
+ case N_RESET_REQ: return "N_RESET_REQ";
+ case N_RESET_RES: return "N_RESET_RES";
+ case N_UDERROR_IND: return "N_UDERROR_IND";
+ case N_UNBIND_REQ: return "N_UNBIND_REQ";
+ case N_UNITDATA_REQ: return "N_UNITDATA_REQ";
+ case N_UNITDATA_IND: return "N_UNITDATA_IND";
+ default:
+ ckmakmsg(buf,sizeof(buf),"UNKNOWN (",ckitoa(n),")",NULL);
+ return buf;
+ }
+}
+
+/*****************************************************************************
+ * Function: x25getmsg()
+ * Description: get a STREAMS message, and check it for errors
+ *
+ * Parameters:
+ * fd - file descriptor to x25 device (opened)
+ * control - control buffer (pre-allocated)
+ * ctl_size - size of control buffer
+ * data - data buffer (pre-allocated)
+ * data_size - size of data buffer
+ * flags - flags for getmsg()
+ * expected - expected Primitive type
+ *
+ * Return Value:
+ * >= 0 OK (size of data returned)
+ * -1 error
+ *
+ */
+int
+x25getmsg( fd, control, ctl_size, data, data_size, get_flags, expected )
+ int fd; /* X25 device (opened) */
+ N_npi_ctl_t *control; /* control buffer (pre-allocated) */
+ int ctl_size; /* size of control buffer */
+ N_npi_data_t *data; /* data buffer (pre-allocated) */
+ int data_size; /* size of data buffer */
+ int *get_flags; /* getmsg() flags */
+ int expected; /* expected primitive type */
+/* x25getmsg */ {
+ int rc = 0; /* return code */
+ struct strbuf *get_ctl=NULL; /* getmsg control */
+ struct strbuf *get_data=NULL; /* getmsg data */
+ int more = 0; /* flag for more data etc */
+ int file_status = -1; /* file async status */
+ N_npi_ctl_t * result; /* pointer to simplify switch() */
+ int packet_type = -1; /* unknown packet thus far */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25getmsg\n" );
+#endif /* TRACE */
+
+ debug( F110, "x25getmsg waiting for packet ", x25prim( expected ), 0);
+ /* prepare the control structures for getmsg */
+ if (control) {
+ if ((get_ctl = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL)
+ {
+ perror("kermit x25getmsg(): get_ctl malloc failed\n");
+ debug( F100, "x25getmsg malloc failed for get_ctl\n", "", 0);
+ return(-1);
+ }
+ /* allow getmsg to return an unexpected packet type (which may be
+ * larger than the expected one)
+ */
+ get_ctl->maxlen = NPI_MAX_CTL;
+ get_ctl->len = 0;
+ get_ctl->buf = (char *)control;
+ } else {
+ printf(
+ "kermit x25getmsg(): internal error. control buffer MUST be pre-allocated!\n"
+ );
+ debug(F100,"x25getmsg internal error. no buffer pre-allocated","",0);
+ return( -1 );
+ }
+ if (data) {
+ if ((get_data = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL)
+ {
+ perror("kermit x25getmsg(): get_data malloc failed\n");
+ debug( F100, "x25getmsg malloc failed for get_data\n", "", 0);
+ return(-1);
+ }
+ get_data->maxlen = (NPI_MAX_DATA < data_size ) ?
+ NPI_MAX_DATA :
+ data_size;
+ get_data->len = 0;
+ get_data->buf = (char *)data;
+ }
+
+ /* get an X.25 packet -
+ * it may be any kind of packet, so check for special cases
+ * it may be split into multiple parts - so loop if necessary
+ */
+ do {
+#ifdef DEBUG
+ printf( "kermit: x25getmsg(): getting a message\n" );
+#endif /* DEBUG */
+ errno = 0;
+ if ((more = getmsg(fd, get_ctl, get_data, get_flags)) < 0) {
+#ifdef DEBUG
+ printf( "kermit: x25getmsg(): getmsg returned an error\n" );
+ perror( "getmsg error was" );
+#endif /* DEBUG */
+ debug(F101, "x25getmsg getmsg returned an error\n", "", errno);
+ if ((errno == EAGAIN) && (get_data && (get_data->len > 0)) ) {
+ /* was in non-blocking mode, nothing to get, but we're
+ * already waiting for the rest of the packet -
+ * switch to blocking mode for the next read.
+ * file_status used to reset file status before returning
+ */
+ if ((file_status = fcntl(fd, F_GETFL, 0)) < 0
+ || fcntl(fd, F_SETFL, file_status & ~O_NDELAY) < 0)
+ {
+ perror("x25getmsg(): couldn't change x25 blocking mode");
+ debug(F101,
+ "x25getmsg fcntl returned an error\n", "", errno);
+ /* netclos(); */
+ rc = -1;
+ break;
+ } else {
+ /* loop again into a blocking getmsg() */
+ continue;
+ }
+ } else {
+ /* no data to get in non-blocking mode - return empty handed */
+ perror( "x25getmsg(): getmsg failed" );
+ debug(F101,"x25getmsg getmsg returned an error\n", "", errno);
+ rc = -1;
+ break;
+ }
+ } else if (more & MORECTL) {
+ /* panic - the control information was larger than the
+ * maximum control buffer size!
+ */
+ /* riehm: close connection? */
+#ifdef DEBUG
+ printf("x25getmsg(): received partial control packet - panic\n");
+#endif /* DEBUG */
+ debug( F101, "x25getmsg getmsg bad control block\n", "", errno);
+ rc = -1;
+ break;
+ }
+
+ if (result = (N_npi_ctl_t *)control) {
+ packet_type = result->bind_ack.PRIM_type;
+ if (packet_type != N_OK_ACK) {
+ x25lastmsg = packet_type;
+ }
+ }
+#ifdef DEBUG
+ /* printf( "kermit: x25getmsg(): getting " ); */
+ if (get_ctl->len > 0) {
+ x25dump_prim(result);
+ }
+ debug(F110,
+ "x25getmsg got packet ",
+ x25prim( result->bind_ack.PRIM_type ),
+ 0
+ );
+#endif /* DEBUG */
+
+ if (get_ctl->len >= (int)sizeof(result->bind_ack.PRIM_type)) {
+ /* not as pretty as a switch(), but switch can't handle
+ * runtime variable values :-(
+ */
+ if (packet_type == expected ) {
+ /* got what we wanted, special case for DATA_IND
+ * packets though */
+ /* riehm: check Q-bit ? */
+#ifdef DEBUG
+ printf("x25getmsg(): got expected packet\nrc is %d\n", rc);
+#endif /* DEBUG */
+ if (packet_type == N_DATA_IND ) {
+ /* data received. May be incomplete, even though
+ * getmsg returned OK
+ */
+ if (result->data_ind.DATA_xfer_flags & N_MORE_DATA_FLAG)
+ more |= MOREDATA;
+ if (result->data_ind.DATA_xfer_flags & N_RC_FLAG)
+ printf( "x25getmsg(): data packet wants ack\n" );
+ }
+ } else if( packet_type == N_DISCON_IND) {
+ printf( "X25 diconnected\n" );
+ /* riehm: need to acknowledge a disconnection? */
+ x25clear();
+ /* x25unbind( ttyfd ); */
+ rc = -1;
+ } else if( packet_type == N_ERROR_ACK) {
+ errno = result->error_ack.UNIX_error;
+ perror( "X25 error received" );
+ rc = -1;
+ } else {
+ printf("x25getmsg(): failed %s\n", x25err(packet_type));
+ rc = -1;
+ }
+ }
+#ifdef COMMENT
+ else {
+ /* Panic - no control data */
+ printf( "kermit: x25getmsg(): no control data with packet\n" );
+ rc = -1;
+ }
+#endif /* COMMENT */
+
+ if (get_data && (get_data->len >= 0)) {
+ get_data->buf += get_data->len;
+ get_data->maxlen -= get_data->len;
+ }
+ } while ((rc == 0)
+ && (get_data && (get_data->maxlen > 0))
+ && (more & MOREDATA)
+ );
+
+ /* return the file status to its original value, unless its still
+ * set to -1, or one of the fcntl's failed */
+ if ((file_status >= 0) && fcntl(fd, F_SETFL, file_status) < 0)
+ rc = -1;
+
+ /*
+ * Verify that we received an expected primitive
+ * there is apparantly an error case where the primitive is set
+ * correctly, but there is not enough data in the control structure
+ */
+ if ((packet_type != expected) && (get_ctl->len >= ctl_size) ) {
+ fprintf(stderr,
+ "x25getmsg(): %s NOT received. Primitive received was %s\n",
+ x25prim( expected ), x25prim( packet_type ));
+ debug(F110, "x25getmsg got an unexpected packet ",
+ x25prim(packet_type),
+ 0
+ );
+ rc = -1;
+ }
+
+ if (rc == 0) {
+ if (get_data && ( get_data->len >= 0)) {
+ rc = get_data->len;
+ }
+ }
+
+ if (get_ctl) { free(get_ctl); get_ctl = NULL; }
+ if (get_data) { free(get_data); get_data = NULL; }
+
+#ifdef COMMENT
+#ifdef DEBUG
+ printf( "kermit x25getmsg(): returning %d\n", rc );
+#endif /* DEBUG */
+#endif /* COMMENT */
+ debug(F110, "x25getmsg returning packet ", x25prim( packet_type ), 0);
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25getmsg\n" );
+#endif /* TRACE */
+ return(rc);
+}
+
+/*****************************************************************************
+ * Function: x25putmsg()
+ *
+ * Description:
+ * send a message to a X25 STREAM
+ *
+ * Parameters:
+ * fd - file descriptor to x25 device (opened)
+ * control - control buffer (pre-allocated)
+ * data - data buffer (pre-allocated)
+ * data_len - length of data to be transmitted
+ * put_flags - flags for putmsg()
+ *
+ * Return Value:
+ * >= 0 number of bytes transmitted
+ * -1 error
+ */
+int
+x25putmsg(fd, control, data, data_len, put_flags)
+ int fd; /* X25 device (opened) */
+ N_npi_ctl_t *control; /* control buffer (pre-allocated) */
+ N_npi_data_t *data; /* data buffer (pre-allocated) */
+ int data_len; /* length of data (not the size of
+ the buffer) */
+ int *put_flags; /* putmsg() flags */
+/* x25putmsg */ {
+ int rc = 0; /* return code */
+ ulong type; /* primitive type */
+ struct strbuf *put_ctl = NULL; /* putmsg control */
+ struct strbuf *put_data = NULL; /* putmsg data */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25putmsg\n" );
+#endif /* TRACE */
+
+#ifdef DEBUG
+ printf( "kermit: x25putmsg(): putting " );
+ x25dump_prim( control );
+ printf( "\tdata:\t\t" );
+ x25dump_data( data, 0, data_len );
+ debug(F110,"x25putmsg: putting packet ",x25prim(control->PRIM_type),0);
+#endif /* DEBUG */
+
+ if (control) {
+ put_ctl = (struct strbuf *)malloc( sizeof( struct strbuf ) );
+ if (put_ctl == NULL) {
+ perror("kermit x25putmsg(): put_ctl malloc failed\n");
+ return(-1);
+ }
+ put_ctl->maxlen = 0; /* unused by putmsg */
+ put_ctl->len = NPI_MAX_CTL;
+ put_ctl->buf = (char *)control;
+ }
+ if (data && ( data_len > 0)) {
+ put_data = (struct strbuf *)malloc( sizeof( struct strbuf ) );
+ if( put_data == NULL) {
+ perror("kermit x25putmsg(): put_data malloc failed\n");
+ return(-1);
+ }
+ put_data->maxlen = 0; /* unused by putmsg */
+ put_data->len = data_len;
+ put_data->buf = (char *)data;
+ }
+
+ errno = 0;
+ rc = putmsg (fd, put_ctl, put_data, 0);
+ if (rc < 0) {
+ printf("x25putmsg(): couldn't put %s\n",x25prim(control->PRIM_type));
+ perror("kermit: x25putmsg(): putmsg failed");
+ return(-1);
+ }
+
+ /* riehm: this should perhaps be discounted! */
+ x25lastmsg = control->PRIM_type;
+
+#ifdef COMMENT
+#ifdef DEBUG
+ printf( "kermit debug: x25putmsg() returning %d\n", data_len );
+#endif /* DEBUG */
+#endif /* COMMENT */
+ debug( F101, "x25putmsg block size put ", "", data_len);
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25putmsg\n" );
+#endif /* TRACE */
+
+ return( data_len );
+}
+
+/*****************************************************************************
+* Function: x25bind
+* Description: The bind submitted to NPI provides the information required
+* by the packet layer for it to listen for suitable incoming
+* calls.
+*
+* WARNING:
+*
+* This routine needs to be called in a completely different manner for
+* the client and server side. When starting a client, the
+* num_waiting_calls and CUD information should all be set to 0! The
+* client's CUD must be inserted in the CONN_REQ data block.
+* When starting a server, the CUD must be set to a CUD pattern, and
+* the number of waiting calls should be set to a number other than 0.
+* (num waiting calls is the number of incomming calls which are to be
+* put on hold while the server is servicing another client.)
+*
+* Who invented this crap?
+*
+* Parameters:
+* fd - X25 device (opened)
+* addr - local address
+* cud - User Data (null terminated)
+* cud_len - User Data length
+* num_waiting_calls - number of outstanding calls allowed on this stream
+* line - logical port number (1)
+* flags - 0, DEFAULT_LISTENER or TOKEN_REQUEST
+*
+* Return Value:
+* if binding is successful, 0 is returned for a client, and a token is
+* returned for a server
+*
+* Return code: 0 if successful
+* -1 if unsuccessful
+*****************************************************************************/
+
+ulong
+x25bind(fd, addr, cud, cud_len, num_waiting_calls, line, bind_flags)
+ int fd; /* X25 device (opened) */
+ char * addr; /* local address */
+ char * cud; /* Call User Data (null terminated) */
+ int cud_len; /* User Data length */
+ int num_waiting_calls; /* Outstanding calls allowed */
+ int line; /* logical port number */
+ ulong bind_flags; /* 0, DEFAULT_LISTENER or TOKEN_REQUEST */
+/* x25bind */ {
+ ulong rc; /* return code */
+ int get_flags; /* priority flag passed to getmsg */
+ int put_flags = 0; /* output flags for putmsg, always 0 */
+ ulong type; /* primitive type */
+ N_bind_req_t *bind_req; /* pointer to N_BIND_REQ primitive */
+ N_bind_ack_t *bind_ack; /* pointer to N_BIND_ACK primitive */
+ char *addtl_info; /* pointer to info in addition to
+ * the N_BIND_REQ primitive that is
+ * passed in the control structure
+ * to putmsg */
+ int addr_len = 0; /* length of address string */
+ ulong bind_req_t_size; /* for debugging only */
+
+#ifdef TRACE
+ printf("TRACE: entering x25bind\n" );
+#endif /* TRACE */
+
+#ifdef DEBUG
+ printf("TRACE: x25bind( %d, %s, %s, %d, %d )\n",
+ fd, addr, cud, line, bind_flags
+ );
+#endif /* DEBUG */
+
+ /*
+ * Allocate and zero out space to hold the control portion of the
+ * message passed to putmsg. This will contain the N_BIND_REQ
+ * primitive and any additional info required for that.
+ *
+ * Note: allocated space is the size of the union typedef
+ * N_npi_ctl_t to allow the use fo the generic x25putmsg routine.
+ */
+ bind_req = (N_bind_req_t *) malloc(sizeof( N_npi_ctl_t));
+ if (bind_req == NULL) {
+ perror("kermit: x25bind(): bind_req malloc failed");
+ debug(F100, "x25bind bind_req malloc failed", "", 0);
+ return(-1);
+ }
+ bzero((char *)bind_req, sizeof(N_npi_ctl_t));
+
+ /* Build the Bind Request Primitive */
+ bind_req->PRIM_type = (ulong) N_BIND_REQ;
+
+ /* Note that the address length is n+2 and NOT n. Two bytes MUST preceed
+ * the actual address in an N_BIND_REQ. The first byte contains the
+ * line number being used with this address, and the second byte is the
+ * X.121 address prefix, which must be zero.
+ */
+ addr_len = strlen(addr);
+ bind_req->ADDR_length = (ulong) (addr_len + 2);
+ bind_req->ADDR_offset = (ulong)(sizeof(N_bind_req_t));
+ bind_req->CONIND_number = (ulong)num_waiting_calls; /* server only */
+ bind_req->BIND_flags = (ulong) bind_flags; /* 0 in client */
+ bind_req->PROTOID_length = (ulong) cud_len; /* 0 in client */
+ if (cud_len == 0) {
+ bind_req->PROTOID_offset = (ulong) 0;
+ } else {
+ /* need to remember the trailing NULL in the address - not
+ * counted in the address length
+ */
+ bind_req->PROTOID_offset
+ = (ulong) (sizeof(N_bind_req_t) + bind_req->ADDR_length);
+ }
+
+ /*
+ * Now fill in the additional information required with this primitive
+ * (address and protocol information (Call User Data))
+ */
+ addtl_info = (char *) ((void *)bind_req + bind_req->ADDR_offset);
+ /*
+ * The bitwise "&" ensures that the line number is only one byte long
+ */
+ *addtl_info++ = (char) line & 0xff;
+ *addtl_info++ = (char) 0; /* X.121 format */
+ bcopy( addr, addtl_info, addr_len ); /* include trailing null */
+ addtl_info += addr_len;
+ if (cud_len > 0)
+ bcopy( cud, addtl_info, cud_len );
+ /*
+ * Call putmsg() to put the bind request message on the stream
+ */
+ if (x25putmsg(fd,
+ (N_npi_ctl_t*)bind_req,
+ (N_npi_data_t *)NULL,
+ 0,
+ &put_flags
+ ) < 0) {
+ printf( "kermit: x25bind(): x25putmsg failed\n" );
+ return(-1);
+ }
+
+ /*
+ * Allocate and zero out space for the N_BIND_ACK primitive
+ */
+ bind_ack = (N_bind_ack_t *) malloc(sizeof(N_npi_ctl_t));
+ if (bind_ack == NULL){
+ perror("kermit: x25bind(): bind_ack malloc failed");
+ return(-1);
+ }
+ bzero(bind_ack, sizeof(N_npi_ctl_t));
+ /*
+ * Initialize the control structure and flag variable sent to getmsg
+ */
+ get_flags=0;
+
+ /* get the ACK for the bind */
+#ifdef DEBUG
+ printf( "kermit: x25bind() trying to get a BIND_ACK\n" );
+#endif /* DEBUG */
+ rc = (ulong)x25getmsg( fd, (N_npi_ctl_t*)bind_ack,
+ (int)sizeof( N_bind_ack_t ), (N_npi_data_t*)NULL, 0, &get_flags,
+ N_BIND_ACK );
+
+ /* turn quantitive return code into a qualitative one */
+ if (rc > 0) rc = 0;
+
+ /* if all went well, get the token from the acknowledgement packet */
+ if ((bind_flags & TOKEN_REQUEST ) && ( rc >= 0)) {
+ rc = bind_ack->TOKEN_value;
+ }
+
+ /* free up the memory we allocated earlier */
+ free(bind_req);
+ free(bind_ack);
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25bind\n" );
+#endif /* TRACE */
+
+ return( rc );
+}
+
+/*****************************************************************************
+* Function: x25call
+* Description: This routine builds and sends an N_CONN_REQ primitive, then
+* checks for an N_CONN_CON primitive in return.
+*
+* Parameters:
+* fd - file descriptor of stream
+* caddr - called address (remote address)
+*
+* Functions Referenced:
+* malloc()
+* bzero()
+* getmsg()
+* putmsg()
+*
+* Return code:
+* 0 - if successful
+* -1 if not successful
+*****************************************************************************/
+int
+x25call(fd, remote_nua, cud)
+ int fd; /* X25 device (opened) */
+ char * remote_nua; /* remote address to call */
+ char * cud; /* call user data */
+/* x25call */ {
+ int rc; /* return code */
+ int flags; /* Connection flags */
+ int get_flags; /* priority flags for getmsg */
+ ulong type; /* primitive type */
+ N_conn_req_t *connreq_ctl; /* pointer to N_CONN_REQ primitive */
+ N_npi_data_t *connreq_data; /* pointer to N_CONN_REQ data (CUD) */
+ int connreq_data_len; /* length of filled data buffer */
+ N_conn_con_t *conncon_ctl; /* pointer to N_CONN_CON primitive */
+ N_npi_data_t *conncon_data; /* pointer to any data associated with
+ * the N_CONN_CON primitive */
+ char *addtl_info; /* pointer to additional info needed
+ * for N_CONN_REQ primitive */
+ int addr_len; /* length of address */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25call\n" );
+#endif /* TRACE */
+
+#ifdef DEBUG
+ printf( "x25call( %d, %s )\n", fd, remote_nua );
+ printf( "connecting to %s on fd %d\n", remote_nua, fd );
+#endif /* DEBUG */
+
+ /*
+ * Allocate and zero out space for the N_CONN_REQ primitive
+ * use the size of the generic NPI primitive control buffer
+ */
+ connreq_ctl = (N_conn_req_t *) malloc(sizeof(N_npi_ctl_t));
+ if (connreq_ctl == NULL){
+ perror("kermit: x25call(): connreq_ctl malloc failed");
+ return(-1);
+ }
+ bzero(connreq_ctl,sizeof(N_npi_ctl_t));
+ /*
+ * Build the Connection Request Primitive
+ */
+ flags = 0;
+ connreq_ctl->PRIM_type = (ulong) N_CONN_REQ;
+
+ /* Note that the address length is nchai+1 and not n+2. The line number
+ * is only passed with the address for the bind. The first byte of
+ * the address for the N_CONN primitives contains the X.121
+ * address prefix, which must be zero. The remaining bytes are the
+ * address itself.
+ */
+ addr_len = strlen( remote_nua );
+ connreq_ctl->DEST_length = (ulong) (addr_len + 1);
+ connreq_ctl->DEST_offset = (ulong) sizeof(N_conn_req_t);
+ /* connreq_ctl->CONN_flags = (ulong)EX_DATA_OPT | REC_CONF_OPT; */
+ connreq_ctl->CONN_flags = (ulong) 0;
+ connreq_ctl->QOS_length = (ulong) 0; /* unsupported in AIX 4.1 */
+ connreq_ctl->QOS_offset = (ulong) 0; /* unsupported in AIX 4.1 */
+
+ addtl_info = (char *) ((void*)connreq_ctl + connreq_ctl->DEST_offset);
+ *addtl_info++ = (char) 0; /* X.121 format */
+ bcopy( remote_nua, addtl_info, addr_len );
+
+ /*
+ * setup the data buffer for the connection request
+ */
+ connreq_data = (N_npi_data_t *) malloc(sizeof(N_npi_data_t));
+ if (connreq_data == NULL){
+ perror("kermit: x25call(): connreq_data malloc failed");
+ return(-1);
+ }
+ bzero(connreq_data,sizeof(N_npi_data_t));
+
+ /* facility selection needs to be put in the front of connreq_data */
+ connreq_data_len = 0;
+ connreq_data_len += x25facilities( (char *)connreq_data );
+ if (cud && *cud) {
+ bcopy(cud,
+ (char *)((char *)connreq_data + connreq_data_len),
+ strlen(cud)
+ );
+ connreq_data_len += strlen( cud );
+ }
+
+ /*
+ * Call putmsg() to put the connection request message on the stream
+ */
+ rc = x25putmsg( fd, (N_npi_ctl_t*)connreq_ctl, connreq_data,
+ connreq_data_len, &flags );
+ if (rc < 0) {
+ return(-1);
+ }
+
+ /*
+ * Allocate and zero out space for the N_CONN_CON primitive
+ */
+ if ((conncon_ctl = (N_conn_con_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) {
+ perror("kermit: x25call(): conncon_ctl malloc failed");
+ return(-1);
+ }
+ bzero(conncon_ctl, sizeof(N_npi_ctl_t));
+
+ /*
+ * Allocate and zero out space for any data associated with N_CONN_CON
+ */
+ if ( (conncon_data = (N_npi_data_t *) malloc(NPI_MAX_DATA)) == NULL) {
+ perror("kermit: x25call(): conncon_data malloc failed");
+ return(-1);
+ }
+ bzero(conncon_data, NPI_MAX_DATA);
+
+ /* Initialize and build the structures for getmsg */
+ get_flags=0;
+
+ rc = x25getmsg( fd, (N_npi_ctl_t*)conncon_ctl, (int)sizeof( N_conn_con_t ),
+ conncon_data, NPI_MAX_DATA, &get_flags, N_CONN_CON );
+
+ /* turn quantitive return code into a qualitative one */
+ if (rc > 0) rc = 0;
+
+ /* Free the space that we no longer need */
+ if (connreq_ctl) { free(connreq_ctl); connreq_ctl = NULL; }
+ if (conncon_ctl) { free(conncon_ctl); conncon_ctl = NULL; }
+ if (conncon_data) { free(conncon_data); conncon_data = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25call\n" );
+#endif /* TRACE */
+
+ return(rc);
+}
+
+/*****************************************************************************
+ * Function: x25getcall
+ *
+ * Description: This routine checks for an incomming call, verified
+ * that it is a CONNIND (connection indication) message, and then
+ * accepts the call and returns the file descriptor of the new stream
+ *
+ * Parameters:
+ * fd - file descriptor of listening stream
+ *
+ * Return Codes:
+ * callfd - file descriptor of connected incomming call.
+ * - set to -1 if an error occured
+ *
+ *****************************************************************************/
+int
+x25getcall(fd) int fd; {
+ int x25callfd; /* fd of incomming call */
+ N_conn_ind_t *connind_ctl; /* connind controll buffer */
+ N_npi_data_t *connind_data; /* connind data buffer */
+ int get_flags; /* flags for getmsg */
+ ulong flags; /* connection flags */
+ int rc; /* return code */
+
+ extern x25addr_t remote_nua; /* remote X.25 addr global var */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25getcall\n" );
+#endif /* TRACE */
+
+ /* allocate space for connection indication buffers */
+ if ((connind_ctl = (N_conn_ind_t *)malloc(sizeof(N_npi_ctl_t))) == NULL) {
+ perror("kermit: x25getcall(): connind_ctl malloc failed");
+ return (-1);
+ }
+ bzero(connind_ctl, sizeof(N_npi_ctl_t));
+
+ if ((connind_data = (N_npi_data_t *)malloc(NPI_MAX_DATA)) == NULL) {
+ perror("kermit: x25getcall(): connind_data malloc failed");
+ return (-1);
+ }
+ bzero(connind_data, NPI_MAX_DATA);
+
+ /* initialise control structures */
+ get_flags = 0;
+
+ /* call getmsg to check for a connection indication */
+ if (x25getmsg(fd,
+ (N_npi_ctl_t*)connind_ctl,
+ (int)sizeof(N_conn_ind_t),
+ connind_data,
+ NPI_MAX_DATA,
+ &get_flags,
+ N_CONN_IND
+ ) < 0) {
+#ifdef DEBUG
+ printf( "x25getcall(): errno is: %d\n", errno );
+#endif /* DEBUG */
+ perror ("x25getcall(): getmsg failed");
+ return(-1);
+ }
+
+ /* a connection indication was received
+ * - pull it to bits and answer the call
+ */
+ x25seqno = connind_ctl->SEQ_number;
+ flags = connind_ctl->CONN_flags;
+#ifdef DEBUG
+ printf( "setting remote_nua to a new value due to incomming call\n" );
+#endif /* DEBUG */
+ /*
+ * no guarantee that the address is null terminated, ensure that
+ * after copying that it is (assumption: remote_nua is longer than
+ * the address + 1)
+ */
+ bzero(remote_nua, sizeof(remote_nua));
+ /* note: connind_ctl contains a x121 address, which has a null as
+ * the FIRST character - strip it off!
+ */
+ ckstrncpy(remote_nua,
+ (char*)((char*)connind_ctl + connind_ctl->SRC_offset + 1),
+ connind_ctl->SRC_length - 1
+ );
+#ifdef DEBUG
+ printf( "remote_nua set to new value of %s\n", remote_nua );
+#endif /* DEBUG */
+
+ /* errors handled by callee */
+ x25callfd = x25accept(x25seqno, flags);
+
+ /* free the malloc'd buffers */
+ if (connind_ctl) { free(connind_ctl); connind_ctl = NULL; }
+ if (connind_data) { free(connind_data); connind_data = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25getcall\n" );
+#endif /* TRACE */
+
+ /* return the file descriptor (or error if < 0) */
+ return( x25callfd );
+}
+
+/*****************************************************************************
+ * Function: x25accept
+ *
+ * Description: accept an incomming call
+ * This essentially means opening a new STREAM and sending
+ * an acknowledge back to the caller.
+ *
+ * Parameters:
+ * seqno - sequence number for acknowledgement
+ * flags - flags passed to us by the caller
+ *
+ * Return Codes:
+ * fd - file descriptor of new STREAM
+ * set to -1 if an error occured
+ *
+ *****************************************************************************/
+int
+x25accept(seqno,flags)
+ ulong seqno; /* connection sequence number */
+ ulong flags; /* connection flags */
+/* x25accept */ {
+ int x25callfd; /* fd for incomming call */
+ int get_flags; /* priority flags for getmsg */
+ int put_flags = 0; /* flags for putmsg, always 0 */
+ int addr_len; /* length of local address */
+ ulong token; /* connection token */
+ N_conn_res_t *conn_res; /* N_CONN_RES primitive */
+ N_ok_ack_t *ok_ack; /* N_OK_ACK primitive */
+ char *addtl_info; /* temp pointer */
+ int rc; /* temporary return code */
+
+/* global variables from ckcmai.c */
+ extern int revcall, closgr, cudata;
+ extern char udata[];
+ extern x25addr_t local_nua; /* local X.25 address */
+ extern char x25name[]; /* x25 device name (sx25a0) */
+ extern char x25dev[]; /* x25 device file /dev/x25pkt */
+ extern int x25port; /* logical port to use */
+ ulong bind_flags = 0; /* flags for binding the X25 stream */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25accept\n" );
+#endif /* TRACE */
+
+ /* open a new packet level stream */
+ if ((x25callfd = open(x25dev, O_RDWR)) < 0) {
+ perror ("kermit: x25accept(): X.25 device open error");
+ debug(F101,"x25accept() device open error","",errno);
+ return(-1);
+ }
+
+ /* push the NPI onto the STREAM */
+ if (ioctl(x25callfd,I_PUSH,"npi") < 0) {
+ perror( "kermit: x25accept(): couldn't push npi on the X25 stream" );
+ debug(F101,"x25accept can't push npi on the X25 stream","",errno);
+ return (-1);
+ }
+
+ /* bind kermit server to the local X25 address */
+ /* taken from /usr/samples/sx25/npi/npiserver.c (AIX 4) */
+ bind_flags |= TOKEN_REQUEST;
+ token = x25bind(x25callfd,local_nua,(char *)NULL,0,0,x25port,bind_flags);
+ if (token < 0) {
+ printf( "kermit: x25accept(): couldn't bind to local X25 address\n" );
+ netclos();
+ return(-1);
+ }
+
+ /* allocate connection response primitive */
+ if ((conn_res = (N_conn_res_t *)malloc( NPI_MAX_CTL )) == NULL) {
+ perror("kermit: x25accept(): conn_res malloc failed");
+ return (-1);
+ }
+ bzero((char *)conn_res, NPI_MAX_CTL);
+
+ /* setup connection response primitive */
+ addr_len = strlen( local_nua );
+ conn_res->PRIM_type = (ulong)N_CONN_RES;
+ conn_res->TOKEN_value = token;
+ /* note address length is n+1 to accomodate the X.121 address prefix */
+ conn_res->RES_length = (ulong)(addr_len + 1);
+ conn_res->RES_offset = (ulong)sizeof( N_conn_res_t );
+ conn_res->SEQ_number = seqno;
+ conn_res->CONN_flags = 0;
+ conn_res->QOS_length = 0; /* unsupported - must be 0 (!?) */
+ conn_res->QOS_offset = 0;
+
+ addtl_info = (char *)((char *)conn_res + conn_res->RES_offset);
+ *addtl_info++ = (char)0; /* X.121 address prefix */
+ bcopy( local_nua, addtl_info, addr_len );
+
+ /*
+ * send off the connect response
+ */
+ if (x25putmsg(x25callfd,
+ (N_npi_ctl_t*)conn_res,
+ (N_npi_data_t *)NULL,
+ 0,
+ &put_flags
+ ) < 0 ) {
+ perror("kermit: x25accept(): putmsg connect response failed");
+ return(-1);
+ }
+
+ /*
+ * Allocate and zero out space for the OK_ACK primitive
+ */
+ if ((ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) {
+ perror("kermit: x25call(): ok_ack malloc failed");
+ return(-1);
+ }
+ bzero(ok_ack, sizeof(N_npi_ctl_t));
+
+ /* Initialize and build the structures for getmsg */
+ get_flags=0;
+
+ rc = (int)x25getmsg(x25callfd,
+ (N_npi_ctl_t*)ok_ack,
+ (int)sizeof(N_ok_ack_t),
+ (N_npi_data_t*)NULL,
+ 0,
+ &get_flags,
+ N_OK_ACK
+ );
+ if (rc == 0) {
+ /* sequence number is only for disconnecting when not connected !? */
+ x25seqno = 0;
+ }
+
+ /* free up malloc'ed buffer space */
+ if (conn_res) { free(conn_res); conn_res = NULL; }
+ if (ok_ack) { free(ok_ack); ok_ack = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25accept\n" );
+#endif /* TRACE */
+
+ return( ( rc >= 0 ) ? x25callfd : -1 );
+}
+
+/*****************************************************************************
+ * Function: x25unbind
+ *
+ * Description: This subroutine builds and sends an unbind request and gets
+ * the acknowledgement for it.
+ *
+ * Parameters:
+ * fd - File descriptor of the stream
+ *
+ * Functions Referenced:
+ * getmsg()
+ * putmsg()
+ * malloc()
+ * bzero()
+ *
+ * Return code:
+ * 0 - if successful
+ * -1 - if not successful
+ *****************************************************************************/
+int
+x25unbind(fd) int fd; { /* X25 device (opened) */
+ int rc; /* return code */
+ int flags; /* bind flags */
+ int get_flags; /* priority flag for getmsg */
+ ulong type; /* primitive type */
+ N_unbind_req_t *unbind_req; /* pointer to N_UNBIND_REQ */
+ N_ok_ack_t *ok_ack; /* pointer to N_OK_ACK */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25unbind\n" );
+#endif /* TRACE */
+
+#ifdef DEBUG
+ /* printf( "x25unbind( %d )\n", fd ); */
+#endif /* DEBUG */
+ debug(F101,"x25unbind closing x25 connection #","",fd);
+
+ /* Allocate and zero out space to hold the N_UNBIND_REQ primitive */
+ unbind_req = (N_unbind_req_t *) malloc(sizeof(N_npi_ctl_t));
+ if (unbind_req == NULL) {
+ perror("kermit: x25unbind(): unbind_req malloc failed");
+ return(-1);
+ }
+ bzero(unbind_req, sizeof(N_npi_ctl_t));
+
+ /*
+ * Build the Unbind Request Primitive
+ */
+ flags = 0;
+ unbind_req->PRIM_type = (ulong) N_UNBIND_REQ;
+
+ /*
+ * Call putmsg() to put the bind request message on the stream
+ */
+ if (x25putmsg(fd,
+ (N_npi_ctl_t*)unbind_req,
+ (N_npi_data_t *)NULL,
+ 0,
+ &flags
+ ) < 0) {
+ perror ("kermit: x25unbind(): putmsg failed");
+ return(-1);
+ }
+
+ /* Allocate and Zero out space for the N_OK_ACK primitive */
+ ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t));
+ if (ok_ack == NULL) {
+ perror("kermit x25unbind(): ok_ack malloc failed\n");
+ return(-1);
+ }
+ bzero(ok_ack, sizeof(N_npi_ctl_t));
+
+ /* Initialize and build the control structure for getmsg */
+ get_flags=0;
+
+ /* Call getmsg() to check for an acknowledgement */
+ rc = x25getmsg(fd,
+ (N_npi_ctl_t*)ok_ack,
+ (int)sizeof(N_ok_ack_t),
+ (N_npi_data_t*)NULL,
+ 0,
+ &get_flags,
+ N_OK_ACK
+ );
+ if (rc < 0) {
+ perror ("kermit: x25unbind: getmsg failed");
+ return(-1);
+ }
+
+ /* Free up the space that we no longer need */
+ if (unbind_req) { free(unbind_req); unbind_req = NULL; }
+ if (ok_ack) { free(ok_ack); ok_ack = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25unbind\n" );
+#endif /* TRACE */
+
+ return(0);
+}
+
+/*****************************************************************************
+ * Function: x25xin
+ *
+ * Description:
+ * Read n characters from X.25 circuit into buf (AIX only)
+ *
+ * Parameters:
+ * data_buf_len maximum size of data buffer
+ * data_buf pointer to pre-allocated buffer space
+ *
+ * Return Value:
+ * the number of characters actually read
+ */
+int
+x25xin(data_buf_len,data_buf) int data_buf_len; CHAR *data_buf; {
+ struct strbuf getmsg_ctl; /* streams control structure */
+ struct strbuf getmsg_data; /* streams data structure */
+ int rc = 0; /* return code */
+ int getmsg_flags; /* packet priority flags */
+ char * ctl_buf; /* npi control buffer */
+ N_npi_ctl_t * result; /* pointer to simplify switch() */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25xin\n" );
+#endif /* TRACE */
+
+ /* ensure that no maximum's are overridden */
+ data_buf_len = (NPI_MAX_DATA < data_buf_len) ? NPI_MAX_DATA : data_buf_len;
+
+ /* allocate space for packet control info */
+ if ((ctl_buf = (char *)malloc(NPI_MAX_CTL)) == NULL) {
+ perror( "kermit: x25xin(): ctl_buf malloc" );
+ return(-1);
+ }
+#ifdef COMMENT
+ /* riehm: need zeroed buffer for getmsg? */
+ bzero( ctl_buf, NPI_MAX_CTL );
+ /* clear data buffer */
+ bzero( data_buf, data_buf_len );
+#endif /* COMMENT */
+
+ getmsg_flags = 0; /* get the first packet available */
+
+ rc = x25getmsg(ttyfd,
+ ctl_buf,
+ NPI_MAX_CTL,
+ data_buf,
+ data_buf_len,
+ &getmsg_flags,
+ N_DATA_IND
+ );
+#ifdef COMMENT
+#ifdef DEBUG
+ if (rc >= 0) {
+ printf( "kermit: x25xin(): got " );
+ x25dump_data( data_buf, 0, rc );
+ } else {
+ printf( "x25xin(): attempt to get data resulted in an error\n" );
+ }
+#endif /* DEBUG */
+#endif /* COMMENT */
+
+ /* free buffers */
+ if (ctl_buf) { free(ctl_buf); ctl_buf = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25xi\n" );
+#endif /* TRACE */
+
+ return(rc);
+}
+
+/*****************************************************************************
+ * Function: x25write
+ *
+ * Description:
+ * write a block of characters to the X25 STREAM (AIX)
+ *
+ * Parameters:
+ * fd file descriptor to write to
+ * databuf buffer containing data to write
+ * databufsize size of the buffer to write
+ *
+ * Return Value:
+ * size the number of bytes actually transmitted
+ */
+int
+x25write(fd, databuf, databufsize)
+ int fd; /* X25 STREAMS file descriptor (ttyfd) */
+ char *databuf; /* buffer to write */
+ int databufsize; /* buffer size */
+/* x25write */ {
+ N_data_req_t *data_req_ctl;
+ int rc; /* return code (size transmitted) */
+ int write_flags = 0; /* always 0 !? */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25write\n" );
+#endif /* TRACE */
+
+ if ((data_req_ctl = (N_data_req_t *)malloc(NPI_MAX_CTL) ) == NULL) {
+ perror( "kermit: x25write(): data_req_ctl malloc" );
+ return(-1);
+ }
+ data_req_ctl->PRIM_type = N_DATA_REQ;
+ data_req_ctl->DATA_xfer_flags = 0;
+
+ /* riehm: possible extension
+ * possibly need to think about splitting up the data buffer
+ * into multiple parts if databufsize > NPI_MAX_DATA
+ */
+
+#ifdef COMMENT
+#ifdef DEBUG
+ printf( "kermit: x25write(): writing data to x25 stream\n" );
+ printf( "\tdata:\t" );
+ x25dump_data(databuf, 0, databufsize);
+#endif /* DEBUG */
+#endif /* COMMENT */
+ rc = x25putmsg(fd,
+ (N_npi_ctl_t*)data_req_ctl,
+ (N_npi_data_t*)databuf,
+ databufsize,
+ &write_flags
+ );
+ if (data_req) { free(data_req_ctl); data_req = NULL; }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25write\n" );
+#endif /* TRACE */
+
+ return(rc);
+}
+
+/*****************************************************************************
+ * Function: x25local_nua
+ *
+ * Description:
+ * This routine is only interesting for IBM computers. In order
+ * to set up a connection (see x25bind()) you need to know the
+ * local NUA (x25 address). Unfortunately, you need all this code
+ * to find that out, I just hope this works for everyone else!
+ *
+ * Parameters:
+ * a pre-allocated character buffer, long enough to hold an X.25 address
+ * and the tailing null.
+ *
+ * Return Value:
+ * the length of the address string.
+ * 0 = error
+ */
+int
+x25local_nua(char *buf) {
+ struct CuAt *response; /* structure to fill with info from ODM */
+ CLASS_SYMBOL retClass; /* ODM class */
+ char query[64]; /* odm database query */
+ int rc = 0; /* return value (length of local NUA) */
+ extern char x25name[]; /* x25 device name (sx25a0) */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25local_nua\n" );
+#endif /* TRACE */
+
+ /* set up query string */
+ if (x25name[0] == '\0') {
+#ifdef DEBUG
+ printf( "kermit: x25local_nua(): No x25 device set, trying sx25a0\n" );
+#endif /* DEBUG */
+ strcpy( x25name, "sx25a0" );
+ }
+ ckmakmsg(query, sizeof(query), "name like ",x25name,
+ " and attribute like local_nua");
+
+ /* initialise ODM database */
+ odmerrno = 0;
+ if (odm_initialize() == -1) {
+ printf( "x25local_nua(): can't initialize ODM database");
+ switch (odmerrno) {
+ case ODMI_INVALID_PATH:
+ printf( "invalid path\n" );
+ break;
+ case ODMI_MALLOC_ERR:
+ printf( "malloc failed\n" );
+ break;
+ default:
+ printf( "unknown error %d\nPlease call IBM\n", odmerrno );
+ }
+ return(rc);
+ }
+
+ /* open the CuAt class */
+ retClass = odm_open_class(CuAt_CLASS);
+ if (((int)retClass) == -1) {
+ printf( "kermit: x25local_nua(): can't open CuAt class in odm. " );
+ switch (odmerrno) {
+ case ODMI_CLASS_DNE:
+ printf( "CuAt class doesn't exist\n" );
+ break;
+ case ODMI_CLASS_PERMS:
+ printf( "permission to CuAt class file denied\n" );
+ break;
+ case ODMI_MAGICNO_ERR:
+ printf( "CuAt is an invalid ODM object class\n" );
+ break;
+ case ODMI_OPEN_ERR:
+ printf( "cannot open CuAt class - and don't know why!\n" );
+ break;
+ case ODMI_INVALID_PATH:
+ printf( "invalid path\n" );
+ break;
+ case ODMI_TOOMANYCLASSES:
+ printf( "too many object classes have been opened\n" );
+ break;
+ default:
+ printf( "unknown error %d\nPlease call IBM\n", odmerrno );
+ }
+ return(rc);
+ }
+
+#ifdef DEBUG
+ printf("retClass= %d\n", retClass);
+#endif /* DEBUG */
+
+ response = (struct CuAt *)odm_get_first( retClass, query, NULL );
+ if (((int)response) == -1) {
+ printf( "kermit: x25local_nua(): odm query failed " );
+ switch (odmerrno) {
+ case ODMI_BAD_CRIT: /* Programming error */
+ printf( "bad search criteria\n" );
+ break;
+ case ODMI_CLASS_DNE:
+ printf( "CuAt class doesn't exist\n" );
+ break;
+ case ODMI_CLASS_PERMS:
+ printf( "permission to CuAt class file denied\n" );
+ break;
+ case ODMI_INTERNAL_ERR:
+ printf("odm internal error\nPlease contact your administrator\n" );
+ break;
+ case ODMI_INVALID_CLXN:
+ printf("CuAt is invalid or inconsistent odm class collection\n");
+ break;
+ case ODMI_INVALID_PATH:
+ printf( "invalid path\n" );
+ break;
+ case ODMI_MAGICNO_ERR:
+ printf( "CuAt is an invalid ODM object class\n" );
+ break;
+ case ODMI_MALLOC_ERR:
+ printf( "malloc failed\n" );
+ break;
+ case ODMI_OPEN_ERR:
+ printf( "cannot open CuAt class - and don't know why!\n" );
+ break;
+ case ODMI_TOOMANYCLASSES:
+ printf( "too many object classes have been opened\n" );
+ break;
+ default:
+ printf( "unknown error %d\nPlease call IBM\n", odmerrno );
+ }
+ return(rc);
+ }
+
+ /* check for a meaningfull response */
+ if (response != NULL) {
+ if (response->value != NULL) {
+ strcpy(buf, response->value);
+ rc = strlen( buf );
+#ifdef DEBUG
+/*
+ printf( "attribute name is: %s\n", (char *)response->attribute );
+ printf( "I think my address is %s\n", (char*)response->value );
+*/
+#endif /* DEBUG */
+ } else {
+ printf( "kermit: x25local_nua(): couldn't find the local NUA\n" );
+ }
+ } else {
+ switch (odmerrno) {
+ case ODMI_BAD_CRIT:
+ printf( "Error: ODMI_BAD_CRIT - bad criteria\n" );
+ break;
+ case ODMI_CLASS_DNE:
+ printf( "Error: ODMI_CLASS_DNE - class doesn't exist\n" );
+ break;
+ case ODMI_CLASS_PERMS:
+ printf( "Error: ODMI_CLASS_PERMS - class permissions\n" );
+ break;
+ case ODMI_INTERNAL_ERR:
+ printf( "Error: ODMI_INTERNAL_ERR - panic\n" );
+ break;
+ case ODMI_INVALID_CLXN:
+ printf( "Error: ODMI_INVALID_CLXN - invalid collection\n" );
+ break;
+ case ODMI_INVALID_PATH:
+ printf( "Error: ODMI_INVALID_PATH - invalid path - what path?\n" );
+ break;
+ case ODMI_MAGICNO_ERR:
+ printf( "Error: ODMI_MAGICNO_ERR - invalid object magic\n" );
+ break;
+ case ODMI_MALLOC_ERR:
+ printf( "Error: ODMI_MALLOC_ERR - malloc failed\n" );
+ break;
+ case ODMI_OPEN_ERR:
+ printf( "Error: ODMI_OPEN_ERR - cannot open class\n" );
+ break;
+ case ODMI_TOOMANYCLASSES:
+ printf( "Error: ODMI_TOOMANYCLASSES - too many classes\n" );
+ break;
+ default:
+ printf( "Unknown error!\n" );
+ }
+ return(rc);
+ }
+
+ /* close the database again */
+ odm_close_class( retClass );
+
+ /* forget about ODM all together */
+ odm_terminate();
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25local_nua\n" );
+#endif /* TRACE */
+
+ debug(F110, "x25local_nua local address is ", buf, 0);
+ return(rc);
+}
+
+/*****************************************************************************
+ * Function: x25facilities
+ *
+ * Description:
+ * build up the facilities data packet for a connection request
+ *
+ * Parameters:
+ * a pre-allocated char buffer, normally NPI_MAX_DATA big.
+ *
+ * Return Value:
+ * the number of characters inserted into the buffer
+ */
+int
+x25facilities(buffer) char *buffer; {
+ extern int revcall;
+ extern int closgr;
+ char *p; /* temp pointer */
+ char *start; /* temp pointer */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25facilities\n" );
+#endif /* TRACE */
+
+ p = buffer + 1;
+ start = p;
+
+#ifdef DEBUG
+ printf( "kermit: x25facilities(): getting X25 facilities\n" );
+#endif /* DEBUG */
+
+ if (revcall != 0) {
+#ifdef DEBUG
+ printf("reverse charge: %d\n", revcall );
+#endif /* DEBUG */
+ *++p = 0x01;
+ *++p = revcall;
+ }
+ if (closgr > 0) {
+#ifdef DEBUG
+ printf("closed user group: %d\n", closgr );
+#endif /* DEBUG */
+ *++p = 0x03;
+ *++p = closgr;
+ }
+
+#ifdef DEBUG
+ if (p == start) {
+ printf( "no facilities\n" );
+ }
+#endif /* DEBUG */
+
+ /* set the size of the facilities buffer */
+ *buffer = (char)( p - start ) & 0xff;
+
+#ifdef DEBUG
+ printf( "kermit: x25facilities(): returning %d\n", (int)(p - buffer) );
+#endif /* DEBUG */
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25facilities\n" );
+#endif /* TRACE */
+
+ /* return the size of the facilities with size byte */
+ /* 1 == no facilities, 0 byte returned as facilities size */
+ return( (int)(p - buffer) );
+}
+
+/*
+ * reset the connection
+ */
+int
+x25reset(cause, diagn) char cause; char diagn; {
+ /* not implemented */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25reset\n" );
+#endif /* TRACE */
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25reset\n" );
+#endif /* TRACE */
+
+ return(0);
+}
+
+/*
+ * clear the x25 connection - ie: hang up
+ */
+int
+x25clear() {
+ int get_flags = 0; /* priority flag for getmsg */
+ int put_flags = 0; /* send flags, always 0 */
+ ulong type; /* primitive type */
+ N_discon_req_t *discon_req; /* pointer to N_DISCON_REQ */
+ N_discon_ind_t *discon_ind; /* pointer to N_DISCON_IND */
+ N_npi_data_t *discon_data; /* pointer to N_DISCON_IND data */
+ int rc = 0; /* return code */
+
+#ifdef TRACE
+ printf( "TRACE: entering x25clear\n" );
+#endif /* TRACE */
+
+#ifdef DEBUG
+ /* printf( "x25clear(): checking last msg: %s\n", x25prim(x25lastmsg)); */
+#endif /* DEBUG */
+
+ /*
+ * The following checks are used to ensure that we don't disconnect
+ * or unbind twice - this seems to throw the NPI interface right out of
+ * kilter.
+ */
+ switch(x25lastmsg) {
+ case N_BIND_ACK:
+ case N_CONN_CON:
+ case N_CONN_REQ:
+ case N_DATA_REQ:
+ case N_DATA_IND:
+ {
+#ifdef DEBUG
+ /* printf("x25clear(): actively disconnecting\n"); */
+#endif /* DEBUG */
+
+ discon_req = (N_discon_req_t *)malloc(NPI_MAX_CTL);
+ if (discon_req == NULL) {
+ perror("kermit x25clear(): discon_req malloc failed\n");
+ /* fallthrough, try to unbind the NPI anyway */
+ } else {
+ discon_req->PRIM_type = N_DISCON_REQ;
+ discon_req->DISCON_reason = 0; /* not used by AIX */
+ discon_req->RES_length = 0;
+ discon_req->RES_offset = (ulong)(sizeof(N_discon_req_t));
+ discon_req->SEQ_number = x25seqno; /* global */
+
+ if (x25putmsg(ttyfd,
+ (N_npi_ctl_t*)discon_req,
+ (N_npi_data_t*)NULL,
+ 0,
+ &put_flags
+ ) < 0) {
+ perror("x25putmsg failed in x25clear()");
+ }
+ discon_ind = (N_discon_ind_t *)malloc(NPI_MAX_CTL);
+ discon_data = (N_npi_data_t *)malloc(NPI_MAX_DATA);
+ if((discon_ind == NULL) || (discon_data == NULL)) {
+ perror("x25clear(): discon_ind malloc failed\n");
+ /* fallthrough, try to unbind the NPI anyway */
+ } else {
+ if(x25getmsg(ttyfd,
+ (N_npi_ctl_t*)discon_ind,
+ NPI_MAX_CTL,
+ (N_npi_data_t*)discon_data,
+ NPI_MAX_DATA,
+ &get_flags,
+ N_OK_ACK
+ ) < 0 ) {
+ perror("x25getmsg failed in x25clear()");
+ /* fallthrough, try to unbind the NPI anyway */
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (x25lastmsg != N_UNBIND_REQ) {
+ rc = x25unbind(ttyfd);
+ }
+
+#ifdef TRACE
+ printf( "TRACE: leaving x25clear\n" );
+#endif /* TRACE */
+
+ return(rc);
+}
+
+#ifdef DEBUG
+/*
+ * only for debugging
+ *
+ * turn the internal representation of a datablock into something
+ * half-way readable. Because the length is known, we can print
+ * the string including null's etc (important, because the first(!)
+ * byte of an X121 address is a null! (X121 addr == 0 + X25 addr)
+ */
+x25dump_data(char *addr, ulong offset, ulong length) {
+ char *ptr = addr + offset;
+ ulong i = length;
+ /* allocate enough memory for all unprintable chars */
+ char *buf = (char *)malloc( length * 4 );
+ char *bptr = buf; /* pointer to current place in the print buffer */
+
+ while (i > 0) {
+ if (isprint(*ptr)) {
+ *bptr++ = *ptr;
+ } else {
+ *bptr++ = '[';
+ strcpy(bptr,ckctox(*ptr,1)); bptr += 2;
+ *bptr++ = ']';
+ }
+ ptr++;
+ i--;
+ }
+ if (length > 0) {
+ *bptr = '\0';
+ printf( "%s", buf );
+ }
+ printf( " (%d+%d)\n", offset, length );
+
+ if (buf) { free(buf); buf = NULL; }
+ return;
+}
+
+/*
+ * only for debugging
+ * print as much useful information about a packet as possible
+ */
+x25dump_prim(primitive) N_npi_ctl_t *primitive; {
+ printf("Primitive");
+ switch (primitive->PRIM_type) {
+ case N_BIND_ACK:
+ printf( "\tN_BIND_ACK\n\taddress:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->bind_ack.ADDR_offset,
+ primitive->bind_ack.ADDR_length );
+ printf( "\tproto id:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->bind_ack.PROTOID_offset,
+ primitive->bind_ack.PROTOID_length );
+ printf( "\tconnind:\t%d\n\ttoken:\t\t%d\n",
+ primitive->bind_ack.CONIND_number,
+ primitive->bind_ack.TOKEN_value );
+ break;
+
+ case N_BIND_REQ:
+ printf( "\tN_BIND_REQ\n\taddress:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->bind_req.ADDR_offset,
+ primitive->bind_req.ADDR_length );
+ printf( "\tproto id:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->bind_req.PROTOID_offset,
+ primitive->bind_req.PROTOID_length );
+ printf( "\tconnind:\t%d\n\tflags:\t\t%d\n",
+ primitive->bind_req.CONIND_number,
+ primitive->bind_req.BIND_flags );
+ break;
+
+ case N_CONN_CON:
+ printf( "\tN_CONN_CON\n" );
+ printf( "\tRES\t\t" );
+ x25dump_data( (char *)primitive,
+ primitive->conn_con.RES_offset,
+ primitive->conn_con.RES_length );
+ printf( "\tflags:\t%d\n", primitive->conn_con.CONN_flags );
+ break;
+
+ case N_CONN_IND:
+ printf( "\tN_CONN_IND\n" );
+ printf( "\tsource:\t\t" );
+ x25dump_data( (char *)primitive,
+ primitive->conn_ind.SRC_offset,
+ primitive->conn_ind.SRC_length );
+ printf( "\tdestination:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->conn_ind.DEST_offset,
+ primitive->conn_ind.DEST_length );
+ printf( "\tSEQ_number:\t%d\n", primitive->conn_ind.SEQ_number );
+ printf( "\tflags:\t%d\n", primitive->conn_ind.CONN_flags );
+ break;
+
+ case N_CONN_REQ:
+ printf( "\tN_CONN_REQ\n\tdestination:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->conn_req.DEST_offset,
+ primitive->conn_req.DEST_length );
+ printf( "\tflags:\t%d\n", primitive->conn_req.CONN_flags );
+ break;
+
+ case N_CONN_RES:
+ printf( "\tN_CONN_RES\n" );
+ printf( "\tTOKEN_value\t%d\n", primitive->conn_res.TOKEN_value );
+ printf( "\tSEQ_number\t%d\n", primitive->conn_res.SEQ_number );
+ printf( "\tCONN_flags\t%d\n", primitive->conn_res.CONN_flags );
+ printf( "\tRES\t\t" );
+ x25dump_data( (char *)primitive,
+ primitive->conn_res.RES_offset,
+ primitive->conn_res.RES_length );
+ break;
+
+ case N_DATACK_IND:
+ printf( "\tN_DATACK_IND\n" );
+ break;
+
+ case N_DATACK_REQ:
+ printf( "\tN_DATACK_REQ\n" );
+ printf( "\tflags:\t%d\n", primitive->data_req.DATA_xfer_flags );
+ break;
+
+ case N_DATA_IND:
+ printf( "\tN_DATA_IND\n" );
+ printf( "\tflags:\t%d\n", primitive->data_ind.DATA_xfer_flags );
+ break;
+
+ case N_DATA_REQ:
+ printf( "\tN_DATA_REQ\n" );
+ break;
+
+ case N_DISCON_IND:
+ printf( "\tN_DISCON_IND\n" );
+ printf( "\torigin:\t%d\n", primitive->discon_ind.DISCON_orig );
+ printf( "\treason:\t\t%d\n", primitive->discon_ind.DISCON_reason );
+ printf( "\tseq no:\t\t%d\n", primitive->discon_ind.SEQ_number );
+ printf( "\tRES:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->discon_ind.RES_offset,
+ primitive->discon_ind.RES_length );
+ break;
+
+ case N_DISCON_REQ:
+ printf( "\tN_DISCON_REQ\n" );
+ printf( "\tDISCON_reason:\t%d\n",
+ primitive->discon_req.DISCON_reason );
+ printf( "\tRES:\t" );
+ x25dump_data( (char *)primitive,
+ primitive->discon_req.RES_offset,
+ primitive->discon_req.RES_length );
+ printf( "\tSEQ_number:\t%d\n", primitive->discon_req.SEQ_number );
+ break;
+
+ case N_ERROR_ACK:
+ printf( "\tN_ERROR_ACK\n" );
+ printf( "\tCaused by:\t%s\n",
+ x25prim( primitive->error_ack.ERROR_prim ) );
+ printf( "\tNPI error:\t%s\n",
+ x25err( primitive->error_ack.NPI_error ));
+ errno = primitive->error_ack.UNIX_error;
+ perror( "\t" );
+ break;
+
+ case N_EXDATA_IND:
+ printf( "\tN_EXDATA_ACK\n" );
+ break;
+
+ case N_EXDATA_REQ:
+ printf( "\tN_EXDATA_REQ\n" );
+ break;
+
+ case N_INFO_ACK:
+ printf( "\tN_INFO_ACK\n" );
+ printf( "\tNSDU size:\t%d\n", primitive->info_ack.NSDU_size );
+ printf( "\tENSDU size:\t%d\n", primitive->info_ack.ENSDU_size );
+ printf( "\tCDATA size:\t%d\n", primitive->info_ack.CDATA_size );
+ printf( "\tDDATA size:\t%d\n", primitive->info_ack.DDATA_size );
+ printf( "\tADDR size:\t%d\n", primitive->info_ack.ADDR_size );
+ printf( "\tNIDU size:\t%d\n", primitive->info_ack.NIDU_size );
+ break;
+
+ case N_INFO_REQ:
+ printf( "\tN_INFO_REQ\n" );
+ break;
+
+ case N_OK_ACK:
+ printf( "\tN_OK_ACK\n" );
+ break;
+
+ case N_OPTMGMT_REQ:
+ printf( "\tN_OPTMGMT_REQ\n" );
+ break;
+
+ case N_RESET_CON:
+ printf( "\tN_RESET_CON\n" );
+ break;
+
+ case N_RESET_IND:
+ printf( "\tN_RESET_IND\n" );
+ printf( "\treason:\t\t%d\n", primitive->reset_ind.RESET_reason );
+ printf( "\torigin:\t\t%d\n", primitive->reset_ind.RESET_orig );
+ break;
+
+ case N_RESET_REQ:
+ printf( "\tN_RESET_REQ\n" );
+ printf( "\treason:\t\t%d\n", primitive->reset_req.RESET_reason );
+ break;
+
+ case N_RESET_RES:
+ printf( "\tN_RESET_RES\n" );
+ break;
+
+ case N_UDERROR_IND:
+ printf( "\tN_UDERROR_IND\n" );
+ break;
+
+ case N_UNBIND_REQ:
+ printf( "\tN_UNBIND_REQ\n" );
+ break;
+
+ case N_UNITDATA_REQ:
+ printf( "\tN_UNITDATA_REQ\n" );
+ break;
+
+ case N_UNITDATA_IND:
+ printf( "\tN_UNITDATA_IND\n" );
+ break;
+
+ default:
+ (void) printf( "Unknown NPI error %d", primitive->PRIM_type );
+ return 0;
+ }
+}
+#endif /* DEBUG */
+
+/* it looks like signal handling is not needed with streams! */
+/* x25oobh() - handle SIGURG signals - take from isode ? */
+
+#endif /* IBMX25 */
+
+#ifndef NOHTTP
+/*
+ Which time.h files to include... See ckcdeb.h for defaults.
+ Note that 0, 1, 2, or all 3 of these can be included according to
+ the symbol definitions.
+*/
+#ifndef NOTIMEH
+#ifdef TIMEH
+#include <time.h>
+#endif /* TIMEH */
+#endif /* NOTIMEH */
+
+#ifndef NOSYSTIMEH
+#ifdef SYSTIMEH
+#include <sys/time.h>
+#endif /* SYSTIMEH */
+#endif /* NOSYSTIMEH */
+
+#ifndef NOSYSTIMEBH
+#ifdef SYSTIMEBH
+#include <sys/timeb.h>
+#endif /* SYSTIMEBH */
+#endif /* NOSYSTIMEBH */
+
+#ifndef TIMEH
+#ifndef SYSTIMEH
+#ifndef SYSTIMEBH
+#ifdef Plan9
+#include <sys/time.h>
+#else
+#ifdef AIX41
+#include <time.h>
+#else
+#ifdef SUNOS4
+#include <sys/time.h>
+#else
+#ifdef SYSTIMEH
+#include <sys/time.h>
+#else
+#ifdef POSIX
+#include <posix/time.h>
+#else
+#ifdef CLIX
+#include <sys/time.h>
+#else
+#ifdef OS2
+#include <time.h>
+#else
+#include <time.h>
+/* #include <utime.h> */
+#endif /* OS2 */
+#endif /* CLIX */
+#endif /* POSIX */
+#endif /* SYSTIMEH */
+#endif /* SUNOS4 */
+#endif /* AIX41 */
+#endif /* Plan9 */
+#endif
+#endif
+#endif
+
+#ifdef OS2
+#include <sys/utime.h>
+#ifdef NT
+#define utimbuf _utimbuf
+#endif /* NT */
+#define utime _utime
+#else
+#ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
+#include <sys/utime.h> /* for extra fields required by */
+#else /* 88Open spec. */
+#ifdef UTIMEH /* or <utime.h> if requested */
+#include <utime.h> /* (SVR4, POSIX) */
+#define SYSUTIMEH /* Use this for both cases. */
+#endif /* UTIMEH */
+#endif /* SYSUTIMEH */
+#endif /* OS2 */
+
+#ifndef HTTP_VERSION
+#define HTTP_VERSION "HTTP/1.1"
+#endif /* HTTP_VERSION */
+
+#ifdef CMDATE2TM
+time_t
+#ifdef CK_ANSIC
+http_date(char * date)
+#else
+http_date(date) char * date;
+#endif /* CK_ANSIC */
+/* http_date */ {
+ /* HTTP dates are of the form: "Sun, 06 Oct 1997 20:11:47 GMT" */
+ /* There are two older formats which we are required to parse
+ * that we currently do not:
+ *
+ * RFC 850: "Sunday, 06-Oct-97 20:11:47 GMT"
+ * asctime(): "Sun Nov 6 20:11:47 1997"
+ *
+ * However, it is required that all dates be sent in the form we
+ * do accept. The other two formats are for compatibility with
+ * really old servers.
+ */
+ extern char cmdatebuf[18];
+ struct tm t_tm;
+ time_t t;
+ char ldate[32];
+ int j;
+
+ j = ckindex(",",date,0,0,0);
+ ckstrncpy(ldate,&date[j+1],25);
+
+ { /*
+ cmcvtate() date changed to return a string pointer.
+ fdc, 12 Aug 2001.
+ */
+ char * dp;
+ dp = (char *)cmcvtdate(ldate,0); /* Convert to normal form */
+ if (!dp)
+ return(0);
+ t_tm = *cmdate2tm(dp,1);
+ }
+/*
+ From Lucas Hart, 5 Dec 2001:
+ "On the systems to which I have access (SunOS 4.1.1, Solaris 8, and Tru64),
+ setting tm_isdst to -1 maintains the correct timezone offsets, i.e., writes
+ the specified (GMT) time if the buffer size is 21, or the contemporaneous
+ localtime if the buffer size is 25. Perhaps tm_isdst should be set in
+ cmdate2tm(), rather than only in http_date."
+*/
+#ifndef NOTM_ISDST /* For platforms where */
+ t_tm.tm_isdst = -1; /* tm_isdst doesn't exist. */
+#endif /* NOTM_ISDST */
+
+ t = mktime(&t_tm); /* NOT PORTABLE */
+
+#ifdef XX_TIMEZONE
+ t -= _timezone;
+#endif /* XX_TIMEZONE */
+
+ return(t);
+}
+#endif /* CMDATE2TM */
+
+char *
+http_now() {
+ static char nowstr[32];
+#ifdef CMDATE2TM
+ struct tm *gmt;
+ time_t ltime; /* NOT PORTABLE */
+
+ time(<ime);
+
+ gmt = gmtime(<ime); /* PROBABLY NOT PORTABLE */
+ strftime(nowstr,32,"%a, %d %b %Y %H:%M:%S GMT",gmt); /* NOT PORTABLE */
+ /* not only is it not portable but it's locale-dependent */
+#else
+/*
+ This is hopeless. First of all, it seems that HTTP wants Day and Month
+ NAMES? In English? Whose idea was that? Even worse, the date/time must be
+ expressed in Zulu (UTC (GMT)), and converting from local time to GMT is a
+ nightmare. Every platform does it differently, if at all -- even if we
+ restrict ourselves to UNIX. For example (quoting from recent C-Kermit edit
+ history), "Fixed a longstanding bug in the BSDI version, in which incoming
+ file dates were set in GMT rather than local time. It seems in 4.4BSD,
+ localtime() does not return the local time, but rather Zero Meridian (Zulu)
+ time (GMT), and must be adjusted by the tm_gmtoff value." Swell. For
+ greater appreciation of the scope of the problem, just take a look at the
+ time-related #ifdefs in ckutio.c. The only right way to do this is to add
+ our own portable API for converting between local time and GMT/UTC/Zulu
+ that shields us not only from UNIXisms like time_t and struct tm, but also
+ the unbelievable amount of differences in time-related APIs -- e.g. is
+ "timezone" an external variable or a function; which header file(s) do we
+ include, etc etc etc. It's a major project.
+*/
+ int x;
+ x = cmcvtdate("",1);
+
+Evidently this code is not used -- if it is, it must be fixed to use
+new (aug 2001) cmcvtdate() calling conventions.
+
+ if (x < 0)
+ return("");
+/* yyyymmdd hh:mm:ss */
+/* 01234567890123456 */
+ nowstr[0] = 'X'; /* 1st letter of day */
+ nowstr[1] = 'x'; /* 2nd letter of day */
+ nowstr[2] = 'x'; /* 3rd letter of day */
+ nowstr[3] = ',';
+ nowstr[4] = ' ';
+ nowstr[5] = cmdate[6];
+ nowstr[6] = cmdate[7];
+ nowstr[7] = ' ';
+ nowstr[8] = ' '; /* first letter of month */
+ nowstr[9] = ' '; /* second letter of month */
+ nowstr[10] = ' '; /* third letter of month */
+ nowstr[11] = ' ';
+ nowstr[12] = cmdate[0];
+ nowstr[13] = cmdate[1];
+ nowstr[14] = cmdate[2];
+ nowstr[15] = cmdate[3];
+ nowstr[16] = ' ';
+ nowstr[17] = cmdate[9];
+ nowstr[18] = cmdate[10];
+ nowstr[19] = cmdate[11];
+ nowstr[20] = cmdate[12];
+ nowstr[21] = cmdate[13];
+ nowstr[22] = cmdate[14];
+ nowstr[23] = cmdate[15];
+ nowstr[24] = cmdate[16];
+ nowstr[25] = ' ';
+ nowstr[26] = 'G';
+ nowstr[27] = 'M';
+ nowstr[28] = 'T';
+ nowstr[29] = '\0';
+#endif /* CMDATE2TM */
+ return(nowstr);
+}
+
+#ifndef OS2
+#ifndef CK_AUTHENTICATION
+/* from ckuusr.h, which this module normally doesn't include */
+_PROTOTYP( int dclarray, (char, int) );
+#endif /* CK_AUTHENTICATION */
+#endif /* OS2 */
+/*
+ Assign http response pairs to given array.
+ For best results, response pairs should contain no spaces.
+
+ Call with:
+ resp = pointer to response list.
+ n = size of response list.
+ array = array letter.
+ Returns:
+ 0 on failure.
+ >= 1, size of array, on success.
+*/
+static int
+#ifdef CK_ANSIC
+http_mkarray(char ** resp, int n, char array)
+#else
+http_mkarray(resp, n, array) char ** resp; int n; char array;
+#endif /* CK_ANSIC */
+{
+#ifndef NOSPL
+ int i, x;
+ char ** ap;
+ extern char ** a_ptr[];
+ extern int a_dim[];
+
+ if (!array || n <= 0)
+ return(0);
+ if ((x = dclarray(array,n)) < 0) {
+ printf("?Array declaration failure\n");
+ return(-9);
+ }
+ /* Note: argument array is 0-based but Kermit array is 1-based */
+ ap = a_ptr[x];
+ ap[0] = NULL; /* 0th element is empty */
+ for (i = 1; i <= n; i++) {
+ ap[i] = resp[i-1]; /* If resp elements were malloc'd */
+ resp[i-1] = NULL;
+ }
+ a_dim[x] = n;
+ return(n);
+#else
+ return(0);
+#endif /* NOSPL */
+}
+
+#define HTTPHEADCNT 64
+int
+http_get_chunk_len()
+{
+ int len = 0;
+ int i = 0, j = -1;
+ char buf[24];
+ int ch;
+
+ while ((ch = http_inc(0)) >= 0 && i < 24) {
+ buf[i] = ch;
+ if ( buf[i] == ';' ) /* Find chunk-extension (if any) */
+ j = i;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ buf[i] = '\0';
+ break;
+ }
+ i++;
+ }
+ if ( i < 24 ) { /* buf now contains len in Hex */
+ len = hextoulong(buf, j == -1 ? i : j-1);
+ }
+
+ return(len);
+}
+
+int
+http_isconnected()
+{
+ return(httpfd != -1);
+}
+
+char *
+http_host()
+{
+ return(httpfd != -1 ? http_host_port : "");
+}
+
+char *
+http_security()
+{
+ if ( httpfd == -1 )
+ return("NULL");
+#ifdef CK_SSL
+ if (tls_http_active_flag) {
+ SSL_CIPHER * cipher;
+ const char *cipher_list;
+ static char buf[128];
+ buf[0] = NUL;
+ cipher = SSL_get_current_cipher(tls_http_con);
+ cipher_list = SSL_CIPHER_get_name(cipher);
+ SSL_CIPHER_description(cipher,buf,sizeof(buf));
+ return(buf);
+ }
+#endif /* CK_SSL */
+ return("NULL");
+}
+
+int
+http_reopen()
+{
+ int rc = 0;
+ char * s = NULL; /* strdup is not portable */
+ if ( tcp_http_proxy ) {
+ char * p;
+ makestr(&s,(char *)http_host_port);
+ p = s;
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ *p++ = '\0'; /* Get service name or number */
+ } else {
+ p="http";
+ }
+ rc = http_open(s,p,http_ssl,NULL,0,http_agent);
+ } else {
+ makestr(&s,(char *)http_ip);
+ rc = http_open(s,ckuitoa(http_port),http_ssl,NULL,0,http_agent);
+ }
+ free(s);
+ return(rc);
+}
+
+
+int
+#ifdef CK_ANSIC
+http_open(char * hostname, char * svcname, int use_ssl, char * rdns_name,
+ int rdns_len, char * agent)
+#else /* CK_ANSIC */
+http_open(hostname, svcname, use_ssl, rdns_name, rdns_len, agent)
+ char * hostname;
+ char * svcname;
+ int use_ssl;
+ char * rdns_name;
+ int rdns_len;
+ char * agent;
+#endif /* CK_ANSIC */
+{
+ char namecopy[NAMECPYL];
+ char *p;
+ int i, x, dns = 0;
+#ifdef TCPSOCKET
+ int isconnect = 0;
+#ifdef SO_OOBINLINE
+ int on = 1;
+#endif /* SO_OOBINLINE */
+ struct servent *service=NULL;
+ struct hostent *host=NULL;
+ struct sockaddr_in r_addr;
+ struct sockaddr_in sin;
+ struct sockaddr_in l_addr;
+ GSOCKNAME_T l_slen;
+#ifdef EXCELAN
+ struct sockaddr_in send_socket;
+#endif /* EXCELAN */
+
+#ifdef INADDRX
+/* inet_addr() is of type struct in_addr */
+#ifdef datageneral
+ extern struct in_addr inet_addr();
+#else
+#ifdef HPUX5WINTCP
+ extern struct in_addr inet_addr();
+#endif /* HPUX5WINTCP */
+#endif /* datageneral */
+ struct in_addr iax;
+#else
+#ifdef INADDR_NONE
+ struct in_addr iax;
+#else /* INADDR_NONE */
+ long iax;
+#endif /* INADDR_NONE */
+#endif /* INADDRX */
+
+ if ( rdns_name == NULL || rdns_len < 0 )
+ rdns_len = 0;
+
+ *http_ip = '\0'; /* Initialize IP address string */
+ namecopy[0] = '\0';
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F110,"http_open hostname",hostname,0);
+ debug(F110,"http_open svcname",svcname,0);
+ }
+#endif /* DEBUG */
+ if (!hostname) hostname = "";
+ if (!svcname) svcname = "";
+ if (!*hostname || !*svcname) return(-1);
+
+
+ service = ckgetservice(hostname,svcname,http_ip,20);
+
+ if (service == NULL) {
+ if ( !quiet )
+ printf("?Invalid service: %s\r\n",svcname);
+ return(-1);
+ }
+
+ /* For HTTP connections we must preserve the original hostname and */
+ /* service requested so we can include them in the Host header. */
+ ckmakmsg(http_host_port,sizeof(http_host_port),hostname,":",
+ ckuitoa(ntohs(service->s_port)),NULL);
+ http_port = ntohs(service->s_port);
+ http_ssl = use_ssl;
+ debug(F111,"http_open",http_host_port,http_port);
+
+ /* 'http_ip' contains the IP address to which we want to connect */
+ /* 'svcnam' contains the service name */
+ /* 'service->s_port' contains the port number in network byte order */
+
+ /* If we are using an http proxy, we need to create a buffer containing */
+ /* hostname:port-number */
+ /* to pass to the http_connect() function. Then we need to replace */
+ /* 'namecopy' with the name of the proxy server and the service->s_port */
+ /* with the port number of the proxy (default port 80). */
+
+ if ( tcp_http_proxy ) {
+
+ ckmakmsg(proxycopy,sizeof(proxycopy),hostname,":",
+ ckuitoa(ntohs(service->s_port)),NULL);
+ ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);
+
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ debug(F110,"http_open name has colon",namecopy,0);
+ *p++ = '\0'; /* Get service name or number */
+ } else {
+ strcpy(++p,"http");
+ }
+
+ service = ckgetservice(namecopy,p,http_ip,20);
+ if (!service) {
+ fprintf(stderr, "Can't find port for service %s\n", p);
+#ifdef TGVORWIN
+ debug(F101,"http_open can't get service for proxy","",socket_errno);
+#else
+ debug(F101,"http_open can't get service for proxy","",errno);
+#endif /* TGVORWIN */
+ errno = 0; /* (rather than mislead) */
+ return(-1);
+ }
+
+ /* copy the proxyname and remove the service if any so we can use
+ * it as the hostname
+ */
+ ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL);
+ p = namecopy; /* Was a service requested? */
+ while (*p != '\0' && *p != ':') p++; /* Look for colon */
+ if (*p == ':') { /* Have a colon */
+ *p = '\0'; /* terminate string */
+ }
+ hostname = namecopy; /* use proxy as hostname */
+ }
+
+ /* Set up socket structure and get host address */
+ bzero((char *)&r_addr, sizeof(r_addr));
+ debug(F100,"http_open bzero ok","",0);
+
+#ifdef INADDR_NONE
+ debug(F101,"http_open INADDR_NONE defined","",INADDR_NONE);
+#else /* INADDR_NONE */
+ debug(F100,"http_open INADDR_NONE not defined","",0);
+#endif /* INADDR_NONE */
+#ifdef INADDRX
+ debug(F100,"http_open INADDRX defined","",0);
+#else /* INADDRX */
+ debug(F100,"http_open INADDRX not defined","",0);
+#endif /* INADDRX */
+
+#ifndef NOMHHOST
+#ifdef INADDRX
+ iax = inet_addr(http_ip[0]?http_ip:hostname);
+ debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr);
+#else /* INADDRX */
+#ifdef INADDR_NONE
+ iax.s_addr = inet_addr(http_ip[0]?http_ip:hostname);
+ debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr);
+#else /* INADDR_NONE */
+#ifndef datageneral
+ iax = (unsigned int) inet_addr(http_ip[0]?http_ip:hostname);
+#else
+ iax = -1L;
+#endif /* datageneral */
+ debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax);
+#endif /* INADDR_NONE */
+#endif /* INADDRX */
+
+ dns = 0;
+ if (
+#ifdef INADDR_NONE
+/* This might give warnings on 64-bit platforms but they should be harmless */
+/* because INADDR_NONE should be all 1's anyway, thus the OR part is */
+/* probably superfluous -- not sure why it's even there, maybe it should be */
+/* removed. */
+ iax.s_addr == INADDR_NONE || iax.s_addr == (unsigned long) -1L
+#else /* INADDR_NONE */
+ iax < 0
+#endif /* INADDR_NONE */
+ ) {
+ if (!quiet) {
+ printf(" DNS Lookup... ");
+ fflush(stdout);
+ }
+ if ((host = gethostbyname(http_ip[0] ? http_ip : hostname)) != NULL) {
+ debug(F100,"http_open gethostbyname != NULL","",0);
+ host = ck_copyhostent(host);
+ dns = 1; /* Remember we performed dns lookup */
+ r_addr.sin_family = host->h_addrtype;
+ if (tcp_rdns && host->h_name && host->h_name[0] && (rdns_len > 0)
+ && (tcp_http_proxy == NULL)
+ )
+ ckmakmsg(rdns_name,rdns_len,host->h_name,":",svcname,NULL);
+
+#ifdef HADDRLIST
+#ifdef h_addr
+ /* This is for trying multiple IP addresses - see <netdb.h> */
+ if (!(host->h_addr_list))
+ return(-1);
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&r_addr.sin_addr,
+ host->h_length
+ );
+#else
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length);
+#endif /* HADDRLIST */
+#ifndef EXCELAN
+ debug(F111,"BCOPY","host->h_addr",host->h_addr);
+#endif /* EXCELAN */
+ debug(F111,"BCOPY"," (caddr_t)&r_addr.sin_addr",
+ (caddr_t)&r_addr.sin_addr);
+ debug(F111,"BCOPY"," r_addr.sin_addr.s_addr",
+ r_addr.sin_addr.s_addr);
+ debug(F111,"BCOPY","host->h_length",host->h_length);
+ }
+ }
+#endif /* NOMHHOST */
+
+ if (!dns) {
+#ifdef INADDRX
+/* inet_addr() is of type struct in_addr */
+ struct in_addr ina;
+ unsigned long uu;
+ debug(F100,"http_open gethostbyname == NULL: INADDRX","",0);
+ ina = inet_addr(http_ip[0]?http_ip:hostname);
+ uu = *(unsigned int *)&ina;
+#else /* Not INADDRX */
+/* inet_addr() is unsigned long */
+ unsigned long uu;
+ debug(F100,"http_open gethostbyname == NULL: Not INADDRX","",0);
+ uu = inet_addr(http_ip[0]?http_ip:hostname);
+#endif /* INADDRX */
+ debug(F101,"http_open uu","",uu);
+ if (
+#ifdef INADDR_NONE
+ !(uu == INADDR_NONE || uu == (unsigned int) -1L)
+#else /* INADDR_NONE */
+ uu != ((unsigned long)-1)
+#endif /* INADDR_NONE */
+ ) {
+ r_addr.sin_addr.s_addr = uu;
+ r_addr.sin_family = AF_INET;
+ } else {
+#ifdef VMS
+ fprintf(stdout, "\r\n"); /* complete any previous message */
+#endif /* VMS */
+ fprintf(stderr, "Can't get address for %s\n",
+ http_ip[0]?http_ip:hostname);
+#ifdef TGVORWIN
+ debug(F101,"http_open can't get address","",socket_errno);
+#else
+ debug(F101,"http_open can't get address","",errno);
+#endif /* TGVORWIN */
+ errno = 0; /* Rather than mislead */
+ return(-1);
+ }
+ }
+
+ /* Get a file descriptor for the connection. */
+
+ r_addr.sin_port = service->s_port;
+ ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20);
+ debug(F110,"http_open trying",http_ip,0);
+ if (!quiet && *http_ip) {
+ printf(" Trying %s... ", http_ip);
+ fflush(stdout);
+ }
+
+ /* Loop to try additional IP addresses, if any. */
+
+ do {
+#ifdef EXCELAN
+ send_socket.sin_family = AF_INET;
+ send_socket.sin_addr.s_addr = 0;
+ send_socket.sin_port = 0;
+ if ((httpfd = socket(SOCK_STREAM, (struct sockproto *)0,
+ &send_socket, SO_REUSEADDR)) < 0)
+#else /* EXCELAN */
+ if ((httpfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+#endif /* EXCELAN */
+ {
+#ifdef EXCELAN
+ experror("TCP socket error");
+#else
+#ifdef TGVORWIN
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ socket_perror("TCP socket error");
+ debug(F101,"http_open socket error","",socket_errno);
+#else
+ perror("TCP socket error");
+ debug(F101,"http_open socket error","",errno);
+#endif /* TGVORWIN */
+#endif /* EXCELAN */
+ return (-1);
+ }
+ errno = 0;
+
+ /* If a specific TCP address on the local host is desired we */
+ /* must bind it to the socket. */
+#ifndef datageneral
+ if (tcp_address) {
+ int s_errno;
+
+ debug(F110,"http_open binding socket to",tcp_address,0);
+ bzero((char *)&sin,sizeof(sin));
+ sin.sin_family = AF_INET;
+#ifdef INADDRX
+ inaddrx = inet_addr(tcp_address);
+ sin.sin_addr.s_addr = *(unsigned long *)&inaddrx;
+#else
+ sin.sin_addr.s_addr = inet_addr(tcp_address);
+#endif /* INADDRX */
+ sin.sin_port = 0;
+ if (bind(httpfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ s_errno = socket_errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(httpfd);
+#else /* TCPIPLIB */
+ close(httpfd);
+#endif /* TCPIPLIB */
+ httpfd = -1;
+ errno = s_errno; /* and report this error */
+ debug(F101,"http_open bind errno","",errno);
+ return(-1);
+ }
+ }
+#endif /* datageneral */
+
+/* Now connect to the socket on the other end. */
+
+#ifdef EXCELAN
+ if (connect(httpfd, &r_addr) < 0)
+#else
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+ if (connect(httpfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0)
+#endif /* EXCELAN */
+ {
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#ifdef OS2
+ i = socket_errno;
+#else /* OS2 */
+#ifdef TGVORWIN
+ i = socket_errno;
+#else
+ i = errno; /* Save error code */
+#endif /* TGVORWIN */
+#endif /* OS2 */
+#ifdef HADDRLIST
+#ifdef h_addr
+ if (host && host->h_addr_list && host->h_addr_list[1]) {
+ perror("");
+ host->h_addr_list++;
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&r_addr.sin_addr,
+ host->h_length);
+
+ ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20);
+ debug(F110,"http_open h_addr_list",http_ip,0);
+ if (!quiet && *http_ip) {
+ printf(" Trying %s... ", http_ip);
+ fflush(stdout);
+ }
+#ifdef TCPIPLIB
+ socket_close(httpfd); /* Close it. */
+#else
+ close(httpfd);
+#endif /* TCPIPLIB */
+ continue;
+ }
+#endif /* h_addr */
+#endif /* HADDRLIST */
+ http_close();
+ httpfd = -1;
+ errno = i; /* And report this error */
+#ifdef EXCELAN
+ if (errno) experror("http_open connect");
+#else
+#ifdef TGVORWIN
+ debug(F101,"http_open connect error","",socket_errno);
+ /* if (errno) socket_perror("http_open connect"); */
+#ifdef OLD_TWG
+ errno = socket_errno;
+#endif /* OLD_TWG */
+ if (!quiet)
+ socket_perror("http_open connect");
+#else /* TGVORWIN */
+ debug(F101,"http_open connect errno","",errno);
+#ifdef VMS
+ if (!quiet)
+ perror("\r\nFailed");
+#else
+ if (!quiet)
+ perror("Failed");
+#endif /* VMS */
+#ifdef DEC_TCPIP
+ if (!quiet)
+ perror("http_open connect");
+#endif /* DEC_TCPIP */
+#ifdef CMU_TCPIP
+ if (!quiet)
+ perror("http_open connect");
+#endif /* CMU_TCPIP */
+#endif /* TGVORWIN */
+#endif /* EXCELAN */
+ return(-1);
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+ isconnect = 1;
+ } while (!isconnect);
+
+#ifdef NON_BLOCK_IO
+ on = 1;
+ x = socket_ioctl(httpfd,FIONBIO,&on);
+ debug(F101,"http_open FIONBIO","",x);
+#endif /* NON_BLOCK_IO */
+
+ /* We have succeeded in connecting to the HTTP PROXY. So now we */
+ /* need to attempt to connect through the proxy to the actual host */
+ /* If that is successful, we have to pretend that we made a direct */
+ /* connection to the actual host. */
+
+ if ( tcp_http_proxy ) {
+#ifdef OS2
+ if (!agent)
+ agent = "Kermit 95"; /* Default user agent */
+#else
+ if (!agent)
+ agent = "C-Kermit";
+#endif /* OS2 */
+
+ if (http_connect(httpfd,
+ tcp_http_proxy_agent ? tcp_http_proxy_agent : agent,
+ NULL,
+ tcp_http_proxy_user,
+ tcp_http_proxy_pwd,
+ 0,
+ proxycopy
+ ) < 0) {
+ http_close();
+ return(-1);
+ }
+ }
+
+#ifdef SO_OOBINLINE
+ /* See note on SO_OOBINLINE in netopen() */
+#ifdef datageneral
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef BSD43
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OSF1
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef POSIX
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef MOTSV88R4
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef SOLARIS
+/*
+ Maybe this applies to all SVR4 versions, but the other (else) way has been
+ compiling and working fine on all the others, so best not to change it.
+*/
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OSK
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef OS2
+ {
+ int rc;
+ rc = setsockopt(httpfd,
+ SOL_SOCKET,
+ SO_OOBINLINE,
+ (char *) &on,
+ sizeof on
+ );
+ debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc);
+ }
+#else
+#ifdef VMS /* or, at least, VMS with gcc */
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+#ifdef CLIX
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on);
+#else
+ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* CLIX */
+#endif /* VMS */
+#endif /* OS2 */
+#endif /* OSK */
+#endif /* SOLARIS */
+#endif /* MOTSV88R4 */
+#endif /* POSIX */
+#endif /* BSD43 */
+#endif /* OSF1 */
+#endif /* datageneral */
+#endif /* SO_OOBINLINE */
+
+#ifndef NOTCPOPTS
+#ifndef datageneral
+#ifdef SOL_SOCKET
+#ifdef TCP_NODELAY
+ no_delay(ttyfd,tcp_nodelay);
+#endif /* TCP_NODELAY */
+#ifdef SO_KEEPALIVE
+ keepalive(ttyfd,tcp_keepalive);
+#endif /* SO_KEEPALIVE */
+#ifdef SO_LINGER
+ ck_linger(ttyfd,tcp_linger, tcp_linger_tmo);
+#endif /* SO_LINGER */
+#ifdef SO_SNDBUF
+ sendbuf(ttyfd,tcp_sendbuf);
+#endif /* SO_SNDBUF */
+#ifdef SO_RCVBUF
+ recvbuf(ttyfd,tcp_recvbuf);
+#endif /* SO_RCVBUF */
+#endif /* SOL_SOCKET */
+#endif /* datageneral */
+#endif /* NOTCPOPTS */
+
+#ifndef datageneral
+ /* Find out our own IP address. */
+ /* We need the l_addr structure for [E]KLOGIN. */
+ l_slen = sizeof(l_addr);
+ bzero((char *)&l_addr, l_slen);
+#ifndef EXCELAN
+ if (!getsockname(httpfd, (struct sockaddr *)&l_addr, &l_slen)) {
+ char * s = (char *)inet_ntoa(l_addr.sin_addr);
+ ckstrncpy(myipaddr, s, 20);
+ debug(F110,"getsockname",myipaddr,0);
+ }
+#endif /* EXCELAN */
+#endif /* datageneral */
+
+/* See note in netopen() on Reverse DNS lookups */
+ if (tcp_rdns == SET_ON) {
+#ifdef NT
+ if (isWin95())
+ sleep(1);
+#endif /* NT */
+ if (!quiet) {
+ printf(" Reverse DNS Lookup... ");
+ fflush(stdout);
+ }
+ if (host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET)) {
+ char * s;
+ host = ck_copyhostent(host);
+ debug(F100,"http_open gethostbyname != NULL","",0);
+ if (!quiet) {
+ printf("(OK)\n");
+ fflush(stdout);
+ }
+ s = host->h_name;
+ if (!s) { /* This can happen... */
+ debug(F100,"http_open host->h_name is NULL","",0);
+ s = "";
+ }
+ /* Something is wrong with inet_ntoa() on HPUX 10.xx */
+ /* The compiler says "Integral value implicitly converted to */
+ /* pointer in assignment." The prototype is right there */
+ /* in <arpa/inet.h> so what's the problem? */
+ /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */
+ if (!*s) { /* No name so substitute the address */
+ debug(F100,"http_open host->h_name is empty","",0);
+ s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */
+ if (!s) /* Trust No 1 */
+ s = "";
+ if (*s) { /* If it worked, use this string */
+ ckstrncpy(http_ip,s,20);
+ }
+ s = http_ip; /* Otherwise stick with the IP */
+ if (!*s) /* or failing that */
+ s = http_host_port; /* the name we were called with. */
+ }
+ if (*s) /* return the rdns name */
+ ckmakmsg(rdns_name,rdns_len,s,":",svcname,NULL);
+
+ if (!quiet && *s
+#ifndef NOICP
+ && !doconx
+#endif /* NOICP */
+ ) {
+ printf(" %s connected on port %s\n",s,
+ ckuitoa(ntohs(service->s_port)));
+#ifdef BETADEBUG
+ /* This is simply for testing the DNS entries */
+ if (host->h_aliases) {
+ char ** a = host->h_aliases;
+ while (*a) {
+ printf(" alias => %s\n",*a);
+ a++;
+ }
+ }
+#endif /* BETADEBUG */
+ }
+ } else {
+ if (!quiet) printf("Failed.\n");
+ }
+ } else if (!quiet) printf("(OK)\n");
+ if (!quiet) fflush(stdout);
+
+
+ if ( tcp_http_proxy ) {
+ /* Erase the IP address since we cannot reuse it */
+ http_ip[0] = '\0';
+ } else {
+ /* This should already have been done but just in case */
+ ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20);
+ }
+ makestr(&http_agent,agent);
+
+#ifdef CK_SSL
+ if (use_ssl && ck_ssleay_is_installed()) {
+ if (!ssl_http_init(hostname)) {
+ if (bio_err!=NULL) {
+ BIO_printf(bio_err,"ssl_tn_init() failed\n");
+ ERR_print_errors(bio_err);
+ } else {
+ fflush(stderr);
+ fprintf(stderr,"ssl_tn_init() failed\n");
+ ERR_print_errors_fp(stderr);
+ }
+ http_close();
+ return(-1);
+ } else if ( ck_ssl_http_client(httpfd,hostname) < 0 ) {
+ http_close();
+ return(-1);
+ }
+ }
+#endif /* CK_SSL */
+#endif /* TCPSOCKET */
+
+ return(0); /* Done. */
+}
+
+int
+#ifdef CK_ANSIC
+http_close(VOID)
+#else /* CK_ANSIC */
+http_close()
+#endif /* CK_ANSIC */
+{
+ int x = 0;
+ debug(F101,"http_close","",httpfd);
+
+ if (httpfd == -1) /* Was open? */
+ return(0); /* Wasn't. */
+
+#ifndef OS2
+ if (httpfd > -1) /* Was. */
+#endif /* OS2 */
+ {
+#ifdef CK_SSL
+ if (tls_http_active_flag) {
+ if (ssl_debug_flag)
+ BIO_printf(bio_err,"calling SSL_shutdown\n");
+ SSL_shutdown(tls_http_con);
+ tls_http_active_flag = 0;
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ x = socket_close(httpfd); /* Close it. */
+#else
+#ifndef OS2
+ x = close(httpfd);
+#endif /* OS2 */
+#endif /* TCPIPLIB */
+ }
+ httpfd = -1; /* Mark it as closed. */
+ /* do not erase http_host_port, http_ip, http_port so they */
+ /* can be used by http_reopen() */
+ return(x);
+}
+
+
+/* http_tol()
+ * Call with s = pointer to string, n = length.
+ * Returns number of bytes actually written on success, or
+ * -1 on i/o error, -2 if called improperly.
+ */
+
+int
+http_tol(s,n) CHAR *s; int n; {
+ int count = 0;
+ int len = n;
+ int try = 0;
+
+ if (httpfd == -1) {
+ debug(F100,"http_tol socket is closed","",0);
+ return -1;
+ }
+ debug(F101,"http_tol TCPIPLIB ttnet","",ttnet);
+#ifdef COMMENT
+ hexdump("http_tol",s,n);
+#endif /* COMMENT */
+
+#ifdef CK_SSL
+ if (tls_http_active_flag) {
+ int error, r;
+ /* Write using SSL */
+ ssl_retry:
+ r = SSL_write(tls_http_con, s, len /* >1024?1024:len */);
+ switch (SSL_get_error(tls_http_con,r)) {
+ case SSL_ERROR_NONE:
+ debug(F111,"http_tol","SSL_write",r);
+ if ( r == len )
+ return(n);
+ s += r;
+ len -= r;
+ goto ssl_retry;
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"http_tol SSL_ERROR_WANT_WRITE","",0);
+ return(-1);
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"http_tol SSL_ERROR_WANT_READ","",0);
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( r == 0 ) { /* EOF */
+ http_close();
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"http_tol SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ return -1;
+#endif /* NT */
+ return(rc);
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"http_tol SSL_ERROR_WANT_X509_LOOKUP","",0);
+ http_close();
+ return(-2);
+ case SSL_ERROR_SSL:
+ debug(F100,"http_tol SSL_ERROR_SSL","",0);
+ http_close();
+ return(-2);
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"http_tol SSL_ERROR_ZERO_RETURN","",0);
+ http_close();
+ return(-2);
+ default:
+ debug(F100,"http_tol SSL_ERROR_?????","",0);
+ http_close();
+ return(-2);
+ }
+ }
+#endif /* CK_SSL */
+
+ http_tol_retry:
+ try++; /* Increase the try counter */
+
+ {
+#ifdef BSDSELECT
+ fd_set wfds;
+ struct timeval tv;
+
+ debug(F101,"http_tol BSDSELECT","",0);
+ tv.tv_usec = 0L;
+ tv.tv_sec=30;
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+#ifdef STREAMING
+ do_select:
+#endif /* STREAMING */
+ FD_ZERO(&wfds);
+ FD_SET(httpfd, &wfds);
+ if (select(FD_SETSIZE, NULL,
+#ifdef __DECC
+#ifndef __DECC_VER
+ (int *)
+#endif /* __DECC_VER */
+#endif /* __DECC */
+ &wfds, NULL, &tv) < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"http_tol select failed","",s_errno);
+#ifdef BETADEBUG
+ printf("http_tol select failed: %d\n", s_errno);
+#endif /* BETADEBUG */
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+ if (!FD_ISSET(httpfd, &wfds)) {
+#ifdef STREAMING
+ if (streaming)
+ goto do_select;
+#endif /* STREAMING */
+ debug(F111,"http_tol","!FD_ISSET",ttyfd);
+#ifdef NT
+ WSASafeToCancel = 0;
+ if (!win95selectbug)
+#endif /* NT */
+ return(-1);
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ {
+ int tries = 0;
+ debug(F101,"http_tol IBMSELECT","",0);
+ while (select(&httpfd, 0, 1, 0, 1000) != 1) {
+ int count;
+ if (tries++ >= 60) {
+ /* if after 60 seconds we can't get permission to write */
+ debug(F101,"http_tol select failed","",socket_errno);
+ return(-1);
+ }
+#ifdef COMMENT
+ if ((count = http_tchk()) < 0) {
+ debug(F111,"http_tol","http_tchk()",count);
+ return(count);
+ }
+#endif /* COMMENT */
+ }
+ }
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+#ifdef TCPIPLIB
+ if ((count = socket_write(httpfd,s,n)) < 0) {
+ int s_errno = socket_errno; /* maybe a function */
+ debug(F101,"http_tol socket_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-2);
+#endif /* OS2 */
+ return(-1); /* Call it an i/o error */
+ }
+#else /* TCPIPLIB */
+ if ((count = write(httpfd,s,n)) < 0) {
+ debug(F101,"http_tol socket_write error","",errno);
+ return(-1); /* Call it an i/o error */
+ }
+#endif /* TCPIPLIB */
+ if (count < n) {
+ debug(F111,"http_tol socket_write",s,count);
+ if (try > 25) {
+ /* don't try more than 25 times */
+ debug(F100,"http_tol tried more than 25 times","",0);
+ return(-1);
+ }
+ if (count > 0) {
+ s += count;
+ n -= count;
+ }
+ debug(F111,"http_tol retry",s,n);
+ goto http_tol_retry;
+ } else {
+ debug(F111,"http_tol socket_write",s,count);
+ return(len); /* success - return total length */
+ }
+ }
+}
+
+
+int
+http_inc(timo) int timo; {
+ int x=-1; unsigned char c; /* The locals. */
+
+ if (httpfd == -1) {
+ debug(F100,"http_inc socket is closed","",0);
+ return(-2);
+ }
+
+#ifdef CK_SSL
+ /*
+ * In the case of OpenSSL, it is possible that there is still
+ * data waiting in the SSL session buffers that has not yet
+ * been read by Kermit. If this is the case we must process
+ * it without calling select() because select() will not return
+ * with an indication that there is data to be read from the
+ * socket. If there is no data pending in the SSL session
+ * buffers then fall through to the select() code and wait for
+ * some data to arrive.
+ */
+ if (tls_http_active_flag) {
+ int error;
+
+ x = SSL_pending(tls_http_con);
+ if (x < 0) {
+ debug(F111,"http_inc","SSL_pending error",x);
+ http_close();
+ return(-1);
+ } else if ( x > 0 ) {
+ ssl_read:
+ x = SSL_read(tls_http_con, &c, 1);
+ error = SSL_get_error(tls_http_con,x);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ debug(F111,"http_inc SSL_ERROR_NONE","x",x);
+ if (x > 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(c); /* Return character. */
+ } else if (x < 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else {
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"http_inc SSL_ERROR_WANT_READ","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( x == 0 ) { /* EOF */
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"http_inc SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+#endif /* NT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(rc);
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_SSL:
+ debug(F100,"http_inc SSL_ERROR_SSL","",0);
+#ifdef COMMENT
+ http_close();
+#endif /* COMMENT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ default:
+ debug(F100,"http_inc SSL_ERROR_?????","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ }
+ }
+#endif /* CK_SSL */
+ {
+#ifdef BSDSELECT
+ fd_set rfds;
+ struct timeval tv;
+ int timeout = timo < 0 ? -timo : 1000 * timo;
+ debug(F101,"http_inc BSDSELECT","",timo);
+
+ for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
+ int rc;
+ debug(F111,"http_inc","timeout",timeout);
+ /* Don't move select() initialization out of the loop. */
+ FD_ZERO(&rfds);
+ FD_SET(httpfd, &rfds);
+ tv.tv_sec = tv.tv_usec = 0L;
+ if (timo)
+ tv.tv_usec = (long) 100000L;
+ else
+ tv.tv_sec = 30;
+#ifdef NT
+ WSASafeToCancel = 1;
+#endif /* NT */
+ rc = select(FD_SETSIZE,
+#ifndef __DECC
+ (fd_set *)
+#endif /* __DECC */
+ &rfds, NULL, NULL, &tv);
+ if (rc < 0) {
+ int s_errno = socket_errno;
+ debug(F111,"http_inc","select",rc);
+ debug(F111,"http_inc","socket_errno",s_errno);
+ if (s_errno)
+ return(-1);
+ }
+ debug(F111,"http_inc","select",rc);
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+ if (FD_ISSET(httpfd, &rfds)) {
+ x = 0;
+ break;
+ } else {
+ /* If waiting forever we have no way of knowing if the */
+ /* socket closed so try writing a 0-length TCP packet */
+ /* which should force an error if the socket is closed */
+ if (!timo) {
+#ifdef TCPIPLIB
+ if ((rc = socket_write(httpfd,"",0)) < 0) {
+ int s_errno = socket_errno;
+ debug(F101,"http_inc socket_write error","",s_errno);
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-2);
+#endif /* OS2 */
+ return(-1); /* Call it an i/o error */
+ }
+#else /* TCPIPLIB */
+ if ((rc = write(httpfd,"",0)) < 0) {
+ debug(F101,"http_inc socket_write error","",errno);
+ return(-1); /* Call it an i/o error */
+ }
+#endif /* TCPIPLIB */
+ }
+ continue;
+ }
+ }
+#ifdef NT
+ WSASafeToCancel = 0;
+#endif /* NT */
+#else /* !BSDSELECT */
+#ifdef IBMSELECT
+ /*
+ Was used by OS/2, currently not used, but might come in handy some day...
+ ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set
+ and timeval stuff since this is the only place where it is used.
+ */
+ int socket = httpfd;
+ int timeout = timo < 0 ? -timo : 1000 * timo;
+
+ debug(F101,"http_inc IBMSELECT","",timo);
+ for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) {
+ if (select(&socket, 1, 0, 0, 100L) == 1) {
+ x = 0;
+ break;
+ }
+ }
+#else /* !IBMSELECT */
+ SELECT is required for this code
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+ }
+
+ if (timo && x < 0) { /* select() timed out */
+ debug(F100,"http_inc select() timed out","",0);
+ return(-1); /* Call it an i/o error */
+ }
+
+#ifdef CK_SSL
+ if ( tls_http_active_flag ) {
+ int error;
+ ssl_read2:
+ x = SSL_read(tls_http_con, &c, 1);
+ error = SSL_get_error(tls_http_con,x);
+ switch (error) {
+ case SSL_ERROR_NONE:
+ debug(F111,"http_inc SSL_ERROR_NONE","x",x);
+ if (x > 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(c); /* Return character. */
+ } else if (x < 0) {
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ } else {
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ case SSL_ERROR_WANT_WRITE:
+ debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_WANT_READ:
+ debug(F100,"http_inc SSL_ERROR_WANT_READ","",0);
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-1);
+ case SSL_ERROR_SYSCALL:
+ if ( x == 0 ) { /* EOF */
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ } else {
+ int rc = -1;
+#ifdef NT
+ int gle = GetLastError();
+ debug(F111,"http_inc SSL_ERROR_SYSCALL",
+ "GetLastError()",gle);
+ rc = os2socketerror(gle);
+ if (rc == -1)
+ rc = -2;
+ else if ( rc == -2 )
+ rc = -1;
+#endif /* NT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(rc);
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_SSL:
+ debug(F100,"http_inc SSL_ERROR_SSL","",0);
+#ifdef COMMENT
+ http_close();
+#endif /* COMMENT */
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ case SSL_ERROR_ZERO_RETURN:
+ debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ default:
+ debug(F100,"http_inc SSL_ERROR_?????","",0);
+ http_close();
+#ifdef OS2
+ ReleaseTCPIPMutex();
+#endif /* OS2 */
+ return(-2);
+ }
+ }
+#endif /* CK_SSL */
+#ifdef TCPIPLIB
+ x = socket_read(httpfd,&c,1);
+#else
+ x = read(httpfd,&c,1);
+#endif
+
+ if (x <= 0) {
+ int s_errno = socket_errno;
+ debug(F101,"ttbufr socket_read","",x);
+ debug(F101,"ttbufr socket_errno","",s_errno);
+#ifdef OS2
+ if (x == 0 || os2socketerror(s_errno) < 0) {
+ http_close();
+ ReleaseTCPIPMutex();
+ return(-2);
+ }
+ ReleaseTCPIPMutex();
+ return(-1);
+#else /* OS2 */
+ http_close(); /* *** *** */
+ return(-2);
+#endif /* OS2 */
+ }
+ return(c);
+}
+
+void
+#ifdef CK_ANSIC
+http_set_code_reply(char * msg)
+#else
+http_set_code_reply(msg)
+ char * msg;
+#endif /* CK_ANSIC */
+{
+ char * p = msg;
+ char buf[16];
+ int i=0;
+
+ while ( *p != SP && *p != NUL ) {
+ buf[i] = *p;
+ p++;
+ i++;
+ }
+
+ http_code = atoi(buf);
+
+ while ( *p == SP )
+ p++;
+
+ ckstrncpy(http_reply_str,p,HTTPBUFLEN);
+}
+
+int
+#ifdef CK_ANSIC
+http_get(char * agent, char ** hdrlist, char * user,
+ char * pwd, char array, char * local, char * remote,
+ int stdio)
+#else
+http_get(agent, hdrlist, user, pwd, array, local, remote, stdio)
+ char * agent; char ** hdrlist; char * user;
+ char * pwd; char array; char * local; char * remote;
+ int stdio;
+#endif /* CK_ANSIC */
+{
+ char * request = NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+#ifdef OS2
+ struct utimbuf u_t;
+#else /* OS2 */
+#ifdef SYSUTIMEH
+ struct utimbuf u_t;
+#else
+ struct utimbuf {
+ time_t atime;
+ time_t mtime;
+ } u_t;
+#endif /* SYSUTIMH */
+#endif /* OS2 */
+ time_t mod_t = 0;
+ time_t srv_t = 0;
+ time_t local_t = 0;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int chunked = 0;
+ int zfile = 0;
+ int first = 1;
+
+#ifdef DEBUG
+ if (deblog) {
+ debug(F101,"http_get httpfd","",httpfd);
+ debug(F110,"http_agent",agent,0);
+ debug(F110,"http_user",user,0);
+ debug(F110,"http_local",local,0);
+ debug(F110,"http_remote",remote,0);
+ }
+#endif /* DEBUG */
+ if (!remote) remote = "";
+
+ if (httpfd == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+ len = 8; /* GET */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if (hdrlist) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += (int) strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd)); /* NOT PORTABLE */
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"GET %s %s\r\n",remote,HTTP_VERSION); /* safe */
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ ckstrncat(request,"\r\n",len);
+
+ getreq:
+ if (http_tol((CHAR *)request,strlen(request)) < 0)
+ {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto getreq;
+ }
+ rc = -1;
+ goto getexit;
+ }
+
+ /* Process the headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ len = -1;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while ( isspace(*p) )
+ p++;
+ switch ( p[0] ) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ local = NULL;
+ }
+ http_set_code_reply(p);
+#ifdef CMDATE2TM
+ } else if (!ckstrcmp(buf,"Last-Modified",13,0)) {
+ mod_t = http_date(&buf[15]);
+ } else if (!ckstrcmp(buf,"Date",4,0)) {
+ srv_t = http_date(&buf[4]);
+#endif /* CMDATE2TM */
+ } else if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
+ len = atoi(&buf[16]);
+ } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
+ if ( ckindex("chunked",buf,18,0,0) != 0 )
+ chunked = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto getreq;
+ }
+ if (http_fnd == 0) {
+ rc = -1;
+ closecon = 1;
+ goto getexit;
+ }
+
+ /* Now we have the contents of the file */
+ if ( local && local[0] ) {
+ if (zopeno(ZOFILE,local,NULL,NULL))
+ zfile = 1;
+ else
+ rc = -1;
+ }
+
+ if ( chunked ) {
+ while ((len = http_get_chunk_len()) > 0) {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ if ((ch = http_inc(0)) != CR)
+ break;
+ if ((ch = http_inc(0)) != LF)
+ break;
+ }
+ } else {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ }
+
+ if ( zfile )
+ zclose(ZOFILE);
+
+ if ( chunked ) { /* Parse Trailing Headers */
+ nullline = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+#ifdef CMDATE2TM
+ if (!ckstrcmp(buf,"Last-Modified",13,0)) {
+ mod_t = http_date(&buf[15]);
+ } else if (!ckstrcmp(buf,"Date",4,0)) {
+ srv_t = http_date(&buf[4]);
+ }
+#endif /* CMDATE2TM */
+ else if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ if ( zfile ) { /* Set timestamp */
+#ifndef NOSETTIME
+#ifdef OS2
+ u_t.actime = srv_t ? srv_t : local_t;
+ u_t.modtime = mod_t ? mod_t : local_t;
+#else /* OS2 */
+#ifdef SYSUTIMEH
+ u_t.actime = srv_t ? srv_t : local_t;
+ u_t.modtime = mod_t ? mod_t : local_t;
+#else
+#ifdef BSD44
+ u_t[0].tv_sec = srv_t ? srv_t : local_t;
+ u_t[1].tv_sec = mod_t ? mod_t : local_t;
+#else
+ u_t.mtime = srv_t ? srv_t : local_t;
+ u_t.atime = mod_t ? mod_t : local_t;
+#endif /* BSD44 */
+#endif /* SYSUTIMEH */
+#endif /* OS2 */
+ utime(local,&u_t);
+#endif /* NOSETTIME */
+ }
+
+ getexit:
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+
+ if ( closecon )
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_head(char * agent, char ** hdrlist, char * user,
+ char * pwd, char array, char * local, char * remote,
+ int stdio)
+#else
+http_head(agent, hdrlist, user, pwd, array, local, remote, stdio)
+ char * agent; char ** hdrlist; char * user;
+ char * pwd; char array; char * local; char * remote;
+ int stdio;
+#endif /* CK_ANSIC */
+{
+ char * request = NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int first = 1;
+
+ if (httpfd == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+ len = 9; /* HEAD */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd)); /* NOT PORTABLE */
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = (char *)malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"HEAD %s %s\r\n",remote,HTTP_VERSION);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ ckstrncat(request,"\r\n",len);
+
+ if ( local && local[0] ) {
+ if (!zopeno(ZOFILE,local,NULL,NULL)) {
+ free(request);
+ return(-1);
+ }
+ }
+
+ headreq:
+ if (http_tol((CHAR *)request,strlen(request)) < 0)
+ {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto headreq;
+ }
+ rc = -1;
+ goto headexit;
+ }
+
+ /* Process the headers */
+
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ switch (p[0]) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else {
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ if ( local && local[0] ) {
+ zsout(ZOFILE,buf);
+ zsout(ZOFILE,"\r\n");
+ }
+ if (stdio)
+ printf("%s\r\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto headreq;
+ }
+ if ( http_fnd == 0 )
+ rc = -1;
+
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+
+ headexit:
+ if ( local && local[0] )
+ zclose(ZOFILE);
+ if (closecon)
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_index(char * agent, char ** hdrlist, char * user, char * pwd,
+ char array, char * local, char * remote, int stdio)
+#else
+http_index(agent, hdrlist, user, pwd, array, local, remote, stdio)
+ char * agent; char ** hdrlist; char * user; char * pwd;
+ char array; char * local; char * remote; int stdio;
+#endif /* CK_ANSIC */
+{
+ char * request = NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int chunked = 0;
+ int zfile = 0;
+ int first = 1;
+
+ if (httpfd == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+ len = 10; /* INDEX */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd));
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"INDEX %s\r\n",HTTP_VERSION);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ ckstrncat(request,"\r\n",len);
+ indexreq:
+ if (http_tol((CHAR *)request,strlen(request)) < 0)
+ {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto indexreq;
+ }
+ rc = -1;
+ goto indexexit;
+ }
+
+ /* Process the headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ len = -1;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ switch ( p[0] ) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else if ( !nullline ) {
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
+ len = atoi(&buf[16]);
+ } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
+ if ( ckindex("chunked",buf,18,0,0) != 0 )
+ chunked = 1;
+ }
+ printf("%s\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto indexreq;
+ }
+ if ( http_fnd == 0 ) {
+ rc = -1;
+ closecon = 1;
+ goto indexexit;
+ }
+
+ /* Now we have the contents of the file */
+ if ( local && local[0] ) {
+ if (zopeno(ZOFILE,local,NULL,NULL))
+ zfile = 1;
+ else
+ rc = -1;
+ }
+
+ if ( chunked ) {
+ while ((len = http_get_chunk_len()) > 0) {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ if ((ch = http_inc(0)) != CR)
+ break;
+ if ((ch = http_inc(0)) != LF)
+ break;
+ }
+ } else {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ }
+
+ if ( zfile )
+ zclose(ZOFILE);
+
+ if ( chunked ) { /* Parse Trailing Headers */
+ nullline = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+ rc = 0;
+
+ indexexit:
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+
+ if (closecon)
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_put(char * agent, char ** hdrlist, char * mime, char * user,
+ char * pwd, char array, char * local, char * remote,
+ char * dest, int stdio)
+#else
+http_put(agent, hdrlist, mime, user, pwd, array, local, remote, dest, stdio)
+ char * agent; char ** hdrlist; char * mime; char * user;
+ char * pwd; char array; char * local; char * remote; char * dest;
+ int stdio;
+#endif /* CK_ANSIC */
+{
+ char * request=NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ int filelen;
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int chunked = 0;
+ int first = 1;
+ int zfile = 0;
+
+ if (httpfd == -1)
+ return(-1);
+ if (!mime) mime = "";
+ if (!remote) remote = "";
+ if (!local) local = "";
+ if (!*local) return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+ filelen = zchki(local);
+ if (filelen < 0)
+ return(-1);
+
+ /* Compute length of request header */
+ len = 8; /* PUT */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd));
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+ len += 16 + strlen(mime); /* Content-type: */
+ len += 32; /* Content-length: */
+ len += 32; /* Date: */
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"PUT %s %s\r\n",remote,HTTP_VERSION);
+ ckstrncat(request,"Date: ",len);
+#ifdef CMDATE2TM
+ ckstrncat(request,http_now(),len);
+#else
+ strcat(request,...);
+#endif /* CMDATE2TM */
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+ ckstrncat(request,"Content-type: ",len);
+ ckstrncat(request,mime,len);
+ ckstrncat(request,"\r\n",len);
+ sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */
+ ckstrncat(request,buf,len);
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ ckstrncat(request,"\r\n",len);
+
+ /* Now we have the contents of the file */
+ if (zopeni(ZIFILE,local)) {
+
+ putreq: /* Send request */
+ if (http_tol((CHAR *)request,strlen(request)) <= 0) {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto putreq;
+ }
+ zclose(ZIFILE);
+ rc = -1;
+ goto putexit;
+ }
+ /* Request headers have been sent */
+
+ i = 0;
+ while (zchin(ZIFILE,&ch) == 0) {
+ buf[i++] = ch;
+ if (i == HTTPBUFLEN) {
+ if (http_tol((CHAR *)buf,HTTPBUFLEN) <= 0) {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto putreq;
+ }
+ }
+ i = 0;
+ }
+ }
+ if (i > 0) {
+ if (http_tol((CHAR *)buf,i) < 0) {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto putreq;
+ }
+ }
+ }
+ zclose(ZIFILE);
+
+ /* Process the response headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ len = -1;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ switch (p[0]) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else {
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
+ len = atoi(&buf[16]);
+ } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
+ if ( ckindex("chunked",buf,18,0,0) != 0 )
+ chunked = 1;
+ }
+ if ( stdio )
+ printf("%s\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto putreq;
+ }
+ if ( http_fnd == 0 ) {
+ closecon = 1;
+ rc = -1;
+ goto putexit;
+ }
+
+ /* Any response data? */
+ if ( dest && dest[0] ) {
+ if (zopeno(ZOFILE,dest,NULL,NULL))
+ zfile = 1;
+ else
+ rc = -1;
+ }
+
+ if ( chunked ) {
+ while ((len = http_get_chunk_len()) > 0) {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ if ((ch = http_inc(0)) != CR)
+ break;
+ if ((ch = http_inc(0)) != LF)
+ break;
+ }
+ } else {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ }
+
+ if ( zfile )
+ zclose(ZOFILE);
+
+ if ( chunked ) { /* Parse Trailing Headers */
+ nullline = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+ } else {
+ rc = -1;
+ }
+
+ putexit:
+ if ( array )
+ http_mkarray(headers,hdcnt,array);
+
+ if (closecon)
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_delete(char * agent, char ** hdrlist, char * user,
+ char * pwd, char array, char * remote)
+#else
+http_delete(agent, hdrlist, user, pwd, array, remote)
+ char * agent; char ** hdrlist; char * user;
+ char * pwd; char array; char * remote;
+#endif /* CK_ANSIC */
+{
+ char * request=NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int chunked = 0;
+ int first = 1;
+
+ if (httpfd == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+
+ /* Compute length of request header */
+ len = 11; /* DELETE */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd));
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+ len += 32; /* Date: */
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"DELETE %s %s\r\n",remote,HTTP_VERSION);
+ ckstrncat(request,"Date: ",len);
+#ifdef CMDATE2TM
+ ckstrncat(request,http_now(),len);
+#else
+ strcat(request,...);
+#endif /* CMDATE2TM */
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ ckstrncat(request,"\r\n",len);
+ delreq:
+ if (http_tol((CHAR *)request,strlen(request)) < 0)
+ {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto delreq;
+ }
+ rc = -1;
+ goto delexit;
+ }
+
+ /* Process the response headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ len = -1;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ switch (p[0]) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else {
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
+ len = atoi(&buf[16]);
+ } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
+ if ( ckindex("chunked",buf,18,0,0) != 0 )
+ chunked = 1;
+ }
+ printf("%s\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto delreq;
+ }
+ if ( http_fnd == 0 ) {
+ rc = -1;
+ closecon = 1;
+ goto delexit;
+ }
+
+ /* Any response data? */
+ if ( chunked ) {
+ while ((len = http_get_chunk_len()) > 0) {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ conoc((CHAR)ch);
+ }
+ if ((ch = http_inc(0)) != CR)
+ break;
+ if ((ch = http_inc(0)) != LF)
+ break;
+ }
+ } else {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ conoc((CHAR)ch);
+ }
+ }
+
+ if ( chunked ) { /* Parse Trailing Headers */
+ nullline = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ delexit:
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+
+ if (closecon)
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_post(char * agent, char ** hdrlist, char * mime, char * user,
+ char * pwd, char array, char * local, char * remote,
+ char * dest, int stdio)
+#else
+http_post(agent, hdrlist, mime, user, pwd, array, local, remote, dest,
+ stdio)
+ char * agent; char ** hdrlist; char * mime; char * user;
+ char * pwd; char array; char * local; char * remote; char * dest;
+ int stdio;
+#endif /* CK_ANSIC */
+{
+ char * request=NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int ch;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ int filelen;
+ char * headers[HTTPHEADCNT];
+ int closecon = 0;
+ int chunked = 0;
+ int zfile = 0;
+ int first = 1;
+
+ if (httpfd == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+ filelen = zchki(local);
+ if (filelen < 0)
+ return(-1);
+
+ /* Compute length of request header */
+ len = 9; /* POST */
+ len += strlen(HTTP_VERSION);
+ len += strlen(remote);
+ len += 16;
+
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ len += strlen(http_host_port) + 8;
+
+ if (agent)
+ len += 13 + strlen(agent);
+ if (user) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd));
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 24;
+ }
+ len += 16 + strlen(mime); /* Content-type: */
+ len += 32; /* Content-length: */
+ len += 32; /* Date: */
+#ifdef HTTP_CLOSE
+ len += 19; /* Connection: close */
+#endif
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"POST %s %s\r\n",remote,HTTP_VERSION);
+ ckstrncat(request,"Date: ",len);
+ ckstrncat(request,http_now(),len);
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user) {
+ ckstrncat(request,"Authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+ ckstrncat(request,"Content-type: ",len);
+ ckstrncat(request,mime,len);
+ ckstrncat(request,"\r\n",len);
+#ifdef HTTP_CLOSE
+ ckstrncat(request,"Connection: close\r\n",len);
+#endif
+ sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */
+ ckstrncat(request,buf,len);
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"\r\n",len);
+
+ /* Now we have the contents of the file */
+ postopen:
+ if (zopeni(ZIFILE,local)) {
+ postreq:
+ if (http_tol((CHAR *)request,strlen(request)) < 0)
+ {
+ http_close();
+ if ( first ) {
+ first--;
+ http_reopen();
+ goto postreq;
+ }
+ rc = -1;
+ zclose(ZIFILE);
+ goto postexit;
+ }
+
+ i = 0;
+ while (zchin(ZIFILE,&ch) == 0) {
+ buf[i++] = ch;
+ if (i == HTTPBUFLEN) {
+ http_tol((CHAR *)buf,HTTPBUFLEN);
+ i = 0;
+ }
+ }
+ if (i > 0)
+ http_tol((CHAR *)buf,HTTPBUFLEN);
+ zclose(ZIFILE);
+
+ /* Process the response headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ len = -1;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ switch (p[0]) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else {
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ } else if (!ckstrcmp(buf,"Content-Length:",15,0)) {
+ len = atoi(&buf[16]);
+ } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) {
+ if ( ckindex("chunked",buf,18,0,0) != 0 )
+ chunked = 1;
+ }
+ if (stdio)
+ printf("%s\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if (ch < 0 && first) {
+ first--;
+ http_close();
+ http_reopen();
+ goto postopen;
+ }
+ if (http_fnd == 0) {
+ rc = -1;
+ closecon = 1;
+ goto postexit;
+ }
+
+ /* Any response data? */
+ if ( dest && dest[0] ) {
+ if (zopeno(ZOFILE,dest,NULL,NULL))
+ zfile = 1;
+ else
+ rc = -1;
+ }
+
+ if ( chunked ) {
+ while ((len = http_get_chunk_len()) > 0) {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ if ((ch = http_inc(0)) != CR)
+ break;
+ if ((ch = http_inc(0)) != LF)
+ break;
+ }
+ } else {
+ while (len && (ch = http_inc(0)) >= 0) {
+ len--;
+ if ( zfile )
+ zchout(ZOFILE,(CHAR)ch);
+ if ( stdio )
+ conoc((CHAR)ch);
+ }
+ }
+
+ if ( zfile )
+ zclose(ZOFILE);
+
+ if ( chunked ) { /* Parse Trailing Headers */
+ nullline = 0;
+ while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if ( buf[i] == 10 ) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"Connection:",11,0)) {
+ if ( ckindex("close",buf,11,0,0) != 0 )
+ closecon = 1;
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ }
+ } else {
+ rc = -1;
+ }
+
+ postexit:
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+ if (closecon)
+ http_close();
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+
+int
+#ifdef CK_ANSIC
+http_connect(int socket, char * agent, char ** hdrlist, char * user,
+ char * pwd, char array, char * host_port)
+#else
+http_connect(socket, agent, hdrlist, user, pwd, array, host_port)
+ int socket;
+ char * agent; char ** hdrlist; char * user;
+ char * pwd; char array; char * host_port;
+#endif /* CK_ANSIC */
+{
+ char * request=NULL;
+ int i, j, len = 0, hdcnt = 0, rc = 0;
+ int http_fnd = 0;
+ char buf[HTTPBUFLEN], *p, ch;
+ int nullline;
+ time_t mod_t;
+ time_t srv_t;
+ time_t local_t;
+ char passwd[64];
+ char b64in[128];
+ char b64out[256];
+ char * headers[HTTPHEADCNT];
+ int connected = 0;
+ int chunked = 0;
+
+ tcp_http_proxy_errno = 0;
+
+ if (socket == -1)
+ return(-1);
+
+ if (array) {
+ for (i = 0; i < HTTPHEADCNT; i++)
+ headers[i] = NULL;
+ }
+
+ /* Compute length of request header */
+ len = 12; /* CONNECT */
+ len += strlen(HTTP_VERSION);
+ len += strlen(host_port);
+ len += (int) strlen(http_host_port) + 8;
+ len += 16;
+ len += strlen("Proxy-Connection: Keep-Alive\r\n");
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++)
+ len += strlen(hdrlist[i]) + 2;
+ }
+ if (agent && agent[0])
+ len += 13 + strlen(agent);
+ if (user && user[0]) {
+ if (!pwd) {
+ readpass("Password: ",passwd,64);
+ pwd = passwd;
+ }
+ ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL);
+ j = b8tob64(b64in,strlen(b64in),b64out,256);
+ memset(pwd,0,strlen(pwd));
+ if (j < 0)
+ return(-1);
+ b64out[j] = '\0';
+ len += j + 72;
+ }
+ len += 32; /* Date: */
+ len += 3; /* blank line + null */
+
+ request = malloc(len);
+ if (!request)
+ return(-1);
+
+ sprintf(request,"CONNECT %s %s\r\n",host_port,HTTP_VERSION);
+ ckstrncat(request,"Date: ",len);
+#ifdef CMDATE2TM
+ ckstrncat(request,http_now(),len);
+#else
+ strcat(request,...);
+#endif /* CMDATE2TM */
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"Host: ", len);
+ ckstrncat(request,http_host_port, len);
+ ckstrncat(request,"\r\n",len);
+ if (agent && agent[0]) {
+ ckstrncat(request,"User-agent: ",len);
+ ckstrncat(request,agent,len);
+ ckstrncat(request,"\r\n",len);
+ }
+ if (user && user[0]) {
+ ckstrncat(request,"Proxy-authorization: Basic ",len);
+ ckstrncat(request,b64out,len);
+ ckstrncat(request,"\r\n",len);
+ ckstrncat(request,"Extension: Security/Remote-Passphrase\r\n",len);
+ }
+ ckstrncat(request,"Proxy-Connection: Keep-Alive\r\n",len);
+ if ( hdrlist ) {
+ for (i = 0; hdrlist[i]; i++) {
+ ckstrncat(request,hdrlist[i],len);
+ ckstrncat(request,"\r\n",len);
+ }
+ }
+ ckstrncat(request,"\r\n",len);
+ len = strlen(request);
+
+#ifdef TCPIPLIB
+ /* Send request */
+ if (socket_write(socket,(CHAR *)request,strlen(request)) < 0) {
+ rc = -1;
+ goto connexit;
+ }
+#else
+ if (write(socket,(CHAR *)request,strlen(request)) < 0) { /* Send request */
+ rc = -1;
+ goto connexit;
+ }
+#endif /* TCPIPLIB */
+
+ /* Process the response headers */
+ local_t = time(NULL);
+ nullline = 0;
+ i = 0;
+ while (!nullline &&
+#ifdef TCPIPLIB
+ (socket_read(socket,&ch,1) == 1) &&
+#else
+ (read(socket,&ch,1) == 1) &&
+#endif /* TCPIPLIB */
+ i < HTTPBUFLEN) {
+ buf[i] = ch;
+ if (buf[i] == 10) { /* found end of line */
+ if (i > 0 && buf[i-1] == 13)
+ i--;
+ if (i < 1)
+ nullline = 1;
+ buf[i] = '\0';
+
+ if (array && !nullline && hdcnt < HTTPHEADCNT)
+ makestr(&headers[hdcnt++],buf);
+ if (!ckstrcmp(buf,"HTTP",4,0)) {
+ http_fnd = 1;
+ j = ckindex(" ",buf,0,0,0);
+ p = &buf[j];
+ while (isspace(*p))
+ p++;
+ tcp_http_proxy_errno = atoi(p);
+ switch (p[0]) {
+ case '1': /* Informational message */
+ break;
+ case '2': /* Success */
+ connected = 1;
+ break;
+ case '3': /* Redirection */
+ case '4': /* Client failure */
+ case '5': /* Server failure */
+ default: /* Unknown */
+ if (!quiet)
+ printf("Failure: Server reports %s\n",p);
+ rc = -1;
+ }
+ http_set_code_reply(p);
+ } else {
+ printf("%s\n",buf);
+ }
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ if ( http_fnd == 0 )
+ rc = -1;
+
+ if (array)
+ http_mkarray(headers,hdcnt,array);
+
+ connexit:
+ if ( !connected ) {
+ if ( socket == ttyfd ) {
+ ttclos(0);
+ }
+ else if ( socket == httpfd ) {
+ http_close();
+ }
+ }
+
+ free(request);
+ for (i = 0; i < hdcnt; i++) {
+ if (headers[i])
+ free(headers[i]);
+ }
+ return(rc);
+}
+#endif /* NOHTTP */
+
+#ifdef CK_DNS_SRV
+
+#define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto dnsout
+#define CHECK(x,y) if (x + y > size + answer.bytes) goto dnsout
+#define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
+
+#ifndef CKQUERYTYPE
+#ifdef UNIXWARE
+#ifndef UW7
+#define CKQUERYTYPE CHAR
+#endif /* UW7 */
+#endif /* UNIXWARE */
+#endif /* CKQUERYTYPE */
+
+#ifndef CKQUERYTYPE
+#define CKQUERYTYPE char
+#endif /* CKQUERYTYPE */
+
+/* 1 is success, 0 is failure */
+int
+locate_srv_dns(host, service, protocol, addr_pp, naddrs)
+ char *host;
+ char *service;
+ char *protocol;
+ struct sockaddr **addr_pp;
+ int *naddrs;
+{
+ int nout, j, count;
+ union {
+ unsigned char bytes[2048];
+ HEADER hdr;
+ } answer;
+ unsigned char *p=NULL;
+ CKQUERYTYPE query[MAX_DNS_NAMELEN];
+#ifdef CK_ANSIC
+ const char * h;
+#else
+ char * h;
+#endif /* CK_ANSIC */
+ struct sockaddr *addr = NULL;
+ struct sockaddr_in *sin = NULL;
+ struct hostent *hp = NULL;
+ int type, class;
+ int priority, weight, size, len, numanswers, numqueries, rdlen;
+ unsigned short port;
+#ifdef CK_ANSIC
+ const
+#endif /* CK_ANSIC */
+ int hdrsize = sizeof(HEADER);
+ struct srv_dns_entry {
+ struct srv_dns_entry *next;
+ int priority;
+ int weight;
+ unsigned short port;
+ char *host;
+ };
+ struct srv_dns_entry *head = NULL;
+ struct srv_dns_entry *srv = NULL, *entry = NULL;
+ char * s = NULL;
+
+ nout = 0;
+ addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
+ if (addr == NULL)
+ return 0;
+
+ count = 1;
+
+ /*
+ * First build a query of the form:
+ *
+ * service.protocol.host
+ *
+ * which will most likely be something like:
+ *
+ * _telnet._tcp.host
+ *
+ */
+ if (((int)strlen(service) + strlen(protocol) + strlen(host) + 5)
+ > MAX_DNS_NAMELEN
+ )
+ goto dnsout;
+
+ /* Realm names don't (normally) end with ".", but if the query
+ doesn't end with "." and doesn't get an answer as is, the
+ resolv code will try appending the local domain. Since the
+ realm names are absolutes, let's stop that.
+
+ But only if a name has been specified. If we are performing
+ a search on the prefix alone then the intention is to allow
+ the local domain or domain search lists to be expanded.
+ */
+ h = host + strlen (host);
+ ckmakxmsg(query, sizeof(query), "_",service,"._",protocol,".", host,
+ ((h > host) && (h[-1] != '.')?".":NULL),
+ NULL,NULL,NULL,NULL,NULL);
+
+ size = res_search(query, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
+
+ if (size < hdrsize)
+ goto dnsout;
+
+ /* We got a reply - See how many answers it contains. */
+
+ p = answer.bytes;
+
+ numqueries = ntohs(answer.hdr.qdcount);
+ numanswers = ntohs(answer.hdr.ancount);
+
+ p += sizeof(HEADER);
+
+ /*
+ * We need to skip over all of the questions, so we have to iterate
+ * over every query record. dn_expand() is able to tell us the size
+ * of compressed DNS names, so we use it.
+ */
+ while (numqueries--) {
+ len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query));
+ if (len < 0)
+ goto dnsout;
+ INCR_CHECK(p, len + 4);
+ }
+
+ /*
+ * We're now pointing at the answer records. Only process them if
+ * they're actually T_SRV records (they might be CNAME records,
+ * for instance).
+ *
+ * But in a DNS reply, if you get a CNAME you always get the associated
+ * "real" RR for that CNAME. RFC 1034, 3.6.2:
+ *
+ * CNAME RRs cause special action in DNS software. When a name server
+ * fails to find a desired RR in the resource set associated with the
+ * domain name, it checks to see if the resource set consists of a CNAME
+ * record with a matching class. If so, the name server includes the CNAME
+ * record in the response and restarts the query at the domain name
+ * specified in the data field of the CNAME record. The one exception to
+ * this rule is that queries which match the CNAME type are not restarted.
+ *
+ * In other words, CNAMEs do not need to be expanded by the client.
+ */
+ while (numanswers--) {
+
+ /* First is the name; use dn_expand() to get the compressed size. */
+ len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query));
+ if (len < 0)
+ goto dnsout;
+ INCR_CHECK(p, len);
+
+ CHECK(p,2); /* Query type */
+ type = NTOHSP(p,2);
+
+ CHECK(p, 6); /* Query class */
+ class = NTOHSP(p,6); /* Also skip over 4-byte TTL */
+
+ CHECK(p,2); /* Record data length */
+ rdlen = NTOHSP(p,2);
+ /*
+ * If this is an SRV record, process it. Record format is:
+ *
+ * Priority
+ * Weight
+ * Port
+ * Server name
+ */
+ if (class == C_IN && type == T_SRV) {
+ CHECK(p,2);
+ priority = NTOHSP(p,2);
+ CHECK(p, 2);
+ weight = NTOHSP(p,2);
+ CHECK(p, 2);
+ port = NTOHSP(p,2);
+ len = dn_expand(answer.
+ bytes,
+ answer.bytes + size,
+ p,
+ query,
+ sizeof(query)
+ );
+ if (len < 0)
+ goto dnsout;
+ INCR_CHECK(p, len);
+ /*
+ * We got everything. Insert it into our list, but make sure
+ * it's in the right order. Right now we don't do anything
+ * with the weight field
+ */
+ srv = (struct srv_dns_entry *)malloc(sizeof(struct srv_dns_entry));
+ if (srv == NULL)
+ goto dnsout;
+
+ srv->priority = priority;
+ srv->weight = weight;
+ srv->port = port;
+ makestr(&s,(char *)query); /* strdup() is not portable */
+ srv->host = s;
+
+ if (head == NULL || head->priority > srv->priority) {
+ srv->next = head;
+ head = srv;
+ } else
+ /*
+ * Confusing. Insert an entry into this spot only if:
+ * . The next person has a higher priority (lower
+ * priorities are preferred), or:
+ * . There is no next entry (we're at the end)
+ */
+ for (entry = head; entry != NULL; entry = entry->next)
+ if ((entry->next &&
+ entry->next->priority > srv->priority) ||
+ entry->next == NULL) {
+ srv->next = entry->next;
+ entry->next = srv;
+ break;
+ }
+ } else
+ INCR_CHECK(p, rdlen);
+ }
+
+ /*
+ * Now we've got a linked list of entries sorted by priority.
+ * Start looking up A records and returning addresses.
+ */
+ if (head == NULL)
+ goto dnsout;
+
+ for (entry = head; entry != NULL; entry = entry->next) {
+ hp = gethostbyname(entry->host);
+ if (hp != 0) {
+
+ /* Watch out - memset() and memcpy() are not portable... */
+
+ switch (hp->h_addrtype) {
+ case AF_INET:
+ for (j = 0; hp->h_addr_list[j]; j++) {
+ sin = (struct sockaddr_in *) &addr[nout++];
+ memset ((char *) sin, 0, sizeof (struct sockaddr));
+ sin->sin_family = hp->h_addrtype;
+ sin->sin_port = htons(entry->port);
+ memcpy((char *) &sin->sin_addr,
+ (char *) hp->h_addr_list[j],
+ sizeof(struct in_addr)); /* safe */
+ if (nout + 1 >= count) {
+ count += 5;
+ addr = (struct sockaddr *)
+ realloc((char *) addr,
+ sizeof(struct sockaddr) * count);
+ if (!addr)
+ goto dnsout;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ for (entry = head; entry != NULL;) {
+ free(entry->host);
+ entry->host = NULL;
+ srv = entry;
+ entry = entry->next;
+ free(srv);
+ srv = NULL;
+ }
+
+ dnsout:
+ if (srv)
+ free(srv);
+
+ if (nout == 0) { /* No good servers */
+ if (addr)
+ free(addr);
+ return 0;
+ }
+ *addr_pp = addr;
+ *naddrs = nout;
+ return 1;
+}
+#undef INCR_CHECK
+#undef CHECK
+#undef NTOHSP
+
+#define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \
+ return 0
+#define CHECK(x, y) if (x + y > size + answer.bytes) \
+ return 0
+#define NTOHSP(x, y) x[0] << 8 | x[1]; x += y
+
+int
+locate_txt_rr(prefix, name, retstr)
+ char *prefix, *name;
+ char **retstr;
+{
+ union {
+ unsigned char bytes[2048];
+ HEADER hdr;
+ } answer;
+ unsigned char *p;
+ char host[MAX_DNS_NAMELEN], *h;
+ int size;
+ int type, class, numanswers, numqueries, rdlen, len;
+
+ /*
+ * Form our query, and send it via DNS
+ */
+
+ if (name == NULL || name[0] == '\0') {
+ strcpy(host,prefix);
+ } else {
+ if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN )
+ return 0;
+
+ /* Realm names don't (normally) end with ".", but if the query
+ doesn't end with "." and doesn't get an answer as is, the
+ resolv code will try appending the local domain. Since the
+ realm names are absolutes, let's stop that.
+
+ But only if a name has been specified. If we are performing
+ a search on the prefix alone then the intention is to allow
+ the local domain or domain search lists to be expanded.
+ */
+ h = host + strlen (host);
+ ckmakmsg(host,sizeof(host),prefix, ".", name,
+ ((h > host) && (h[-1] != '.'))?".":NULL);
+
+ }
+ size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes));
+
+ if (size < 0)
+ return 0;
+
+ p = answer.bytes;
+
+ numqueries = ntohs(answer.hdr.qdcount);
+ numanswers = ntohs(answer.hdr.ancount);
+
+ p += sizeof(HEADER);
+
+ /*
+ * We need to skip over the questions before we can get to the answers,
+ * which means we have to iterate over every query record. We use
+ * dn_expand to tell us how long each compressed name is.
+ */
+
+ while (numqueries--) {
+ len = dn_expand(answer.bytes, answer.bytes + size, p, host,
+ sizeof(host));
+ if (len < 0)
+ return 0;
+ INCR_CHECK(p, len + 4); /* Name plus type plus class */
+ }
+
+ /*
+ * We're now pointing at the answer records. Process the first
+ * TXT record we find.
+ */
+
+ while (numanswers--) {
+
+ /* First the name; use dn_expand to get the compressed size */
+ len = dn_expand(answer.bytes, answer.bytes + size, p,
+ host, sizeof(host));
+ if (len < 0)
+ return 0;
+ INCR_CHECK(p, len);
+
+ /* Next is the query type */
+ CHECK(p, 2);
+ type = NTOHSP(p,2);
+
+ /* Next is the query class; also skip over 4 byte TTL */
+ CHECK(p,6);
+ class = NTOHSP(p,6);
+
+ /* Record data length - make sure we aren't truncated */
+
+ CHECK(p,2);
+ rdlen = NTOHSP(p,2);
+
+ if (p + rdlen > answer.bytes + size)
+ return 0;
+
+ /*
+ * If this is a TXT record, return the string. Note that the
+ * string has a 1-byte length in the front
+ */
+ /* XXX What about flagging multiple TXT records as an error? */
+
+ if (class == C_IN && type == T_TXT) {
+ len = *p++;
+ if (p + len > answer.bytes + size)
+ return 0;
+ *retstr = malloc(len + 1);
+ if (*retstr == NULL)
+ return ENOMEM;
+ strncpy(*retstr, (char *) p, len);
+ (*retstr)[len] = '\0';
+ /* Avoid a common error. */
+ if ( (*retstr)[len-1] == '.' )
+ (*retstr)[len-1] = '\0';
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#undef INCR_CHECK
+#undef CHECK
+#undef NTOHSP
+#endif /* CK_DNS_SRV */
+
+#ifdef TNCODE
+#ifdef CK_FORWARD_X
+#ifdef UNIX
+#include <sys/un.h>
+#define FWDX_UNIX_SOCK
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+#ifndef PF_LOCAL
+#define PF_LOCAL PF_UNIX
+#endif
+#ifndef SUN_LEN
+/* Evaluate to actual length of the `sockaddr_un' structure. */
+#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ + strlen ((ptr)->sun_path))
+#endif
+#endif /* UNIX */
+int
+fwdx_create_listen_socket(screen) int screen; {
+#ifdef NOPUTENV
+ return(-1);
+#else /* NOPUTENV */
+ struct sockaddr_in saddr;
+ int display, port, sock=-1, i;
+ static char env[512];
+
+ /*
+ * X Windows Servers support multiple displays by listening on
+ * one socket per display. Display 0 is port 6000; Display 1 is
+ * port 6001; etc.
+ *
+ * We start by trying to open port 6001 so that display 0 is
+ * reserved for the local X Windows Server.
+ */
+
+ for ( display=1; display < 1000 ; display++ ) {
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ debug(F111,"fwdx_create_listen_socket()","socket() < 0",sock);
+ return(-1);
+ }
+
+ port = 6000 + display;
+ bzero((char *)&saddr, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = inet_addr(myipaddr);
+ saddr.sin_port = htons(port);
+
+ if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ i = errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(sock);
+#else /* TCPIPLIB */
+ close(sock);
+#endif /* TCPIPLIB */
+ sock = -1;
+ debug(F110,"fwdx_create_listen_socket()","bind() < 0",0);
+ continue;
+ }
+
+ debug(F100,"fdwx_create_listen_socket() bind OK","",0);
+ break;
+ }
+
+ if ( display > 1000 ) {
+ debug(F100,"fwdx_create_listen_socket() Out of Displays","",0);
+ return(-1);
+ }
+
+ if (listen(sock, 5) < 0) {
+ i = errno; /* Save error code */
+#ifdef TCPIPLIB
+ socket_close(sock);
+#else /* TCPIPLIB */
+ close(sock);
+#endif /* TCPIPLIB */
+ debug(F101,"fdwx_create_listen_socket() listen() errno","",errno);
+ return(-1);
+ }
+ debug(F100,"fwdx_create_listen_socket() listen OK","",0);
+
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = sock;
+ if (!myipaddr[0])
+ getlocalipaddr();
+ if ( myipaddr[0] )
+ ckmakxmsg(env,sizeof(env),"DISPLAY=",myipaddr,":",
+ ckuitoa(display),":",ckuitoa(screen),
+ NULL,NULL,NULL,NULL,NULL,NULL);
+ else
+ ckmakmsg(env,sizeof(env),"DISPLAY=",ckuitoa(display),":",
+ ckuitoa(screen));
+ putenv(env);
+ return(0);
+#endif /* NOPUTENV */
+}
+
+
+int
+fwdx_open_client_channel(channel) int channel; {
+ char * env;
+ struct sockaddr_in saddr;
+#ifdef FWDX_UNIX_SOCK
+ struct sockaddr_un saddr_un = { AF_LOCAL };
+#endif /* FWDX_UNIX_SOCK */
+ int colon, dot, display, port, sock, i, screen;
+ int family;
+ char buf[256], * host=NULL, * rest=NULL;
+#ifdef TCP_NODELAY
+ int on=1;
+#endif /* TCP_NODELAY */
+
+ debug(F111,"fwdx_create_client_channel()","channel",channel);
+
+ for ( i=0; i<MAXFWDX ; i++ ) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel) {
+ /* Already open */
+ debug(F110,"fwdx_create_client_channel()","already open",0);
+ return(0);
+ }
+ }
+
+ env = getenv("DISPLAY");
+ if ( !env )
+ env = tn_get_display();
+ if ( env )
+ ckstrncpy(buf,env,256);
+ else
+ ckstrncpy(buf,"127.0.0.1:0.0",256);
+
+ bzero((char *)&saddr,sizeof(saddr));
+ saddr.sin_family = AF_INET;
+
+ if (!fwdx_parse_displayname(buf,
+ &family,
+ &host,
+ &display,
+ &screen,
+ &rest
+ )
+ ) {
+ if ( host ) free(host);
+ if ( rest ) free(rest);
+ return(0);
+ }
+ if (rest) free(rest);
+
+#ifndef FWDX_UNIX_SOCK
+ /* if $DISPLAY indicates use of unix domain sockets, but we don't support it,
+ * we change things to use inet sockets on the ip loopback interface instead,
+ * and hope that it works.
+ */
+ if (family == FamilyLocal) {
+ debug(F100,"fwdx_create_client_channel() FamilyLocal","",0);
+ family = FamilyInternet;
+ if (host) free(host);
+ if (host = malloc(strlen("localhost") + 1))
+ strcpy(host, "localhost");
+ else {
+ return(-1);
+ }
+ }
+#else /* FWDX_UNIX_SOCK */
+ if (family == FamilyLocal) {
+ if (host) free(host);
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ return(-1);
+
+ ckmakmsg(buf,sizeof(buf),"/tmp/.X11-unix/X",ckitoa(display),NULL,NULL);
+ strncpy(saddr_un.sun_path, buf, sizeof(saddr_un.sun_path));
+ if (connect(sock,(struct sockaddr *)&saddr_un, SUN_LEN(&saddr_un)) < 0)
+ return(-1);
+ } else
+#endif /* FWDX_UNIX_SOCK */
+ {
+ /* Otherwise, we are assuming FamilyInternet */
+ if (host) {
+ ckstrncpy(buf,host,sizeof(buf));
+ free(host);
+ } else
+ ckstrncpy(buf,myipaddr,sizeof(buf));
+
+ debug(F111,"fwdx_create_client_channel()","display",display);
+
+ port = 6000 + display;
+ saddr.sin_port = htons(port);
+
+ debug(F110,"fwdx_create_client_channel() ip-address",buf,0);
+ saddr.sin_addr.s_addr = inet_addr(buf);
+ if ( saddr.sin_addr.s_addr == (unsigned long) -1
+#ifdef INADDR_NONE
+ || saddr.sin_addr.s_addr == INADDR_NONE
+#endif /* INADDR_NONE */
+ )
+ {
+ struct hostent *host;
+ host = gethostbyname(buf);
+ if ( host == NULL )
+ return(-1);
+ host = ck_copyhostent(host);
+#ifdef HADDRLIST
+#ifdef h_addr
+ /* This is for trying multiple IP addresses - see <netdb.h> */
+ if (!(host->h_addr_list))
+ return(-1);
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&saddr.sin_addr,
+ host->h_length
+ );
+#else
+ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
+#endif /* HADDRLIST */
+ }
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ debug(F111,"fwdx_create_client_channel()","socket() < 0",sock);
+ return(-1);
+ }
+
+ if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ debug(F110,"fwdx_create_client_channel()","connect() failed",0);
+#ifdef TCPIPLIB
+ socket_close(sock);
+#else /* TCPIPLIB */
+ close(sock);
+#endif /* TCPIPLIB */
+ return(-1);
+ }
+
+#ifdef TCP_NODELAY
+ setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char *)&on,sizeof(on));
+#endif /* TCP_NODELAY */
+ }
+
+ for (i = 0; i < MAXFWDX; i++) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == -1) {
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd = sock;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id = channel;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 1;
+ debug(F111,"fwdx_create_client_channel()","socket",sock);
+ return(0);
+ }
+ }
+ return(-1);
+}
+
+int
+fwdx_server_avail() {
+ char * env;
+ struct sockaddr_in saddr;
+#ifdef FWDX_UNIX_SOCK
+ struct sockaddr_un saddr_un = { AF_LOCAL };
+#endif /* FWDX_UNIX_SOCK */
+ int colon, dot, display, port, sock, i, screen;
+ char buf[256], *host=NULL, *rest=NULL;
+#ifdef TCP_NODELAY
+ int on=1;
+#endif /* TCP_NODELAY */
+ int family;
+
+ env = getenv("DISPLAY");
+ if ( !env )
+ env = tn_get_display();
+ if ( env )
+ ckstrncpy(buf,env,256);
+ else
+ ckstrncpy(buf,"127.0.0.1:0.0",256);
+
+ bzero((char *)&saddr,sizeof(saddr));
+ saddr.sin_family = AF_INET;
+
+ if (!fwdx_parse_displayname(buf,&family,&host,&display,&screen,&rest)) {
+ if ( host ) free(host);
+ if ( rest ) free(rest);
+ return(0);
+ }
+ if (rest) free(rest);
+
+#ifndef FWDX_UNIX_SOCK
+ /* if $DISPLAY indicates use of unix domain sockets, but we don't support it,
+ * we change things to use inet sockets on the ip loopback interface instead,
+ * and hope that it works.
+ */
+ if (family == FamilyLocal) {
+ family = FamilyInternet;
+ if (host) free(host);
+ if (host = malloc(strlen("localhost") + 1))
+ strcpy(host, "localhost");
+ else {
+ return(-1);
+ }
+ }
+#else /* FWDX_UNIX_SOCK */
+ if (family == FamilyLocal) {
+ debug(F100,"fwdx_server_avail() FamilyLocal","",0);
+ if (host) free(host);
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0)
+ return(0);
+
+ ckmakmsg(buf,sizeof(buf),"/tmp/.X11-unix/X",ckitoa(display),NULL,NULL);
+ strncpy(saddr_un.sun_path, buf, sizeof(saddr_un.sun_path));
+ if (connect(sock,(struct sockaddr *)&saddr_un,SUN_LEN(&saddr_un)) < 0)
+ return(0);
+ close(sock);
+ return(1);
+ }
+#endif /* FWDX_UNIX_SOCK */
+
+ /* Otherwise, we are assuming FamilyInternet */
+ if (host) {
+ ckstrncpy(buf,host,sizeof(buf));
+ free(host);
+ } else
+ ckstrncpy(buf,myipaddr,sizeof(buf));
+
+ debug(F111,"fwdx_server_avail()","display",display);
+
+ port = 6000 + display;
+ saddr.sin_port = htons(port);
+
+ debug(F110,"fwdx_server_avail() ip-address",buf,0);
+ saddr.sin_addr.s_addr = inet_addr(buf);
+ if ( saddr.sin_addr.s_addr == (unsigned long) -1
+#ifdef INADDR_NONE
+ || saddr.sin_addr.s_addr == INADDR_NONE
+#endif /* INADDR_NONE */
+ )
+ {
+ struct hostent *host;
+ host = gethostbyname(buf);
+ if ( host == NULL ) {
+ debug(F110,"fwdx_server_avail() gethostbyname() failed",
+ myipaddr,0);
+ return(-1);
+ }
+ host = ck_copyhostent(host);
+#ifdef HADDRLIST
+#ifdef h_addr
+ /* This is for trying multiple IP addresses - see <netdb.h> */
+ if (!(host->h_addr_list))
+ return(-1);
+ bcopy(host->h_addr_list[0],
+ (caddr_t)&saddr.sin_addr,
+ host->h_length
+ );
+#else
+ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
+#endif /* h_addr */
+#else /* HADDRLIST */
+ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length);
+#endif /* HADDRLIST */
+ }
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ debug(F111,"fwdx_server_avail()","socket() < 0",sock);
+ return(0);
+ }
+
+ if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ debug(F110,"fwdx_server_avail()","connect() failed",0);
+#ifdef TCPIPLIB
+ socket_close(sock);
+#else /* TCPIPLIB */
+ close(sock);
+#endif /* TCPIPLIB */
+ return(0);
+ }
+
+#ifdef TCPIPLIB
+ socket_close(sock);
+#else /* TCPIPLIB */
+ close(sock);
+#endif /* TCPIPLIB */
+ return(1);
+}
+
+int
+fwdx_open_server_channel() {
+ int sock, ready_to_accept, sock2,channel,i;
+#ifdef TCP_NODELAY
+ int on=1;
+#endif /* TCP_NODELAY */
+#ifdef UCX50
+ static u_int saddrlen;
+#else
+ static SOCKOPT_T saddrlen;
+#endif /* UCX50 */
+ struct sockaddr_in saddr;
+ char sb[8];
+ extern char tn_msg[];
+#ifdef BSDSELECT
+ fd_set rfds;
+ struct timeval tv;
+#else
+#ifdef BELLSELCT
+ fd_set rfds;
+#else
+ fd_set rfds;
+ struct timeval {
+ long tv_sec;
+ long tv_usec;
+ } tv;
+#endif /* BELLSELECT */
+#endif /* BSDSELECT */
+ unsigned short nchannel;
+ unsigned char * p;
+
+ sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket;
+
+ try_again:
+
+#ifdef BSDSELECT
+ tv.tv_sec = tv.tv_usec = 0L;
+ tv.tv_usec = 50;
+ FD_ZERO(&rfds);
+ FD_SET(sock, &rfds);
+ ready_to_accept =
+ ((select(FD_SETSIZE,
+#ifdef HPUX
+#ifdef HPUX1010
+ (fd_set *)
+#else
+
+ (int *)
+#endif /* HPUX1010 */
+#else
+#ifdef __DECC
+ (fd_set *)
+#endif /* __DECC */
+#endif /* HPUX */
+ &rfds, NULL, NULL, &tv) > 0) &&
+ FD_ISSET(sock, &rfds));
+#else /* BSDSELECT */
+#ifdef IBMSELECT
+ ready_to_accept = (select(&sock, 1, 0, 0, 50) == 1);
+#else
+#ifdef BELLSELECT
+ FD_ZERO(rfds);
+ FD_SET(sock, rfds);
+ ready_to_accept =
+ ((select(128, rfds, NULL, NULL, 50) > 0) &&
+ FD_ISSET(sock, rfds));
+#else
+/* Try this - what's the worst that can happen... */
+
+ tv.tv_sec = tv.tv_usec = 0L;
+ tv.tv_usec = 50;
+ FD_ZERO(&rfds);
+ FD_SET(sock, &rfds);
+ ready_to_accept =
+ ((select(FD_SETSIZE,
+ (fd_set *) &rfds, NULL, NULL, &tv) > 0) &&
+ FD_ISSET(sock, &rfds));
+#endif /* BELLSELECT */
+#endif /* IBMSELECT */
+#endif /* BSDSELECT */
+
+ if ( !ready_to_accept )
+ return(0);
+
+ if ((sock2 = accept(sock,(struct sockaddr *)&saddr,&saddrlen)) < 0) {
+ int i = errno; /* save error code */
+ debug(F101,"tcpsrv_open accept errno","",i);
+ return(-1);
+ }
+
+ /*
+ * Now we have the open socket. We must now find a channel to store
+ * it in, and then notify the client.
+ */
+
+ for ( channel=0;channel<MAXFWDX;channel++ ) {
+ if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].fd == -1 )
+ break;
+ }
+
+ if ( channel == MAXFWDX ) {
+#ifdef TCPIPLIB
+ socket_close(sock2);
+#else /* TCPIPLIB */
+ close(sock2);
+#endif /* TCPIPLIB */
+ return(-1);
+ }
+
+ if ( fwdx_send_open(channel) < 0 ) {
+#ifdef TCPIPLIB
+ socket_close(sock2);
+#else /* TCPIPLIB */
+ close(sock2);
+#endif /* TCPIPLIB */
+ }
+
+#ifdef TCP_NODELAY
+ setsockopt(sock2,IPPROTO_TCP,TCP_NODELAY,(char *)&on,sizeof(on));
+#endif /* TCP_NODELAY */
+
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].fd = sock2;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].id = channel;
+ goto try_again;
+
+ return(0); /* never reached */
+}
+
+int
+fwdx_close_channel(channel) int channel; {
+ int i,fd;
+
+ for ( i=0; i<MAXFWDX ; i++ ) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel)
+ break;
+ }
+ if ( i == MAXFWDX )
+ return(-1);
+
+ fd = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd = -1;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id = -1;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0;
+
+#ifdef TCPIPLIB
+ socket_close(fd);
+#else /* TCPIPLIB */
+ close(fd);
+#endif /* TCPIPLIB */
+ return(0);
+}
+
+int
+fwdx_close_all() {
+ int x,fd;
+
+ debug(F111,"fwdx_close_all()",
+ "TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket",
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);
+
+ if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket != -1 ) {
+#ifdef TCPIPLIB
+ socket_close(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);
+#else /* TCPIPLIB */
+ close(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket);
+#endif /* TCPIPLIB */
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = -1;
+ }
+
+ for (x = 0; x < MAXFWDX; x++) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd != -1) {
+ fd = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd = -1;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id = -1;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].need_to_send_xauth = 0;
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend = 0;
+#ifdef TCPIPLIB
+ socket_close(fd);
+#else /* TCPIPLIB */
+ close(fd);
+#endif /* TCPIPLIB */
+ }
+ }
+ return(0);
+}
+
+/* The following definitions are for Unix */
+#ifndef socket_write
+#define socket_write(f,s,n) write(f,s,n)
+#endif /* socket_write */
+#ifndef socket_read
+#define socket_read(f,s,n) read(f,s,n)
+#endif /* socket_read */
+
+int
+fwdx_write_data_to_channel(channel, data, len)
+ int channel; char * data; int len;
+{
+ int sock, count, try=0, length = len, i;
+
+ if ( len <= 0 )
+ return(0);
+
+ for ( i=0; i<MAXFWDX ; i++ ) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel)
+ break;
+ }
+ if ( i == MAXFWDX ) {
+ debug(F110,"fwdx_write_data_to_channel",
+ "attempting to write to closed channel",0);
+ return(-1);
+ }
+
+ sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd;
+ debug(F111,"fwdx_write_data_to_channel","socket",sock);
+ hexdump("fwdx_write_data_to_channel",data,len);
+
+ fwdx_write_data_to_channel_retry:
+
+ if ((count = socket_write(sock,data,len)) < 0) {
+ int s_errno = socket_errno; /* maybe a function */
+ debug(F101,"fwdx_write_data_to_channel socket_write error","",s_errno);
+#ifdef BETATEST
+ printf("fwdx_write_data_to_channel error\r\n");
+#endif /* BETATEST */
+#ifdef OS2
+ if (os2socketerror(s_errno) < 0)
+ return(-2);
+#endif /* OS2 */
+ return(-1); /* Call it an i/o error */
+ }
+ if (count < len) {
+ debug(F111,"fwdx_write_data_to_channel socket_write",data,count);
+ if (count > 0) {
+ data += count;
+ len -= count;
+ }
+ debug(F111,"fwdx_write_data_to_channel retry",data,len);
+ if ( len > 0 )
+ goto fwdx_write_data_to_channel_retry;
+ }
+
+ debug(F111,"fwdx_write_data_to_channel complete",data,length);
+ return(length); /* success - return total length */
+}
+
+VOID
+fwdx_check_sockets(fd_set *ibits)
+{
+ int x, sock, channel, bytes;
+ static char buffer[32000];
+
+ debug(F100,"fwdx_check_sockets()","",0);
+ if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) ||
+ !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) {
+ debug(F110,"fwdx_check_sockets()","TELOPT_FORWARD_X not negotiated",0);
+ return;
+ }
+
+ for (x = 0; x < MAXFWDX; x++) {
+ if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd == -1 ||
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend )
+ continue;
+
+ sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd;
+ if (FD_ISSET(sock, ibits))
+ {
+ channel = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id;
+ debug(F111,"fwdx_check_sockets()","channel set",channel);
+
+ bytes = socket_read(sock, buffer, sizeof(buffer));
+ if (bytes > 0)
+ fwdx_send_data_from_channel(channel, buffer, bytes);
+ else if (bytes == 0) {
+ fwdx_close_channel(channel);
+ fwdx_send_close(channel);
+ }
+ }
+ }
+}
+
+int
+fwdx_init_fd_set(fd_set *ibits)
+{
+ int x,set=0,cnt=0;
+
+ if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) ||
+ !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) {
+ debug(F110,"fwdx_init_fd_set()","TELOPT_FORWARD_X not negotiated",0);
+ return(0);
+ }
+
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket != -1) {
+ set++;
+ FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket, ibits);
+ }
+ for (x = 0; x < MAXFWDX; x++) {
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd != -1) {
+ cnt++;
+ if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend)
+ continue;
+ set++;
+ FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd, ibits);
+ }
+ }
+ if (set + cnt == 0) {
+ return(-1);
+ } else {
+ return(set);
+ }
+}
+
+#ifdef NT
+VOID
+fwdx_thread( VOID * dummy )
+{
+ fd_set ifds;
+ struct timeval tv;
+ extern int priority;
+ int n;
+
+ setint();
+ SetThreadPrty(priority,isWin95() ? 3 : 11);
+
+ while ( !sstelnet && TELOPT_U(TELOPT_FORWARD_X) ||
+ sstelnet && TELOPT_ME(TELOPT_FORWARD_X))
+ {
+ FD_ZERO(&ifds);
+ n = fwdx_init_fd_set(&ifds);
+ if (n > 0) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 2500;
+ if ( select(FD_SETSIZE, &ifds, NULL, NULL, &tv) > 0 )
+ fwdx_check_sockets(&ifds);
+
+ } else if (n < 0) {
+ TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 0;
+ ckThreadEnd(NULL);
+ } else {
+ sleep(1);
+ }
+ }
+}
+#endif /* NT */
+#endif /* CK_FORWARD_X */
+#endif /* TNCODE */
+#endif /* NETCONN */
diff --git a/ckermit-8.0.211/ckcnet.h b/ckermit-8.0.211/ckcnet.h
new file mode 100644
index 0000000..c8d5576
--- /dev/null
+++ b/ckermit-8.0.211/ckcnet.h
@@ -0,0 +1,1441 @@
+/* ckcnet.h -- Symbol and macro definitions for C-Kermit network support */
+
+/*
+ Author: Frank da Cruz <fdc@columbia.edu>
+ Columbia University Academic Information Systems, New York City.
+
+ Copyright (C) 1985, 2004,
+ Trustees of Columbia University in the City of New York.
+ All rights reserved. See the C-Kermit COPYING.TXT file or the
+ copyright text in the ckcmai.c module for disclaimer and permissions.
+*/
+#ifndef CKCNET_H
+#define CKCNET_H
+
+/* Network types */
+
+#define NET_NONE 0 /* None */
+#define NET_TCPB 1 /* TCP/IP Berkeley (socket) */
+#define NET_TCPA 2 /* TCP/IP AT&T (streams) */
+#define NET_SX25 3 /* SUNOS SunLink X.25 */
+#define NET_DEC 4 /* DECnet */
+#define NET_VPSI 5 /* VAX PSI */
+#define NET_PIPE 6 /* LAN Manager Named Pipe */
+#define NET_VX25 7 /* Stratus VOS X.25 */
+#define NET_BIOS 8 /* IBM NetBios */
+#define NET_SLAT 9 /* Meridian Technologies' SuperLAT */
+#define NET_FILE 10 /* Read from a file */
+#define NET_CMD 11 /* Read from a sub-process */
+#define NET_DLL 12 /* Load a DLL for use as comm channel*/
+#define NET_IX25 13 /* IBM AIX 4.1 X.25 */
+#define NET_HX25 14 /* HP-UX 10 X.25 */
+#define NET_PTY 15 /* Pseudoterminal */
+#define NET_SSH 16 /* SSH */
+
+#ifdef OS2 /* In OS/2, only the 32-bit */
+#ifndef __32BIT__ /* version gets NETBIOS */
+#ifdef CK_NETBIOS
+#undef CK_NETBIOS
+#endif /* CK_NETBIOS */
+#endif /* __32BIT__ */
+#endif /* OS2 */
+
+#ifdef _M_PPC
+#ifdef SUPERLAT
+#undef SUPERLAT
+#endif /* SUPERLAT */
+#endif /* _M_PPC */
+
+#ifdef NPIPE /* For items in common to */
+#define NPIPEORBIOS /* Named Pipes and NETBIOS */
+#endif /* NPIPE */
+#ifdef CK_NETBIOS
+#ifndef NPIPEORBIOS
+#define NPIPEORBIOS
+#endif /* NPIPEORBIOS */
+#endif /* CK_NETBIOS */
+
+/* Network virtual terminal protocols (for SET HOST connections) */
+/* FTP, HTTP and SSH have their own stacks */
+
+#define NP_DEFAULT 255
+#define NP_NONE 0 /* None (async) */
+#define NP_TELNET 1 /* TCP/IP telnet */
+#define NP_VTP 2 /* ISO Virtual Terminal Protocol */
+#define NP_X3 3 /* CCITT X.3 */
+#define NP_X28 4 /* CCITT X.28 */
+#define NP_X29 5 /* CCITT X.29 */
+#define NP_RLOGIN 6 /* TCP/IP Remote login */
+#define NP_KERMIT 7 /* TCP/IP Kermit */
+#define NP_TCPRAW 8 /* TCP/IP Raw socket */
+#define NP_TCPUNK 9 /* TCP/IP Unknown */
+#define NP_SSL 10 /* TCP/IP SSLv23 */
+#define NP_TLS 11 /* TCP/IP TLSv1 */
+#define NP_SSL_TELNET 12 /* TCP/IP Telnet over SSLv23 */
+#define NP_TLS_TELNET 13 /* TCP/IP Telnet over TLSv1 */
+#define NP_K4LOGIN 14 /* TCP/IP Kerberized remote login */
+#define NP_EK4LOGIN 15 /* TCP/IP Encrypted Kerberized ... */
+#define NP_K5LOGIN 16 /* TCP/IP Kerberized remote login */
+#define NP_EK5LOGIN 17 /* TCP/IP Encrypted Kerberized ... */
+#define NP_K5U2U 18 /* TCP/IP Kerberos 5 User to User */
+#define NP_CTERM 19 /* DEC CTERM */
+#define NP_LAT 20 /* DEC LAT */
+/* others here... */
+
+#ifdef CK_SSL
+#define IS_TELNET() (nettype == NET_TCPB && (ttnproto == NP_TELNET \
+ || ttnproto == NP_SSL_TELNET \
+ || ttnproto == NP_TLS_TELNET \
+ || ttnproto == NP_KERMIT))
+#else /* CK_SSL */
+#define IS_TELNET() (nettype == NET_TCPB && (ttnproto == NP_TELNET \
+ || ttnproto == NP_KERMIT))
+#endif /* CK_SSL */
+
+#ifdef CK_KERBEROS
+#ifdef KRB5
+#ifdef KRB4
+#define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \
+ || ttnproto == NP_K5LOGIN \
+ || ttnproto == NP_EK5LOGIN \
+ || ttnproto == NP_K4LOGIN \
+ || ttnproto == NP_EK4LOGIN \
+ ))
+#else /* KRB4 */
+#define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \
+ || ttnproto == NP_K5LOGIN \
+ || ttnproto == NP_EK5LOGIN \
+ ))
+#endif /* KRB4 */
+#else /* KRB5 */
+#ifdef KRB4
+#define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \
+ || ttnproto == NP_K4LOGIN \
+ || ttnproto == NP_EK4LOGIN \
+ ))
+#else /* KRB4 */
+KERBEROS defined without either KRB4 or KRB5
+#endif /* KRB4 */
+#endif /* KRB5 */
+#else /* CK_KERBEROS */
+#define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN))
+#endif /* CK_KERBEROS */
+
+#define IS_SSH() (nettype == NET_SSH)
+
+/* RLOGIN Modes */
+#define RL_RAW 0 /* Do Not Process XON/XOFF */
+#define RL_COOKED 1 /* Do Process XON/XOFF */
+
+/* Encryption types */
+
+#define CX_NONE 999
+
+#ifdef ENCTYPE_ANY
+#define CX_AUTO ENCTYPE_ANY
+#else
+#define CX_AUTO 0
+#endif /* ENCTYPE_ANY */
+
+#ifdef ENCTYPE_DES_CFB64
+#define CX_DESC64 ENCTYPE_DES_CFB64
+#else
+#define CX_DESC64 1
+#endif /* ENCTYPE_DES_CFB64 */
+
+#ifdef ENCTYPE_DES_OFB64
+#define CX_DESO64 ENCTYPE_DES_OFB64
+#else
+#define CX_DESO64 2
+#endif /* ENCTYPE_DES_OFB64 */
+
+#ifdef ENCTYPE_DES3_CFB64
+#define CX_DES3C64 ENCTYPE_DES3_CFB64
+#else
+#define CX_DES3C64 3
+#endif /* ENCTYPE_DES_CFB64 */
+
+#ifdef ENCTYPE_DES3_OFB64
+#define CX_DESO64 ENCTYPE_DES3_OFB64
+#else
+#define CX_DES3O64 4
+#endif /* ENCTYPE_DES_OFB64 */
+
+#ifdef ENCTYPE_CAST5_40_CFB64
+#define CX_C540C64 ENCTYPE_CAST5_40_CFB64
+#else
+#define CX_C540C64 8
+#endif /* ENCTYPE_CAST5_40_CFB64 */
+
+#ifdef ENCTYPE_CAST5_40_OFB64
+#define CX_C540O64 ENCTYPE_CAST5_40_OFB64
+#else
+#define CX_C540O64 9
+#endif /* ENCTYPE_CAST5_40_OFB64 */
+
+#ifdef ENCTYPE_CAST128_CFB64
+#define CX_C128C64 ENCTYPE_CAST128_CFB64
+#else
+#define CX_C128C64 10
+#endif /* ENCTYPE_CAST128_CFB64 */
+
+#ifdef ENCTYPE_CAST128_OFB64
+#define CX_C128O64 ENCTYPE_CAST128_OFB64
+#else
+#define CX_C128O64 11
+#endif /* ENCTYPE_CAST128_OFB64 */
+
+/* Basic network function prototypes, common to all. */
+
+_PROTOTYP( int netopen, (char *, int *, int) );
+_PROTOTYP( int netclos, (void) );
+_PROTOTYP( int netflui, (void) );
+_PROTOTYP( int nettchk, (void) );
+_PROTOTYP( int netbreak, (void) );
+_PROTOTYP( int netinc, (int) );
+_PROTOTYP( int netxin, (int, CHAR *) );
+_PROTOTYP( int nettol, (CHAR *, int) );
+_PROTOTYP( int nettoc, (CHAR) );
+/*
+ SunLink X.25 support by Marcello Frutig, Catholic University,
+ Rio de Janeiro, Brazil, 1990.
+
+ Maybe this can be adapted to VAX PSI and other X.25 products too.
+*/
+#ifndef SUNOS4 /* Only valid for SUNOS4 */
+#ifndef SOLARIS
+#ifdef SUNX25
+#undef SUNX25
+#endif /* SUNX25 */
+#endif /* SOLARIS */
+#endif /* SUNOS4 */
+
+#ifdef STRATUSX25
+#define ANYX25
+#define MAX_USER_DATA 128 /* SUN defines this in a header file, I believe. */
+#endif /* STRATUSX25 */
+
+#ifdef SUNX25
+#define ANYX25
+#endif /* SUNX25 */
+
+#ifdef IBMX25 /* AIX 4.1 X.25 */
+#ifndef AIX41
+#undef IBMX25
+#else /* AIX41 */
+#define ANYX25
+#define MAX_USER_DATA NPI_MAX_DATA /* used for buffer sizes */
+#endif /* AIX41 */
+#endif /* IBMX25 */
+
+#ifdef HPX25 /* HP-UX 10.* X.25 */
+#ifndef HPUX10
+#undef HPX25
+#else /* HPUX10 */
+#define ANYX25
+#endif /* HPUX10 */
+#endif /* HPX25 */
+
+#ifdef ANYX25
+#ifndef NETCONN /* ANYX25 implies NETCONN */
+#define NETCONN
+#endif /* NETCONN */
+
+#define MAXPADPARMS 22 /* Number of PAD parameters */
+#define MAXCUDATA 12 /* Max length of X.25 call user data */
+#define X29PID 1 /* X.29 protocol ID */
+#define X29PIDLEN 4 /* X.29 protocol ID length */
+
+#define X29_SET_PARMS 2
+#define X29_READ_PARMS 4
+#define X29_SET_AND_READ_PARMS 6
+#define X29_INVITATION_TO_CLEAR 1
+#define X29_PARAMETER_INDICATION 0
+#define X29_INDICATION_OF_BREAK 3
+#define X29_ERROR 5
+
+#define INVALID_PAD_PARM 1
+
+#define PAD_BREAK_CHARACTER 0
+
+#define PAD_ESCAPE 1
+#define PAD_ECHO 2
+#define PAD_DATA_FORWARD_CHAR 3
+#define PAD_DATA_FORWARD_TIMEOUT 4
+#define PAD_FLOW_CONTROL_BY_PAD 5
+#define PAD_SUPPRESSION_OF_SIGNALS 6
+#define PAD_BREAK_ACTION 7
+#define PAD_SUPPRESSION_OF_DATA 8
+#define PAD_PADDING_AFTER_CR 9
+#define PAD_LINE_FOLDING 10
+#define PAD_LINE_SPEED 11
+#define PAD_FLOW_CONTROL_BY_USER 12
+#define PAD_LF_AFTER_CR 13
+#define PAD_PADDING_AFTER_LF 14
+#define PAD_EDITING 15
+#define PAD_CHAR_DELETE_CHAR 16
+#define PAD_BUFFER_DELETE_CHAR 17
+#define PAD_BUFFER_DISPLAY_CHAR 18
+
+#define MAXIX25 MAX_USER_DATA*7
+#define MAXOX25 MAX_USER_DATA
+#endif /* ANYX25 */
+
+#ifdef SUNX25
+#ifdef SOLARIS25 /* and presumably SunLink 9.xx */
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioccom.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sundev/syncstat.h>
+#include <netx25/x25_pk.h>
+#include <netx25/x25_ctl.h>
+#include <netx25/x25_ioctl.h>
+#else
+#include <sys/ioctl.h> /* X.25 includes, Sun only */
+#include <sys/systm.h>
+#ifndef SOLARIS
+#include <sys/mbuf.h>
+#endif /* SOLARIS */
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#ifdef SOLARIS
+#include <sys/sockio.h>
+#else
+#include <sys/domain.h>
+#endif /* SOLARIS */
+#include <sys/socketvar.h>
+#include <net/if.h>
+#include <sundev/syncstat.h>
+#include <netx25/x25_pk.h>
+#include <netx25/x25_ctl.h>
+#include <netx25/x25_ioctl.h>
+#endif /* SOLARIS25 */
+#endif /* SUNX25 */
+
+#ifdef ANYX25
+
+#ifdef IBMX25 /* X.25 includes, AIX only */
+#include <fcntl.h>
+#include <sys/twtypes.h>
+#include <sys/twlib.h>
+
+#include <sys/stream.h>
+#include <stropts.h>
+
+#define NPI_20 /* required to include the whole NPI */
+#include <sys/npi_20.h>
+#include <sys/npiapi.h>
+#include <sys/pktintf.h>
+
+#include <odmi.h> /* required for access to the ODM */
+#include <sys/cfgodm.h> /* database, needed to find out the */
+ /* local NUA. see x25local_nua() */
+
+
+/* IBM X25 NPI generic primitive type */
+typedef union N_npi_ctl_t {
+ ulong PRIM_type; /* generic primitive type */
+ char buffer[NPI_MAX_CTL]; /* maximum primitive size */
+ N_bind_ack_t bind_ack;
+ N_bind_req_t bind_req;
+ N_conn_con_t conn_con;
+ N_conn_ind_t conn_ind;
+ N_conn_req_t conn_req;
+ N_conn_res_t conn_res;
+ N_data_req_t data_req;
+ N_data_ind_t data_ind;
+ N_discon_ind_t discon_ind;
+ N_discon_req_t discon_req;
+ N_error_ack_t error_ack;
+ N_exdata_ind_t exdata_ind;
+ N_info_ack_t info_ack;
+ N_ok_ack_t ok_ack;
+ N_reset_con_t reset_con;
+ N_reset_req_t reset_req;
+ N_reset_ind_t reset_ind;
+} N_npi_ctl_t;
+
+/* some extra definitions to help out */
+typedef char x25addr_t[45]; /* max 40 defined by CCITT */
+typedef char N_npi_data_t[NPI_MAX_DATA];
+
+/* fd or server waiting for connections, used by netclos and netopen */
+extern int x25serverfd;
+
+#endif /* IBMX25 */
+
+#ifdef HPX25 /* X.25 includes, HP-UX only */
+#include <x25/ccittproto.h>
+#include <x25/x25.h>
+#include <x25/x25addrstr.h>
+#include <x25/x25codes.h>
+#include <x25/x25hd_ioctl.h>
+#include <x25/x25ioctls.h>
+#include <x25/x25str.h>
+#include <sys/ioctl.h>
+#endif /* HPX25 */
+
+/* C-Kermit X.3 / X.25 / X.29 / X.121 support functions */
+
+/* (riehm: this list of functions isn't quite right for AIX) */
+
+_PROTOTYP( int shopad, (int) );
+_PROTOTYP( int shox25, (int) );
+_PROTOTYP( VOID initpad, (void) );
+_PROTOTYP( VOID setpad, (CHAR *, int) );
+_PROTOTYP( VOID readpad, (CHAR *, int, CHAR *) );
+_PROTOTYP( int qbitpkt, (CHAR *, int) );
+_PROTOTYP( VOID setqbit, (void) );
+_PROTOTYP( VOID resetqbit, (void) );
+_PROTOTYP( VOID breakact, (void) );
+_PROTOTYP( int pkx121, (char *, CHAR *) );
+_PROTOTYP( SIGTYP x25oobh, (int) );
+_PROTOTYP( int x25diag, (void) );
+_PROTOTYP( int x25intr, (char) );
+_PROTOTYP( int x25reset, (char, char) );
+_PROTOTYP( int x25clear, (void) );
+_PROTOTYP( int x25stat, (void) );
+_PROTOTYP( int x25in, (int, CHAR *) );
+_PROTOTYP( int setpadp, (void) );
+_PROTOTYP( int setx25, (void) );
+_PROTOTYP( int x25xin, (int, CHAR *) );
+_PROTOTYP( int x25inl, (CHAR *, int, int, CHAR) );
+
+#ifdef IBMX25
+ /* setup x25 */
+_PROTOTYP( ulong x25bind, (int, char *, char *, int, int, int, ulong) );
+_PROTOTYP( int x25call, (int, char *, char *) ); /* connect to remote */
+_PROTOTYP( int x25unbind, (int) ); /* disconnect */
+_PROTOTYP( char *x25prim, (int) ); /* display primitives */
+_PROTOTYP( int x25local_nua, (char *) ); /* find local NUA */
+#endif /* IBMX25 */
+
+#endif /* ANYX25 */
+
+/* CMU-OpenVMS/IP */
+
+#ifdef CMU_TCPIP /* CMU_TCPIP implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef TCPIPLIB
+#define TCPIPLIB
+#endif /* TCPIPLIB */
+#endif /* CMU_TCPIP */
+
+/* DEC TCP/IP for (Open)VMS, previously known as UCX */
+
+#ifdef DEC_TCPIP /* DEC_TCPIP implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef TCPIPLIB
+#define TCPIPLIB
+#endif /* TCPIPLIB */
+#endif /* DEC_TCPIP */
+
+/* SRI/TGV/Cisco/Process MultiNet, TCP/IP for VAX/VMS */
+
+#ifdef MULTINET /* MULTINET implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef TCPIPLIB
+#define TCPIPLIB
+#endif /* TCPIPLIB */
+#ifndef TGVORWIN /* MULTINET and WINTCP */
+#define TGVORWIN /* share a lot of code... */
+#endif /* TGVORWIN */
+#endif /* MULTINET */
+
+/* Wollongong TCP/IP for VAX/VMS */
+
+#ifdef WINTCP /* WINTCP implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef TCPIPLIB
+#define TCPIPLIB
+#endif /* TCPIPLIB */
+#ifndef TGVORWIN /* WINTCP and MULTINET */
+#define TGVORWIN /* share a lot of code... */
+#endif /* TGVORWIN */
+#endif /* WINTCP */
+
+/* Wollongong TCP/IP for AT&T Sys V */
+
+#ifdef WOLLONGONG /* WOLLONGONG implies TCPSOCKET */
+#ifndef TCPSOCKET /* Don't confuse WOLLONGONG */
+#define TCPSOCKET /* (which is for UNIX) with */
+#endif /* TCPSOCKET */ /* WINTCP, which is for VMS! */
+#endif /* WOLLONGONG */
+
+#ifdef EXCELAN /* EXCELAN implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#endif /* EXCELAN */
+
+#ifdef INTERLAN /* INTERLAN implies TCPSOCKET */
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#endif /* INTERLAN */
+
+#ifdef BEBOX
+#ifndef TCPSOCKET
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#ifndef TCPIPLIB
+#define TCPIPLIB
+#endif /* TCPIPLIB */
+#define socket_errno h_errno
+#define socket_read(x,y,z) recv(x,y,sizeof(char),z)
+#define socket_write(x,y,z) send(x,y,sizeof(char),z)
+#define socket_ioctl ioctl
+#define socket_close(x) closesocket(x)
+#ifndef FIONBIO
+#define FIONBIO 2
+#endif /* FIONBIO */
+#ifndef FIONREAD
+#define FIONREAD 1
+#endif /* FIONREAD */
+#ifndef SIOCATMARK
+#define SIOCATMARK 3
+#endif /* SIOCATMARK */
+#endif /* BEBOX */
+
+#ifdef COMMENT /* no longer used but might come in handy again later... */
+/*
+ CK_READ0 can (and should) be defined if and only if:
+ (a) read(fd,&x,0) can be used harmlessly on a TCP/IP socket connection.
+ (b) read(fd,&x,0) returns 0 if the connection is up, -1 if it is down.
+*/
+#ifndef CK_READ0
+#ifdef TCPSOCKET
+#ifdef SUNOS41 /* It works in SunOS 4.1 */
+#define CK_READ0
+#else
+#ifdef NEXT /* and NeXTSTEP */
+#define CK_READ0
+#endif /* NEXT */
+#endif /* SUNOS41 */
+#endif /* TCPSOCKET */
+#endif /* CK_READ0 */
+#endif /* COMMENT */
+
+/* Telnet protocol */
+
+#ifdef TCPSOCKET /* TCPSOCKET implies TNCODE */
+#ifndef TNCODE /* Which means... */
+#define TNCODE /* Compile in telnet code */
+#endif /* TNCODE */
+
+/*
+ Platforms where we must call gethostname(buf,len) and then
+ gethostbyname(buf) to get local IP address, rather than calling
+ gethostbyname("").
+*/
+#ifndef CKGHNLHOST
+#ifdef datageneral
+#define CKGHNLHOST
+#else
+#ifdef SOLARIS
+#define CKGHNLHOST
+#else
+#ifdef SUNOS4
+#define CKGHNLHOST
+#else
+#ifdef UNIXWARE
+#define CKGHNLHOST
+#else
+#ifdef SINIX
+#define CKGHNLHOST
+#endif /* SINIX */
+#endif /* UNIXWARE */
+#endif /* SUNOS4 */
+#endif /* SOLARIS */
+#endif /* datageneral */
+#endif /* CKGHNLHOST */
+
+#ifndef RLOGCODE /* What about Rlogin? */
+#ifndef NORLOGIN
+/*
+ Rlogin can be enabled only for UNIX versions that have both SIGURG
+ (SCO doesn't) and CK_TTGWSIZ (OSF/1 doesn't), so we don't assume that
+ any others have these without verifying first. Not that it really makes
+ much difference since you can only use Rlogin if you are root...
+*/
+#ifdef SUNOS41
+#define RLOGCODE
+#else
+#ifdef SOLARIS
+#define RLOGCODE
+#else
+#ifdef HPUX9
+#define RLOGCODE
+#else
+#ifdef HPUX10
+#define RLOGCODE
+#else
+#ifdef OSF40
+#define RLOGCODE
+#else
+#ifdef NEXT
+#define RLOGCODE
+#else
+#ifdef AIX41
+#define RLOGCODE
+#else
+#ifdef UNIXWARE
+#define RLOGCODE
+#else
+#ifdef IRIX51
+#define RLOGCODE
+#else
+#ifdef IRIX60
+#define RLOGCODE
+#else
+#ifdef QNX
+#define RLOGCODE
+#else
+#ifdef __linux__
+#define RLOGCODE
+#else
+#ifdef BSD44
+#define RLOGCODE
+#endif /* BSD44 */
+#endif /* __linux__ */
+#endif /* QNX */
+#endif /* IRIX60 */
+#endif /* IRIX51 */
+#endif /* UNIXWARE */
+#endif /* AIX41 */
+#endif /* NEXT */
+#endif /* OSF40 */
+#endif /* HPUX10 */
+#endif /* HPUX9 */
+#endif /* SOLARIS */
+#endif /* SUNOS41 */
+#endif /* NORLOGIN */
+#ifdef VMS /* VMS */
+#define RLOGCODE
+#endif /* VMS */
+#endif /* RLOGCODE */
+#endif /* TCPSOCKET */
+
+#ifdef TNCODE
+/*
+ Telnet local-echo buffer, used for saving up user data that can't be
+ properly displayed and/or evaluated until pending Telnet negotiations are
+ complete. TTLEBUF is defined for platforms (like UNIX) where net i/o is
+ done by the same routines that do serial i/o (in which case the relevant
+ code goes into the ck?tio.c module, in the ttinc(), ttchk(), etc, routines);
+ NETLETBUF is defined for platforms (like VMS) that use different APIs for
+ network and serial i/o, and enables the copies of the same routines that
+ are in ckcnet.c.
+*/
+#ifndef TTLEBUF
+#ifdef UNIX
+#define TTLEBUF
+#else
+#ifdef datageneral
+#define TTLEBUF
+#endif /* datageneral */
+#endif /* UNIX */
+#endif /* TTLEBUF */
+
+#ifndef NETLEBUF
+#ifdef VMS
+#define NETLEBUF
+#endif /* VMS */
+#endif /* NETLEBUF */
+#endif /* TNCODE */
+
+#ifdef SUNX25 /* SUNX25 implies TCPSOCKET */
+#ifndef TCPSOCKET /* But doesn't imply TNCODE */
+#define TCPSOCKET
+#endif /* TCPSOCKET */
+#endif /* SUNX25 */
+
+#ifndef TCPSOCKET
+#ifndef NO_DNS_SRV
+#define NO_DNS_SRV
+#endif /* NO_DNS_SRV */
+#endif /* TCPSOCKET */
+
+/* This is another TCPSOCKET section... */
+
+#ifdef TCPSOCKET
+#ifndef NETCONN /* TCPSOCKET implies NETCONN */
+#define NETCONN
+#endif /* NETCONN */
+
+#ifndef NO_DNS_SRV
+#ifdef NOLOCAL
+#define NO_DNS_SRV
+#endif /* NOLOCAL */
+#ifdef OS2ONLY
+#define NO_DNS_SRV
+#endif /* OS2ONLY */
+#ifdef NT
+#ifdef _M_PPC
+#define NO_DNS_SRV
+#endif /* _M_DNS */
+#endif /* NO_DNS_SRV */
+#ifdef VMS
+#define NO_DNS_SRV
+#endif /* VMS */
+#ifdef STRATUS
+#define NO_DNS_SRV
+#endif /* STRATUS */
+#ifdef datageneral
+#define NO_DNS_SRV
+#endif /* datageneral */
+#ifdef ultrix
+#define NO_DNS_SRV
+#endif /* ultrix */
+#ifdef NEXT
+#define NO_DNS_SRV
+#endif /* NEXT */
+#endif /* NO_DNS_SRV */
+
+#ifndef CK_DNS_SRV /* Use DNS SRV records to determine */
+#ifndef NO_DNS_SRV /* host and ports */
+#define CK_DNS_SRV
+#endif /* NO_DNS_SRV */
+#endif /* CK_DNS_SRV */
+
+#ifndef NOLISTEN /* select() is required to support */
+#ifndef SELECT /* incoming connections. */
+#ifndef VMS
+#ifndef OS2
+#define NOLISTEN
+#endif /* OS2 */
+#endif /* VMS */
+#endif /* SELECT */
+#endif /* NOLISTEN */
+
+/* BSD sockets library header files */
+
+#ifdef VMS
+/*
+ Because bzero() and bcopy() are not portable among VMS versions,
+ or compilers, or TCP/IP products, etc.
+*/
+#ifndef bzero
+#define bzero(s,n) memset(s,0,n)
+#endif /* bzero */
+#ifndef bcopy
+#define bcopy(h,a,l) memcpy(a,h,l)
+#endif /* bcopy */
+#endif /* VMS */
+
+#ifdef UNIX /* UNIX section */
+
+#ifdef SVR4
+/*
+ These suggested by Rob Healey, rhealey@kas.helios.mn.org, to avoid
+ bugs in Berkeley compatibility library on Sys V R4 systems, but untested
+ by me (fdc). Remove this bit if it gives you trouble.
+ (Later corrected by Marc Boucher <mboucher@iro.umontreal.ca> because
+ bzero/bcopy are not argument-compatible with memset/memcpy|memmove.)
+*/
+#ifndef bzero
+#define bzero(s,n) memset(s,0,n)
+#endif
+#ifdef SOLARIS
+#ifdef SUNX25
+#undef bzero
+/*
+ WOULD YOU BELIEVE... That the Solaris X.25 /opt/SUNWcomm/lib/libsockx25
+ library references bzero, even though the use of bzero is forbidden in
+ Solaris? Look for the function definition in ckcnet.c.
+*/
+_PROTOTYP( void bzero, (char *, int) );
+#endif /* SUNX25 */
+#ifndef bcopy
+#define bcopy(h,a,l) memcpy(a,h,l)
+#endif
+#else
+#ifndef bcopy
+#define bcopy(h,a,l) memmove(a,h,l)
+#endif
+#endif /* SOLARIS */
+#else /* !SVR4 */
+#ifdef PTX /* Sequent DYNIX PTX 1.3 */
+#ifndef bzero
+#define bzero(s,n) memset(s,0,n)
+#endif
+#ifndef bcopy
+#define bcopy(h,a,l) memcpy(a,h,l)
+#endif
+#endif /* PTX */
+#endif /* SVR4 */
+
+#ifdef INTERLAN /* Racal-Interlan TCP/IP */
+#include <interlan/socket.h>
+#include <interlan/il_types.h>
+#include <interlan/telnet.h>
+#include <interlan/il_errno.h>
+#include <interlan/in.h>
+#include <interlan/telnet.h> /* Why twice ? ? ? */
+#else /* Not Interlan */
+#ifdef BEBOX
+#include <socket.h>
+#else /* Not BEBOX */ /* Normal BSD TCP/IP library */
+#ifdef COMMENT
+#ifndef HPUX
+#include <arpa/telnet.h>
+#endif /* HPUX */
+#endif /* COMMENT */
+#ifdef SCO234
+#include <sys/errno.tcp.h>
+#include <sys/types.tcp.h>
+#endif /* SCO234 */
+#include <sys/socket.h>
+#ifdef WOLLONGONG
+#include <sys/in.h>
+#else
+#include <netinet/in.h>
+#ifndef SV68R3V6 /* (maybe this should be SVR3 in general) */