From: Ian Beckwith Date: Wed, 12 May 2010 00:21:53 +0000 (+0100) Subject: applied 060_speeling.patch X-Git-Tag: 211-14~12 X-Git-Url: http://erislabs.net/gitweb/?p=ckermit.git;a=commitdiff_plain;h=cb562cea088d0e6858dfb4b30daf7ada8e9119b3 applied 060_speeling.patch --- diff --git a/.pc/060_speeling.patch/.timestamp b/.pc/060_speeling.patch/.timestamp new file mode 100644 index 0000000..e69de29 diff --git a/.pc/060_speeling.patch/ckcftp.c b/.pc/060_speeling.patch/ckcftp.c new file mode 100644 index 0000000..d8d9ddd --- /dev/null +++ b/.pc/060_speeling.patch/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 + Secure Endpoints Inc., New York City + Frank da Cruz , + 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) , E(BCDIC) , I(MAGE), L(OCAL) + 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 + ACCT + ALLO [ R ] + APPE + CDUP + CWD + DELE + FEAT + HELP [ ] + LANG [ ] + LIST [ ] + MKD + MLSD [ ] + MLST [ ] + MODE + NLST [ ] + NOOP + OPTS [ ] + PASS + PASV + PORT + PWD + QUIT + REIN + REST + RETR + RMD + RNFR + RNTO + SITE + SIZE + SMNT + STAT [ ] + STOR + STOU + STRU + SYST + TYPE + USER +*/ +#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 +#else /* CK_ANSIC */ +#include +#endif /* CK_ANSIC */ +#include +#ifdef OS2 +#ifdef OS2ONLY +#include +#endif /* OS2ONLY */ +#include "ckowin.h" +#include "ckocon.h" +#endif /* OS2 */ +#ifndef ZILOG +#ifdef NT +#include +#ifdef NTSIG +extern int TlsIndex; +#endif /* NTSIG */ +#else /* NT */ +#include +#endif /* NT */ +#else +#include +#endif /* ZILOG */ +#include "ckcsig.h" +#include +#include +#include +#ifndef NOTIMEH +#include +#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 +#endif /* SYSTIMEH */ +#endif /* NOSYSTIMEH */ +#ifndef NOSYSTIMEBH +#ifdef SYSTIMEBH +#include +#endif /* SYSTIMEBH */ +#endif /* NOSYSTIMEBH */ +#endif /* DCLTIMEVAL */ + +#include +#include +#include +#ifdef HAVE_STDLIB_H +#include +#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 +#else +#ifdef UTIMEH +#include +#define SYSUTIMEH +#endif /* UTIMEH */ +#endif /* SYSUTIMEH */ +#endif /* NOSETTIME */ + +#ifndef SCO_OSR504 +#ifdef SELECT_H +#include +#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 +/* + 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 +#include +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 .~~.", + " /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 .~~.", +#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 {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", , "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/.pc/060_speeling.patch/ckuus2.c b/.pc/060_speeling.patch/ckuus2.c new file mode 100644 index 0000000..1290763 --- /dev/null +++ b/.pc/060_speeling.patch/ckuus2.c @@ -0,0 +1,13838 @@ +#ifdef SSHTEST +#define SSHBUILTIN +#endif /* SSHTEST */ + +/* C K U U S 2 -- User interface strings & help text module for C-Kermit */ + +/* + Authors: + Frank da Cruz , + The Kermit Project, Columbia University, New York City + Jeffrey E Altman + Secure Endpoints Inc., 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. + + This module contains HELP command and other long text strings. + + IMPORTANT: Character string constants longer than about 250 are not portable. + Longer strings should be broken up into arrays of strings and accessed with + hmsga() rather than hmsg(). +*/ +#include "ckcsym.h" +#include "ckcdeb.h" +#include "ckcnet.h" +#include "ckcasc.h" +#include "ckcker.h" +#include "ckuusr.h" +#include "ckcxla.h" +#ifdef OS2 +#ifdef NT +#include +#else /* not NT */ +#define INCL_KBD +#ifdef OS2MOUSE +#define INCL_MOU +#endif /* OS2MOUSE */ +#define INCL_DOSMISC +#define INCL_DOSDEVICES +#include /* This pulls in a whole load of stuff */ +#undef COMMENT +#endif /* NT */ +#include "ckocon.h" +#include "ckokvb.h" +#include "ckokey.h" +#endif /* OS2 */ + +extern xx_strp xxstring; +extern char * ccntab[]; +/* + hlptok contains the string for which the user requested help. This is + useful for distinguishing synonyms, in case different help text is needed + depending on which synonym was given. +*/ +extern char * hlptok; + +#ifndef NOIKSD + extern int inserver; +#endif /* IKSD */ + +#ifndef NOICP +extern int cmflgs; + +#ifdef DCMDBUF +extern char *cmdbuf, *atmbuf; +#else +extern char cmdbuf[], atmbuf[]; +#endif /* DCMDBUF */ +#endif /* NOICP */ + +extern char *xarg0; +extern int nrmt, nprm, dfloc, local, parity, escape; +extern int turn, flow; +extern int binary, quiet, keep; +extern int success, xaskmore; +#ifdef OS2 +extern int tt_rows[], tt_cols[]; +#else /* OS2 */ +extern int tt_rows, tt_cols; +#endif /* OS2 */ +extern int cmd_rows, cmd_cols; + +extern long speed; +extern char *dftty, *versio, *ckxsys; +#ifndef NOHELP +extern char *helpfile; +#endif /* NOHELP */ +extern struct keytab prmtab[]; +#ifndef NOXFER +extern struct keytab remcmd[]; +#endif /* NOXFER */ + +#ifndef NOICP + +/* Interactive help strings */ + +/* Top-level HELP text. IMPORTANT: Also see tophlpi[] for IKSD. */ + +static char *tophlp[] = { +"Trustees of Columbia University in the City of New York.\n", + +#ifndef NOHELP +" Type EXIT to exit.", +#ifdef OS2 +" Type INTRO for a brief introduction to the Kermit Command screen.", +" Type LICENSE to see the Kermit 95 license.", +#else +" Type INTRO for a brief introduction to C-Kermit.", +" Type LICENSE to see the C-Kermit license.", +#endif /* OS2 */ +" Type HELP followed by a command name for help about a specific command.", +#ifndef NOPUSH +#ifdef UNIX +" Type MANUAL to access the C-Kermit manual page.", +#else +#ifdef VMS +" Type MANUAL to access the C-Kermit help topic.", +#else +#ifdef OS2 +" Type MANUAL to access the K95 manual.", +#else +" Type MANUAL to access the C-Kermit manual.", +#endif /* OS2 */ +#endif /* VMS */ +#endif /* UNIX */ +#endif /* NOPUSH */ +" Type NEWS for news about new features.", +" Type SUPPORT to learn how to get technical support.", +" Press ? (question mark) at the prompt, or anywhere within a command,", +" for a menu (context-sensitive help, menu on demand).", +#else +"Press ? for a list of commands; see documentation for detailed descriptions.", +#endif /* NOHELP */ + +#ifndef NOCMDL +#ifndef NOHELP +" ", +" Type HELP OPTIONS for help with command-line options.", +#endif /* NOHELP */ +#endif /* NOCMDL */ +" ", +#ifndef OS2 +#ifdef MAC +"Documentation for Command Window: \"Using C-Kermit\" by Frank da Cruz and", +"Christine M. Gianone, Digital Press, 1997, ISBN: 1-55558-164-1. To order,", +"call +1 212 854-3703 or +1 800 366-2665.", +#else +"DOCUMENTATION: \"Using C-Kermit\" by Frank da Cruz and Christine M. Gianone,", +"2nd Edition, Digital Press / Butterworth-Heinemann 1997, ISBN 1-55558-164-1,", +"plus supplements at http://www.columbia.edu/kermit/ckermit.html.", +#endif /* MAC */ +#endif /* OS2 */ +#ifdef MAC +" ", +"Also see the Mac Kermit Doc and Bwr files on the Mac Kermit diskette.\n", +#else +#ifdef HPUX10 +" ", +"See the files in /usr/share/lib/kermit/ for additional information.", +#endif /* HPUX10 */ +#endif /* MAC */ +"" +}; + +#ifndef NOIKSD +static char *tophlpi[] = { /* Top-level help for IKSD */ + +"Trustees of Columbia University in the City of New York.\n", + +#ifndef NOHELP +" Type INTRO for a brief introduction to Kermit commands.", +" Type VERSION for version and copyright information.", +" Type HELP followed by a command name for help about a specific command.", +" Type SUPPORT to learn how to get technical support.", +" Type LOGOUT (or EXIT) to log out.", +" Press ? (question mark) at the prompt, or anywhere within a command,", +" for a menu (context-sensitive help, menu on demand).", +#else +"Press ? for a list of commands; see documentation for detailed descriptions.", +#endif /* NOHELP */ +" ", +"DOCUMENTATION: \"Using C-Kermit\" by Frank da Cruz and Christine M. Gianone,", +"2nd Edition, Digital Press / Butterworth-Heinemann 1997, ISBN 1-55558-164-1.", +"To order: +1 212 854-3703 or +1 800 366-2665. More info at the Kermit", + +"Project website, http://www.columbia.edu/kermit/.", +"" +}; +#endif /* NOIKSD */ + +#ifndef NOHELP +char *newstxt[] = { +#ifdef OS2 +"Welcome to Kermit 95 2.1.3. Major new features include:", +#else +"Welcome to C-Kermit 8.0.206. Major new features include:", +#endif /* OS2 */ +#ifdef NT +#ifdef KUI +" . Runs in GUI window", +#else +" . GUI version available", +#endif /* KUI */ +#endif /* NT */ +#ifdef SSHBUILTIN +" . New built-in SSH v1 and v2 clients", +#endif /* SSHBUILTIN */ +#ifdef NEWFTP +" . A new built-in FTP client", +#endif /* NEWFTP */ +#ifndef NOHTTP +" . A new HTTP 1.1 client", +#endif /* NOHTTP */ +#ifdef TN_COMPORT +" . Telnet Com Port Option for dialing from Telnet modem servers", +#endif /* TN_COMPORT */ +" . File scanning for automatic text/binary determination", +#ifdef CKLEARN +#ifndef OS2 +" . Learned scripts", +#endif /* OS2 */ +#endif /* CKLEARN */ +#ifndef NOSPL +#ifndef NOSEXP +" . LISP-like S-Expressions and natural floating-point arithmetic", +#endif /* NOSEXP */ +" . Lots of script programming improvements", +#endif /* NOSPL */ +" . Performance improvements and bug fixes", +" ", +"Documentation:", +" 1. \"Using C-Kermit\", second edition (1997), current with C-Kermit 6.0.", +" 2. http://www.columbia.edu/kermit/ckermit70.html", +" which documents the new features of C-Kermit 7.0.", +" 3. http://www.columbia.edu/kermit/ckermit80.html", +" which documents the new features of C-Kermit 8.0.", +" ", +"If the release date shown by the VERSION command is long past, be sure to", +"check with the Kermit Project to see if there have been updates.", +"" +}; +#endif /* NOHELP */ + +#ifndef NOHELP +char *introtxt[] = { +#ifdef OS2 +"Welcome to K-95, Kermit communications software for:", +#else +#ifdef UNIX +#ifdef HPUX +"Welcome to HP-UX C-Kermit communications software for:", +#else +"Welcome to UNIX C-Kermit communications software for:", +#endif /* HPUX */ +#else +#ifdef VMS +"Welcome to VMS C-Kermit communications software for:", +#else +#ifdef VOS +"Welcome to VOS C-Kermit communications software for:", +#else +#ifdef MAC +"Welcome to Mac Kermit communications software for:", +#else +"Welcome to C-Kermit communications software for:", +#endif /* MAC */ +#endif /* VOS */ +#endif /* VMS */ +#endif /* UNIX */ +#endif /* OS2 */ +#ifndef NOXFER +" . Error-free and efficient file transfer", +#endif /* NOXFER */ +#ifndef NOLOCAL +#ifdef OS2 +" . VT320/220/102/100/52, ANSI, Wyse, Linux, Televideo, and other emulations", +#else +#ifdef MAC +" . VT220 terminal emulation", +#else +" . Terminal connection", +#endif /* MAC */ +#endif /* OS2 */ +#endif /* NOLOCAL */ +#ifndef NOSPL +" . Script programming", +#endif /* NOSPL */ +#ifndef NOICS +" . International character set conversion", +#endif /* NOICS */ +#ifndef NODIAL +#ifndef NOSPL +" . Numeric and alphanumeric paging", +#endif /* NOSPL */ +#endif /* NODIAL */ + +#ifndef NOLOCAL +" ", +"Supporting:", +" . Serial connections, direct or dialed.", +#ifndef NODIAL +" . Automatic modem dialing", +#endif /* NODIAL */ +#ifdef TCPSOCKET +" . TCP/IP network connections:", +#ifdef TNCODE +" - Telnet sessions", +#endif /* TNCODE */ +#ifdef SSHBUILTIN +" - SSH v1 and v2 connections", +#else +#ifdef ANYSSH +" - SSH connections via external agent", +#endif /* ANYSSH */ +#endif /* SSHBUILTIN */ +#ifdef RLOGCODE +" - Rlogin sessions", +#endif /* RLOGCODE */ +#ifdef NEWFTP +" - FTP sessions", +#endif /* NEWFTP */ +#ifdef CKHTTP +" - HTTP 1.1 sessions", +#endif /* CKHTTP */ +#ifdef IKSD +" - Internet Kermit Service", +#endif /* IKSD */ +#endif /* TCPSOCKET */ +#ifdef ANYX25 +" . X.25 network connections", +#endif /* ANYX25 */ +#ifdef OS2 +#ifdef DECNET +" . DECnet/PATHWORKS LAT Ethernet connections", +#endif /* DECNET */ +#ifdef SUPERLAT +" . Meridian Technologies' SuperLAT connections", +#endif /* SUPERLAT */ +#ifdef NPIPE +" . Named-pipe connections", +#endif /* NPIPE */ +#ifdef CK_NETBIOS +" . NETBIOS connections", +#endif /* CK_NETBIOS */ +#endif /* OS2 */ +#endif /* NOLOCAL */ + +" ", +"While typing commands, you may use the following special characters:", +" . DEL, RUBOUT, BACKSPACE, CTRL-H: Delete the most recent character typed.", +" . CTRL-W: Delete the most recent word typed.", +" . CTRL-U: Delete the current line.", +" . CTRL-R: Redisplay the current line.", + +#ifdef CK_RECALL +#ifdef OS2 +" . Uparrow: Command recall - go backwards in command recall buffer.", +" . Downarrow: Command recall - go forward in command recall buffer.", +#ifndef NOIKSD +" (Note: Arrow keys can be used only on the PC's physical keyboard.)", +#endif /* NOIKSD */ +#endif /* OS2 */ +" . CTRL-P: Command recall - go backwards in command recall buffer.", +" . CTRL-B: Command recall - same as Ctrl-P.", +" . CTRL-N: Command recall - go forward in command recall buffer.", +#endif /* CK_RECALL */ + +" . ? (question mark) Display a menu for the current command field." +, +" . ESC (or TAB) Attempt to complete the current field.", +" . \\ (backslash) include the following character literally", +#ifndef NOSPL +" or introduce a backslash code, variable, or function.", +#else +" or introduce a numeric backslash code.", +#endif /* NOSPL */ +" ", + +"IMPORTANT: Since backslash (\\) is Kermit's command-line escape character,", +"you must enter DOS, Windows, or OS/2 pathnames using either forward slash (/)" +, +"or double backslash (\\\\) as the directory separator in most contexts.", +"Examples: C:/TMP/README.TXT, C:\\\\TMP\\\\README.TXT.", +" ", + +"Command words other than filenames can be abbreviated in most contexts.", +" ", + +"Basic commands:", +" EXIT Exit from Kermit", +" HELP Request general help", +" HELP command Request help about the given command", +" TAKE Execute commands from a file", +" TYPE Display a file on your screen", +" ORIENTATION Explains directory structure", +" ", + +#ifndef NOXFER +"Commands for file transfer:", +" SEND Send files", +" RECEIVE Receive files", +" GET Get files from a Kermit server", +#ifdef CK_RESEND +" RESEND Recover an interrupted send", +" REGET Recover an interrupted get from a server", +#endif /* CK_RESEND */ +#ifndef NOSERVER +" SERVER Be a Kermit server", +#endif /* NOSERVER */ +" ", +"File-transfer speed selection:", +" FAST Use fast settings -- THIS IS THE DEFAULT", +" CAUTIOUS Use slower, more cautious settings", +" ROBUST Use extremely slow and cautious settings", +" ", +"File-transfer performance fine tuning:", +" SET RECEIVE PACKET-LENGTH Kermit packet size", +" SET WINDOW Number of sliding window slots", +" SET PREFIXING Amount of control-character prefixing", +#endif /* NOXFER */ + +#ifndef NOLOCAL +" ", +"To make a direct serial connection:", +#ifdef OS2 +#ifdef NT +#ifdef CK_TAPI +" SET PORT TAPI Select TAPI communication device", +#endif /* CK_TAPI */ +" SET PORT Select serial communication device", +#else +" SET PORT Select serial communication port or server", +#endif /* NT */ +#else +" SET LINE Select serial communication device", +#endif /* OS2 */ +" SET SPEED Select communication speed", +" SET PARITY Communications parity (if necessary)", +#ifdef CK_RTSCTS +" SET FLOW Communications flow control, such as RTS/CTS", +#else +" SET FLOW Communications flow control, such as XON/XOFF", +#endif /* CK_RTSCTS */ +" CONNECT Begin terminal connection", + +#ifndef NODIAL +" ", +"To dial out with a modem:", +" SET DIAL DIRECTORY Specify dialing directory file (optional)", +" SET DIAL COUNTRY-CODE Country you are dialing from (*)", +" SET DIAL AREA-CODE Area-code you are dialing from (*)", +" LOOKUP Lookup entries in your dialing directory (*)", +" SET MODEM TYPE Select modem type", +#ifdef OS2 +#ifdef NT +#ifdef CK_TAPI +" SET PORT TAPI Select TAPI communication device", +#endif /* CK_TAPI */ +" SET PORT Select serial communication device", +#else +" SET PORT Select serial communication port or server", +#endif /* NT */ +#else +" SET LINE Select serial communication device", +#endif /* OS2 */ +" SET SPEED Select communication speed", +" SET PARITY Communications parity (if necessary)", +" DIAL Dial the phone number", +" CONNECT Begin terminal connection", +" ", +#ifdef OS2 +"Further info: HELP DIAL, HELP SET MODEM, HELP SET PORT, HELP SET DIAL", +#else +"Further info: HELP DIAL, HELP SET MODEM, HELP SET LINE, HELP SET DIAL", +#endif /* OS2 */ +"(*) (For use with optional dialing directory)", +#endif /* NODIAL */ + +#ifdef NETCONN +" ", +"To make a network connection:", +#ifndef NODIAL +" SET NETWORK DIRECTORY Specify a network services directory (optional)", +" LOOKUP Lookup entries in your network directory", +#endif /* NODIAL */ +" SET NETWORK TYPE Select network type (if more than one available)", +" SET HOST Make a network connection but stay in command mode", +" CONNECT Begin terminal connection", +#ifdef TNCODE +" TELNET Select a Telnet host and CONNECT to it", +#endif /* TNCODE */ +#ifdef RLOGCODE +" RLOGIN Select an Rlogin host and CONNECT to it", +#endif /* RLOGCODE */ +#ifdef ANYSSH +" SSH [ OPEN ] Select an SSH host and CONNECT to it", +#endif /* ANYSSH */ +#ifdef NEWFTP +" FTP [ OPEN ] Make an FTP connection", +#endif /* NEWFTP */ +#ifdef CKHTTP +" HTTP OPEN Make an HTTP connection", +#endif /* CKHTTP */ +#endif /* NETCONN */ + +#ifdef NT +" ", +"To return from the terminal window to the K-95> prompt:", +#else +#ifdef OS2 +" ", +"To return from the terminal window to the K/2> prompt:", +#else +" ", +"To return from a terminal connection to the C-Kermit prompt:", +#endif /* OS2 */ +#endif /* NT */ +#ifdef OS2 +" \ +Press the key or key-combination shown after \"Command:\" in the status line", +" (such as Alt-x) or type your escape character followed by the letter C.", +#else +" Type your escape character followed by the letter C.", +#endif /* OS2 */ +" ", +"To display your escape character:", +" SHOW ESCAPE", +" ", +"To display other settings:", +" SHOW COMMUNICATIONS, SHOW TERMINAL, SHOW FILE, SHOW PROTOCOL, etc.", +#else /* !NOLOCAL */ +" ", +"To display settings:", +" SHOW COMMUNICATIONS, SHOW FILE, SHOW PROTOCOL, etc.", +#endif /* NOLOCAL */ +" ", +#ifdef OS2 +"For a Kermit 95 tutorial, visit:", +" http://www.columbia.edu/kermit/k95tutor.html", +" ", +#endif /* OS2 */ +"For a C-Kermit tutorial, visit:", +" http://www.columbia.edu/kermit/ckututor.html", +" ", +"To learn about script programming and automation:", +" Read the manual, \"Using C-Kermit\". For a brief tutorial, visit:", +" http://www.columbia.edu/kermit/ckscripts.html", +" ", +"For further information about a particular command, type HELP xxx,", +"where xxx is the name of the command. For documentation, news of new", +"releases, and information about other Kermit software, contact:", +" ", +" The Kermit Project E-mail: kermit@columbia.edu", +" Columbia University Web: http://www.columbia.edu/kermit/", +" 612 West 115th Street Voice: +1 (212) 854-3703", +" New York NY 10025-7799 Fax: +1 (212) 662-6442", +" USA", +"" +}; + +static char * hmxymatch[] = { +"SET MATCH { DOTFILE, FIFO } { ON, OFF }", +" Tells whether wildcards should match dotfiles (files whose names begin", +" with period) or UNIX FIFO special files. MATCH FIFO default is OFF.", +" MATCH DOTFILE default is OFF in UNIX, ON elsewhere.", +"" +}; + +#ifdef OS2 +#ifdef KUI +static char * hmxygui[] = { +"SET GUI DIALOGS { ON, OFF }", +" ON means that popups, alerts, use GUI dialogs; OFF means to use", +" text-mode popups or prompts. ON by default.", +" ", +"SET GUI FONT name size", +" Chooses the font and size. Type \"set gui font ?\" to see the list of", +" choices. The size can be a whole number or can contain a decimal point", +" and a fraction (which is rounded to the nearest half point).", +" ", +"SET GUI RGBCOLOR colorname redvalue greenvalue bluevalue", +" Specifies the red-green-blue mixture to be used to render the given", +" color name. Type \"set gui rgbcolor\" to see a list of colornames.", +" the RGB values are whole numbers from 0 to 255.", +" ", +"SET GUI WINDOW POSITION x y", +" Moves the K95 window to the given X,Y coordinates, pixels from top left.", +" (Not yet implemented -- use command-line options to do this.)", +" ", +"SET GUI WINDOW RESIZE-MODE { CHANGE-DIMENSIONS, SCALE-FONT }", +" Default is CHANGE-DIMENSIONS.", +"", +"SET GUI WINDOW RUN-MODE { MAXIMIZE, MINIMIZE, RESTORE }", +" Changes the run mode state of the GUI window.", +"" +}; +#endif /* KUI */ +#endif /* OS2 */ + +#ifdef ANYSSH +static char * hmxxssh[] = { +#ifdef SSHBUILTIN +"Syntax: SSH { ADD, AGENT, CLEAR, KEY, [ OPEN ], V2 } operands...", +" Performs an SSH-related action, depending on the keyword that follows:", +" ", +"SSH ADD LOCAL-PORT-FORWARD local-port host port", +" Adds a port forwarding triplet to the local port forwarding list.", +" The triplet specifies a local port to be forwarded and the hostname /", +" ip-address and port number to which the port should be forwarded from", +" the remote host. Port forwarding is activated at connection", +" establishment and continues until the connection is terminated.", +" ", +"SSH ADD REMOTE-PORT-FORWARD remote-port host port", +" Adds a port forwarding triplet to the remote port forwarding list.", +" The triplet specifies a remote port to be forwarded and the", +" hostname/ip-address and port number to which the port should be", +" forwarded from the local machine. Port forwarding is activated at", +" connection establishment and continues until the connection is", +" terminated.", +" ", +"SSH AGENT ADD [ identity-file ]", +" Adds the contents of the identity-file (if any) to the SSH AGENT", +" private key cache. If no identity-file is specified, all files", +" specified with SET SSH IDENTITY-FILE are added to the cache.", +" ", +"SSH AGENT DELETE [ identity-file ]", +" Deletes the contents of the identity-file (if any) from the SSH AGENT", +" private key cache. If no identity-file is specified, all files", +" specified with SET SSH IDENTITY-FILE are deleted from the cache.", +" ", +"SSH AGENT LIST [ /FINGERPRINT ]", +" Lists the contents of the SSH AGENT private key cache. If /FINGERPRINT", +" is specified, the fingerprint of the private keys are displayed instead", +" of the keys.", +" ", +"SSH CLEAR LOCAL-PORT-FORWARD", +" Clears the local port forwarding list.", +" ", +"SSH CLEAR REMOTE-PORT-FORWARD", +" Clears the remote port forwarding list.", +" ", +"SSH KEY commands:", +" The SSH KEY commands create and manage public and private key pairs", +" (identities). There are three forms of SSH keys. Each key pair is", +" stored in its own set of files:", +" ", +" Key Type Private Key File Public Key File", +" v1 RSA keys \\v(appdata)ssh/identity \\v(appdata)ssh/identity.pub", +" v2 RSA keys \\v(appdata)ssh/id_rsa \\v(appdata)ssh/id_rsa.pub", +" v2 DSA keys \\v(appdata)ssh/id_dsa \\v(appdata)ssh/id_dsa.pub", +" ", +" Keys are stored using the OpenSSH keyfile format. The private key", +" files can be (optionally) protected by specifying a passphrase. A", +" passphrase is a longer version of a password. English text provides", +" no more than 2 bits of key data per character. 56-bit keys can be", +" broken by a brute force attack in approximately 24 hours. When used,", +" private key files should therefore be protected by a passphrase of at", +" least 40 characters (about 80 bits).", +" ", +" To install a public key file on the host, you must transfer the file", +" to the host and append it to your \"authorized_keys\" file. The file", +" permissions must be 600 (or equivalent).", +" ", +"SSH KEY CHANGE-PASSPHRASE [ /NEW-PASSPHRASE:passphrase", +" /OLD-PASSPHRASE:passphrase ] filename", +" This re-encrypts the specified private key file with a new passphrase.", +" The old passphrase is required. If the passphrases (and filename) are", +" not provided Kermit prompts your for them.", +" ", +"SSH KEY CREATE [ /BITS:bits /PASSPHRASE:passphrase", +" /TYPE:{ V1-RSA, V2-DSA, V2-RSA } /V1-RSA-COMMENT:comment ] filename", +" This command creates a new private/public key pair. The defaults are:", +" BITS:1024 and TYPE:V2-RSA. The filename is the name of the private", +" key file. The public key is created with the same name with .pub", +" appended to it. If a filename is not specified Kermit prompts you for", +" it. V1 RSA key files may have an optional comment, which is ignored", +" for other key types.", +" ", +"SSH KEY DISPLAY [ /FORMAT:{FINGERPRINT,IETF,OPENSSH,SSH.COM} ] filename", +" This command displays the contents of a public or private key file.", +" The default format is OPENSSH.", +" ", +"SSH KEY V1 SET-COMMENT filename comment", +" This command replaces the comment associated with a V1 RSA key file.", +" ", +"SSH [ OPEN ] host [ port ] [ /COMMAND:command /USER:username", +" /PASSWORD:pwd /VERSION:{ 1, 2 } /X11-FORWARDING:{ ON, OFF } ]", +" This command establishes a new connection using SSH version 1 or", +" version 2 protocol. The connection is made to the specified host on", +" the SSH port (you can override the port by including a port name or", +" number after the host name). Once the connection is established the", +" authentication negotiations begin. If the authentication is accepted,", +" the local and remote port forwarding lists are used to establish the", +" desired connections. If X11 Forwarding is active, this results in a", +" remote port forwarding between the X11 clients on the remote host and", +" X11 Server on the local machine. If a /COMMAND is provided, the", +" command is executed on the remote host in place of your default shell.", +" ", +" An example of a /COMMAND to execute C-Kermit in SERVER mode is:", +" SSH OPEN hostname /COMMAND:{kermit -x -l 0}", +" ", +"SSH V2 REKEY", +" Requests that an existing SSH V2 connection generate new session keys.", +#else /* SSHBUILTIN */ +"Syntax: SSH [ options ] [ command ]", +" Makes an SSH connection using the external ssh program via the SET SSH", +" COMMAND string, which is \"ssh -e none\" by default. Options for the", +" external ssh program may be included. If the hostname is followed by a", +" command, the command is executed on the host instead of an interactive", +" shell.", +#endif /* SSHBUILTIN */ +"" +}; + +static char *hmxyssh[] = { +#ifdef SSHBUILTIN +"SET SSH AGENT-FORWARDING { ON, OFF }", +" If an authentication agent is in use, setting this value to ON", +" results in the connection to the agent being forwarded to the remote", +" computer. The default is OFF.", +" ", +"SET SSH CHECK-HOST-IP { ON, OFF }", +" Specifies whether the remote host's ip-address should be checked", +" against the matching host key in the known_hosts file. This can be", +" used to determine if the host key changed as a result of DNS spoofing.", +" The default is ON.", +" ", +"SET SSH COMPRESSION { ON, OFF }", +" Specifies whether compression will be used. The default is ON.", +" ", +"SET SSH DYNAMIC-FORWARDING { ON, OFF }", +" Specifies whether Kermit is to act as a SOCKS4 service on port 1080", +" when connected to a remote host via SSH. When Kermit acts as a SOCKS4", +" service, it accepts connection requests and forwards the connections", +" through the remote host. The default is OFF.", +" ", +"SET SSH GATEWAY-PORTS { ON, OFF }", +" Specifies whether Kermit should act as a gateway for forwarded", +" connections received from the remote host. The default is OFF.", +" ", +"SET SSH GSSAPI DELEGATE-CREDENTIALS { ON, OFF }", +" Specifies whether Kermit should delegate GSSAPI credentials to ", +" the remote host after authentication. Delegating credentials allows", +" the credentials to be used from the remote host. The default is OFF.", +" ", +"SET SSH HEARTBEAT-INTERVAL ", +" Specifies a number of seconds of idle time after which an IGNORE", +" message will be sent to the server. This pulse is useful for", +" maintaining connections through HTTP Proxy servers and Network", +" Address Translators. The default is OFF (0 seconds).", +" ", +"SET SSH IDENTITY-FILE filename [ filename [ ... ] ]", +" Specifies one or more files from which the user's authorization", +" identities (private keys) are to be read when using public key", +" authorization. These are files used in addition to the default files:", +" ", +" \\v(appdata)ssh/identity V1 RSA", +" \\v(appdata)ssh/id_rsa V2 RSA", +" \\v(appdata)ssh/id_dsa V2 DSA", +" ", +"SET SSH KERBEROS4 TGT-PASSING { ON, OFF }", +" Specifies whether Kermit should forward Kerberos 4 TGTs to the host.", +" The default is OFF.", +" ", +"SET SSH KERBEROS5 TGT-PASSING { ON, OFF }", +" Specifies whether Kermit should forward Kerberos 5 TGTs to to the", +" host. The default is OFF.", +" ", +"SET SSH PRIVILEGED-PORT { ON, OFF }", +" Specifies whether a privileged port (less than 1024) should be used", +" when connecting to the host. Privileged ports are not required except", +" when using SSH V1 with Rhosts or RhostsRSA authorization. The default", +" is OFF.", +" ", +"SET SSH QUIET { ON, OFF }", +" Specifies whether all messages generated in conjunction with SSH", +" protocols should be suppressed. The default is OFF.", +" ", +"SET SSH STRICT-HOST-KEY-CHECK { ASK, ON, OFF }", +" Specifies how Kermit should behave if the the host key check fails.", +" When strict host key checking is OFF, the new host key is added to the", +" protocol-version-specific user-known-hosts-file. When strict host key", +" checking is ON, the new host key is refused and the connection is", +" dropped. When set to ASK, Kermit prompt you to say whether the new", +" host key should be accepted. The default is ASK.", +" ", +" Strict host key checking protects you against Trojan horse attacks.", +" It depends on you to maintain the contents of the known-hosts-file", +" with current and trusted host keys.", +" ", +"SET SSH USE-OPENSSH-CONFIG { ON, OFF }", +" Specifies whether Kermit should parse an OpenSSH configuration file", +" after applying Kermit's SET SSH commands. The configuration file", +" would be located at \\v(home)ssh/ssh_config. The default is OFF.", +" ", +"SET SSH V1 CIPHER { 3DES, BLOWFISH, DES }", +" Specifies which cipher should be used to protect SSH version 1", +" connections. The default is 3DES.", +" ", +"SET SSH V1 GLOBAL-KNOWN-HOSTS-FILE filename", +" Specifies the location of the system-wide known-hosts file. The", +" default is:", +" ", +" \v(common)ssh_known_hosts", +" ", +"SET SSH V1 USER-KNOWN-HOSTS-FILE filename", +" Specifies the location of the user-known-hosts-file. The default", +" location is:", +" ", +" \\v(appdata)ssh/known_hosts", +" ", +"SET SSH V2 AUTHENTICATION { EXTERNAL-KEYX, GSSAPI, HOSTBASED, ", +" KEYBOARD-INTERACTIVE, PASSWORD, PUBKEY, SRP-GEX-SHA1 } [ ... ]", +" Specifies an ordered list of SSH version 2 authentication methods to", +" be used when connecting to the remote host. The default list is:", +" ", +" external-keyx gssapi hostbased publickey srp-gex-sha1 publickey", +" keyboard-interactive password none", +" ", +"SET SSH V2 AUTO-REKEY { ON, OFF }", +" Specifies whether Kermit automatically issues rekeying requests", +" once an hour when SSH version 2 in in use. The default is ON.", +" ", +"SET SSH V2 CIPHERS { 3DES-CBC, AES128-CBC AES192-CBC AES256-CBC", +" ARCFOUR BLOWFISH-CBC CAST128-CBC RIJNDAEL128-CBC RIJNDAEL192-CBC", +" RIJNDAEL256-CBC }", +" Specifies an ordered list of SSH version ciphers to be used to encrypt", +" the established connection. The default list is:", +" ", +" aes128-cbc 3des-cbc blowfish-cbc cast128-cbc arcfour aes192-cbc", +" aes256-cbc", +" ", +" \"rijndael\" is an alias for \"aes\".", +" ", +"SET SSH V2 GLOBAL-KNOWN-HOSTS-FILE filename", +" Specifies the location of the system-wide known-hosts file. The default", +" location is:", +" ", +" \\v(common)ssh/known_hosts2", +" ", +"SET SSH V2 HOSTKEY-ALGORITHMS { SSH-DSS, SSH-RSA }", +" Specifies an ordered list of hostkey algorithms to be used to verify", +" the identity of the host. The default list is", +" ", +" ssh-rsa ssh-dss", +" ", +"SET SSH V2 MACS { HMAC-MD5 HMAC-MD5-96 HMAC-RIPEMD160 HMAC-SHA1", +" HMAC-SHA1-96 }", +" Specifies an ordered list of Message Authentication Code algorithms to", +" be used for integrity protection of the established connection. The", +" default list is:", +" ", +" hmac-md5 hmac-sha1 hmac-ripemd160 hmac-sha1-96 hmac-md5-96", +" ", +"SET SSH V2 USER-KNOWN-HOSTS-FILE filename", +" Specifies the location of the user-known-hosts file. The default", +" location is:", +" ", +" \\v(appdata)ssh/known_hosts2", +" ", +"SET SSH VERBOSE level", +" Specifies how many messages should be generated by the OpenSSH engine.", +" The level can range from 0 to 7. The default value is 2.", +" ", +"SET SSH VERSION { 1, 2, AUTOMATIC }", +" Specifies which SSH version should be negotiated. The default is", +" AUTOMATIC which means use version 2 if supported; otherwise to fall", +" back to version 1.", +" ", +"SET SSH X11-FORWARDING { ON, OFF }", +" Specifies whether X Windows System Data is to be forwarded across the", +" established SSH connection. The default is OFF. When ON, the DISPLAY", +" value is either set using the SET TELNET ENV DISPLAY command or read", +" from the DISPLAY environment variable.", +" ", +"SET SSH XAUTH-LOCATION filename", +" Specifies the location of the xauth executable (if provided with the", +" X11 Server software.)", +#else /* SSHBUILTIN */ +"Syntax: SET SSH COMMAND command", +" Specifies the external command to be used to make an SSH connection.", +" By default it is \"ssh -e none\" (ssh with no escape character).", +#endif /* SSHBUILTIN */ +"" +}; +#endif /* ANYSSH */ + +#ifdef NEWFTP +static char *hmxygpr[] = { +"Syntax: SET GET-PUT-REMOTE { AUTO, FTP, KERMIT}", +" Tells Kermit whether GET, PUT, and REMOTE commands should be directed", +" at a Kermit server or an FTP server. The default is AUTO, meaning that", +" if you have only one active connection, the appropriate action is taken", +" when you give a GET, PUT, or REMOTE command. SET GET-PUT-REMOTE FTP forces" +, +" Kermit to treat GET, PUT, and REMOTE as FTP client commands; setting this", +" to KERMIT forces these commands to be treated as Kermit client commands.", +" NOTE: PUT includes SEND, MPUT, MSEND, and all other similar commands.", +" Also see HELP REMOTE, HELP SET LOCUS, HELP FTP.", +"" +}; +#endif /* NEWFTP */ + +#ifdef LOCUS +static char *hmxylocus[] = { +#ifdef KUI +"Syntax: SET LOCUS { ASK, AUTO, LOCAL, REMOTE }", +#else +"Syntax: SET LOCUS { AUTO, LOCAL, REMOTE }", +#endif /* KUI */ +" Specifies whether unprefixed file management commands should operate", +" locally or (when there is a connection to a remote FTP or Kermit", +" server) sent to the server. The affected commands are: CD (CWD), PWD,", +" CDUP, DIRECTORY, DELETE, RENAME, MKDIR, and RMDIR. To force any of", +" these commands to be executed locally, give it an L-prefix: LCD, LDIR,", +" etc. To force remote execution, use the R-prefix: RCD, RDIR, and so", +" on. SHOW COMMAND shows the current Locus.", +" ", +" By default, the Locus for file management commands is switched", +" automatically whenever you make or close a connection: if you make an", +" FTP connection, the Locus becomes REMOTE; if you close an FTP connection", +" or make any other kind of connection, the Locus becomes LOCAL.", +#ifdef KUI +" ", +" There are two kinds of automatic switching: ASK (the default) which", +" asks you if it's OK to switch, and AUTO, which switches without asking.", +#endif /* KUI */ +" ", +" If you give a SET LOCUS LOCAL or SET LOCUS REMOTE command, this sets", +" the locus as indicated and disables automatic switching.", +#ifdef KUI +" SET LOCUS AUTO or SET LOCUS ASK restores automatic switching.", +" You can also change Locus switching and behavior in the Actions menu.", +#else +" SET LOCUS AUTO restores automatic switching.", +#endif /* KUI */ +"", +}; +#endif /* LOCUS */ + +static char *hmxxtak[] = { +"Syntax: TAKE filename [ arguments ]", +" Tells Kermit to execute commands from the named file. Optional argument", +" words, are automatically assigned to the macro argument variables \\%1", +" through \\%9. Kermit command files may themselves contain TAKE commands,", +" up to any reasonable depth of nesting.", +"" +}; + +#ifdef TCPSOCKET +static char *hmxxfirew[] = { +#ifdef OS2 +"Firewall Traversal in Kermit 95", +#else +"Firewall Traversal in C-Kermit", +#endif +" ", +#ifndef NEWFTP +#ifndef CKHTTP +#ifndef CK_SOCKS +#define NOFIREWALL +#endif +#endif +#endif +#ifdef NOFIREWALL +"This version of Kermit was built with no support for firewall traversal", +"protocols. Kermit can be built with support for HTTP Proxy Servers,", +"SOCKS authorized firewall traversal, and FTP Passive connection modes.", +" ", +#else /* NOFIREWALL */ +#ifdef CKHTTP +"The simplist form of firewall traversal is the HTTP CONNECT command. The", +"CONNECT command was implemented to allow a public web server which usually", +"resides on the boundary between the public and private networks to forward", +"HTTP requests from clients on the private network to public web sites. To", +"allow secure web connections, the HTTP CONNECT command authenticates the", +"client with a username/password and then establishes a tunnel to the", +"desired host.", + +" ", + +"Web servers that support the CONNECT command can be configured to allow", +"outbound connections for authenticated users to any TCP/IP hostname-port", +"combination accessible to the Web server. HTTP CONNECT can be used only", +"with TCP-based protocols. Protocols such as Kerberos authentication that", +"use UDP/IP cannot be tunneled using HTTP CONNECT.", + +" ", + +"SET TCP HTTP-PROXY [switches] [[:]]", +" If a hostname or ip-address is specified, Kermit uses the given", +" proxy server when attempting outgoing TCP connections. If no hostnamer", +" or ip-address is specified, any previously specified Proxy server is", +" removed. If no port number is specified, the \"http\" service is used.", +" [switches] can be one or more of:", +" /AGENT: /USER: /PASSWORD:", +" Switch parameters are used when connecting to the proxy server and", +" override any other values associated with the connection.", +" ", + +#endif /* CKHTTP */ +#ifdef CK_SOCKS + +"In the early 1990s as firewalls were becoming prevalent, David Koblas", +"developed the SOCKS protocol for TCP/IP firewall traversal. Two versions", +"of SOCKS are currently in use: Version 4.2 lets TCP/IP client applications", +"traverse firewalls, similar to HTTP CONNECT, except that the SOCKS client", +"is aware of the public source IP address and port, which can be used within", +"the application protocol to assist in securing the connection (e.g. FTP", +"sessions secured with GSSAPI Kerberos 5).", + +" ", + +"In 1995 the IETF issued SOCKS Protocol Version 5 (RFC 1928), which is", +"significantly more general than version 4. Besides supporting client-", +"to-server TCP/IP connections, it also includes:", + +" ", +" . Authenticated firewall traversal of UDP/IP packets.", +" . Authenticated binding of incoming public ports on the firewall.", +" ", + +"This lets a service on the private network offer public services. It also", +"lets client applications like FTP establish a temporary public presence", +"that can be used by the FTP server to create a data channel. By allowing", +"the client to bind to a public port on the firewall and be aware of the", +"public address, SOCKS 5 lets the application protocol communicate this", +"information to the server.", + +" ", + +#ifdef OS2 +#ifdef NT +"Kermit 95 supports SOCKS 4.2. The SOCKS Server is specified with:", +" ", +" SET TCP SOCKS-SERVER hostname/ip-address", +" ", +"The SOCKS.CONF file is found by examining the ETC environment variable;", +"searching in \\WINDOWS on Windows 95/98/ME; or the", +"\\WINDOWS\\SYSTEM\\DRIVERS\\ETC directory on NT\\2000\\XP systems.", + +#else /* NT */ + +"Kermit/2 provides support for SOCKS 4.2 servers when using IBM TCP/IP 2.0,", +"IBM OS/2 WARP, or a compatible protocol stack. SOCKS is one popular means", +"of implementing a firewall between a private network and the Internet.", +" ", +"Kermit/2 shares the same SOCKS environment variables as IBM Gopher. It also", +"supports the use of local SOCKS configuration files.", +" ", +"To specify the default SOCKS Server, add SET SOCKS_SERVER= to your", +"CONFIG.SYS file.", +" ", +"If you must use a SOCKS Distributed Name Server, add SET SOCKS_NS= to your", +"CONFIG.SYS file.", +" ", + +"If you must use a specific with your SOCKS server, be sure to add SET USER=", +"to your CONFIG.SYS file. Otherwise, \"os2user\" is used by default.", + +" ", + +"The SOCKS configuration file must be placed in the directory pointed to by", +"the ETC environment variable as declared in your CONFIG.SYS file. The name", +"should be SOCKS.CONF. On a FAT file system, use SOCKS.CNF.", + +" ", +"The format of the lines in the SOCKS configuration file are as follows:", +" ", +" . # comments", +" . deny [*=userlist] dst_addr dst_mask [op port]", +" . direct [*=userlist] dst_addr dst_mask [op port]", +" . sockd [@=serverlist] [*=userlist] dst_addr dst_mask [op port]", +" ", + +"op must be one of 'eq', 'neq', 'lt', 'gt', 'le', or 'ge'. dst_addr,", +"dst_mask, and port may be either numeric or name equivalents.", + +" ", + +"Kermit/2 ignores the [*=userlist] and [@=serverlist] fields. Matches are", +"determined on a first match not a best match basis. Addresses for which no", +"match is found default to \"sockd\".", + +" ", + +"For completeness: Fields in square brackets are optional. The optional", +"@=serverlist field with a 'sockd' line specifies the list of SOCKS servers", +"the client should try (in the given order) instead of the default SOCKS", +"server. If the @=serverlist part is omitted, then the default SOCKS server", +"is used. Commas are used in the userlist and serverlist as separators, no", +"white spaces are allowed.", + +#endif /* NT */ + +" ", + +#else /* OS2 */ +#ifdef CK_SOCKS5 +"This version of C-Kermit supports SOCKS version 5.", +#else /* CK_SOCKS5 */ +"This version of C-Kermit supports SOCKS version 4.", +#endif /* CK_SOCKS5 */ + +"See the man page (or other system documentation) for information on", +"configuring the SOCKS library via the /etc/socks.conf file.", + +#endif /* OS2 */ +" ", +#endif /* CK_SOCKS */ + +#ifdef NEWFTP + +"FTP is one of the few well-known Internet services that requires", +"multiple connections. As described above, FTP originally required the", +"server to establish the data connection to the client using a destination", +"address and port provided by the client. This doesn't work with port", +"filtering firewalls.", + +" ", + +"Later, FTP protocol added a \"passive\" mode, in which connections for", +"the data channels are created in the reverse direction. Instead of the", +"server establishing a connection to the client, the client makes a second", +"connection with the server as the destination. This works just fine as", +"long as the client is behind the firewall and the server is in public", +"address space. If the server is behind a firewall then the traditional", +"active mode must be used. If both the client and server are behind their", +"own port filtering firewalls then data channels cannot be established.", + +" ", + +"In Kermit's FTP client, passive mode is controlled with the command:", + +" ", +" SET FTP PASSIVE-MODE { ON, OFF }", +" ", + +"The default is ON, meaning to use passive mode.", + +#endif /* NEWFTP */ +#endif /* NOFIREWALL */ + +"" +}; +#endif /* TCPSOCKET */ + +static char *hmxxsave[] = { +"Syntax: SAVE item filename { NEW, APPEND }", +" Saves the requested material in the given file. A new file is created", +" by default; include APPEND at the end of the command to append to an", +" existing file. Items:", +#ifndef NOSETKEY +" KEYMAP Saves the current key settings.", +#endif /* NOSETKEY */ +#ifdef CK_RECALL +" COMMAND HISTORY Saves the current command recall (history) buffer", +#endif /* CK_RECALL */ +#ifdef OS2 +" COMMAND SCROLLBACK Saves the current command-screen scrollback buffer", +" TERMINAL SCROLLBACK Saves the current terminal-screen scrollback buffer", +#endif /* OS2 */ +"" +}; + +#ifdef CKROOT +static char *hmxxchroot[] = { +"Syntax: SET ROOT directoryname", +" Sets the root for file access to the given directory and disables access", +" to system and shell commands and external programs. Once this command", +" is given, no files or directories outside the tree rooted by the given", +" directory can be opened, read, listed, deleted, renamed, or accessed in", +" any other way. This command can not be undone by a subsequent SET ROOT", +" command. Primarily for use with server mode, to restrict access of", +" clients to a particular directory tree. Synonym: CHROOT.", +"" +}; +#endif /* CKROOT */ + +static char *hmxxscrn[] = { +"Syntax: SCREEN { CLEAR, CLEOL, MOVE row column }", +#ifdef OS2 +" Performs screen-formatting actions.", +#else +" Performs screen-formatting actions. Correct operation of these commands", +" depends on proper terminal setup on both ends of the connection -- mainly", +" that the host terminal type is set to agree with the kind of terminal or", +" the emulation you are viewing C-Kermit through.", +#endif /* OS2 */ +" ", +"SCREEN CLEAR", +" Moves the cursor to home position and clears the entire screen.", +#ifdef OS2 +" Synonyms: CLS, CLEAR SCREEN, CLEAR COMMAND-SCREEN ALL", +#else +" Synonyms: CLS, CLEAR SCREEN.", +#endif /* OS2 */ +" ", +"SCREEN CLEOL", +" Clears from the current cursor position to the end of the line.", +#ifdef OS2 +" Synonym: CLEAR COMMAND-SCREEN EOL", +#endif /* OS2 */ +" ", +"SCREEN MOVE row column", +" Moves the cursor to the indicated row and column. The row and column", +" numbers are 1-based so on a 24x80 screen, the home position is 1 1 and", +" the lower right corner is 24 80. If a row or column number is given that", +" too large for what Kermit or the operating system thinks is your screen", +" size, the appropriate number is substituted.", +" ", +"Also see:", +#ifdef OS2 +" HELP FUNCTION SCRNCURX, HELP FUNCTION SCRNCURY, HELP FUNCTION SCRSTR,", +#endif /* OS2 */ +" SHOW VARIABLE TERMINAL, SHOW VARIABLE COLS, SHOW VAR ROWS, SHOW COMMAND.", +"" +}; + +#ifndef NOSPL +static char *hmfword[] = { +"\\fword(s1,n1,s2,s3,n2,n3) - Extract word from string.", +" s1 = source string", +" n1 = word number (1-based)", +" s2 = optional break set.", +" s3 = optional include set.", +" n2 = optional grouping mask.", +" n3 = optional separator flag:", +" 0 = collapse adjacent separators", +" 1 = don't collapse adjacent separators.", +" ", +" Default break set is all characters except ASCII letters and digits.", +" ASCII (C0) control characters are always treated as break characters.", +" Default include set is null.", +" ", +" If grouping mask given and nonzero, words can be grouped by quotes or", +" brackets selected by the sum of the following:", +" ", +" 1 = doublequotes: \"a b c\"", +" 2 = braces: {a b c}", +" 4 = apostrophes: 'a b c'", +" 8 = parentheses: (a b c)", +" 16 = square brackets: [a b c]", +" 32 = angle brackets: ", +" ", +" Nesting is possible with {}()[]<> but not with quotes or apostrophes.", +" ", +"Returns string:", +" Word number n, if there is one, otherwise an empty string.", +"" +}; + +static char *hmxxprompt[] = { +"Syntax: PROMPT [ text ]", +" Enters interactive command level from within a script in such a way that", +" the script can be continued with an END or RETURN command. STOP, EXIT,", +" SHOW STACK, TRACE, and Ctrl-C all have their normal effects. The PROMPT", +" command allows variables to be examined or changed, or any other commands", +" to be given, in any number, prior to returning to the script, allowing", +" Kermit to serve as its own debugger; adding the PROMPT command to a script", +" is like setting a breakpoint. If the optional text is included, it is", +" used as the new prompt for this level, e.g. \"prompt Breakpoint_1>\".", +"" +}; + +static char *hxxinp[] = { +"Syntax: INPUT [ /NOMATCH ] { number-of-seconds, time-of-day } [ text ]", +"Example: INPUT 5 Login: or INPUT 23:59:59 RING", +" Waits up to the given number of seconds, or until the given time of day,", +" for the given text to arrive on the connection. If no text is given,", +" INPUT waits for any character. If the /NOMATCH switch is included, INPUT", +" does not attempt to match any characters, but continues reading from the", +" communication connection until the timeout interval expires. If the", +" timeout interval is 0, the INPUT command does not wait; i.e. the given", +" text must already be available for reading for the INPUT command to", +" succeed. If the interval is negative, the INPUT command waits forever.", +" For use in script programs with IF FAILURE and IF SUCCESS. Also see", +" MINPUT, REINPUT, SET INPUT. See HELP PAUSE for details on time-of-day", +" format. The text, if given, can be a \\pattern() invocation, in which", +" case it is treated as a pattern rather than a literal string (HELP", +" PATTERNS for details).", +""}; + +static char *hxxout[] = { +"Syntax: OUTPUT text", +" Sends the text out the communications connection, as if you had typed it", +" during CONNECT mode. The text may contain backslash codes, variables,", +" etc, plus the following special codes:", +" ", +" \\N - Send a NUL (ASCII 0) character (you can't use \\0 for this).", +" \\B - Send a BREAK signal.", +" \\L - Send a Long BREAK signal.", +" ", +"Also see SET OUTPUT.", +"" }; +#endif /* NOSPL */ + +static char *hxypari[] = { +"SET PARITY NONE", +" Chooses 8 data bits and no parity.", +" ", +"SET PARITY { EVEN, ODD, MARK, SPACE }", +" Chooses 7 data bits plus the indicated kind of parity.", +" Forces 8th-bit prefixing during file transfer.", +" ", +#ifdef HWPARITY +"SET PARITY HARDWARE { EVEN, ODD }", +" Chooses 8 data bits plus the indicated kind of parity.", +" ", +"Also see SET TERMINAL BYTESIZE, SET SERIAL, and SET STOP-BITS.", +#else +"Also see SET TERMINAL BYTESIZE and SET SERIAL.", +#endif /* HWPARITY */ +""}; + +#ifndef NOLOCAL +static char *hxyesc[] = { +#ifdef OS2 +"Syntax: SET ESCAPE number", +" Decimal ASCII value for escape character during CONNECT, normally 29", +" (Control-]). Type the escape character followed by C to get back to the", +" C-Kermit prompt or followed by ? to see other options, or use the \\Kexit", +" keyboard verb, normally assigned to Alt-x.", +#else +#ifdef NEXT +"Syntax: SET ESCAPE number", +" Decimal ASCII value for escape character during CONNECT, normally 29", +" (Control-]). Type the escape character followed by C to get back to the", +" C-Kermit prompt or followed by ? to see other options.", +#else +"Syntax: SET ESCAPE number", +" Decimal ASCII value for escape character during CONNECT, normally 28", +" (Control-\\). Type the escape character followed by C to get back to the", +" C-Kermit prompt or followed by ? to see other options.", +#endif /* NEXT */ +#endif /* OS2 */ +" ", +"You may also enter the escape character as ^X (circumflex followed by a", +"letter or one of: @, ^, _, [, \\, or ], to indicate a control character;", +"for example, SET ESC ^_ sets your escape character to Ctrl-Underscore.", +" ", +"You can also specify an 8-bit character (128-255) as your escape character,", +"either by typing it literally or by entering its numeric code.", +"" }; +#endif /* NOLOCAL */ + +#ifndef NOSPL +static char *hxyout[] = { +"SET OUTPUT PACING ", +" How many milliseconds to pause after sending each OUTPUT character,", +" normally 0.", +" ", +"SET OUTPUT SPECIAL-ESCAPES { ON, OFF }", +" Whether to process the special OUTPUT-only escapes \\B, \\L, and \\N.", +" Normally ON (they are processed).", +"" }; + +static char *hxyinp[] = { +"Syntax: SET INPUT parameter value", +" ", +#ifdef CK_AUTODL +"SET INPUT AUTODOWNLOAD { ON, OFF }", +" Controls whether autodownloads are allowed during INPUT command execution.", +" ", +#endif /* CK_AUTODL */ +"SET INPUT BUFFER-LENGTH number-of-bytes", +" Removes the old INPUT buffer and creates a new one with the given length.", +" ", +"SET INPUT CANCELLATION { ON, OFF }", +" Whether an INPUT in progress can be can interrupted from the keyboard.", +" ", +"SET INPUT CASE { IGNORE, OBSERVE }", +" Tells whether alphabetic case is to be significant in string comparisons.", +" This setting is local to the current macro or command file, and is", +" inherited by subordinate macros and take files.", +" ", +"SET INPUT ECHO { ON, OFF }", +" Tells whether to display arriving characters read by INPUT on the screen.", +" ", +#ifdef CKFLOAT +"SET INPUT SCALE-FACTOR ", +" A number to multiply all INPUT timeouts by, which may include a fractional", +" part, e.g. 2.5. All INPUT commands that specify a timeout in seconds", +" (as opposed to a specific time of day) have their time limit adjusted", +" automatically by this factor, which is also available in the built-in", +" read-only variable \\v(inscale). The default value is 1.0.", +" ", + +#endif /* CKFLOAT */ + +"SET INPUT SILENCE ", +" The maximum number to seconds of silence (no input at all) before the", +" INPUT command times out, 0 for no maximum.", +" ", +#ifdef OS2 +"SET INPUT TERMINAL { ON, OFF }", +" Determines whether the data received during an INPUT command is displayed", +" in the terminal window. Default is ON.", +" ", +#endif /* OS2 */ +"SET INPUT TIMEOUT-ACTION { PROCEED, QUIT }", +" Tells whether to proceed or quit from a script program if an INPUT command", +" fails. PROCEED (default) allows use of IF SUCCESS / IF FAILURE commands.", +" This setting is local to the current macro or command file, and is", +" inherited by subordinate macros and take files.", +"" }; + +static char *hxyfunc[] = { +"SET FUNCTION DIAGNOSTICS { ON, OFF }", +" Whether to issue diagnostic messages for illegal function calls and", +" references to nonexistent built-in variables. ON by default.", +" ", +"SET FUNCTION ERROR { ON, OFF }", +" Whether an illegal function call or reference to a nonexistent built-in", +" variable should cause a command to fail. OFF by default.", +"" }; +#endif /* NOSPL */ + +static char *hxyxyz[] = { +#ifdef CK_XYZ +#ifdef XYZ_INTERNAL + +/* This is for built-in protocols */ + +"Syntax: SET PROTOCOL { KERMIT, XMODEM, YMODEM, ZMODEM } [ s1 s2 [ s3 ] ]", +" Selects protocol to use for transferring files. String s1 is a command to", +" send to the remote host prior to SENDing files with this protocol in", +" binary mode; string s2 is the same thing but for text mode. Use \"%\" in", +" any of these strings to represent the filename(s). If the protocol is", +" KERMIT, you may also specify a string s3, the command to start a Kermit", +" server on the remote host when you give a GET, REGET, REMOTE, or other", +" client command. Use { braces } if any command contains spaces. Examples:", +" ", +" set proto xmodem {rx %s} {rx -a %s}", +" set proto kermit {kermit -YQir} {kermit -YQTr} {kermit -YQx}", + +#else /* This is for when non-Kermit protocols are external */ + +"Syntax: \ +SET PROTOCOL { KERMIT, XMODEM, YMODEM, ZMODEM } [ s1 s2 s3 s4 s5 s6 ]", +" Selects protocol to use for transferring files. s1 and s2 are commands to", +" output prior to SENDing with this protocol, to automatically start the", +" RECEIVE process on the other end in binary or text mode, respectively.", +" If the protocol is KERMIT, s3 is the command to start a Kermit server on", +" the remote computer, and there are no s4-s6 commands. Otherwise, s3 and", +" s4 are commands used on this computer for sending files with this protocol", +" in binary or text mode, respectively; s5 and s6 are the commands for", +" receiving files with this protocol. Use \"%s\" in any of these strings", +" to represent the filename(s). Use { braces } if any command contains", +" spaces. Examples:", +" ", +" set proto kermit {kermit -YQir} {kermit -YQTr} {kermit -YQx}", +" set proto ymodem rb {rb -a} {sb %s} {sb -a %s} rb rb", +" ", +"External protocols require REDIRECT and external file transfer programs that", +"use redirectable standard input/output.", +#endif /* XYZ_INTERNAL */ +#else +"Syntax: \ +SET PROTOCOL KERMIT [ s1 [ s2 [ s3 ] ] ]", +" Lets you specify the autoupload binary, autoupload text, and autoserver", +" command strings to be sent to the remote system in advance of any SEND", +" or GET commands. By default these are \"kermit -ir\", \"kermit -r\", and", +" \"kermit -x\". Use { braces } around any command that contains spaces.", +" Example:", +" ", +" set proto kermit {kermit -Yir} {kermit -YTr} {kermit -Yx}", +#endif /* CK_XYZ */ +" ", +" SHOW PROTOCOL displays the current settings.", +""}; + +static char *hmxxbye = "Syntax: BYE\n\ + Shut down and log out a remote Kermit server"; + +#ifdef CK_PERMS +#ifdef UNIX +static char *hmxxchmod[] = { +"Syntax: CHMOD [ switches ] code filespec", +" UNIX only. Changes permissions of the given file(s) to the given code,", +" which must be an octal number such as 664 or 775. Optional switches:", +" ", +" /FILES Only change permissions of regular files.", +" /DIRECTORIES Only change permissions of directory files.", +" /TYPE:BINARY Only change permissions of binary files.", +" /TYPE:TEXT Only change permissions of text files.", +" /DOTFILES Include files whose names begin with dot (.).", +" /RECURSIVE Change permissions in subdirectories too.", +" /LIST List each file (synonym: /VERBOSE).", +" /NOLIST Operate silently (synonym: /QUIET).", +" /PAGE When listing, pause at end of each screen (implies /LIST).", +" /NOPAGE When listing, don't pause at end of each screen.", +" /SIMULATE Show what would be done but don't actually do it.", +"" +}; +#endif /* UNIX */ +#endif /* CK_PERMS */ + +#ifndef NOSPL +#ifndef NOSEXP +static char *hmxxsexp[] = { +"Syntax: (operation operand [ operand [ ... ] ])", +" ", +" C-Kermit includes a simple LISP-like S-Expression parser operating on", +" numbers only. An S-Expression is always enclosed in parentheses. The", +" parentheses can contain (a) a number, (b) a variable, (c) a function that", +" returns a number, or (d) an operator followed by one or more operands.", +" Operands can be any of (a) through (c) or an S-Expression. Numbers can be", +" integers or floating-point. Any operand that is not a number and does not", +" start with backslash (\\) is treated as a Kermit macro name. Operators:", +" ", +" Operator Action Example Value", +" EVAL (.) Returns the contained value (6) 6", +" QUOTE (') Inhibits evaluation of following value (quote a) a", +" SETQ Assigns a value to a global variable (setq a 2) 2", +" LET Assigns a value to a local variable (let b -1.3) -1.3", +" + Adds all operands (1 or more) (+ a b) 0.7", +" - Subtracts all operands (1 or more) (- 9 5 2 1) 1", +" * Multiplies all operands (1 or more) (* a (+ b 1) 3) -1.8", +" / Divides all operands (1 or more) (/ b a 2) -0.325", +" ^ Raise given number to given power (^ 3 2) 9", +" ++ Increments a variable (++ a 1.2) 3.2", +" -- Decrements a variable (-- a) 1", +" ABS Absolute value of 1 operand (abs (* a b 3)) 7.8", +" MAX Maximum of all operands (1 or more) (max 1 2 3 4) 4", +" MIN Minimum of all operands (1 or more) (min 1 2 3 4) 1", +" MOD Modulus of all operands (1 or more) (mod 7 4 2) 1", +" TRUNCATE Integer part of floating-point operand (truncate 1.333) 1", +" CEILING Ceiling of floating-point operand (ceiling 1.25) 2", +" FLOOR Floor of floating-point operand (floor 1.25) 1", +" ROUND Operand rounded to nearest integer (round 1.75) 2", +" SQRT Square root of 1 operand (sqrt 2) 1.414..", +" EXP e (2.71828..) to the given power (exp -1) 0.367..", +" SIN Sine of angle expressed in radians (sin (/ pi 2)) 1.0", +" COS Cosine of given number (cos pi) -1.0", +" TAN Tangent of given number (tan pi) 0.0", +" LOG Natural log (base e) of given number (log 2.7183) 1.000..", +" LOG10 Log base 10 of given number (log10 1000) 3.0", +" ", +"Predicate operators return 0 if false, 1 if true, and if it is the outermost", +"operator, sets SUCCESS or FAILURE accordingly:", +" ", +" < Operands in strictly descending order (< 6 5 4 3 2 1) 1", +" <= Operands in descending order (<= 6 6 5 4 3 2) 1", +" != Operands are not equal (!= 1 1 1.0) 0", +" = (==) All operands are equal (= 3 3 3 3) 1", +" > Operands in strictly ascending order (> 1 2 3 4 5 6) 1", +" >= Operands in ascending order (> 1 1 2 3 4 5) 1", +" AND (&&) Operands are all true (and 1 1 1 1 0) 0", +" OR (||) At least one operand is true (or 1 1 1 1 0) 1", +" XOR Logical Exclusive OR (xor 3 1) 0", +" NOT (!) Reverses truth value of operand (not 3) 0", +" ", +"Bit-oriented operators:", +" ", +" & Bitwise AND (& 7 2) 2", +" | Bitwise OR (| 1 2 3 4) 7", +" # Bitwise Exclusive OR (# 3 1) 2", +" ~ Reverses all bits (~ 3) -4", +" ", +"Operators that work on truth values:", +" ", +" IF Conditional evaluation (if (1) 2 3) 2", +" ", +"Operators can also be names of Kermit macros that return either numeric", +"values or no value at all.", +" ", +"Built-in constants are:", +" ", +" t True (1)", +" nil False (empty)", +" pi The value of Pi (3.1415926...)", +" ", +"If SET SEXPRESSION ECHO-RESULT is AUTO (the default), the value of the", +"S-Expression is printed if the S-Expression is given at top level; if ON,", +"it is printed at any level; if OFF it is not printed. At all levels, the", +"variable \\v(sexpression) is set to the most recent S-Expression, and", +"\\v(svalue) is set to its value. You can use the \\fsexpresssion() function", +"to evaluate an S-Expression anywhere in a Kermit command.", +"" +}; +#endif /* NOSEXP */ +#endif /* NOSPL */ + +static char *hmxxgrep[] = { +#ifdef UNIXOROSK +"Syntax: GREP [ options ] pattern filespec", +#else +"Syntax: FIND [ options ] pattern filespec", +#endif /* UNIXOROSK */ +" Searches through the given file or files for the given character string", +" or pattern. In the normal case, all lines containing any text that matches" +, +" the pattern are printed. Pattern syntax is as described in HELP PATTERNS", +" except that '*' is implied at the beginning unless the pattern starts with", +" '^' and also at the end unless the pattern ends with '$'. Therefore,", +" \"grep something *.txt\" lists all lines in all *.txt files that contain", +" the word \"something\", but \"grep ^something *.txt\" lists only the lines", +" that START with \"something\". The command succeeds if any of the given", +" files contained any lines that match the pattern, otherwise it fails.", +#ifdef UNIXOROSK +" Synonym: FIND.", +#else +" Synonym: GREP.", +#endif /* UNIXOROSK */ +" ", +"File selection options:", +" /NOBACKUPFILES", +" Excludes backup files (like oofa.txt.~3~) from the search.", +" /DOTFILES", +" Includes files whose names start with dot (.) in the search.", +" /NODOTFILES", +" Excludes files whose names start with dot (.) from the search.", +#ifdef RECURSIVE +" /RECURSIVE", +" Searches through files in subdirectories too.", +#endif /* RECURSIVE */ +" /TYPE:TEXT", +" Search only text files (requires FILE SCAN ON).", +" /TYPE:BINARY", +" Search only binary files (requires FILE SCAN ON).", +" ", +"Pattern-matching options:", +" /NOCASE", +" Ignores case of letters (ASCII only) when comparing.", +" /NOMATCH", +" Searches for lines that do NOT match the pattern.", +" ", +"Display options:", +" /COUNT:variable-name", +" For each file, prints only the filename and a count of matching lines", +" and assigns the total match count to the variable, if one is given.", +" /NAMEONLY", +" Prints the name of each file that contains at least one matching line,", +" one name per line, rather than showing each matching line.", +" /NOLIST", +" Doesn't print anything (but sets SUCCESS or FAILURE appropriately).", +" /LINENUMBERS", +" Precedes each file line by its line number within the file.", +" /PAGE", +" Pauses after each screenful.", +" /NOPAGE", +" Doesn't pause after each screenful.", +" /OUTPUT:name", +" Sends results to the given file. If this switch is omitted, the", +" results appear on your screen. This switch overrides any express or", +" implied /PAGE switch.", +""}; + +static char *hmxxdir[] = { +#ifdef DOMYDIR +"Syntax: DIRECTORY [ switches ] [ filespec [ filespec [ ... ] ] ]", +#ifdef LOCUS +" If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", +" this command is equivalent to REMOTE DIRECTORY (RDIR). Otherwise:", +" ", +#endif /* LOCUS */ +" Lists local files. The filespec may be a filename, possibly containing", +" wildcard characters, or a directory name. If no filespec is given, all", +" files in the current directory are listed. If a directory name is given,", +" all the files in it are listed. Multiple filespecs can be given.", +" Optional switches:", +" ", +" /BRIEF List filenames only.", +#ifdef CK_PERMS +" /VERBOSE + Also list permissions, size, and date.", +#else +" /VERBOSE + Also list date and size.", +#endif /* CK_PERMS */ +" /FILES Show files but not directories.", +" /DIRECTORIES Show directories but not files.", +" /ALL + Show both files and directories.", +" /ARRAY:&a Store file list in specified array (e.g. \\%a[]).", +" /PAGE Pause after each screenful.", +" /NOPAGE Don't pause after each screenful.", +#ifdef UNIXOROSK +" /DOTFILES Include files whose names start with dot (period).", +" /NODOTFILES + Don't include files whose names start with dot.", +" /FOLLOWLINKS Follow symbolic links.", +" /NOFOLLOWLINKS + Don't follow symbolic links.", +" /BACKUP + Include backup files (names end with .~n~).", +" /NOBACKUPFILES Don't include backup files.", +#endif /* UNIXOROSK */ +" /OUTPUT:file Store directory listing in the given file.", +" /HEADING Include heading and summary.", +" /NOHEADING + Don't include heading or summary.", +" /SUMMARY Print only count and total size of matching files.", +" /XFERMODE Show pattern-based transfer mode (T=Text, B=Binary).", +" /TYPE: Show only files of the specified type (text or binary).", +" /MESSAGE:text Add brief message to each listing line.", +" /NOMESSAGE + Don't add message to each listing line.", +" /NOXFERMODE + Don't show pattern-based transfer mode", +" /ISODATE + In verbose listings, show date in ISO 8061 format.", +" /ENGLISHDATE In verbose listings, show date in \"English\" format.", +#ifdef RECURSIVE +" /RECURSIVE Descend through subdirectories.", +" /NORECURSIVE + Don't descend through subdirectories.", +#endif /* RECURSIVE */ +" /SORT:key Sort by key, NAME, DATE, or SIZE; default key is NAME.", +" /NOSORT + Don't sort.", +" /ASCENDING + If sorting, sort in ascending order.", +" /REVERSE If sorting, sort in reverse order.", +" ", +"Factory defaults are marked with +. Default for paging depends on SET", +"COMMAND MORE-PROMPTING. Use SET OPTIONS DIRECTORY [ switches ] to change", +"defaults; use SHOW OPTIONS to display customized defaults.", +#else +"Syntax: DIRECTORY [ filespec ]", +" Lists the specified file or files. If no filespec is given, all files", +" in the current directory are listed.", +#endif /* DOMYDIR */ +""}; + + +#ifndef NOSPL +static char *hmxxkcd[] = { +"Syntax: KCD symbolic-directory-name", +" Kermit Change Directory: Like CD (q.v.) but (a) always acts locally, and", +" (b) takes a symbolic directory name rather than an actual directory name.", +" The symbolic names correspond to Kermit's directory-valued built-in", +" variables, such as \\v(download), \\v(exedir), and so on. Here's the list:" +, +" ", +#ifdef NT +" appdata Your personal Kermit 95 application data directory", +" common Kermit 95's application data directory for all users", +" desktop Your Windows desktop", +#endif /* NT */ +" download Your download directory (if any)", +#ifdef OS2ORUNIX +" exedir The directory where the Kermit executable resides", +#endif /* OS2ORUNIX */ +" home Your home, login, or default directory", +" inidir The directory where Kermit's initialization was found", +#ifdef UNIX +" lockdir The UNIX UUCP lockfile directory on this computer", +#endif /* UNIX */ +#ifdef NT +" personal Your \"My Documents\" directory", +#endif /* NT */ +" startup Your current directory at the time Kermit started", +" textdir The directory where Kermit text files reside, if any", +" tmpdir Your temporary directory", +" ", +" Also see CD, SET FILE DOWNLOAD, SET TEMP-DIRECTORY.", +"" +}; +#endif /* NOSPL */ + +static char *hmxxcwd[] = { +#ifdef LOCUS +" If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", +" this command is equivalent to REMOTE CD (RCD). Otherwise:", +" ", +#endif /* LOCUS */ +#ifdef vms +"Syntax: CD [ directory or device:directory ]", +" Change Working Directory. Equivalent to VMS SET DEFAULT command.", +#else +#ifdef datageneral +"Change Working Directory, equivalent to AOS/VS 'dir' command.", +#else +#ifdef OS2 +"Syntax: CD [ disk or directory name ]", +" Change Disk or Directory. If a disk or directory name is not specified,", +" your current directory becomes the one specified by HOME environment", +" variable, if any. A disk letter must be followed by a colon.", +#else +"Syntax: CD [ directory name ]", +" Change Directory. Changes your current, working, default directory to the", +" one given, so that future non-absolute filename references are relative to", +" this directory. If the directory name is omitted, your home (login)", +" directory is supplied.", +#endif /* OS2 */ +#endif /* datageneral */ +#endif /* vms */ +" C-Kermit's default prompt shows your current directory.", +" Synonyms: LCD, CWD.", +#ifdef LOCUS +" Also see: SET LOCUS, PWD, CDUP, BACK, REMOTE CD (RCD), SET CD, SET PROMPT.", +#else +" Also see: PWD, CDUP, BACK, REMOTE CD (RCD), SET CD, SET PROMPT.", +#endif /* LOCUS */ +#ifndef NOSPL +" And see: HELP KCD.", +#endif /* NOSPL */ +" Relevant environment variables: CDPATH, HOME.", +""}; + +static char *hmxxdel[] = { +"Syntax: DELETE [ switches... ] filespec", +#ifdef LOCUS +" If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", +" this command is equivalent to REMOTE DELETE (RDELETE). Otherwise:", +" ", +#endif /* LOCUS */ +" Deletes a file or files on the computer where C-Kermit is running.", +" The filespec may denote a single file or can include wildcard characters", +" to match multiple files. RM is a synonym for DELETE. Switches include:", +" ", +"/AFTER:date-time", +#ifdef VMS +" Specifies that only those files created after the given date-time are", +#else +" Specifies that only those files modified after the given date-time are", +#endif /* VMS */ +" to be deleted. HELP DATE for info about date-time formats.", +" ", +"/BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified before the given date-time", +#else +" Specifies that only those files modified before the given date-time", +#endif /* VMS */ +" are to be deleted.", +" ", +"/NOT-AFTER:date-time", +#ifdef VMS +" Specifies that only those files modified at or before the given date-time", +#else +" Specifies that only those files modified at or before the given date-time", +#endif /* VMS */ +" are to be deleted.", +" ", +"/NOT-BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified at or after the given date-time", +#else +" Specifies that only those files modified at or after the given date-time", +#endif /* VMS */ +" are to be deleted.", +" ", +"/LARGER-THAN:number", +" Specifies that only those files longer than the given number of bytes are", +" to be deleted.", +" ", +"/SMALLER-THAN:number", +" Specifies that only those files smaller than the given number of bytes are", +" to be sent.", +" ", +"/EXCEPT:pattern", +" Specifies that any files whose names match the pattern, which can be a", +" regular filename or may contain wildcards, are not to be deleted. To", +" specify multiple patterns (up to 8), use outer braces around the group", +" and inner braces around each pattern:", +" ", +" /EXCEPT:{{pattern1}{pattern2}...}", +" ", +#ifdef UNIXOROSK +"/DOTFILES", +" Include (delete) files whose names begin with \".\".", +" ", +"/NODOTFILES", +" Skip (don't delete) files whose names begin with \".\".", +" ", +#endif /* UNIXOROSK */ +"/TYPE:TEXT", +" Delete only regular text files (requires FILE SCAN ON)", +" ", +"/TYPE:BINARY", +" Delete only regular binary files (requires FILE SCAN ON)", +" ", +"/DIRECTORIES", +" Include directories. If this switch is not given, only regular files", +" are deleted. If it is given, Kermit attempts to delete any directories", +" that match the given file specification, which succeeds only if the", +" directory is empty.", +" ", +#ifdef RECURSIVE +"/RECURSIVE", +" The DELETE command applies to the entire directory tree rooted in the", +" current or specified directory. When the /DIRECTORIES switch is also", +" given, Kermit deletes all the (matching) files in each directory before", +" attempting to delete the directory itself.", +" ", +#endif /* RECURSIVE */ +#ifdef UNIX +#ifdef RECURSIVE +"/ALL", +" This is a shortcut for /RECURSIVE /DIRECTORIES /DOTFILES.", +#else +"/ALL", +" This is a shortcut for /DIRECTORIES /DOTFILES.", +#endif /* RECURSIVE */ +#else /* !UNIX */ +#ifdef RECURSIVE +"/ALL", +" This is a shortcut for /RECURSIVE /DIRECTORIES.", +#else +"/ALL", +" This is a synonym for /DIRECTORIES.", +#endif /* RECURSIVE */ +#endif /* UNIX */ +" ", +"/LIST", +" List each file and tell whether it was deleted. Synonyms: /LOG, /VERBOSE.", +" ", +"/NOLIST", +" Don't list files while deleting. Synonyms: /NOLOG, /QUIET.", +" ", +"/HEADING", +" Print heading and summary information.", +" ", +"/NOHEADING", +" Don't print heading and summary information.", +" ", +"/SUMMARY", +" Like /HEADING /NOLIST, but only prints the summary line.", +" ", +"/PAGE", +" If listing, pause after each screenful.", +" ", +"/NOPAGE", +" Don't pause after each screenful.", +" ", +"/ASK", +" Interactively ask permission to delete each file. Reply Yes or OK to", +" delete it, No not to delete it, Quit to cancel the DELETE command, and", +" Go to go ahead and delete all the rest of the files without asking.", +" ", +"/NOASK", +" Delete files without asking permission.", +" ", +"/SIMULATE", +" Preview files selected for deletion without actually deleting them.", +" Implies /LIST.", +" ", +"Use SET OPTIONS DELETE to make selected switches effective for every DELETE", +"command \ +unless you override them; use SHOW OPTIONS to see selections currently", +#ifdef LOCUS +"in effect. Also see HELP SET LOCUS, HELP PURGE, HELP WILDCARD.", +#else +"in effect. Also see HELP PURGE, HELP WILDCARD.", +#endif /* LOCUS */ +""}; + +#ifndef NOHTTP +static char *hmxxhttp[] = { +"Syntax:", +#ifdef CK_SSL +"HTTP [ ] OPEN [{ /SSL, /TLS }] ", +#else +"HTTP [ ] OPEN ", +#endif /*CK_SSL */ +" Instructs Kermit to open a new connection for HTTP communication with", +" the specified host on the specified port. The default port is \"http\".", +#ifdef CK_SSL +" If /SSL or /TLS are specified or if the service is \"https\" or port 443,", +" a secure connection will be established using the current authentication", +" settings. See HELP SET AUTH for details.", +#endif /* CK_SSL */ +" If are specified, they are applied to all subsequent HTTP", +" actions (GET, PUT, ...) until an HTTP CLOSE command is executed.", +" A URL can be included in place of the hostname and service or port.", +" ", +"HTTP CLOSE", +" Instructs Kermit to close any open HTTP connection and clear any saved", +" switch values.", +" ", +"HTTP [ ] CONNECT [:]", +" Instructs the server to establish a connection with the specified host", +" and to redirect all data transmitted between Kermit and the host for the", +" life of the connection.", +" ", +"HTTP [ ] GET [ ]", +" Retrieves the named file on the currently open HTTP connection. The", +" default local filename is the same as the remote filename, but with any", +" path stripped. If you want the file to be displayed on the screen instead", +" of stored on disk, include the /TOSCREEN switch and omit the local", +" filename. If you give a URL instead of a remote filename, this commands", +" opens the connection, GETs the file, and closes the connection; the same", +" is true for the remaining HTTP commands for which you can specify a", +" remote filename, directory name, or path.", +" ", +"HTTP [ ] HEAD [ ]", +" Like GET except without actually getting the file; instead it gets only", +" the headers, storing them into the given file (if a local filename is", +" specified), one line per header item as shown in the /ARRAY: switch", +" description.", +" ", +"HTTP [ ] INDEX [ ]", +" Retrieves the file listing for the given server directory.", +" NOTE: This command is not supported by most Web servers, and even when", +" the server understand it, there is no stardard response format.", +" ", +"HTTP [ ] POST [ /MIME-TYPE: ] ", +" [ ]", +" Used to send a response as if it were sent from a form. The data to be", +" posted must be read from a file.", +" ", +"HTTP [ ] PUT [ /MIME-TYPE: ] ", +" [ ]", +" Uploads the given local file to server file. If the remote filename is", +" omitted, the local name is used, but with any path stripped.", +" ", +"HTTP [ ] DELETE ", +" Instructs the server to delete the specified filename.", +" ", +"where are:", +"/AGENT:", +" Identifies the client to the server; \"C-Kermit\" or \"Kermit-95\"", +" by default.", +" ", +"/HEADER:", +" Used for specifying any optional headers. A list of headers is provided", +" using braces for grouping:", +" ", +" /HEADER:{{:}{:}...}", +" ", +" For a listing of valid value and formats see RFC 1945:", +" \"Hypertext Transfer Protocol -- HTTP/1.0\". A maximum of eight headers", +" may be specified.", +" ", +"/TOSCREEN", +" Display server responses on the screen.", +" ", +"/USER:", +" In case a page requires a username for access.", +" ", +"/PASSWORD:", +" In case a page requires a password for access.", +" ", +"/ARRAY:", +" Tells Kermit to store the response headers in the given array, one line", +" per element. The array need not be declared in advance. Example:", +" ", +" http /array:c get kermit/index.html", +" show array c", +" Dimension = 9", +" 1. Date: Fri, 26 Nov 1999 23:12:22 GMT", +" 2. Server: Apache/1.3.4 (Unix)", +" 3. Last-Modified: Mon, 06 Sep 1999 22:35:58 GMT", +" 4. ETag: \"bc049-f72-37d441ce\"", +" 5. Accept-Ranges: bytes", +" 6. Content-Length: 3954", +" 7. Connection: close ", +" 8. Content-Type: text/html", +" ", +"As you can see, the header lines are like MIME e-mail header lines:", +"identifier, colon, value. The /ARRAY switch is the only method available", +"to a script to process the server responses for a POST or PUT command.", +" ", +"" +}; +#endif /* NOHTTP */ + +#ifdef CK_KERBEROS +static char *hmxxauth[] = { +"Syntax:", +"AUTHENTICATE { KERBEROS4, KERBEROS5 [ switches ] } [ switches ]", +" Obtains or destroys Kerberos tickets and lists information about them.", +" Actions are INITIALIZE, DESTROY, and LIST-CREDENTIALS. KERBEROS4 can be", +" abbreviated K4 or KRB4; KERBEROS5 can be abbreviated K5 or KRB5. Use ? to", +" see which keywords, switches, or other quantities are valid at each point", +" in the command.", +" ", +" The actions are INITIALIZE, DESTROY, and LIST-CREDENTIALS:", +" ", +" AUTH { K4, K5 } { INITIALIZE [switches], DESTROY,", +" LIST-CREDENTIALS [switches] }", +" ", +" The INITIALIZE action is the most complex, and its format is different", +" for Kerberos 4 and Kerberos 5. The format for Kerberos 4 is:", +" ", +" AUTH K4 INITIALIZE [ /INSTANCE: /LIFETIME: -", +" /PASSWORD: /PREAUTH /REALM: ]", +" ", +" All switches are optional. Kerberos 4 INITIALIZE switches are:", +" ", +" /INSTANCE:", +" Allows an Instance (such as a hostname) to be specified.", +" ", +" /LIFETIME:", +" Specifies the requested lifetime in minutes for the ticket. If no", +" lifetime is specified, 600 minutes is used. If the lifetime is greater", +" than the maximum supported by the ticket granting service, the resulting", +" lifetime is shortened accordingly.", +" ", +" /NOT-PREAUTH", +" Instructs Kermit to send a ticket getting ticket (TGT) request to the", +" KDC without any preauthentication data.", +" ", +" /PASSWORD:", +" Allows a password to be included on the command line or in a script", +" file. If no /PASSWORD switch is included, you are prompted on a separate" +, +" line. The password switch is provided on a use-at-your-own-risk basis", +" for use in automated scripts. WARNING: Passwords should not be stored in" +, +" files.", +" ", +" /PREAUTH", +" Instructs Kermit to send a preauthenticated Ticket-Getting Ticket (TGT)", +" request to the KDC instead of a plaintext request. The default when", +" supported by the Kerberos libraries.", +" ", +" /REALM:", +" Allows a realm to be specified (overriding the default realm).", +" ", +" ", +" Your identity in the given or default Kerberos realm, of the form:", +" userid[.instance[.instance]]@[realm] ", +" Can be omitted if it is the same as your username or SET LOGIN USERID", +" value on the client system.", +" ", +" The format for Kerberos 5 is as follows:", +" ", +" AUTH K5 [ /CACHE: ] { INITIALIZE [ switches ], DESTROY,", +" LIST-CREDENTIALS ...}", +" ", +"The INITIALIZE command for Kerberos 5 can include a number of switches;", +"all are optional:", +" ", +"AUTH K5 [ /CACHE: ] INITITIALIZE [ /ADDRESSES:", +" /FORWARDABLE /KERBEROS4 /LIFETIME: /PASSWORD:", +" /POSTDATE: /PROXIABLE /REALM: /RENEW /RENEWABLE:", +" /SERVICE: /VALIDATE ]", +" ", +" All Kerberos 5 INITIALIZE switches are optional:", +" ", +" /ADDRESSES:{list of ip-addresses}", +" Specifies a list of IP addresses that should be placed in the Ticket", +" Getting Ticket in addition to the local machine addresses.", +" ", +" /FORWARDABLE", +" Requests forwardable tickets.", +" ", +" /INSTANCE:", +" Allows an Instance (such as a hostname) to be specified.", +" ", +" /KERBEROS4", +" Instructs Kermit to get Kerberos 4 tickets in addition to Kerberos 5", +" tickets. If Kerberos 5 tickets are not supported by the server, a", +" mild warning is printed and Kerberos 4 tickets are requested.", +" ", +" /LIFETIME:", +" Specifies the requested lifetime in minutes for the ticket. If no", +" lifetime is specified, 600 minutes is used. If the lifetime is greater", +" than the maximum supported by the ticket granting service, the resulting", +" lifetime is shortened.", +" ", +" /NO-KERBEROS4", +" Instructs Kermit to not attempt to retrieve Kerberos 4 credentials.", +" ", +" /NOT-FORWARDABLE", +" Requests non-forwardable tickets.", +" ", +" /NOT-PROXIABLE", +" Requests non-proxiable tickets.", +" ", +" /PASSWORD:", +" Allows a password to be included on the command line or in a script", +" file. If no /PASSWORD switch is included, you are prompted on a separate" +, +" line. The password switch is provided on a use-at-your-own-risk basis", +" for use in automated scripts. WARNING: Passwords should not be stored in" +, +" files.", +" ", +" /POSTDATE:", +" Requests a postdated ticket, valid starting at . Postdated", +" tickets are issued with the invalid flag set, and need to be fed back to", +" the KDC before use with the /VALIDATE switch. Type HELP DATE for info", +" on date-time formats.", +" ", +" /PROXIABLE", +" Requests proxiable tickets.", +" ", +" /REALM:", +" Allows an alternative realm to be specified.", +" ", +" /RENEW", +" Requests renewal of a renewable Ticket-Granting Ticket. Note that ", +" an expired ticket cannot be renewed even if it is within its renewable ", +" lifetime.", +" ", +" /RENEWABLE:", +" Requests renewable tickets, with a total lifetime of minutes.", +" ", +" /SERVICE:", +" Allows a service other than the ticket granting service to be specified.", +" ", +" /VALIDATE", +" Requests that the Ticket Granting Ticket in the cache (with the invalid", +" flag set) be passed to the KDC for validation. If the ticket is within", +" its requested time range, the cache is replaced with the validated", +" ticket.", +" ", +" ", +" Your identity in the given or default Kerberos realm, of the form:", +" userid[/instance][@realm] ", +" Can be omitted if it is the same as your username or SET LOGIN USERID", +" value on the client system.", +" ", +" Note: Kerberos 5 always attempts to retrieve a Ticket-Getting Ticket (TGT)", +" using the preauthenticated TGT request.", +" ", +" AUTHORIZE K5 LIST-CREDENTIALS [ /ADDRESSES /FLAGS /ENCRYPTION ]", +" ", +" Shows start time, expiration time, service or principal name, plus", +" the following additional information depending the switches:", +" ", +" /ADDRESSES displays the hostnames and/or IP addresses embedded within", +" the tickets.", +" ", +" /FLAGS provides the following information (if applicable) for each ticket:", +" F - Ticket is Forwardable", +" f - Ticket was Forwarded", +" P - Ticket is Proxiable", +" p - Ticket is a Proxy", +" D - Ticket may be Postdated", +" d - Ticket has been Postdated", +" i - Ticket is Invalid", +" R - Ticket is Renewable", +" I - Ticket is the Initial Ticket", +" H - Ticket has been authenticated by Hardware", +" A - Ticket has been Pre-authenticated", +" ", +" /ENCRYPTION displays the encryption used by each ticket (if applicable):", +" DES-CBC-CRC", +" DES-CBC-MD4", +" DES-CBC-MD5", +" DES3-CBC-SHA", +"" +}; +#endif /* CK_KERBEROS */ + +#ifndef NOCSETS +static char *hmxxassoc[] = { +"ASSOCIATE FILE-CHARACTER-SET ", +" Tells C-Kermit that whenever the given file-character set is selected, and", +" SEND CHARACTER-SET (q.v.) is AUTOMATIC, the given transfer character-set", +" is selected automatically.", +" ", +"ASSOCIATE XFER-CHARACTER-SET ", +" Tells C-Kermit that whenever the given transfer-character set is selected,", +" either by command or by an announcer attached to an incoming text file,", +" and SEND CHARACTER-SET is AUTOMATIC, the specified file character-set is", +" to be selected automatically. Synonym: ASSOCIATE TRANSFER-CHARACTER-SET.", +" ", +"Use SHOW ASSOCIATIONS to list the current character-set associations, and", +"SHOW CHARACTER-SETS to list the current settings.", +"" +}; +#endif /* NOCSETS */ + +static char *hmxxpat[] = { +"A \"pattern\" is notation used in a search string when searching through", +"text. C-Kermit uses three kinds of patterns: floating patterns, anchored", +"patterns, and wildcards. Wildcards are anchored patterns that are used to", +"match file names; type HELP WILDCARD to learn about them.", +" ", +"In a pattern, certain characters are special:", +" ", +"* Matches any sequence of zero or more characters. For example, \"k*t\"", +" matches all strings that start with \"k\" and end with \"t\" including", +" \"kt\", \"kit\", \"knight\", or \"kermit\".", +" ", +#ifdef VMS +"% Matches any single character. For example, \"k%%%%t\" matches all strings", +#else +"? Matches any single character. For example, \"k????t\" matches all strings", +#endif /* VMS */ +" that are exactly 6 characters long and start with \"k\" and end with", +#ifdef VMS +" with \"t\".", +#else +" with \"t\". When typing commands at the prompt, you must precede any", +" question mark to be used for matching by a backslash (\\) to override the", +" normal function of question mark, which is providing menus and file lists.", +#endif /* VMS */ +" ", +#ifdef OS2ORUNIX +#ifdef CKREGEX +"[abc]", +" Square brackets enclosing a list of characters matches any character in", +" the list. Example: h[aou]t matches hat, hot, and hut.", +" ", +"[a-z]", +" Square brackets enclosing a range of characters matches any character in", +" the range; a hyphen (-) separates the low and high elements of the range.", +" For example, [a-z] matches any character from a to z.", +" ", +"[acdm-z]", +" Lists and ranges may be combined. This example matches a, c, d, or any", +" letter from m through z.", +" ", +"{string1,string2,...}", +" Braces enclose a list of strings to be matched. For example:", +" ker{mit,nel,beros} matches kermit, kernel, and kerberos. The strings", +" may themselves contain *, ?, [abc], [a-z], or other lists of strings.", +#endif /* CKREGEX */ +#endif /* OS2ORUNIX */ +#ifndef NOSPL +" ", +"To force a special pattern character to be taken literally, precede it with", +"a backslash, e.g. [a\\-z] matches a, hyphen, and z rather than a through z.", +" ", +"A floating pattern can also include the following special characters:", +" ", +"^ (First character of pattern) Anchors the pattern at the beginning.", +"$ (Last character of pattern) Anchors the pattern at the end.", +" ", +"If a floating pattern does not start with \"^\", the pattern can match", +"anywhere in the string instead of only at the beginning; in other words, a", +"leading \"*\" is assumed. Similarly, if the pattern doesn't end with \"$\",", +"a trailing \"*\" is assumed.", +" ", +"The following commands and functions use floating patterns:", +" GREP [ ] ", +" TYPE /MATCH: ", +" \\farraylook(,)", +" \\fsearch(,[,])", +" \\frsearch(,[,])", +" The /EXCEPT: clause in SEND, GET, DELETE, etc.", +" ", +"Example:", +" \\fsearch(abc,xxabcxxx) succeeds because xxabcxx contains abc.", +" \\fsearch(^abc,xxabcxx) fails because xxabcxx does not start with abc.", +" ", +"All other commands and functions that use patterns use anchored patterns,", +"meaning that ^ and $ are not treated specially, and * is not assumed at the", +"beginning or end of the pattern. This is true mainly of filename patterns", +"(wildcards), since you would not want a command like \"delete x\" to delete", +"all files whose names contained \"x\"!", +" ", +"You can use anchored patterns not only in filenames, but also in SWITCH", +"case labels, in the INPUT and MINPUT commands, and in file binary- and", +"text-patterns for filenames. The IF MATCH pattern is also anchored.", +#endif /* NOSPL */ +"" }; + +static char *hmxxwild[] = { + +"A \"wildcard\" is a notation used in a filename to match multiple files.", +"For example, in \"send *.txt\" the asterisk is a wildcard. Kermit commands", +"that accept filenames also accepts wildcards, except commands that are", +"allowed to operate on only one file, such as TRANSMIT.", +"This version of Kermit accepts the following wildcards:", +" ", +"* Matches any sequence of zero or more characters. For example, \"ck*.c\"", +" matches all files whose names start with \"ck\" and end with \".c\"", +" including \"ck.c\".", +" ", +#ifdef VMS +"% Matches any single character. For example, \"ck%.c\" matches all files", +#else +"? Matches any single character. For example, \"ck?.c\" matches all files", +#endif /* VMS */ +" whose names are exactly 5 characters long and start with \"ck\" and end", +#ifdef VMS +" with \".c\".", +#else +" with \".c\". When typing commands at the prompt, you must precede any", +" question mark to be used for matching by a backslash (\\) to override the", +" normal function of question mark, which is providing menus and file lists.", +#endif /* VMS */ +" ", +#ifdef OS2ORUNIX +#ifdef CKREGEX +"[abc]", +" Square brackets enclosing a list of characters matches any character in", +" the list. Example: ckuusr.[ch] matches ckuusr.c and ckuusr.h.", +" ", +"[a-z]", +" Square brackets enclosing a range of characters matches any character in", +" the range; a hyphen (-) separates the low and high elements of the range.", +" For example, [a-z] matches any character from a to z.", +" ", +"[acdm-z]", +" Lists and ranges may be combined. This example matches a, c, d, or any", +" letter from m through z.", +" ", +"{string1,string2,...}", +" Braces enclose a list of strings to be matched. For example:", +" ck{ufio,vcon,cmai}.c matches ckufio.c, ckvcon.c, or ckcmai.c. The strings", +" may themselves contain *, ?, [abc], [a-z], or other lists of strings.", +#endif /* CKREGEX */ +#endif /* OS2ORUNIX */ +" ", +"To force a special pattern character to be taken literally, precede it with", +"a backslash, e.g. [a\\-z] matches a, hyphen, and z rather than a through z.", +" ", +#ifndef NOSPL +"Similar notation can be used in general-purpose string matching. Type HELP", +"PATTERNS for details. Also see HELP SET MATCH.", +#endif /* NOSPL */ +"" }; + +#ifndef NOXFER +static char *hmxxfast[] = { +"FAST, CAUTIOUS, and ROBUST are predefined macros that set several", +"file-transfer parameters at once to achieve the desired file-transfer goal.", +"FAST chooses a large packet size, a large window size, and a fair amount of", +"control-character unprefixing at the risk of possible failure on some", +"connections. FAST is the default tuning in C-Kermit 7.0 and later. In case", +"FAST file transfers fail for you on a particular connection, try CAUTIOUS.", +"If that fails too, try ROBUST. You can also change the definitions of each", +"macro with the DEFINE command. To see the current definitions, type", +"\"show macro fast\", \"show macro cautious\", or \"show macro robust\".", +"" +}; +#endif /* NOXFER */ + +#ifdef VMS +static char * hmxxpurge[] = { +"Syntax: PURGE [ switches ] [ filespec ]", +" Runs the DCL PURGE command. Switches and filespec are not parsed or", +" verified by Kermit, but passed directly to DCL.", +"" +}; +#else +#ifdef CKPURGE +static char * hmxxpurge[] = { +"Syntax: PURGE [ switches ] [ filespec ]", +" Deletes backup files; that is, files whose names end in \".~n~\", where", +" n is a number. PURGE by itself deletes all backup files in the current", +" directory. Switches:", + +" ", +"/AFTER:date-time", +#ifdef VMS +" Specifies that only those files created after the given date-time are", +#else +" Specifies that only those files modified after the given date-time are", +#endif /* VMS */ +" to be purged. HELP DATE for info about date-time formats.", +" ", +"/BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified before the given date-time", +#else +" Specifies that only those files modified before the given date-time", +#endif /* VMS */ +" are to be purged.", +" ", +"/NOT-AFTER:date-time", +#ifdef VMS +" Specifies that only those files modified at or before the given date-time", +#else +" Specifies that only those files modified at or before the given date-time", +#endif /* VMS */ +" are to be purged.", +" ", +"/NOT-BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified at or after the given date-time", +#else +" Specifies that only those files modified at or after the given date-time", +#endif /* VMS */ +" are to be purged.", +" ", +"/LARGER-THAN:number", +" Specifies that only those files longer than the given number of bytes are", +" to be purged.", +" ", +"/SMALLER-THAN:number", +" Specifies that only those files smaller than the given number of bytes are", +" to be purged.", +" ", +"/EXCEPT:pattern", +" Specifies that any files whose names match the pattern, which can be a", +" regular filename or may contain wildcards, are not to be purged. To", +" specify multiple patterns (up to 8), use outer braces around the group", +" and inner braces around each pattern:", +" ", +" /EXCEPT:{{pattern1}{pattern2}...}", +" ", +#ifdef UNIXOROSK +"/DOTFILES", +" Include (purge) files whose names begin with \".\".", +" ", +"/NODOTFILES", +" Skip (don't purge) files whose names begin with \".\".", +" ", +#endif /* UNIXOROSK */ +#ifdef RECURSIVE +"/RECURSIVE", +" Descends through the current or specified directory tree.", +" ", +#endif /* RECURSIVE */ +"/KEEP:n", +" Retain the 'n' most recent (highest-numbered) backup files for each file.", +" By default, none are kept. If /KEEP is given without a number, 1 is used.", +" ", +"/LIST", +" Display each file as it is processed and say whether it is purged or kept.", +" Synonyms: /LOG, /VERBOSE.", +" ", +"/NOLIST", +" The PURGE command should operate silently (default).", +" Synonyms: /NOLOG, /QUIET.", +" ", +"/HEADING", +" Print heading and summary information.", +" ", +"/NOHEADING", +" Don't print heading and summary information.", +" ", +"/PAGE", +" When /LIST is in effect, pause at the end of each screenful, even if", +" COMMAND MORE-PROMPTING is OFF.", +" ", +"/NOPAGE", +" Don't pause, even if COMMAND MORE-PROMPTING is ON.", +" ", +"/ASK", +" Interactively ask permission to delete each backup file.", +" ", +"/NOASK", +" Purge backup files without asking permission.", +" ", +"/SIMULATE", +" Inhibits the actual deletion of files; use to preview which files would", +" actually be deleted. Implies /LIST.", +" ", +"Use SET OPTIONS PURGE [ switches ] to change defaults; use SHOW OPTIONS to", +"display customized defaults. Also see HELP DELETE, HELP WILDCARD.", +"" +}; +#endif /* CKPURGE */ +#endif /* VMS */ + +static char *hmxxclo[] = { +"Syntax: CLOSE [ item ]", +" Close the indicated item. The default item is CONNECTION, which is the", +" current SET LINE or SET HOST connection. The other items are:", +" ", +#ifdef CKLOGDIAL +" CX-LOG (connection log, opened with LOG CX)", +#endif /* CKLOGDIAL */ +#ifndef NOLOCAL +" SESSION-LOG (opened with LOG SESSION)", +#endif /* NOLOCAL */ +#ifdef TLOG +" TRANSACTION-LOG (opened with LOG TRANSACTIONS)", +#endif /* TLOG */ +" PACKET-LOG (opened with LOG PACKETS)", +#ifdef DEBUG +" DEBUG-LOG (opened with LOG DEBUG)", +#endif /* DEBUG */ +#ifndef NOSPL +" READ-FILE (opened with OPEN READ)", +" WRITE-FILE (opened with OPEN WRITE or OPEN APPEND)", +#endif /* NOSPL */ +" ", +"Type HELP LOG and HELP OPEN for further info.", +"" +}; + +#ifdef CKLEARN +static char * hmxxlearn[] = { +"Syntax: LEARN [ /ON /OFF /CLOSE ] [ filename ]", +" Records a login script. If you give a filename, the file is opened for", +" subsequent recording. If you don't give any switches, /ON is assumed.", +" /ON enables recording to the current file (if any); /OFF disables", +" recording. /CLOSE closes the current file (if any). After LEARN /CLOSE", +" or exit from Kermit, your script is available for execution by the TAKE", +" command.", +"" +}; +#endif /* CKLEARN */ + +#ifdef CK_MINPUT +static char *hmxxminp[] = { +"Syntax: MINPUT n [ string1 [ string2 [ ... ] ] ]", +"Example: MINPUT 5 Login: {Username: } {NO CARRIER} BUSY RING", +" For use in script programs. Waits up to n seconds for any one of the", +" strings to arrive on the communication device. If no strings are given,", +" the command waits for any character at all to arrive. Strings are", +" separated by spaces; use { braces } for grouping. If any of the strings", +" is encountered within the timeout interval, the command succeeds and the", +" \\v(minput) variable is set to the number of the string that was matched:", +" 1, 2, 3, etc. If none of the strings arrives, the command times out,", +" fails, and \\v(minput) is set to 0. If the timeout interval is 0 the", +" MINPUT command does not wait; i.e. the given text must already be", +" available for reading for the MINPUT command to succeed. If the interval", +" is negative, the MINPUT command waits forever.", +" ", +"Also see: INPUT, REINPUT, SET INPUT.", +"" }; +#endif /* CK_MINPUT */ + +#ifndef NOLOCAL +static char *hmxxcon[] = { +"Syntax: CONNECT (or C, or CQ) [ switches ]", +" Connect to a remote computer via the serial communications device given in", +#ifdef OS2 +" the most recent SET PORT command, or to the network host named in the most", +#else +" the most recent SET LINE command, or to the network host named in the most", +#endif /* OS2 */ +" recent SET HOST command. Type the escape character followed by C to get", +" back to the C-Kermit prompt, or followed by ? for a list of CONNECT-mode", +#ifdef OS2 +" escape commands. You can also assign the \\Kexit verb to the key or", +" key-combination of your choice; by default it is assigned to Alt-x.", +#else +" escape commands.", +" ", +"Include the /QUIETLY switch to suppress the informational message that", +"tells you how to escape back, etc. CQ is a synonym for CONNECT /QUIETLY.", +#endif /* OS2 */ +" ", +"Other switches include:", +#ifdef CK_TRIGGER +" ", +"/TRIGGER:string", +" One or more strings to look for that will cause automatic return to", +" command mode. To specify one string, just put it right after the", +" colon, e.g. \"/TRIGGER:Goodbye\". If the string contains any spaces, you", +" must enclose it in braces, e.g. \"/TRIGGER:{READY TO SEND...}\". To", +" specify more than one trigger, use the following format:", +" ", +" /TRIGGER:{{string1}{string2}...{stringn}}", +" ", +" Upon return from CONNECT mode, the variable \\v(trigger) is set to the", +" trigger string, if any, that was actually encountered. This value, like", +" all other CONNECT switches applies only to the CONNECT command with which", +" it is given, and overrides (temporarily) any global SET TERMINAL TRIGGER", +" string that might be in effect.", +#endif /* CK_TRIGGER */ +#ifdef OS2 +" ", +"/IDLE-LIMIT:number", +" The number of seconds of idle time, after which Kermit returns", +" automatically to command mode; default 0 (no limit).", +" ", +"/IDLE-INTERVAL:number", +" The number of seconds of idle time, after which Kermit automatically", +" transmits the idle string.", +" ", +"/IDLE-STRING:string", +" The string to transmit whenever the idle interval has passed.", +" ", +"/TIME-LIMIT:number", +" The maximum number of seconds for which the CONNECT session may last.", +" The default is 0 (no limit). If a nonzero number is given, Kermit returns", +" automatically to command mode after this many seconds.", +#endif /* OS2 */ +"" }; +#endif /* NOLOCAL */ + +static char *hmxxmget[] = { +"Syntax: MGET [ switches... ] remote-filespec [ remote-filespec ... ]", +" ", +"Just like GET (q.v.) except allows a list of remote file specifications,", +"separated by spaces.", +"" +}; + +static char *hmxxget[] = { +"Syntax: GET [ switches... ] remote-filespec [ as-name ]", +" Tells the other Kermit, which must be in (or support autoswitching into)", +" server mode, to send the named file or files. If the remote-filespec or", +" the as-name contain spaces, they must be enclosed in braces. If as-name", +" is the name of an existing local directory, incoming files are placed in", +" that directory; if it is the name of directory that does not exist, Kermit", +" tries to create it. Optional switches include:", +" ", +"/AS-NAME:text", +" Specifies \"text\" as the name to store the incoming file under, or", +" directory to store it in. You can also specify the as-name as the second", +" filename on the GET command line.", +" ", +"/BINARY", +" Performs this transfer in binary mode without affecting the global", +" transfer mode.", +" ", +"/COMMAND", +" Receives the file into the standard input of a command, rather than saving", +" it on disk. The /AS-NAME or the second \"filename\" on the GET command", +" line is interpreted as the name of a command.", +" ", +"/DELETE", +" Asks the other Kermit to delete the file (or each file in the group)", +" after it has been transferred successfully.", +" ", +"/EXCEPT:pattern", +" Specifies that any files whose names match the pattern, which can be a", +" regular filename, or may contain \"*\" and/or \"?\" metacharacters,", +" are to be refused. To specify multiple patterns (up to 8), use outer", +" braces around the group, and inner braces around each pattern:", +" ", +" /EXCEPT:{{pattern1}{pattern2}...}", +" ", +"/FILENAMES:{CONVERTED,LITERAL}", +" Overrides the global SET FILE NAMES setting for this transfer only.", +" ", +"/FILTER:command", +" Causes the incoming file to passed through the given command (standard", +" input/output filter) before being written to disk.", +" ", +#ifdef VMS +"/IMAGE", +" Transfer in image mode.", +" ", +#endif /* VMS */ +#ifdef CK_LABELED +"/LABELED", +" VMS and OS/2 only: Specifies labeled transfer mode.", +" ", +#endif /* CK_LABELED */ + +"/MOVE-TO:directory-name", +" Specifies that each file that arrives should be moved to the specified", +" directory after, and only if, it has been received successfully.", +" ", +"/PATHNAMES:{OFF,ABSOLUTE,RELATIVE,AUTO}", +" Overrides the global SET RECEIVE PATHNAMES setting for this transfer.", +" ", +"/PIPES:{ON,OFF}", +" Overrides the TRANSFER PIPES setting for this command only. ON allows", +" reception of files with names like \"!tar xf -\" to be automatically", +" directed to a pipeline.", +" ", +"/QUIET", +" When sending in local mode, this suppresses the file-transfer display.", +" ", +"/RECOVER", +" Used to recover from a previously interrupted transfer; GET /RECOVER", +" is equivalent REGET. Works only in binary mode.", +" ", +"/RECURSIVE", +" Tells the server to descend through the directory tree when locating", +" the files to be sent.", +" ", +"/RENAME-TO:string", +" Specifies that each file that arrives should be renamed as specified", +" after, and only if, it has been received successfully. The string should", +" normally contain variables like \\v(filename) or \\v(filenum).", +" ", +"/TEXT", +" Performs this transfer in text mode without affecting the global", +" transfer mode.", +" ", +"/TRANSPARENT", +" Inhibits character-set translation of incoming text files for the duration", +" of the GET command without affecting subsequent commands.", +" ", +"Also see HELP MGET, HELP SEND, HELP RECEIVE, HELP SERVER, HELP REMOTE.", +""}; + +static char *hmxxlg[] = { +"Syntax: LOG (or L) log-type [ filename [ { NEW, APPEND } ] ]", +" ", +"Record information in a log file:", +" ", +#ifdef CKLOGDIAL +"CX", +" Connections made with SET LINE, SET PORT, SET HOST, DIAL, TELNET, etc.", +" The default filename is CX.LOG in your home directory and APPEND is the", +" default mode for opening.", +" ", +#endif /* CKLOGDIAL */ +#ifdef DEBUG +"DEBUG", +" Debugging information, to help track down bugs in the C-Kermit program.", +" The default log name is debug.log in current directory.", +" ", +#endif /* DEBUG */ +"PACKETS", +" Kermit packets, to help with protocol problems. The default filename is", +" packet.log in current directory.", +" ", +#ifndef NOLOCAL +"SESSION", +" Records your CONNECT session (default: session.log in current directory).", +" ", +#endif /* NOLOCAL */ +#ifdef TLOG +"TRANSACTIONS", +" Names and statistics about files transferred (default: transact.log in", +" current directory; see HELP SET TRANSACTION-LOG for transaction-log format", +" options.)", +" ", +#endif /* TLOG */ +"If you include the APPEND keyword after the filename, the existing log file,", +"if any, is appended to; otherwise a new file is created (except APPEND is", +"the default for the connection log). Use CLOSE to stop logging.", +#ifdef OS2ORUNIX +" ", +"Note: The filename can also be a pipe, e.g.:", +" ", +" log transactions |lpr", +" log debug {| grep \"^TELNET\" > debug.log}", +" ", +"Braces are required if the pipeline or filename contains spaces.", +#endif /* OS2ORUNIX */ +"" }; + +#ifndef NOSCRIPT +static char *hmxxlogi[] = { "\ +Syntax: SCRIPT text", +" A limited and cryptic \"login assistant\", carried over from old C-Kermit", +" releases for comptability, but not recommended for use. Instead, please", +" use the full script programming language described in chapters 17-19 of", +" \"Using C-Kermit\".", +" ", +" Login to a remote system using the text provided. The login script", +" is intended to operate similarly to UNIX uucp \"L.sys\" entries.", +" A login script is a sequence of the form:", +" ", +" expect send [expect send] . . .", +" ", +" where 'expect' is a prompt or message to be issued by the remote site, and", +" 'send' is the names, numbers, etc, to return. The send may also be the", +" keyword EOT to send Control-D, or BREAK (or \\\\b) to send a break signal.", +" Letters in send may be prefixed by ~ to send special characters:", +" ", +" ~b backspace, ~s space, ~q '?', ~n linefeed, ~r return, ~c don\'t", +" append a return, and ~o[o[o]] for octal of a character. As with some", +" UUCP systems, sent strings are followed by ~r unless they end with ~c.", +" ", +" Only the last 7 characters in each expect are matched. A null expect,", +" e.g. ~0 or two adjacent dashes, causes a short delay. If you expect", +" that a sequence might not arrive, as with uucp, conditional sequences", +" may be expressed in the form:", +" ", +" -send-expect[-send-expect[...]]", +" ", +" where dashed sequences are followed as long as previous expects fail.", +"" }; +#endif /* NOSCRIPT */ + +#ifndef NOFRILLS +static char * hmxxtyp[] = { +"Syntax: TYPE [ switches... ] file", +" Displays a file on the screen. Pauses automatically at end of each", +" screenful if COMMAND MORE-PROMPTING is ON. Optional switches:", +" ", +" /PAGE", +" Pause at the end of each screenful even if COMMAND MORE-PROMPTING OFF.", +" Synonym: /MORE", +" /NOPAGE", +" Don't pause at the end of each screen even if COMMAND MORE-PROMPTING ON." +, +" /HEAD:n", +" Only type the first 'n' lines of the file.", +" /TAIL:n", +" Only type the last 'n' lines of the file.", +" /MATCH:pattern", +" Only type lines that match the given pattern. HELP WILDCARDS for info", +" info about patterns. /HEAD and /TAIL apply after /MATCH.", +" /PREFIX:string", +" Print the given string at the beginning of each line.", +" /NUMBER", +" Add line numbers (conflicts with /PREFIX)", +" /WIDTH:number", +" Truncate each line at the given column number before printing.", +#ifdef KUI +" Or when combined with /GUI specifies the width of the dialog box.", +" /HEIGHT:number", +" When combined with /GUI specifies the height of the dialog box.", +" /GUI:string", +" Specifies the title to use for the dialog box.", +#endif /* KUI */ +" /COUNT", +" Count lines (and matches) and print the count(s) but not the lines.", +#ifdef UNICODE +" /CHARACTER-SET:name", +" Translates from the named character set.", +#ifndef OS2 +" /TRANSLATE-TO:name", +" Translates to the named character set (default = current file charset).", +#endif /* OS2 */ +" /TRANSPARENT", +" Inhibits character-set translation.", +#endif /* UNICODE */ +" /OUTPUT:name", +" Sends results to the given file. If this switch is omitted, the", +" results appear on your screen. This switch overrides any express or", +" implied /PAGE switch.", +" ", +"You can use SET OPTIONS TYPE to set the defaults for /PAGE or /NOPAGE and", +"/WIDTH. Use SHOW OPTIONS to see current TYPE options.", +"" +}; + +static char * hmxxcle[] = { +"Syntax: CLEAR [ item-name ]", +" ", +"Clears the named item. If no item is named, DEVICE-AND-INPUT is assumed.", +" ", +" ALARM Clears any pending alarm (see SET ALARM).", +#ifdef CK_APC +" APC-STATUS Clears Application Program Command status.", +#endif /* CK_APC */ +#ifdef PATTERNS +" BINARY-PATTERNS Clears the file binary-patterns list.", +#endif /* PATTERNS */ +#ifdef OS2 +" COMMAND-SCREEN Clears the current command screen.", +#endif /* OS2 */ +" DEVICE Clears the current port or network input buffer.", +" DEVICE-AND-INPUT Clears both the device and the INPUT buffer.", +" DIAL-STATUS Clears the \\v(dialstatus) variable.", +" \ +INPUT Clears the INPUT-command buffer and the \\v(input) variable.", +" KEYBOARD-BUFFER Clears the command terminal keyboard input buffer.", +#ifdef OS2 +" \ +SCROLLBACK empties the scrollback buffer including the current screen.", +#endif /* OS2 */ +" SEND-LIST Clears the current SEND list (see ADD).", +#ifdef OS2 +" \ +TERMINAL-SCREEN Clears the current screen a places it into the scrollback.", +" buffer.", +#endif /* OS2 */ +#ifdef PATTERNS +" TEXT-PATTERNS Clears the file text-patterns list.", +#endif /* PATTERNS */ +""}; +#endif /* NOFRILLS */ + +static char * hmxxdate[] = { +"Syntax: DATE [ date-time [ timezone ] ] [ delta-time ]", +" Prints a date-time in standard format: yyyymmdd_hh:mm:ss.", +" Various date-time formats are accepted:", +" ", +" . The date, if given, must precede the time.", +" . The year must be four digits or else a 2-digit format dd mmm yy,", +" in which case if (yy < 50) yyyy = yy + 2000; else yyyy = yy + 1900.", +" . If the year comes first, the second field is the month.", +" . The day, month, and year may be separated by spaces, /, -, or underscore." +," . The date and time may be separated by spaces or underscore.", +" . The month may be numeric (1 = January) or spelled out or abbreviated in", +" English.", +" . The time may be in 24-hour format or 12-hour format.", +" . If the hour is 12 or less, AM is assumed unless AM or PM is included.", +" . If the date is omitted but a time is given, the current date is supplied." +, +" . If the time is given but date omitted, 00:00:00 is supplied.", +" . If both the date and time are omitted, the current date and time are", +" supplied.", +" ", +" The following shortcuts can also be used in place of dates:", +" ", +" TODAY", +" Today's date, optionally followed by a time; 00:00:00 if no time given.", +" ", +" YESTERDAY", +" Yesterday's date, optionally followed by a time (default 00:00:00).", +" ", +" TOMORROW", +" Tomorrows's date, optionally followed by a time (default 00:00:00).", +" ", +" Timezone specifications are similar to those used in e-mail and HTTP", +" headers, either a USA timezone name, e.g. EST or a signed four-digit", +" timezone offset, {+,-}hhmm, e.g., -0500; it is used to convert date-time," +, +" a local time in that timezone, to GMT which is then converted to the", +" local time at the host. If no timezone is given, the date-time is local." +, +" ", +" Delta times are given as {+,-}[number date-units][hh[:mm[:ss]]]", +" A date in the future/past relative to the date-time; date-units may be", +" DAYS, WEEKS, MONTHS, YEARS: +3days, -7weeks, +3:00, +1month 8:00.", +" ", +"All the formats shown above are acceptable as arguments to date-time switches" +, +"such as /AFTER: or /BEFORE:, and to functions such as \\fcvtdate(),", +"\\fdiffdate(), and \\futcdate(), that take date-time strings as arguments.", +"" +}; + + +#ifndef NOXFER +static char * hmxxsen[] = { +"Syntax: SEND (or S) [ switches...] [ filespec [ as-name ] ]", +" Sends the file or files specified by filespec. If the filespec is omitted", +" the SEND-LIST is used (HELP ADD for more info). The filespec may contain", +" wildcard characters. An 'as-name' may be given to specify the name(s)", +" the files(s) are sent under; if the as-name is omitted, each file is", +" sent under its own name. Also see HELP MSEND, HELP WILDCARD.", +" Optional switches include:", +" ", +#ifndef NOSPL +"/ARRAY:", +" Specifies that the data to be sent comes from the given array, such as", +" \\&a[]. A range may be specified, e.g. SEND /ARRAY:&a[100:199]. Leave", +" the brackets empty or omit them altogether to send the whole 1-based array." +, +" Include /TEXT to have Kermit supply a line terminator at the end of each", +" array element (and translate character sets if character-set translations", +" are set up), or /BINARY to treat the array as one long string of characters" +, +" to be sent as-is. If an as-name is not specified, the array is sent with", +" the name _ARRAY_X_, where \"X\" is replaced by actual array letter.", +" ", +#endif /* NOSPL */ + +"/AS-NAME:", +" Specifies as the name to send the file under instead of its real", +" name. This is equivalent to giving an as-name after the filespec.", +" ", +"/BINARY", +" Performs this transfer in binary mode without affecting the global", +" transfer mode.", +" ", +"/TEXT", +" Performs this transfer in text mode without affecting the global", +" transfer mode.", +" ", +"/TRANSPARENT", +" Inhibits character-set translation for text files for the duration of", +" the SEND command without affecting subsequent commands.", +" ", +"/NOBACKUPFILES", +" Skip (don't send) Kermit or EMACS backup files (files with names that", +" end with .~n~, where n is a number).", +" ", +#ifdef UNIXOROSK +"/DOTFILES", +" Include (send) files whose names begin with \".\".", +" ", +"/NODOTFILES", +" Don't send files whose names begin with \".\".", +" ", +"/FOLLOWLINKS", +" Send files that are pointed to by symbolic links.", +" ", +"/NOFOLLOWLINKS", +" Skip over symbolic links (default).", +" ", +#endif /* UNIXOROSK */ + +#ifdef VMS +"/IMAGE", +" Performs this transfer in image mode without affecting the global", +" transfer mode.", +" ", +#endif /* VMS */ +#ifdef CK_LABELED +"/LABELED", +" Performs this transfer in labeled mode without affecting the global", +" transfer mode.", +" ", +#endif /* CK_LABELED */ +"/COMMAND", +" Sends the output from a command, rather than the contents of a file.", +" The first \"filename\" on the SEND command line is interpreted as the name", +" of a command; the second (if any) is the as-name.", +" ", +"/FILENAMES:{CONVERTED,LITERAL}", +" Overrides the global SET FILE NAMES setting for this transfer only.", +" ", +"/PATHNAMES:{OFF,ABSOLUTE,RELATIVE}", +" Overrides the global SET SEND PATHNAMES setting for this transfer.", +" ", +"/FILTER:command", +" Specifies a command \ +(standard input/output filter) to pass the file through", +" before sending it.", +" ", +"/DELETE", +" Deletes the file (or each file in the group) after it has been sent", +" successfully (applies only to real files).", +" ", +"/QUIET", +" When sending in local mode, this suppresses the file-transfer display.", +" ", +"/RECOVER", +" Used to recover from a previously interrupted transfer; SEND /RECOVER", +" is equivalent RESEND (use in binary mode only).", +" ", +"/RECURSIVE", +" Tells C-Kermit to look not only in the given or current directory for", +" files that match the filespec, but also in all its subdirectories, and", +" all their subdirectories, etc.", +" ", +"/RENAME-TO:name", +" Tells C-Kermit to rename each source file that is sent successfully to", +" the given name (usually you should include \\v(filename) in the new name,", +" which is replaced by the original filename.", +" ", +"/MOVE-TO:directory", +" Tells C-Kermit to move each source file that is sent successfully to", +" the given directory.", +" ", +"/STARTING:number", +" Starts sending the file from the given byte position.", +" SEND /STARTING:n filename is equivalent to PSEND filename n.", +" ", +"/SUBJECT:text", +" Specifies the subject of an email message, to be used with /MAIL. If the", +" text contains spaces, it must be enclosed in braces.", +" ", +"/MAIL:address", +" Sends the file as e-mail to the given address; use with /SUBJECT:.", +" ", +"/PRINT:options", +" Sends the file to be printed, with optional options for the printer.", +" ", +#ifdef CK_XYZ +"/PROTOCOL:name", +" Uses the given protocol to send the file (Kermit, Zmodem, etc) for this", +" transfer without changing global protocol.", +" ", +#endif /* CK_XYZ */ +"/AFTER:date-time", +#ifdef VMS +" Specifies that only those files created after the given date-time are", +#else +" Specifies that only those files modified after the given date-time are", +#endif /* VMS */ +" to be sent. HELP DATE for info about date-time formats.", +" ", +"/BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified before the given date-time", +#else +" Specifies that only those files modified before the given date-time", +#endif /* VMS */ +" are to be sent.", +" ", +"/NOT-AFTER:date-time", +#ifdef VMS +" Specifies that only those files modified at or before the given date-time", +#else +" Specifies that only those files modified at or before the given date-time", +#endif /* VMS */ +" are to be sent.", +" ", +"/NOT-BEFORE:date-time", +#ifdef VMS +" Specifies that only those files modified at or after the given date-time", +#else +" Specifies that only those files modified at or after the given date-time", +#endif /* VMS */ +" are to be sent.", +" ", +"/LARGER-THAN:number", +" Specifies that only those files longer than the given number of bytes are", +" to be sent.", +" ", +"/SMALLER-THAN:number", +" Specifies that only those files smaller than the given number of bytes are", +" to be sent.", +" ", +"/EXCEPT:pattern", +" Specifies that any files whose names match the pattern, which can be a", +" regular filename, or may contain \"*\" and/or \"?\" metacharacters,", +" are not to be sent. To specify multiple patterns (up to 8), use outer", +" braces around the group, and inner braces around each pattern:", +" ", +" /EXCEPT:{{pattern1}{pattern2}...}", +" ", +"/TYPE:{ALL,TEXT,BINARY}", +" Send only files of the given type (see SET FILE SCAN).", +" ", +"/LISTFILE:filename", +" Specifies the name of a file that contains the list of names of files", +" that are to be sent. The filenames should be listed one name per line", +" in this file (but a name can contain wildcards).", +" ", +"Also see HELP RECEIVE, HELP GET, HELP SERVER, HELP REMOTE.", +""}; + +static char *hmxxrc[] = { +"Syntax: RECEIVE (or R) [ switches... ] [ as-name ]", +" Wait for a file to arrive from the other Kermit, which must be given a", +" SEND command. If the optional as-name is given, the incoming file or", +" files are stored under that name, otherwise it will be stored under", +#ifndef CK_TMPDIR +" the name it arrives with.", +#else +#ifdef OS2 +" the name it arrives with. If the filespec denotes a disk and/or", +" directory, the incoming file or files will be stored there.", +#else +" the name it arrives with. If the filespec denotes a directory, the", +" incoming file or files will be placed in that directory.", +#endif /* OS2 */ +#endif /* CK_TMPDIR */ +" ", +"Optional switches include:", +" ", +"/AS-NAME:text", +" Specifies \"text\" as the name to store the incoming file under.", +" You can also specify the as-name as a filename on the command line.", +" ", +"/BINARY", +" Skips text-mode conversions unless the incoming file arrives with binary", +" attribute", +" ", +"/COMMAND", +" Receives the file into the standard input of a command, rather than saving", +" it on disk. The /AS-NAME or the \"filename\" on the RECEIVE command line", +" is interpreted as the name of a command.", +" ", +"/EXCEPT:pattern", +" Specifies that any files whose names match the pattern, which can be a", +" regular filename, or may contain \"*\" and/or \"?\" metacharacters,", +" are to be refused. To specify multiple patterns (up to 8), use outer", +" braces around the group, and inner braces around each pattern:", +" ", +" /EXCEPT:{{pattern1}{pattern2}...}", +" ", +"/FILENAMES:{CONVERTED,LITERAL}", +" Overrides the global SET FILE NAMES setting for this transfer only.", +" ", +"/FILTER:command", +" Causes the incoming file to passed through the given command (standard", +" input/output filter) before being written to disk.", +" ", +#ifdef VMS +"/IMAGE", +" Receives the file in image mode.", +" ", +#endif /* VMS */ +#ifdef CK_LABELED +"/LABELED", +" Specifies labeled transfer mode.", +" ", +#endif /* CK_LABELED */ + +"/MOVE-TO:directory-name", +" Specifies that each file that arrives should be moved to the specified", +" directory after, and only if, it has been received successfully.", +" ", +"/PATHNAMES:{OFF,ABSOLUTE,RELATIVE,AUTO}", +" Overrides the global SET RECEIVE PATHNAMES setting for this transfer.", +" ", +"/PIPES:{ON,OFF}", +" Overrides the TRANSFER PIPES setting for this command only. ON allows", +" reception of files with names like \"!tar xf -\" to be automatically", +" directed to a pipeline.", +" ", +"/PROTOCOL:name", +" Use the given protocol to receive the incoming file(s).", +" ", +"/QUIET", +" When sending in local mode, this suppresses the file-transfer display.", +" ", +"/RECURSIVE", +" Equivalent to /PATHNAMES:RELATIVE.", +" ", +"/RENAME-TO:string", +" Specifies that each file that arrives should be renamed as specified", +" after, and only if, it has been received successfully. The string should", +" normally contain variables like \\v(filename) or \\v(filenum).", +" ", +"/TEXT", +" Forces text-mode conversions unless the incoming file has the binary", +" attribute", +" ", +"/TRANSPARENT", +" Inhibits character-set translation of incoming text files for the duration", +" of the RECEIVE command without affecting subsequent commands.", +" ", +"Also see HELP SEND, HELP GET, HELP SERVER, HELP REMOTE.", +"" }; + +#ifndef NORESEND +static char *hmxxrsen = "\ +Syntax: RESEND filespec [name]\n\n\ + Resend the file or files, whose previous transfer was interrupted.\n\ + Picks up from where previous transfer left off, IF the receiver was told\n\ + to SET FILE INCOMPLETE KEEP. Only works for binary-mode transfers.\n\ + Requires the other Kermit to have RESEND capability."; + +static char *hmxxrget = "\ +Syntax: REGET filespec\n\n\ + Ask a server to RESEND a file to C-Kermit."; + +static char *hmxxpsen = "\ +Syntax: PSEND filespec position [name]\n\n\ + Just like SEND, except sends the file starting at the given byte position."; +#endif /* NORESEND */ + +#ifndef NOMSEND +static char *hmxxmse[] = { +"Syntax: MSEND [ switches... ] filespec [ filespec [ ... ] ]", +" Sends the files specified by the filespecs. One or more filespecs may be", +" listed, separated by spaces. Any or all filespecs may contain wildcards", +" and they may be in different directories. Alternative names cannot be", +" given. Switches include /BINARY /DELETE /MAIL /PROTOCOL /QUIET /RECOVER", +" /TEXT /TYPE; see HELP SEND for descriptions.", +"" +}; +#endif /* NOMSEND */ + +static char *hmxxadd[] = { +#ifndef NOMSEND +"ADD SEND-LIST filespec [ [ ] ]", +" Adds the specified file or files to the current SEND list. Use SHOW", +" SEND-LIST and CLEAR SEND-LIST to display and clear the list; use SEND", +" by itself to send the files from it.", +" ", +#endif /* NOMSEND */ +#ifdef PATTERNS +"ADD BINARY-PATTERNS [ [ ... ] ]", +" Adds the pattern(s), if any, to the SET FILE BINARY-PATTERNS list.", +" ", +"ADD TEXT-PATTERNS [ [ ... ] ]", +" Adds the pattern(s), if any, to the SET FILE TEXT-PATTERNS list.", +" Use SHOW PATTERNS to see the lists. See HELP SET FILE for further info.", +#endif /* PATTERNS */ +""}; + +static char *hmxxremv[] = { +#ifdef PATTERNS +"REMOVE BINARY-PATTERNS [ [ ... ] ]", +" Removes the pattern(s), if any, from the SET FILE BINARY-PATTERNS list", +" ", +"REMOVE TEXT-PATTERNS [ [ ... ] ]", +" Removes the given patterns from the SET FILE TEXT-PATTERNS list.", +" Use SHOW PATTERNS to see the lists. See HELP SET FILE for further info.", +#endif /* PATTERNS */ +""}; +#endif /* NOXFER */ + +#ifndef NOSERVER +static char *hmxxser = "Syntax: SERVER\n\ + Enter server mode on the current connection. All further commands\n\ + are taken in packet form from the other Kermit program. Use FINISH,\n\ + BYE, or REMOTE EXIT to get C-Kermit out of server mode."; +#endif /* NOSERVER */ + +static char *hmhset[] = { +" The SET command establishes communication, file, scripting, or other", +" parameters. The SHOW command can be used to display the values of", +" SET parameters. Help is available for each individual parameter;", +" type HELP SET ? to see what's available.", +"" }; + +#ifndef NOSETKEY +static char *hmhskey[] = { +"Syntax: SET KEY k text", +"Or: SET KEY CLEAR", +" Configure the key whose \"scan code\" is k to send the given text when", +" pressed during CONNECT mode. SET KEY CLEAR restores all the default", +" key mappings. If there is no text, the default key binding is restored", +#ifndef NOCSETS +" for the key k. SET KEY mappings take place before terminal character-set", +" translation.", +#else +" the key k.", +#endif /* NOCSETS */ +#ifdef OS2 +" ", +" The text may contain \"\\Kverbs\" to denote actions, to stand for DEC", +" keypad, function, or editing keys, etc. For a list of available keyboard", +" verbs, type SHOW KVERBS.", +#endif /* OS2 */ +" ", +" To find out the scan code and mapping for a particular key, use the", +" SHOW KEY command.", +""}; +#endif /* NOSETKEY */ + +static char *hmxychkt[] = { "Syntax: SET BLOCK-CHECK type", +" ", +" Type of packet block check to be used for error detection, 1, 2, 3, or", +" BLANK-FREE-2. Type 1 is standard, and catches most errors. Types 2 and 3", +" specify more rigorous checking at the cost of higher overhead. The", +" BLANK-FREE-2 type is the same as Type 2, but is guaranteed to contain no", +" blanks.", +"" }; + +static char * hmxydeb[] = { +"Syntax: SET DEBUG { SESSION, ON, OFF, TIMESTAMP }", +" ", +"SET DEBUG ON", +#ifdef DEBUG +" Opens a debug log file named debug.log in the current directory.", +" Use LOG DEBUG if you want specify a different log file name or path.", +#else +" (Has no effect in this version of Kermit.)", +#endif /* DEBUG */ +" ", +"SET DEBUG OFF", +" Stops debug logging and session debugging.", +" ", +"SET DEBUG SESSION", +#ifndef NOLOCAL +" Displays control and 8-bit characters symbolically during CONNECT mode.", +" Equivalent to SET TERMINAL DEBUG ON.", +#else +" (Has no effect in this version of Kermit.)", +#endif /* NOLOCAL */ +" ", +"SET DEBUG TIMESTAMP { ON, OFF }", +" Enables/Disables timestamps on debug log entries.", +"" }; + +#ifdef CK_SPEED +static char *hmxyqctl[] = { +"Syntax: SET CONTROL-CHARACTER { PREFIXED, UNPREFIXED } { ..., ALL }", +" ", +" is the numeric ASCII code for a control character 1-31,127-159,255." +, +" The word \"ALL\" means all characters in this range.", +" ", +" PREFIXED means the given control character must be converted to a", +" printable character and prefixed, the default for all control characters.", +" ", +" UNPREFIXED means you think it is safe to send the given control", +" character as-is, without a prefix. USE THIS OPTION AT YOUR OWN RISK!", +" ", +" SHOW CONTROL to see current settings. SET CONTROL PREFIXED ALL is", +" recommended for safety. You can include multiple values in one", +" command, separated by spaces.", +"" }; +#endif /* CK_SPEED */ + +#ifndef NODIAL +static char *hxymodm[] = { +"Syntax: SET MODEM ...", +" ", +"Note: Many of the SET MODEM parameters are configured automatically when", +"you SET MODEM TYPE, according to the modem's capabilities. SHOW MODEM to", +"see them. Also see HELP DIAL and HELP SET DIAL.", +" ", +"SET MODEM TYPE ", + +" Tells Kermit which kind of modem you have, so it can issue the", +" appropriate modem-specific commands for configuration, dialing, and", +" hanging up. For a list of the modem types known to Kermit, type \"set", +" modem type ?\". The default modem type is GENERIC, which should work", +" with any AT command-set modem that is configured for error correction,", +" data compression, and hardware flow control. Use SET MODEM TYPE NONE", +" for direct serial, connections. Use SET MODEM TYPE USER-DEFINED to use", +" a type of modem that is not built in to Kermit, and then use SET MODEM", +" CAPABILITIES, SET MODEM, DIAL-COMMAND, and SET MODEM COMMAND to tell", +" Kermit how to configure and control it.", + +" ", + +"SET MODEM CAPABILITIES ", +" Use this command for changing Kermit's idea of your modem's capabilities,", +" for example, if your modem is supposed to have built-in error correction", +" but in fact does not. Also use this command to define the capabilities", +" of a USER-DEFINED modem. Capabilities are:", +" ", +" AT AT-commands", +" DC data-compression", +" EC error-correction", +" HWFC hardware-flow", +" ITU v25bis-commands", +" SWFC software-flow", +" KS kermit-spoof", +" SB speed-buffering", +" TB Telebit", +" ", +"SET MODEM CARRIER-WATCH { AUTO, ON, OFF }", +" Synonym for SET CARRIER-WATCH (q.v.)", +" ", +"SET MODEM COMPRESSION { ON, OFF }", +" Enables/disables the modem's data compression feature, if any.", +" ", +"SET MODEM DIAL-COMMAND ", +" The text replaces Kermit's built-in modem dialing command. It must", +" include '%s' (percent s) as a place-holder for the telephone numbers", +" given in your DIAL commands.", +" ", +"SET MODEM ERROR-CORRECTION { ON, OFF }", +" Enables/disables the modem's error-correction feature, if any.", +" ", +"SET MODEM ESCAPE-CHARACTER number", +" Numeric ASCII value of modem's escape character, e.g. 43 for '+'.", +" For Hayes-compatible modems, Kermit uses three copies, e.g. \"+++\".", +" ", +"SET MODEM FLOW-CONTROL {AUTO, NONE, RTS/CTS, XON/XOFF}", +" Selects the type of local flow control to be used by the modem.", +" ", +"SET MODEM HANGUP-METHOD { MODEM-COMMAND, RS232-SIGNAL, DTR }", +" How hangup operations should be done. MODEM-COMMAND means try to", +" escape back to the modem's command processor and give a modem-specific", +" hangup command. RS232-SIGNAL means turn off the DTR signal. DTR is a", +" synonym for RS232-SIGNAL.", +" ", +"SET MODEM KERMIT-SPOOF {ON, OFF}", +" If the selected modem type supports the Kermit protocol directly,", +" use this command to turn its Kermit protocol function on or off.", +" ", +"SET MODEM MAXIMUM-SPEED ", +" Specify the maximum interface speed for the modem.", +" ", +"SET MODEM NAME ", +" Descriptive name for a USER-DEFINED modem.", +" ", +"SET MODEM SPEAKER {ON, OFF}", +" Turns the modem's speaker on or off during dialing.", +" ", +"SET MODEM SPEED-MATCHING {ON, OFF}", +" ON means that C-Kermit changes its serial interface speed to agree with", +" the speed reported by the modem's CONNECT message, if any. OFF means", +" Kermit should not change its interface speed.", +" ", +"SET MODEM VOLUME {LOW, MEDIUM, HIGH}", +" Selects the desired modem speaker volume for when the speaker is ON.", +" ", +"SET MODEM COMMAND commands are used to override built-in modem commands for", +"each modem type, or to fill in commands for the USER-DEFINED modem type.", +"Omitting the optional [ text ] restores the built-in modem-specific command,", +"if any:", +" ", +"SET MODEM COMMAND AUTOANSWER {ON, OFF} [ text ]", +" Modem commands to turn autoanswer on and off.", +" ", +"SET MODEM COMMAND COMPRESSION {ON, OFF} [ text ]", +" Modem commands to turn data compression on and off.", +" ", +"SET MODEM COMMAND ERROR-CORRECTION {ON, OFF} [ text ]", +" Modem commands to turn error correction on and off.", +" ", +"SET MODEM COMMAND HANGUP [ text ]", +" Command that tells the modem to hang up the connection.", +" ", +"SET MODEM COMMAND IGNORE-DIALTONE [ text ]", +" Command that tells the modem not to wait for dialtone before dialing.", +" ", +"SET MODEM COMMAND INIT-STRING [ text ]", +" The 'text' is a replacement for C-Kermit's built-in initialization command", +" for the modem.", +" ", +"SET MODEM COMMAND PREDIAL-INIT [ text ]", +" A second INIT-STRING that is to be sent to the modem just prior to \ +dialing.", +" ", +"SET MODEM COMMAND HARDWARE-FLOW [ text ]", +" Modem command to enable hardware flow control (RTS/CTS) in the modem.", +" ", +"SET MODEM COMMAND SOFTWARE-FLOW [ text ]", +" Modem command to enable local software flow control (Xon/Xoff) in modem.", +" ", +"SET MODEM COMMAND SPEAKER { ON, OFF } [ text ]", +" Modem command to turn the modem's speaker on or off.", +" ", +"SET MODEM COMMAND NO-FLOW-CONTROL [ text ]", +" Modem command to disable local flow control in the modem.", +" ", +"SET MODEM COMMAND PULSE [ text ]", +" Modem command to select pulse dialing.", +" ", +"SET MODEM COMMAND TONE [ text ]", +" Modem command to select tone dialing.", +" ", +"SET MODEM COMMAND VOLUME { LOW, MEDIUM, HIGH } [ text ]", +" Modem command to set the modem's speaker volume.", +""}; + +static char *hmxydial[] = { +"The SET DIAL command establishes or changes all parameters related to", +"dialing the telephone. Also see HELP DIAL and HELP SET MODEM. Use SHOW", +"DIAL to display all of the SET DIAL values.", +" ", +"SET DIAL COUNTRY-CODE ", +" Tells Kermit the telephonic country-code of the country you are dialing", +" from, so it can tell whether a portable-format phone number from your", +" dialing directory will result in a national or an international call.", +" Examples: 1 for USA, Canada, Puerto Rico, etc; 7 for Russia, 39 for Italy,", +" 351 for Portugal, 47 for Norway, 44 for the UK, 972 for Israel, 81 for", +" Japan, ...", +" ", +" If you have not already set your DIAL INTL-PREFIX and LD-PREFIX, then this", +" command sets default values for them: 011 and 1, respectively, for country", +" code 1; 00 and 0, respectively, for all other country codes. If these are", +" not your true international and long-distance dialing prefixes, then you", +" should follow this command by DIAL INTL-PREFIX and LD-PREFIX to let Kermit", +" know what they really are.", +" ", +"SET DIAL AREA-CODE [ ]", +" Tells Kermit the area or city code that you are dialing from, so it can", +" tell whether a portable-format phone number from the dialing directory is", +" local or long distance. Be careful not to include your long-distance", +" dialing prefix as part of your area code; for example, the area code for", +" central London is 171, not 0171.", +" ", +"SET DIAL CONFIRMATION {ON, OFF}", +" Kermit does various transformations on a telephone number retrieved from", +" the dialing directory prior to dialing (use LOOKUP to see them).", +" In case the result might be wrong, you can use SET DIAL CONFIRM ON to have", +" Kermit ask you if it is OK to dial the number, and if not, to let you type", +" in a replacement.", +" ", +"SET DIAL CONNECT { AUTO, ON, OFF }", +" Whether to CONNECT (enter terminal mode) automatically after successfully", +" dialing. ON means to do this; OFF means not to. AUTO (the default) means", +" do it if the DIAL command was given interactively, but don't do it if the", +" DIAL command was issued from a macro or command file. If you specify ON", +" or AUTO, you may follow this by one of the keywords VERBOSE or QUIET, to", +" indicate whether the verbose 4-line 'Connecting...' message is to be", +" displayed if DIAL succeeds and Kermit goes into CONNECT mode.", +" ", +"SET DIAL CONVERT-DIRECTORY {ASK, ON, OFF}", +" The format of Kermit's dialing directory changed in version 5A(192). This", +" command tells Kermit what to do when it encounters an old-style directory:", +" ASK you whether to convert it, or convert it automatically (ON), or leave", +" it alone (OFF). Old-style directories can still be used without", +" conversion, but the parity and speed fields are ignored.", +" ", +"SET DIAL DIRECTORY [ filename [ filename [ filename [ ... ] ] ] ]", +" The name(s) of your dialing directory file(s). If you do not supply any", +" filenames, the dialing directory feature is disabled and all numbers are", +" dialed literally as given in the DIAL command. If you supply more than", +" one directory, all of them are searched.", +" ", +"SET DIAL SORT {ON, OFF}", +" When multiple entries are obtained from your dialing directory, they are", +" sorted in \"cheapest-first\" order. If this does not produce the desired", +" effect, SET DIAL SORT OFF to disable sorting, and the numbers will be", +" dialed in the order in which they were found.", +" ", +"SET DIAL DISPLAY {ON, OFF}", +" Whether to display dialing progress on the screen; default is OFF.", +" ", +"SET DIAL HANGUP {ON, OFF}", +" Whether to hang up the phone prior to dialing; default is ON.", +" ", +"SET DIAL IGNORE-DIALTONE {ON, OFF}", +" Whether to ignore dialtone when dialing; default is OFF.", +" ", +#ifndef NOSPL +"SET DIAL MACRO [ name ]", +" Specify the name of a macro to execute on every phone number dialed, just", +" prior to dialing it, in order to perform any last-minute alterations.", +" ", +#endif /* NOSPL */ +"SET DIAL METHOD {AUTO, DEFAULT, TONE, PULSE}", +" Whether to use the modem's DEFAULT dialing method, or to force TONE or", +" PULSE dialing. AUTO (the default) means to choose tone or pulse dialing", +" based on the country code. (Also see SET DIAL TONE-COUNTRIES and SET DIAL", +" PULSE-COUNTRIES.)", +" ", +"SET DIAL PACING number", +" How many milliseconds to pause between sending each character to the modem", +" dialer. The default is -1, meaning to use the number from the built-in", +" modem database.", +" ", +"SET DIAL PULSE-COUNTRIES [ cc [ cc [ ... ] ] ]", +" Sets the list of countries in which pulse dialing is required. Each cc", +" is a country code.", +" ", +"SET DIAL TEST { ON, OFF }", +" OFF for normal dialing. Set to ON to test dialing procedures without", +" actually dialing.", +" ", +"SET DIAL TONE-COUNTRIES [ cc [ cc [ ... ] ] ]", +" Sets the list of countries in which tone dialing is available. Each cc", +" is a country code.", +" ", +"SET DIAL TIMEOUT number", +" How many seconds to wait for a dialed call to complete. Use this command", +" to override the DIAL command's automatic timeout calculation. A value", +" of 0 turns off this feature and returns to Kermit's automatic dial", +" timeout calculation.", +" ", +"SET DIAL RESTRICT { INTERNATIONAL, LOCAL, LONG-DISTANCE, NONE }", +" Prevents placing calls of the type indicated, or greater. For example", +" SET DIAL RESTRICT LONG prevents placing of long-distance and international", +" calls. If this command is not given, there are no restrictions. Useful", +" when dialing a list of numbers fetched from a dialing directory.", +" ", +"SET DIAL RETRIES ", +" How many times to redial each number if the dialing result is busy or no", +" no answer, until the call is succesfully answered. The default is 0", +" because automatic redialing is illegal in some countries.", +" ", +"SET DIAL INTERVAL ", +" How many seconds to pause between automatic redial attempts; default 10.", +" ", +"The following commands apply to all phone numbers, whether given literally", +"or found in the dialing directory:", +" ", +"SET DIAL PREFIX [ text ]", +" Establish a prefix to be applied to all phone numbers that are dialed,", +" for example to disable call waiting.", +" ", +"SET DIAL SUFFIX [ text ]", +" Establish a suffix to be added after all phone numbers that are dialed.", +" ", +"The following commands apply only to portable-format numbers obtained from", +"the dialing directory; i.e. numbers that start with a \"+\" sign and", +"country code, followed by area code in parentheses, followed by the phone", +"number.", +" ", +"SET DIAL LC-AREA-CODES [ ]", +" Species a list of area codes to which dialing is local, i.e. does not", +" require the LD-PREFIX. Up to 32 area codes may be listed, separated by", +" spaces. Any area codes in this list will be included in the final dial", +" string so do not include your own area code if it should not be dialed.", +" ", +"SET DIAL LC-PREFIX [ ]", +" Specifies a prefix to be applied to local calls made from portable dialing", +" directory entries. Normally no prefix is used for local calls.", +" ", +"SET DIAL LC-SUFFIX [ ]", +" Specifies a suffix to be applied to local calls made from portable dialing", +" directory entries. Normally no suffix is used for local calls.", +" ", +"SET DIAL LD-PREFIX [ ]", +" Your long-distance dialing prefix, to be used with portable dialing", +" directory entries that result in long-distance calls.", +" ", +"SET DIAL LD-SUFFIX [ ]", +" Long-distance dialing suffix, if any, to be used with portable dialing", +" directory entries that result in long-distance calls. This would normally", +" be used for appending a calling-card number to the phone number.", +" ", +"SET DIAL FORCE-LONG-DISTANCE { ON, OFF }", +" Whether to force long-distance dialing for calls that normally would be", +" local. For use (e.g.) in France.", +" ", +"SET DIAL TOLL-FREE-AREA-CODE [ [ [ ... ] ] ]", +" Tells Kermit the toll-free area code(s) in your country.", +" ", +"SET DIAL TOLL-FREE-PREFIX [ ]", +" You toll-free dialing prefix, in case it is different from your long-", +" distance dialing prefix.", +" ", +"SET DIAL INTL-PREFIX ", +" Your international dialing prefix, to be used with portable dialing", +" directory entries that result in international calls.", +" ", +"SET DIAL INTL-SUFFIX ", +" International dialing suffix, if any, to be used with portable dialing", +" directory entries that result in international calls.", +" ", +"SET DIAL PBX-OUTSIDE-PREFIX ", +" Use this to tell Kermit how to get an outside line when dialing from a", +" Private Branch Exchange (PBX).", +" ", +"SET DIAL PBX-EXCHANGE [ [ ... ] ]", +" If PBX-OUTSIDE-PREFIX is set, then you can use this command to tell Kermit", +" the leading digits of one or more local phone numbers that identify it as", +" being on your PBX, so it can make an internal call by deleting those digits" +, +" from the phone number.", +" ", +"SET DIAL PBX-INTERNAL-PREFIX ", +" If PBX-EXCHANGE is set, and Kermit determines from it that a call is", +" internal, then this prefix, if any, is added to the number prior to", +" \ +dialing. Use this if internal calls from your PBX require a special prefix.", +"" }; +#endif /* NODIAL */ + +static char *hmxyflo[] = { "Syntax: SET FLOW [ switch ] value", +" ", +#ifndef NOLOCAL +" Selects the type of flow control to use during file transfer, terminal", +" connection, and script execution.", +#else +" Selects the type of flow control to use during file transfer.", +#endif /* NOLOCAL */ +" ", +" Switches let you associate a particular kind of flow control with each", +" kind of connection: /REMOTE, /MODEM, /DIRECT-SERIAL, /TCPIP, etc; type", +" \"set flow ?\" for a list of available switches. Then whenever you make", +" a connection, the associated flow-control is chosen automatically.", +" The flow-control values are NONE, KEEP, XON/XOFF, and possibly RTS/CTS", +" and some others; again, type \"set flow ?\" for a list. KEEP tells Kermit", +" not to try to change the current flow-control method for the connection.", +" ", +" If you omit the switch and simply supply a value, this value becomes the", +" current flow control type, overriding any default value that might have", +" been chosen in your most recent SET LINE, SET PORT, or SET HOST, or other", +" connection-establishment command.", +" ", +" Type SHOW FLOW-CONTROL to see the current defaults for each connection type" +, +" as well as the current connection type and flow-control setting. SHOW", +" COMMUNICATIONS also shows the current flow-control setting.", +""}; + +static char *hmxyf[] = { +"Syntax: SET FILE parameter value", +" ", +"Sets file-related parameters. Use SHOW FILE to view them. Also see SET", +"(and SHOW) TRANSFER and PROTOCOL.", +" ", +#ifdef VMS +"SET FILE TYPE { TEXT, BINARY, IMAGE, LABELED }", +#else +#ifdef STRATUS +"SET FILE TYPE { TEXT, BINARY, LABELED }", +#else +#ifdef MAC +"SET FILE TYPE { TEXT, BINARY, MACBINARY }", +#else +"SET FILE TYPE { TEXT, BINARY }", +#endif /* STRATUS */ +#endif /* MAC */ +#endif /* VMS */ +" How file contents are to be treated during file transfer in the absence", +" of any other indication. TYPE can be TEXT for conversion of record format", +" and character set, which is usually needed when transferring text files", +" between unlike platforms (such as UNIX and Windows), or BINARY for no", +" conversion if TRANSFER MODE is MANUAL, which is not the default. Use", +" BINARY with TRANSFER MODE MANUAL for executable programs or binary data or", +" whenever you wish to duplicate the original contents of the file, byte for" +, +" byte. In most modern Kermit programs, the file sender informs the receiver" +, +" of the file type automatically. However, when sending files from C-Kermit", +" to an ancient or non-Columbia Kermit implementation, you might need to set", +" the corresponding file type at the receiver as well.", +" ", +#ifdef VMS +" FILE TYPE settings of TEXT and BINARY have no effect when sending files,", +" since VMS C-Kermit determines each file's type automatically from its", +" record format: binary for fixed, text for others. For incoming files,", +" these settings are effective only in the absence of a file-type indication", +" from the sender.", +" ", +" You may include an optional record-format after the word BINARY. This may", +" be FIXED (the default) or UNDEFINED. UNDEFINED is used when you need to", +" receive binary files in binary mode and have them stored with UNDEFINED", +" record format, which is required by certain VMS applications.", +" ", +" Two additional VMS file types are also supported: IMAGE and LABELED.", +" IMAGE means raw block i/o, no interference from RMS, applies to file", +" transmission only, and overrides the normal automatica file type", +" determination. LABELED means to send or interpret RMS attributes", +" with the file.", +" ", +#else +" When TRANSFER MODE is AUTOMATIC (as it is by default), various automatic", +" methods (depending on the platform) are used to determine whether a file", +" is transferred in text or binary mode; these methods (which might include", +" content scan (see SET FILE SCAN below), filename pattern matching (SET FILE" +, +" PATTERNS), client/server \"kindred-spirit\" recognition, or source file", +" record format) supersede the FILE TYPE setting but can, themselves, be", +" superseded by including a /BINARY or /TEXT switch in the SEND, GET, or", +" RECEIVE command.", +" ", +" When TRANSFER MODE is MANUAL, the automatic methods are skipped for sending" +, +" files; the FILE TYPE setting is used instead, which can be superseded on", +" a per-command basis with a /TEXT or /BINARY switch.", +#endif /* VMS */ +" ", + +#ifndef NOXFER + +"SET FILE BYTESIZE { 7, 8 }", +" Normally 8. If 7, Kermit truncates the 8th bit of all file bytes.", +" ", +#ifndef NOCSETS +"SET FILE CHARACTER-SET name", +" Tells the encoding of the local file, ASCII by default.", +" The names ITALIAN, PORTUGUESE, NORWEGIAN, etc, refer to 7-bit ISO-646", +" national character sets. LATIN1 is the 8-bit ISO 8859-1 Latin Alphabet 1", +" for Western European languages.", +" NEXT is the 8-bit character set of the NeXT workstation.", +" The CPnnn sets are for PCs. MACINTOSH-LATIN is for the Macintosh.", +#ifndef NOLATIN2 +" LATIN2 is ISO 8859-2 for Eastern European languages that are written with", +" Roman letters. Mazovia is a PC code page used in Poland.", +#endif /* NOLATIN2 */ +#ifdef CYRILLIC +" KOI-CYRILLIC, CYRILLIC-ISO, and CP866 are 8-bit Cyrillic character sets.", +" SHORT-KOI is a 7-bit ASCII coding for Cyrillic. BULGARIA-PC is a PC code", +" page used in Bulgaria", +#endif /* CYRILLIC */ +#ifdef HEBREW +" HEBREW-ISO is ISO 8859-8 Latin/Hebrew. CP862 is the Hebrew PC code page.", +" HEBREW-7 is like ASCII with the lowercase letters replaced by Hebrew.", +#endif /* HEBREW */ +#ifdef GREEK +" GREEK-ISO is ISO 8859-7 Latin/Greek. CP869 is the Greek PC code page.", +" ELOT-927 is like ASCII with the lowercase letters replaced by Greek.", +#endif /* GREEK */ +#ifdef KANJI +" JAPANESE-EUC, JIS7-KANJI, DEC-KANJI, and SHIFT-JIS-KANJI are Japanese", +" Kanji character sets.", +#endif /* KANJI */ +#ifdef UNICODE +" UCS-2 is the 2-byte form of the Universal Character Set.", +" UTF-8 is the serialized form of the Universal Character Set.", +#endif /* UNICODE */ +" Type SET FILE CHAR ? for a complete list of file character sets.", +" ", +"SET FILE DEFAULT 7BIT-CHARACTER-SET", +" When automatically switching among different kinds of files while sending", +" this tells the character set to be used for 7-bit text files.", +" ", +"SET FILE DEFAULT 8BIT-CHARACTER-SET", +" This tells the character set to be used for 8-bit text files when", +" switching automatically among different kinds of files.", +" ", +#endif /* NOCSETS */ + +"SET FILE COLLISION option", +" Tells what to do when a file arrives that has the same name as", +" an existing file. The options are:", +" BACKUP (default) - Rename the old file to a new, unique name and store", +" the incoming file under the name it was sent with.", +" OVERWRITE - Overwrite (replace) the existing file.", +" APPEND - Append the incoming file to the end of the existing file.", +" DISCARD - Refuse and/or discard the incoming file.", +" RENAME - Give the incoming file a unique name.", +" UPDATE - Accept the incoming file only if newer than the existing file.", +" ", + +"SET FILE DESTINATION { DISK, PRINTER, SCREEN, NOWHERE }", +" DISK (default): Store incoming files on disk.", +" PRINTER: Send incoming files to SET PRINTER device.", +" SCREEN: Display incoming files on screen (local mode only).", +" NOWHERE: Do not put incoming files anywhere (use for calibration).", +" ", +"SET FILE DISPLAY option", +" Selects the format of the file transfer display for local-mode file", +" transfer. The choices are:", +" ", +" BRIEF A line per file, showing size, mode, status, and throughput.", +" SERIAL One dot is printed for every K bytes transferred.", +" CRT Numbers are continuously updated on a single screen line.", +" This format can be used on any video display terminal.", +#ifdef CK_CURSES +" FULLSCREEN A fully formatted 24x80 screen showing lots of information.", +" This requires a terminal or terminal emulator.", +#endif /* CK_CURSES */ +" NONE No file transfer display at all.", +" ", + +"SET FILE DOWNLOAD-DIRECTORY [ ]", +" The directory into which all received files should be placed. By default,", +" received files go into your current directory.", +" ", +#endif /* NOXFER */ + +#ifdef CK_CTRLZ +"SET FILE EOF { CTRL-Z, LENGTH }", +" End-Of-File detection method, normally LENGTH. Applies only to text-mode", +" transfers. When set to CTRL-Z, this makes the file sender treat the first", +" Ctrl-Z in the input file as the end of file (EOF), and it makes the file", +" receiver tack a Ctrl-Z onto the end of the output file if it does not", +" already end with Ctrl-Z.", +" ", +#endif /* CK_CTRLZ */ + +"SET FILE END-OF-LINE { CR, CRLF, LF }", +" Use this command to specify nonstandard line terminators for text files.", +" ", + +#ifndef NOXFER +"SET FILE INCOMPLETE { AUTO, KEEP, DISCARD }", +" What to do with an incompletely received file: KEEP, DISCARD, or AUTO.", +" AUTO (the default) means DISCARD if transfer is in text mode, KEEP if it", +" is in binary mode.", +" ", +#ifdef VMS +"SET FILE LABEL { ACL, BACKUP-DATE, NAME, OWNER, PATH } { ON, OFF }", +" Tells which items to include (ON) or exclude (OFF) in labeled file", +" transfers", +" ", +#else +#ifdef OS2 +"SET FILE LABEL { ARCHIVE, READ-ONLY, HIDDEN, SYSTEM, EXTENDED } { ON, OFF }", +" Tells which items to include (ON) or exclude (OFF) in labeled file", +" transfers.", +" ", +#endif /* OS2 */ +#endif /* VMS */ + +#ifdef UNIX +#ifdef DYNAMIC +"SET FILE LISTSIZE number", +" Changes the size of the internal wildcard expansion list. Use SHOW FILE", +" to see the current size. Use this command to increase the size if you get", +" a \"?Too many files\" error. Also see SET FILE STRINGSPACE.", +" ", +#endif /* DYNAMIC */ +#endif /* UNIX */ + +"SET FILE NAMES { CONVERTED, LITERAL }", +" File names are normally CONVERTED to \"common form\" during transmission", +" (e.g. lowercase to uppercase, extra periods changed to underscore, etc).", +" LITERAL means use filenames literally (useful between like systems). Also", +" see SET SEND PATHNAMES and SET RECEIVE PATHNAMES.", +" ", + +#ifdef UNIX +"SET FILE OUTPUT { { BUFFERED, UNBUFFERED } [ size ], BLOCKING, NONBLOCKING }", +" Lets you control the disk output buffer for incoming files. Buffered", +" blocking writes are normal. Nonblocking writes might be faster on some", +" systems but might also be risky, depending on the underlying file service.", +" Unbuffered writes might be useful in critical applications to ensure that", +" cached disk writes are not lost in a crash, but will probably also be", +" slower. The optional size parameter after BUFFERED or UNBUFFERED lets you", +" change the disk output buffer size; this might make a difference in", +" performance.", +" ", +#endif /* UNIX */ + +#ifdef PATTERNS +"SET FILE PATTERNS { ON, OFF, AUTO }", +" ON means to use filename pattern lists to determine whether to send a file", +" in text or binary mode. OFF means to send all files in the prevailing", +" mode. AUTO (the default) is like ON if the other Kermit accepts Attribute", +" packets and like OFF otherwise. FILE PATTERNS are used only if FILE SCAN", +" is OFF (see SET FILE SCAN).", +" ", +"SET FILE BINARY-PATTERNS [ [ ... ] ]", +" Zero or more filename patterns which, if matched, cause a file to be sent", +" in binary mode when FILE PATTERNS are ON. HELP WILDCARDS for a description" +, +" of pattern syntax. SHOW PATTERNS to see the current file pattern lists.", +" ", +"SET FILE TEXT-PATTERNS [ [ ... ] ]", +" Zero or more filename patterns which, if matched, cause a file to be sent", +" in text mode when FILE PATTERNS is ON; if a file does not match a text or", +" binary pattern, the prevailing SET FILE TYPE is used.", +" ", +#endif /* PATTERNS */ + +#ifdef VMS +"SET FILE RECORD-LENGTH number", +" Sets the record length for received files of type BINARY. Use this to", +" receive VMS BACKUP savesets or other fixed-format files that do not use", +" the default record length of 512.", +" ", +#endif /* VMS */ + +"SET FILE SCAN { ON [ size ], OFF }", +" If TRANSFER MODE is AUTOMATIC and FILE SCAN is ON (as it is by default)", +" Kermit peeks at the file's contents to see if it's text or binary. Use", +" SET FILE SCAN OFF to disable file peeking, while still keeping TRANSFER", +" MODE automatic to allow name patterns and other methods. The optional", +" size is the number of file bytes to scan, 49152 by default. -1 means to", +" scan the whole file. Also see SET FILE PATTERNS.", +" ", + +#ifdef UNIX +#ifdef DYNAMIC +"SET FILE STRINGSPACE number", +" Changes the size (in bytes) of the internal buffer that holds lists of", +" filenames such as wildcard expansion lists. Use SHOW FILE to see the", +" current size. Use this command to increase the size if you get a", +" \"?String space exhausted\" error. Also see SET FILE LISTSIZE.", +" ", +#endif /* DYNAMIC */ +#endif /* UNIX */ + +#ifdef UNICODE +"SET FILE UCS BOM { ON, OFF }", +" Whether to write a Byte Order Mark when creating a UCS-2 file.", +" ", +"SET FILE UCS BYTE-ORDER { BIG-ENDIAN, LITTLE-ENDIAN }", +" Byte order to use when creating UCS-2 files, and to use when reading UCS-2", +" files that do not start with a Byte Order Mark.", +" ", +#endif /* UNICODE */ + +"SET FILE WARNING { ON, OFF }", +" SET FILE WARNING is superseded by the newer command, SET FILE", +" COLLISION. SET FILE WARNING ON is equivalent to SET FILE COLLISION RENAME", +" and SET FILE WARNING OFF is equivalent to SET FILE COLLISION OVERWRITE.", +#endif /* NOXFER */ +"" }; + +static char *hmxyhsh[] = { +"Syntax: SET HANDSHAKE { NONE, XON, LF, BELL, ESC, CODE number }", +" Character to use for half duplex line turnaround handshake during file", +" transfer. C-Kermit waits for this character from the other computer", +" before sending its next packet. Default is NONE; you can give one of the", +" other names like BELL or ESC, or use SET HANDSHAKE CODE to specify the", +" numeric code value of the handshake character. Type SET HANDSH ? for a", +" complete list of possibilities.", +"" }; + +#ifndef NOSERVER +static char *hsetsrv[] = { +"SET SERVER CD-MESSAGE {ON,OFF}", +" Tells whether the server, after successfully executing a REMOTE CD", +" command, should send the contents of the new directory's READ.ME", +" (or similar) file to your screen.", +" ", +"SET SERVER CD-MESSAGE FILE name", +" Tells the name of the file to be displayed as a CD-MESSAGE, such as", +" READ.ME (SHOW SERVER tells the current CD-MESSAGE FILE name).", +" To specify more than one filename to look for, use {{name1}{name2}..}.", +" Synonym: SET CD MESSAGE FILE .", +" ", +"SET SERVER DISPLAY {ON,OFF}", +" Tells whether local-mode C-Kermit during server operation should put a", +" file transfer display on the screen. Default is OFF.", +" ", +"SET SERVER GET-PATH [ directory [ directory [ ... ] ] ]", +" Tells the C-Kermit server where to look for files whose names it receives", +" from client GET commands when the names are not fully specified pathnames.", +" Default is no GET-PATH, so C-Kermit looks only in its current directory.", +" ", +"SET SERVER IDLE-TIMEOUT seconds", +" Idle time limit while in server mode, 0 for no limit.", +#ifndef OS2 +" NOTE: SERVER IDLE-TIMEOUT and SERVER TIMEOUT are mutually exclusive.", +#endif /* OS2 */ +" ", +"SET SERVER KEEPALIVE {ON,OFF}", +" Tells whether C-Kermit should send \"keepalive\" packets while executing", +" REMOTE HOST commands, which is useful in case the command takes a long", +" time to produce any output and therefore might cause the operation to time", +" out. ON by default; turn it OFF if it causes trouble with the client or", +" slows down the server too much.", +" ", +"SET SERVER LOGIN [ username [ password [ account ] ] ]", +" Sets up a username and optional password which must be supplied before", +" the server will respond to any commands other than REMOTE LOGIN. The", +" account is ignored. If you enter SET SERVER LOGIN by itself, then login", +" is no longer required. Only one SET SERVER LOGIN command can be in effect", +" at a time; C-Kermit does not support multiple user/password pairs.", +" ", +"SET SERVER TIMEOUT n", +" Server command wait timeout interval, how often the C-Kermit server issues", +" a NAK while waiting for a command packet. Specify 0 for no NAKs at all.", +" Default is 0.", +"" +}; +#endif /* NOSERVER */ + +static char *hmhrmt[] = { +#ifdef NEWFTP +"The REMOTE command sends file management instructions or other commands", +"to a Kermit or FTP server. If you have a single connection, the command is", +"directed to the server you are connected to; if you have multiple connections" +, +"the command is directed according to your GET-PUT-REMOTE setting.", +#else +"The REMOTE command sends file management instructions or other commands", +"to a Kermit server. There should already be a Kermit running in server", +"mode on the other end of the connection.", +#endif /* NEWFTP */ +"Type REMOTE ? to see a list of available remote commands. Type HELP REMOTE", +"xxx to get further information about a particular remote command xxx.", +" ", +"All REMOTE commands except LOGIN and LOGOUT have R-command shortcuts;", +"for example, RDIR for REMOTE DIR, RCD for REMOTE CD, etc.", +" ", +#ifdef NEWFTP +#ifdef LOCUS +"Also see: HELP SET LOCUS, HELP FTP, HELP SET GET-PUT-REMOTE.", +#else +"Also see: HELP FTP, HELP SET GET-PUT-REMOTE.", +#endif /* LOCUS */ +#else +#ifdef LOCUS +"Also see: HELP SET LOCUS.", +#endif /* LOCUS */ +#endif /* NEWFTP */ +"" }; + +#ifndef NOSPL +static char *ifhlp[] = { "Syntax: IF [NOT] condition commandlist", +" ", +"If the condition is (is not) true, do the commandlist. The commandlist", +"can be a single command, or a list of commands separated by commas and", +"enclosed in braces. The condition can be a single condition or a group of", +"conditions separated by AND (&&) or OR (||) and enclosed in parentheses.", +"If parentheses are used they must be surrounded by spaces. Examples:", +" ", +" IF EXIST oofa.txt ", +" IF ( EXIST oofa.txt || = \\v(nday) 3 ) ", +" IF ( EXIST oofa.txt || = \\v(nday) 3 ) { , , ... }", +" ", +"The conditions are:", +" ", +" SUCCESS - The previous command succeeded", +" OK - Synonym for SUCCESS", +" FAILURE - The previous command failed", +" ERROR - Synonym for FAILURE", +" FLAG - Succeeds if SET FLAG ON, fails if SET FLAG OFF", +" BACKGROUND - C-Kermit is running in the background", +#ifdef CK_IFRO +" FOREGROUND - C-Kermit is running in the foreground", +" REMOTE-ONLY - C-Kermit was started with the -R command-line option", +#else +" FOREGROUND - C-Kermit is running in the foreground", +#endif /* CK_IFRO */ +" KERBANG - A Kerbang script is running", +" ALARM - SET ALARM time has passed", +" ASKTIMEOUT - The most recent ASK, ASKQ, GETC, or GETOK timed out", +" EMULATION - Succeeds if executed while in CONNECT mode", +#ifdef OS2 +" TAPI - Current connection is via a Microsoft TAPI device", +#endif /* OS2 */ +" ", +" MS-KERMIT - Program is MS-DOS Kermit", +" C-KERMIT - Program is C-Kermit", +" K-95 - Program is Kermit 95", +" GUI - Program runs in a GUI window", +" ", +" AVAILABLE CRYPTO - Encryption is available", +" AVAILABLE KERBEROS4 - Kerberos 4 authentication is available", +" AVAILABLE KERBEROS5 - Kerberos 5 authentication is available", +" AVAILABLE NTLM - NTLM authentication is available", +" AVAILABLE SRP - SRP authentication is available", +" AVAILABLE SSL - SSL/TLS authentication is available", +" MATCH string pattern - Succeeds if string matches pattern", +#ifdef CKFLOAT +" FLOAT number - Succeeds if floating-point number", +#endif /* CKFLOAT */ +" COMMAND word - Succeeds if word is built-in command", +" DEFINED variablename or macroname - The named variable or macro is defined", +" DECLARED arrayname - The named array is declared", +" NUMERIC variable or constant - The variable or constant is numeric", +" EXIST filename - The named file exists", +" ABSOLUTE filename - The filename is absolute, not relative", +#ifdef CK_TMPDIR +" DIRECTORY string - The string is the name of a directory", +#endif /* CK_TMPDIR */ +" READABLE filename - Succeeds if the file is readable", +" WRITEABLE filename - Succeeds if the file is writeable", +#ifdef ZFCDAT +" NEWER file1 file2 - The 1st file is newer than the 2nd one", +#endif /* ZFCDAT */ +" OPEN { READ-FILE,SESSION-LOG,...} - The given file or log is open", +#ifndef NOLOCAL +" OPEN CONNECTION - A connection is open", +#endif /* NOLOCAL */ +" KBHIT - A key has been pressed", +" ", +" VERSION - equivalent to \"if >= \\v(version) ...\"", +" COUNT - subtract one from COUNT, execute the command if the result is", +" greater than zero (see SET COUNT)", +" ", +" EQUAL s1 s2 - s1 and s2 (character strings or variables) are equal", +" LLT s1 s2 - s1 is lexically (alphabetically) less than s2", +" LGT s1 s1 - s1 is lexically (alphabetically) greater than s2", +" ", +" = n1 n2 - n1 and n2 (numbers or variables containing numbers) are equal", +" < n1 n2 - n1 is arithmetically less than n2", +" <= n1 n2 - n1 is arithmetically less than or equal to n2", +" > n1 n2 - n1 is arithmetically greater than n2", +" >= n1 n2 - n1 is arithmetically greater than or equal to n2", +" ", +" (number by itself) - fails if the number is 0, succeeds otherwise", +" ", +" TRUE - always succeeds", +" FALSE - always fails", +" ", +"The IF command may be followed on the next line by an ELSE command. Example:", +" ", +" IF < \\%x 10 ECHO It's less", +" ELSE echo It's not less", +" ", +"It can also include an ELSE part on the same line if braces are used:", +" ", +" IF < \\%x 10 { ECHO It's less } ELSE { ECHO It's not less }", +" ", +"Also see HELP WILDCARD (for IF MATCH pattern syntax).", +"" }; + +static char *hmxxeval[] = { "Syntax: EVALUATE variable expression", +" Evaluates the expression and assigns its value to the given variable.", +" The expression can contain numbers and/or numeric-valued variables or", +" functions, combined with mathematical operators and parentheses in", +" traditional notation. Operators include +-/*(), etc. Example:", +" EVALUATE \\%n (1+1) * (\\%a / 3).", +" ", +" NOTE: Prior to C-Kermit 7.0, the syntax was \"EVALUATE expression\"", +" (no variable), and the result was printed. Use SET EVAL { OLD, NEW }", +" to choose the old or new behavior, which is NEW by default.", +" ", +"Alse see: HELP FUNCTION EVAL.", +"" }; +#endif /* NOSPL */ + +static char *hmxxexit[] = { +"Syntax: EXIT (or QUIT) [ number [ text ] ]", +" Exits from the Kermit program, closing all open files and devices.", +" If a number is given it becomes Kermit's exit status code. If text is", +" included, it is printed. Also see SET EXIT.", +"" }; + +#ifndef NOSPL +static char *ifxhlp[] = { "\ +Syntax: XIF condition { commandlist } [ ELSE { commandlist } ]", +" Obsolete. Same as IF (see HELP IF).", +"" }; + +static char *forhlp[] = { "\ +Syntax: FOR variablename initial-value final-value increment { commandlist }", +" FOR loop. Execute the comma-separated commands in the commandlist the", +" number of times given by the initial value, final value and increment.", +" Example: FOR \\%i 10 1 -1 { pause 1, echo \\%i }", "" }; + +static char *whihlp[] = { "\ +Syntax: WHILE condition { commandlist }", +" WHILE loop. Execute the comma-separated commands in the bracketed", +" commandlist while the condition is true. Conditions are the same as for", +" IF commands.", +"" }; + +static char *swihlp[] = { +"Syntax: SWITCH { case-list }", +" Selects from a group of commands based on the value of a variable.", +" The case-list is a series of lines like these:", +" ", +" :x, command, command, ..., break", +" ", +" where \"x\" is a possible value for the variable. At the end of the", +" case-list, you can put a \"default\" label to catch when the variable does", +" not match any of the labels:", +" ", +" :default, command, command, ...", +" ", +"The case label \"x\" can be a character, a string, a variable, a function", +"invocation, a pattern, or any combination of these. See HELP WILDCARDS", +"for information about patterns.", +""}; + +static char *openhlp[] = { +"Syntax: OPEN mode filename", +" For use with READ and WRITE commands. Open the local file in the", +" specified mode: READ, WRITE, or APPEND. !READ and !WRITE mean to read", +" from or write to a system command rather than a file. Examples:", +" ", +" OPEN READ oofa.txt", +" OPEN !READ sort foo.bar", +"" }; + +static char *hxxask[] = { +"Syntax: ASK [ switches ] variablename [ prompt ]", +"Example: ASK \\%n { What is your name\\? }", +" Issues the prompt and defines the variable to be whatever is typed in", +" response, up to the terminating carriage return. Use braces to preserve", +" leading and/or trailing spaces in the prompt.", +" ", +"Syntax: ASKQ [ switches ] variablename [ prompt ]", +"Example: ASKQ \\%p { Password:}", +" Like ASK except the response does not echo on the screen.", +" ", +"Switches:", +" /DEFAULT:text", +" Text to supply if the user enters a blank response or the /TIMEOUT", +" limit expired with no response.", +" ", +#ifdef OS2 +" /POPUP", +" The prompt and response dialog takes place in a text-mode popup.", +" K95 only; in C-Kermit this switch is ignored.", +" ", +#ifdef KUI +" /GUI", +" The prompt and response dialog takes place in a GUI popup.", +" K95 GUI version only; in C-Kermit and the K95 console version,", +" this switch is ignored.", +" ", +#endif /* KUI */ +#endif /* OS2 */ +" /TIMEOUT:number", +" If the response is not entered within the given number of seconds, the", +" command fails. This is equivalent to setting ASK-TIMER to a positive", +" number, except it applies only to this command. Also see SET ASK-TIMER.", +" NOTE: If a /DEFAULT: value was also given, it is supplied automatically", +" upon timeout and the command does NOT fail.", + +" ", +" /QUIET", +" Suppresses \"?Timed out\" message when /TIMEOUT is given and user doesn't", +" respond within the time limit.", +""}; +static char *hxxgetc[] = { +"Syntax: GETC variablename [ prompt ]", +"Example: GETC \\%c { Type any character to continue...}", +" Issues the prompt and sets the variable to the first character you type.", +" Use braces to preserve leading and/or trailing spaces in the prompt.", +" ", +"Also see SET ASK-TIMER.", +""}; + +static char *hmxytimer[] = { +"Syntax: SET ASK-TIMER number", +" For use with ASK, ASKQ, GETOK, and GETC. If ASK-TIMER is set to a number", +" greater than 0, these commands will time out after the given number of", +" seconds with no response. This command is \"sticky\", so to revert to", +" \ +untimed ASKs after a timed one, use SET ASK-TIMER 0. Also see IF ASKTIMEOUT.", +""}; + +static char *hxxdot[] = { +"Syntax: . ", +" Assigns the value to the variable in the manner indicated by the", +" assignment operator:", +" = Copies without evaluation (like DEFINE).", +" := Copies with evaluation (like ASSIGN).", +" ::= Copies with arithmetic evaluation (like EVALUATE).", +""}; + +static char *hxxdef[] = { +"Syntax: DEFINE name [ definition ]", +" Defines a macro or variable. Its value is the definition, taken", +" literally. No expansion or evaluation of the definition is done. Thus", +" if the definition includes any variable or function references, their", +" names are included, rather than their values (compare with ASSIGN). If", +" the definition is omitted, then the named variable or macro is undefined.", +" ", +"A typical macro definition looks like this:", +" ", +" DEFINE name command, command, command, ...", +" ", +"for example:", +" ", +" DEFINE vax set parity even, set duplex full, set flow xon/xoff", +" ", +"which defines a Kermit command macro called 'vax'. The definition is a", +"comma-separated list of Kermit commands. Use the DO command to execute", +"the macro, or just type its name, followed optionally by arguments.", +" ", +"The definition of a variable can be anything at all, for example:", +" ", +" DEFINE \\%a Monday", +" DEFINE \\%b 3", +" ", +"These variables can be used almost anywhere, for example:", +" ", +" ECHO Today is \\%a", +" SET BLOCK-CHECK \\%b", +"" }; + +static char *hxxass[] = { +"Syntax: ASSIGN variablename string.", +"Example: ASSIGN \\%a My name is \\%b.", +" Assigns the current value of the string to the variable (or macro).", +" The definition string is fully evaluated before it is assigned, so that", +" the values of any variables that are contained are used, rather than their", +" names. Compare with DEFINE. To illustrate the difference, try this:", +" ", +" DEFINE \\%a hello", +" DEFINE \\%x \\%a", +" ASSIGN \\%y \\%a", +" DEFINE \\%a goodbye", +" ECHO \\%x \\%y", +" ", +" This prints 'goodbye hello'.", "" }; + +static char *hxxdec[] = { +"Syntax: DECREMENT variablename [ number ]", +" Decrement (subtract one from) the value of a variable if the current value", +" is numeric. If the number argument is given, subtract that number", +" instead.", +" ", +"Examples: DECR \\%a, DECR \\%a 7, DECR \\%a \\%n", "" }; + +static char *hxxinc[] = { +"Syntax: INCREMENT variablename [ number ]", +" Increment (add one to) the value of a variable if the current value is", +" numeric. If the number argument is given, add that number instead.", +" ", +"Examples: INCR \\%a, INCR \\%a 7, INCR \\%a \\%n", "" }; +#endif /* NOSPL */ + +#ifdef ANYX25 +#ifndef IBMX25 +static char *hxxpad[] = { +"Syntax: PAD command", +"X.25 PAD commands:", +" ", +" PAD CLEAR - Clear the virtual call", +" PAD STATUS - Return the status of virtual call", +" PAD RESET - Send a reset packet", +" PAD INTERRUPT - Send an interrupt packet", +""}; +#endif /* IBMX25 */ + +static char *hxyx25[] = { +"Syntax: SET X.25 option { ON [ data ], OFF }", +" ", +"X.25 call options:", +" CLOSED-USER-GROUP { ON index, OFF }", +" Enable or disable closed user group call, where index is the group", +" index, 0 to 99.", +" REVERSE-CHARGE { ON, OFF }", +" Tell whether you want to reverse the charges for the call.", +" CALL-USER-DATA { ON string, OFF }", +" Specify call user-data for the X.25 call.", +""}; +#endif /* ANYX25 */ + +static char *hxyprtr[] = { +#ifdef PRINTSWI +"Syntax: SET PRINTER [ switches ] [ name ]", +" ", +" Specifies the printer to be used for transparent-print, autoprint, and", +" screen-dump material during terminal emulation, as well as for the PRINT", +" command, plus various options governing print behavior.", +" ", +"Switches for specifying the printer by type:", +" ", +"/NONE", +" Include this switch to specify that all printer actions should simply be", +" skipped. Use this, for example, if you have no printer.", +" ", +"/DOS-DEVICE[:name]", +" Include this to declare a DOS printer and to specify its name, such as", +" PRN, LPT1, etc.", +" ", +#ifdef NT +"/WINDOWS-QUEUE[:[queue-name]]", +" Include this to declare a Windows printer and specify its queue name.", +" Type question mark (?) after the colon (:) to see a list of known queue", +" names. If the colon is absent, the switch indicates the currently", +" selected printer is a Windows Print Queue. If the colon is provided", +" and the name is absent, the Windows Print Queue chosen as the Default", +" Printer is selected.", +" ", +#endif /* NT */ +"/FILE[:name]", +" Specifies that all printer material is to be appended to the named file,", +" rather than being sent to a printer. If the file does not exist, it is", +" created the first time any material is to be printed.", +" ", +"/PIPE[:name]", +" Specifies that all printer material is to be sent as standard input to", +" the program or command whose name is given. Example:", +" ", +" SET PRINTER /PIPE:{textps > lpt1}", +" ", +"If you give a printer name without specifying any of these switches, then it", +"is assumed to be a DOS printer device or filename unless the name given", +"(after removing enclosing braces, if any) starts with \"|\", \ +in which case it", +"is a pipe. Examples:", +" ", +" SET PRINTER LPT1 <-- DOS device", +" SET PRINTER {| textps > lpt1} <-- Pipe", +" ", +"The next group of switches tells whether the printer is one-way or", +"bidirectional (two-way):", +" ", +"/OUTPUT-ONLY", +" Include this to declare the printer capable only of receiving material to", +" be printed, but not sending anything back. This is the normal kind of", +" printer, Kermit's default kind, and the opposite of /BIDIRECTIONAL.", +" ", +"/BIDIRECTIONAL", +" Include this to declare the printer bidirectional. This is the opposite ", +" of /OUTPUT-ONLY. You can also use this option with serial printers, even", +" if they aren't bidirectional, in case you need to specify speed, flow", +" control, or parity.", +" ", +"The next group applies only to bidirectional and/or serial printers:", +" ", +"/FLOW-CONTROL:{NONE,XON/XOFF,RTS/CTS,KEEP}", +" Flow control to use with a serial bidirectional printer, default KEEP;", +#ifdef NT +" i.e. use whatever the Windows driver for the port normally uses.", +#else +" i.e. use whatever the OS/2 driver for the port normally uses.", +#endif /* NT */ +" ", +"/PARITY:{NONE,EVEN,ODD,SPACE,MARK}", +" Parity to use with a serial printer, default NONE; i.e. use 8 data bits", +" and no parity. If you omit the colon and the keyword, NONE is selected.", +" ", +"/SPEED:number", +" Interface speed, in bits per second, to use with a serial printer, such as", +" 2400, 9600, 19200, etc. Type SET PRINTER /SPEED:? for a list of possible", +" speeds.", +" ", +"The next group deals with print jobs -- how to identify them, how to start", +"them, how to terminate them:", +" ", +"/TIMEOUT[:number]", +" Used with host-directed transparent or auto printing, this is the number", +" of seconds to wait after the host closes the printer before terminating", +" the print job if the printer is not opened again during the specified", +" amount of time.", +" ", +"/JOB-HEADER-FILE[:filename]", +" The name of a file to be sent to the printer at the beginning of each", +" print job, as a burst page, or to configure the printer. Normally no file", +" is is sent.", +" ", +"/END-OF-JOB-STRING[:string]", +" String of characters to be sent to the printer at the end of the print", +" job, usually used to force the last or only page out of the printer. When", +" such a string is needed, it usually consists of a single formfeed: \"set", +" printer /end-of-job:{\\12}\". No end-of-job string is sent unless you", +" specify one with this option. If the string contains any spaces or", +" control characters (even in backslash notation, as above), enclose it in", +" braces.", +" ", +"The next group is for use with printers that print only PostScript:", +" ", +"/POSTSCRIPT or /PS", +" Indicates that K95 should convert all text to PostScript before sending", +" it to the printer. The fixed-pitch Courier-11 font is used.", +" ", +"/WIDTH:number", +" Specifies the width of the page in characters. If this switch is not", +" given, 80 is used.", +" ", +"/HEIGHT:number", +" Specifies the height of the page in lines. If this switch is not given", +" 66 is used.", +" ", +"/NOPOSTSCRIPT or /NOPS", +" Indicates that K95 should not convert all text to PostScript before", +" sending it to the printer.", +" ", +"The final switch is for use with AutoPrint mode and Screen Dumps", +" ", +"/CHARACTER-SET:", +" Specifies the character set used by the printer which may be different", +" from both the character set used by the host and by the local computer.", +" The default value is CP437.", +" ", +"SHOW PRINTER displays your current printer settings.", +#else +#ifdef UNIX +"Syntax: SET PRINTER [ { |command, filename } ]", +" Specifies the command (such as \"|lpr\") or filename to be used by the", +" PRINT command. If a filename is given, each PRINT command appends to the", +" given file. If the SET PRINTER argument contains spaces, it must be", +" enclosed in braces, e.g. \"set printer {| lpr -Plaser}\". If the argument", +" is omitted the default value is restored. SHOW PRINTER lists the current", +" printer. See HELP PRINT for further info.", +#else +"Sorry, SET PRINTER not available yet.", +#endif /* UNIX */ +#endif /* PRINTSWI */ +""}; + +#ifdef OS2 +#ifdef BPRINT +static char *hxybprtr[] = { +"Syntax: SET BPRINTER [ portname speed [ parity [ flow-control ] ] ]", +" (Obsolete, replaced by SET PRINTER /BIDIRECTIONAL.)", +""}; +#endif /* BPRINT */ +#endif /* OS2 */ + +static char *hxyexit[] = { +"Syntax: SET EXIT HANGUP { ON, OFF }", +" When ON (which is the default), C-Kermit executes an implicit HANGUP and", +" CLOSE command on the communications device or connection when it exits.", +" When OFF, Kermit skips this sequence.", +" ", +"Syntax: SET EXIT ON-DISCONNECT { ON, OFF }", +" When ON, C-Kermit EXITs automatically when a network connection", +" is terminated either by the host or by issuing a HANGUP command.", +" ", +"Syntax: SET EXIT STATUS number", +#ifdef NOSPL +" Set C-Kermit's program return code to the given number.", +#else +" Set C-Kermit's program return code to the given number, which can be a", +" constant, variable, function result, or arithmetic expression.", +#endif /* NOSPL */ +" ", +"Syntax: SET EXIT WARNING { ON, OFF, ALWAYS }", +" When EXIT WARNING is ON, issue a warning message and ask for confirmation", +" before EXITing if a connection to another computer might still be open.", +" When EXIT WARNING is ALWAYS, confirmation is always requested. When OFF", +" it is never requested. The default is ON.", +"" }; + +#ifndef NOSPL +static char *hxxpau[] = { +"Syntax: PAUSE [ { number-of-seconds, hh:mm:ss } ]", +"Example: PAUSE 3 or PAUSE 14:52:30", +" Do nothing for the specified number of seconds or until the given time of", +" day in 24-hour hh:mm:ss notation. If the time of day is earlier than the", +" current time, it is assumed to be tomorrow. If no argument given, one", +" second is used. The pause can be interrupted by typing any character on", +" the keyboard unless SLEEP CANCELLATION is OFF. If interrupted, PAUSE", +" fails, otherwise it succeeds. Synonym: SLEEP.", +"" }; + +static char *hxxmsl[] = { +"Syntax: MSLEEP [ number ]", +"Example: MSLEEP 500", +" Do nothing for the specified number of milliseconds; if no number given,", +" 100 milliseconds.","" }; +#endif /* NOSPL */ + +#ifndef NOPUSH +extern int nopush; +static char *hxxshe[] = { +"Syntax: !, @, RUN, PUSH, or SPAWN, optionally followed by a command.", +" Gives the command to the local operating system's command processor, and", +" displays the results on the screen. If the command is omitted, enters the", +" system's command line interpreter or shell; exit from it (the command for", +" this is usually EXIT or QUIT or LOGOUT) to return to Kermit.", +"" +}; +#endif /* NOPUSH */ + +#ifndef NOXMIT +static char *hxxxmit[] = { +"Syntax: TRANSMIT [ switches ] filename", +" Sends the contents of a file, without any error checking or correction,", +" to the computer on the other end of your SET LINE or SET HOST connection", +" (or if C-Kermit is in remote mode, displays it on the screen). The", +" filename is the name of a single file (no wildcards) to be sent or, if", +" the /PIPE switch is included, the name of a command whose output is to be", +" sent.", +" ", +" The file is sent according to your current FILE TYPE setting (BINARY or", +" TEXT), which you can override with a /BINARY or /TEXT switch without", +" changing the global setting. In text mode, it is sent a line at a time,", +" with carriage return at the end of each line (as if you were typing it at", +" your keyboard), and C-Kermit waits for a linefeed to echo before sending", +" the next line; use /NOWAIT to eliminate the feedback requirement. In", +" binary mode, it is sent a character at a time, with no feedback required.", +" ", +" Normally the transmitted material is echoed to your screen. Use SET", +" TRANSMIT ECHO OFF or the /NOECHO switch to suppress echoing. Note that", +" TRANSMIT /NOECHO /NOWAIT /BINARY is a special case, that more or less", +" blasts the file out at full speed.", +" ", +#ifndef NOCSETS +" Character sets are translated according to your current FILE and TERMINAL", +" CHARACTER-SET settings when TRANSMIT is in text mode. Include /TRANSPARENT" +, +" to disable character-set translation in text mode (/TRANSPARENT implies", +" /TEXT).", +" ", +#endif /* NOCSETS */ +" There can be no guarantee that the other computer will receive the file", +" correctly and completely. Before you start the TRANSMIT command, you", +" must put the other computer in data collection mode, for example by", +" starting a text editor. TRANSMIT may be interrupted by Ctrl-C. Synonym:", +" XMIT. See HELP SET TRANSMIT for further information.", +"" }; +#endif /* NOXMIT */ + +#ifndef NOCSETS +static char *hxxxla[] = { +"Syntax: TRANSLATE file1 cs1 cs2 [ file2 ]", +" Translates file1 from the character set cs1 into the character set cs2", +" and stores the result in file2. The character sets can be any of", +" C-Kermit's file character sets. If file2 is omitted, the translation", +" is displayed on the screen. An appropriate intermediate character-set", +" is chosen automatically, if necessary. Synonym: XLATE. Example:", +" ", +" TRANSLATE lasagna.lat latin1 italian lasagna.nrc", +" ", +" Multiple files can be translated if file2 is a directory or device name,", +" rather than a filename, or if file2 is omitted.", +"" }; +#endif /* NOCSETS */ + +#ifndef NOSPL +static char *hxxwai[] = { +"Syntax: WAIT { number-of-seconds, hh:mm:ss } [ ]", +" ", +"Examples:", +" wait 5 cd cts", +" wait 23:59:59 cd", +" ", +" Waits up to the given number of seconds or the given time of day for the", +" specified item or event, which can be FILE, the name(s) of one or more", +" modem signals, or nothing. If nothing is specified, WAIT acts like SLEEP.", +" If one or more modem signal names are given, Kermit waits for the specified" +, +" modem signals to appear on the serial communication device.", +" Sets FAILURE if the signals do not appear in the given time or interrupted", +" from the keyboard during the waiting period.", +" ", +"Signals:", +" cd = Carrier Detect;", +" dsr = Dataset Ready;", +" cts = Clear To Send;", +" ri = Ring Indicate.", +" ", +"If you want Kermit to wait for a file event, then the syntax is:", +" ", +" WAIT