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