applied 050_ck_patch.patch
[ckermit.git] / ckcftp.c
1 /*  C K C F T P  --  FTP Client for C-Kermit  */
2
3 char *ckftpv = "FTP Client, 8.0.226, 7 Jan 2004";
4
5 /*
6   Authors:
7     Jeffrey E Altman <jaltman@secure-endpoints.com>
8       Secure Endpoints Inc., New York City
9     Frank da Cruz <fdc@columbia.edu>,
10       The Kermit Project, Columbia University.
11
12   Copyright (C) 2000, 2004,
13     Trustees of Columbia University in the City of New York.
14     All rights reserved.  See the C-Kermit COPYING.TXT file or the
15     copyright text in the ckcmai.c module for disclaimer and permissions.
16
17   Portions of conditionally included code Copyright Regents of the
18     University of California and The Stanford SRP Authentication Project;
19     see notices below.
20 */
21
22 /*
23   Pending...
24
25   . Implement recursive NLST downloads by trying to CD to each filename.
26     If it works, it's a directory; if not, it's a file -- GET it.  But
27     that won't work with servers like wu-ftpd that don't send directory 
28     names.  Recursion with MLSD is done.
29
30   . Make syslog entries for session?  Files?
31
32   . Messages are printed to stdout and stderr in random fashion.  We should
33     either print everything to stdout, or else be systematic about when
34     to use stderr.
35
36   . Implement mail (MAIL, MLFL, MSOM, etc) if any servers support it.
37
38   . Adapt to VMS.  Big job because of its record-oriented file system.
39     RMS programmer required.  There are probably also some VMS TCP/IP
40     product-specific wrinkles, e.g. attribute preservation in VMS-to-VMS
41     transfers using special options for Multinet or other FTP servers
42     (find out about STRU VMS).
43 */
44
45 /*
46   Quick FTP command reference:
47
48   RFC765 (1980) and earlier:
49     MODE  S(tream), B(lock), C(ompressed)
50     STRU  F(ILE), R(ECORD), P(AGE)
51     TYPE  A(SCII) <format>,  E(BCDIC) <format>, I(MAGE), L(OCAL) <bytesize>
52     PORT  - Port
53     PASV  - Passive mode
54     USER  - User
55     PASS  - Password
56     ACCT  - Account
57     CWD   - Change Working Directory
58     REIN  - Logout but not disconnect
59     QUIT  - Bye
60     RETR  - Retreive
61     STOR  - Store
62     APPE  - Append
63     ALLO  - Allocate
64     REST  - Restart
65     RNFR  - Rename from
66     RNTO  - Rename to
67     ABOR  - Cancel
68     DELE  - Delete
69     LIST  - Directory
70     NLST  - Name List
71     SITE  - Site parameters or commands
72     STAT  - Status
73     HELP  - Help
74     NOOP  - Noop
75
76   RFC959 (1985):
77     CDUP  - Change to Parent Directory
78     SMNT  - Structure Mount
79     STOU  - Store Unique
80     RMD   - Remove Directory
81     MKD   - Make Directory
82     PWD   - Print Directory
83     SYST  - System
84
85   RFC2389 (1998):
86     FEAT  - List Features (done)
87     OPTS  - Send options (done)
88
89   RFC2640 (1999):
90     LANG  - Specify language for messages (not done)
91
92   Pending (Internet Drafts):
93     SIZE  - File size (done)
94     MDTM  - File modification date-time (done)
95     MLST  - File name and attribute list (single file) (not done)
96     MLSD  - File list with attributes (multiple files) (done)
97     MAIL, MLFL, MSOM - mail delivery (not done)
98
99   Alphabetical syntax list:
100     ABOR <CRLF>
101     ACCT <SP> <account-information> <CRLF>
102     ALLO <SP> <decimal-integer> [<SP> R <SP> <decimal-integer>] <CRLF>
103     APPE <SP> <pathname> <CRLF>
104     CDUP <CRLF>
105     CWD  <SP> <pathname> <CRLF>
106     DELE <SP> <pathname> <CRLF>
107     FEAT <CRLF>
108     HELP [<SP> <string>] <CRLF>
109     LANG [<SP> <language-tag> ] <CRLF>
110     LIST [<SP> <pathname>] <CRLF>
111     MKD  <SP> <pathname> <CRLF>
112     MLSD [<SP> <pathname>] <CRLF>
113     MLST [<SP> <pathname>] <CRLF>
114     MODE <SP> <mode-code> <CRLF>
115     NLST [<SP> <pathname-or-wildcard>] <CRLF>
116     NOOP <CRLF>
117     OPTS <SP> <commandname> [ <SP> <command-options> ] <CRLF>
118     PASS <SP> <password> <CRLF>
119     PASV <CRLF>
120     PORT <SP> <host-port> <CRLF>
121     PWD  <CRLF>
122     QUIT <CRLF>
123     REIN <CRLF>
124     REST <SP> <marker> <CRLF>
125     RETR <SP> <pathname> <CRLF>
126     RMD  <SP> <pathname> <CRLF>
127     RNFR <SP> <pathname> <CRLF>
128     RNTO <SP> <pathname> <CRLF>
129     SITE <SP> <string> <CRLF>
130     SIZE <SP> <pathname> <CRLF>
131     SMNT <SP> <pathname> <CRLF>
132     STAT [<SP> <pathname>] <CRLF>
133     STOR <SP> <pathname> <CRLF>
134     STOU <CRLF>
135     STRU <SP> <structure-code> <CRLF>
136     SYST <CRLF>
137     TYPE <SP> <type-code> <CRLF>
138     USER <SP> <username> <CRLF>
139 */
140 #include "ckcsym.h"                     /* Standard includes */
141 #include "ckcdeb.h"
142
143 #ifndef NOFTP                           /* NOFTP  = no FTP */
144 #ifndef SYSFTP                          /* SYSFTP = use external ftp client */
145 #ifdef TCPSOCKET                        /* Build only if TCP/IP included */
146 #define CKCFTP_C
147
148 /* Note: much of the following duplicates what was done in ckcdeb.h */
149 /* but let's not mess with it unless it causes trouble. */
150
151 #ifdef CK_ANSIC
152 #include <stdarg.h>
153 #else /* CK_ANSIC */
154 #include <varargs.h>
155 #endif /* CK_ANSIC */
156 #include <signal.h>
157 #ifdef OS2
158 #ifdef OS2ONLY
159 #include <os2.h>
160 #endif /* OS2ONLY */
161 #include "ckowin.h"
162 #include "ckocon.h"
163 #endif /* OS2 */
164 #ifndef ZILOG
165 #ifdef NT
166 #include <setjmpex.h>
167 #ifdef NTSIG
168 extern int TlsIndex;
169 #endif /* NTSIG */
170 #else /* NT */
171 #include <setjmp.h>
172 #endif /* NT */
173 #else
174 #include <setret.h>
175 #endif /* ZILOG */
176 #include "ckcsig.h"
177 #include <sys/stat.h>
178 #include <ctype.h>
179 #include <errno.h>
180 #ifndef NOTIMEH
181 #include <time.h>
182 #endif /* NOTIMEH */
183 #ifndef EPIPE
184 #define EPIPE 32                        /* Broken pipe error */
185 #endif /* EPIPE */
186
187 /* Kermit includes */
188
189 #include "ckcasc.h"
190 #include "ckcker.h"
191 #include "ckucmd.h"
192 #include "ckuusr.h"
193 #include "ckcnet.h"                     /* Includes ckctel.h */
194 #include "ckctel.h"                     /* (then why include it again?) */
195 #include "ckcxla.h"
196
197 /*
198   How to get the struct timeval definition so we can call select().  The
199   xxTIMEH symbols are defined in ckcdeb.h, overridden in various makefile
200   targets.  The problem is: maybe we have already included some header file
201   that defined struct timeval, and maybe we didn't.  If we did, we don't want
202   to include another header file that defines it again or the compilation will
203   fail.  If we didn't, we have to include the header file where it's defined.
204   But in some cases even that won't work because of strict POSIX constraints
205   or somesuch, or because this introduces other conflicts (e.g. struct tm
206   multiply defined), in which case we have to define it ourselves, but this
207   can work only if we didn't already encounter a definition.
208 */
209 #ifndef DCLTIMEVAL
210 #ifdef SV68R3V6
211 #define DCLTIMEVAL
212 #else
213 #ifdef SCO234
214 #define DCLTIMEVAL
215 #endif /* SCO234 */
216 #endif /* SV68R3V6 */
217 #endif /* DCLTIMEVAL */
218
219 #ifdef DCLTIMEVAL
220 /* Also maybe in some places the elements must be unsigned... */
221 struct timeval {
222     long tv_sec;
223     long tv_usec;
224 };
225 #ifdef COMMENT
226 /* Currently we don't use this... */
227 struct timezone {
228     int tz_minuteswest;
229     int tz_dsttime;
230 };
231 #endif /* COMMENT */
232 #else  /* !DCLTIMEVAL */
233 #ifndef NOSYSTIMEH
234 #ifdef SYSTIMEH
235 #include <sys/time.h>
236 #endif /* SYSTIMEH */
237 #endif /* NOSYSTIMEH */
238 #ifndef NOSYSTIMEBH
239 #ifdef SYSTIMEBH
240 #include <sys/timeb.h>
241 #endif /* SYSTIMEBH */
242 #endif /* NOSYSTIMEBH */
243 #endif /* DCLTIMEVAL */
244
245 #include <sys/types.h>
246 #include <stdio.h>
247 #include <string.h>
248 #ifdef HAVE_STDLIB_H
249 #include <stdlib.h>
250 #endif /* HAVE_STDLIB_H */
251
252 #ifndef NOSETTIME
253 #ifdef COMMENT
254 /* This section moved to ckcdeb.h */
255 #ifdef POSIX
256 #define UTIMEH
257 #else
258 #ifdef HPUX9
259 #define UTIMEH
260 #else
261 #ifdef OS2
262 #define SYSUTIMEH
263 #endif /* OS2 */
264 #endif /* HPUX9 */
265 #endif /* POSIX */
266 #endif /* COMMENT */
267
268 #ifdef SYSUTIMEH
269 #include <sys/utime.h>
270 #else
271 #ifdef UTIMEH
272 #include <utime.h>
273 #define SYSUTIMEH
274 #endif /* UTIMEH */
275 #endif /* SYSUTIMEH */
276 #endif /* NOSETTIME */
277
278 #ifndef SCO_OSR504
279 #ifdef SELECT_H
280 #include <sys/select.h>
281 #endif /* SELECT_H */
282 #endif /* SCO_OSR504 */
283
284 /* select() dialects... */
285
286 #ifdef UNIX
287 #define BSDSELECT                       /* BSD select() syntax/semantics */
288 #else
289 #ifdef OS2                              /* OS/2 or Win32 */
290 #ifdef NT
291 #define BSDSELECT
292 #else /* NT */
293 #define IBMSELECT
294 #endif /* NT */
295 #endif /* OS2 */
296 #endif /* UNIX */
297
298 /* Other select() peculiarities */
299
300 #ifdef HPUX
301 #ifndef HPUX10                          /* HP-UX 9.xx and earlier */
302 #ifndef HPUX1100
303 /* The three interior args to select() are (int *) rather than (fd_set *) */
304 #ifndef INTSELECT
305 #define INTSELECT
306 #endif /* INTSELECT */
307 #endif /* HPUX1100 */
308 #endif /* HPUX10 */
309 #endif /* HPUX */
310
311 #ifdef CK_SOCKS                         /* SOCKS Internet relay package */
312 #ifdef CK_SOCKS5                        /* SOCKS 5 */
313 #define accept  SOCKSaccept
314 #define bind    SOCKSbind
315 #define connect SOCKSconnect
316 #define getsockname SOCKSgetsockname
317 #define listen SOCKSlisten
318 #else  /* Not SOCKS 5 */
319 #define accept  Raccept
320 #define bind    Rbind
321 #define connect Rconnect
322 #define getsockname Rgetsockname
323 #define listen Rlisten
324 #endif /* CK_SOCKS5 */
325 #endif /* CK_SOCKS */
326
327 #ifndef NOHTTP
328 extern char * tcp_http_proxy;           /* Name[:port] of http proxy server */
329 extern int    tcp_http_proxy_errno;
330 extern char * tcp_http_proxy_user;
331 extern char * tcp_http_proxy_pwd;
332 extern char * tcp_http_proxy_agent;
333 #define HTTPCPYL 1024
334 static char proxyhost[HTTPCPYL];
335 #endif /* NOHTTP */
336 int ssl_ftp_proxy = 0;                  /* FTP over SSL/TLS Proxy Server */
337
338 /* Feature selection */
339
340 #ifndef USE_SHUTDOWN
341 /*
342   We don't use shutdown() because (a) we always call it just before close()
343   so it's redundant and unnecessary, and (b) it introduces a long pause on
344   some platforms like SV/68 R3.
345 */
346 /* #define USE_SHUTDOWN */
347 #endif /* USE_SHUTDOWN */
348
349 #ifndef NORESEND
350 #ifndef NORESTART                       /* Restart / recover */
351 #ifndef FTP_RESTART
352 #define FTP_RESTART
353 #endif /* FTP_RESTART */
354 #endif /* NORESTART */
355 #endif /* NORESEND */
356
357 #ifndef NOUPDATE                        /* Update mode */
358 #ifndef DOUPDATE
359 #define DOUPDATE
360 #endif /* DOUPDATE */
361 #endif /* NOUPDATE */
362
363 #ifndef UNICODE                         /* Unicode required */
364 #ifndef NOCSETS                         /* for charset translation */
365 #define NOCSETS
366 #endif /* NOCSETS */
367 #endif /* UNICODE */
368
369 #ifndef OS2
370 #ifndef HAVE_MSECS                      /* Millisecond timer */
371 #ifdef UNIX
372 #ifdef GFTIMER
373 #define HAVE_MSECS
374 #endif /* GFTIMER */
375 #endif /* UNIX */
376 #endif /* HAVE_MSECS */
377 #endif /* OS2 */
378
379 #ifdef PIPESEND                         /* PUT from pipe */
380 #ifndef PUTPIPE
381 #define PUTPIPE
382 #endif /* PUTPIPE */
383 #endif /* PIPESEND */
384
385 #ifndef NOSPL                           /* PUT from array */
386 #ifndef PUTARRAY
387 #define PUTARRAY
388 #endif /* PUTARRAY */
389 #endif /* NOSPL */
390
391 /* Security... */
392
393 #ifdef CK_SRP
394 #define FTP_SRP
395 #endif /* CK_SRP */
396
397 #ifdef CK_KERBEROS
398 #ifdef KRB4
399 /*
400   There is a conflict between the Key Schedule formats used internally
401   within the standalone MIT KRB4 library and that used by Eric Young
402   in OpenSSL and his standalone DES library.  Therefore, KRB4 FTP AUTH
403   cannot be supported when either of those two packages are used.
404 */
405 #ifdef KRB524
406 #define FTP_KRB4
407 #else /* KRB524 */
408 #ifndef CK_SSL
409 #ifndef LIBDES
410 #define FTP_KRB4
411 #endif /* LIBDES */
412 #endif /* CK_SSL */
413 #endif /* KRB524 */
414 #endif /* KRB4 */
415 #ifdef KRB5
416 #ifndef HEIMDAL
417 #define FTP_GSSAPI
418 #endif /* HEIMDAL */
419 #endif /* KRB5 */
420 #endif /* CK_KERBEROS */
421
422 /* FTP_SECURITY is defined if any of the above is selected */
423 #ifndef FTP_SECURITY
424 #ifdef FTP_GSSAPI
425 #define FTP_SECURITY
426 #else
427 #ifdef FTP_KRB4
428 #define FTP_SECURITY
429 #else
430 #ifdef FTP_SRP
431 #define FTP_SECURITY
432 #else
433 #ifdef CK_SSL
434 #define FTP_SECURITY
435 #endif /* CK_SSL */
436 #endif /* FTP_SRP */
437 #endif /* FTP_KRB4 */
438 #endif /* FTP_GSSAPI */
439 #endif /* FTP_SECURITY */
440
441 #ifdef CK_DES
442 #ifdef CK_SSL
443 #ifndef LIBDES
444 #define LIBDES
445 #endif /* LIBDES */
446 #endif /* CK_SSL */
447 #endif /* CK_DES */
448
449 #ifdef CRYPT_DLL
450 #ifndef LIBDES
451 #define LIBDES
452 #endif /* LIBDES */
453 #endif /* CRYPT_DLL */
454
455 #ifdef FTP_KRB4
456 #define des_cblock Block
457 #define des_key_schedule Schedule
458 #ifdef KRB524
459 #ifdef NT
460 #define _WINDOWS
461 #endif /* NT */
462 #include "kerberosIV/krb.h"
463 #else /* KRB524 */
464 #ifdef SOLARIS
465 #ifndef sun
466 /* For some reason lost in history the Makefile Solaris targets have -Usun */
467 #define sun
468 #endif /* sun */
469 #endif /* SOLARIS */
470 #include "krb.h"
471 #define krb_get_err_text_entry krb_get_err_text
472 #endif /* KRB524 */
473 #endif /* FTP_KRB4 */
474
475 #ifdef CK_SSL
476 #ifdef FTP_KRB4
477 #ifndef HEADER_DES_H
478 #define HEADER_DES_H
479 #endif /* HEADER_DES_H */
480 #endif /* FTP_KRB4 */
481 #include "ck_ssl.h"
482 #endif /* CK_SSL */
483
484 #ifdef FTP_SRP
485 #ifdef HAVE_PWD_H
486 #include "pwd.h"
487 #endif /* HAVE_PWD_H */
488 #include "t_pwd.h"
489 #include "t_client.h"
490 #include "krypto.h"
491 #endif /* FTP_SRP */
492
493 #ifdef FTP_GSSAPI
494 #include <gssapi/gssapi.h>
495 /*
496   Need to include the krb5 file, because we're doing manual fallback
497   from the v2 mech to the v1 mech.  Once there's real negotiation,
498   we can be generic again.
499 */
500 #include <gssapi/gssapi_generic.h>
501 #include <gssapi/gssapi_krb5.h>
502 static gss_ctx_id_t gcontext;
503 #endif /* FTP_GSSAPI */
504
505 #ifdef OS2
506 #ifdef FTP_SRP
507 #define MAP_KRYPTO
508 #ifdef SRPDLL
509 #define MAP_SRP
510 #endif /* SRPDLL */
511 #endif /* FTP_SRP */
512 #ifdef FTP_KRB4
513 #define MAP_KRB4
514 #ifdef CK_ENCRYPTION
515 #define MAP_DES
516 #endif /* CK_ENCRYPTION */
517 #endif /* FTP_KRB4 */
518 #ifdef FTP_GSSAPI
519 #define MAP_GSSAPI
520 #define GSS_OIDS
521 #endif /* FTP_GSSAPI */
522 #include "ckoath.h"
523
524 extern int k95stdout, wherex[], wherey[];
525 extern unsigned char colorcmd;
526 #endif /* OS2 */
527
528 #ifdef FTP_KRB4
529 static char ftp_realm[REALM_SZ + 1];
530 static KTEXT_ST ftp_tkt;
531 #ifdef OS2
532 static LEASH_CREDENTIALS ftp_cred;
533 #else /* OS2 */
534 static CREDENTIALS ftp_cred;
535 #endif /* OS2 */
536 static MSG_DAT ftp_msg_data;
537 static des_key_schedule ftp_sched;
538 static int foo[4] = {99,99,99,99};
539 #endif /* FTP_KRB4 */
540
541 /* getreply() function codes */
542
543 #define GRF_AUTH 1                      /* Reply to AUTH command */
544 #define GRF_FEAT 2                      /* Reply to FEAT command */
545
546 /* Operational definitions */
547
548 #define DEF_VBM 0                       /* Default verbose mode */
549 /* #define SETVBM */                    /* (see getreply) */
550
551 #define URL_ONEFILE                     /* GET, not MGET, for FTP URL */
552
553 #define FTP_BUFSIZ 10240                /* Max size for FTP cmds & replies */
554 #define SRVNAMLEN 32                    /* Max length for server type name */
555 #define PWDSIZ 256
556 #define PASSBUFSIZ 256
557 #define PROMPTSIZ 256
558
559 #ifndef MGETMAX                         /* Max operands for MGET command */
560 #define MGETMAX 1000
561 #endif /* MGETMAX */
562
563 #ifdef FTP_SRP
564 #define FUDGE_FACTOR 100
565 #endif /* FTP_SRP */
566
567 /*
568   Amount of growth from cleartext to ciphertext.  krb_mk_priv adds this
569   number bytes.  Must be defined for each auth type.
570   GSSAPI appears to add 52 bytes, but I'm not sure it is a constant--hartmans
571   3DES requires 56 bytes.  Lets use 96 just to be sure.
572 */
573 #ifdef FTP_GSSAPI
574 #ifndef FUDGE_FACTOR
575 #define FUDGE_FACTOR 96
576 #endif /* FUDGE_FACTOR */
577 #endif /* FTP_GSSAPI */
578
579 #ifdef FTP_KRB4
580 #ifndef FUDGE_FACTOR
581 #define FUDGE_FACTOR 32
582 #endif /* FUDGE_FACTOR */
583 #endif /* FTP_KRB4 */
584
585 #ifndef FUDGE_FACTOR                    /* In case no auth types define it */
586 #define FUDGE_FACTOR 0
587 #endif /* FUDGE_FACTOR */
588
589 #ifndef MAXHOSTNAMELEN
590 #define MAXHOSTNAMELEN 64
591 #endif /* MAXHOSTNAMELEN */
592 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
593
594 /* Fascist compiler toadying */
595
596 #ifndef SENDARG2TYPE
597 #ifdef COMMENT                          /* Might be needed here and there */
598 #define SENDARG2TYPE const char *
599 #else
600 #define SENDARG2TYPE char *
601 #endif /* COMMENT */
602 #endif /* SENDARG2TYPE */
603
604 /* Common text messages */
605
606 static char *nocx = "?No FTP control connection\n";
607
608 static char *fncnam[] = {
609   "rename", "overwrite", "backup", "append", "discard", "ask", "update",
610   "dates-differ", ""
611 };
612
613 /* Macro definitions */
614
615 /* Used to speed up text-mode PUTs */
616 #define zzout(fd,c) \
617 ((fd<0)?(-1):((nout>=ucbufsiz)?(zzsend(fd,c)):(ucbuf[nout++]=c)))
618
619 #define CHECKCONN() if(!connected){printf(nocx);return(-9);}
620
621 /* Externals */
622
623 #ifdef CK_URL
624 extern struct urldata g_url;
625 #endif /* CK_URL */
626
627 #ifdef DYNAMIC
628 extern char *zinbuffer, *zoutbuffer;    /* Regular Kermit file i/o */
629 #else
630 extern char zinbuffer[], zoutbuffer[];
631 #endif /* DYNAMIC */
632 extern char *zinptr, *zoutptr;
633 extern int zincnt, zoutcnt, zobufsize, fncact;
634
635 #ifdef CK_TMPDIR
636 extern int f_tmpdir;                    /* Directory changed temporarily */
637 extern char savdir[];                   /* For saving current directory */
638 extern char * dldir;
639 #endif /* CK_TMPDIR */
640
641 extern char * rfspec, * sfspec, * srfspec, * rrfspec; /* For WHERE command */
642
643 extern xx_strp xxstring;
644 extern struct keytab onoff[], txtbin[], rpathtab[];
645 extern int nrpathtab, xfiletype, patterns, gnferror, moving, what, pktnum;
646 extern int success, nfils, sndsrc, quiet, nopush, recursive, inserver, binary;
647 extern int filepeek, nscanfile, fsecs, xferstat, xfermode, lastxfer, tsecs;
648 extern int backgrd, spackets, rpackets, spktl, rpktl, xaskmore, cmd_rows;
649 extern int nolinks, msgflg, keep;
650 extern long fsize, ffc, tfc, filcnt, xfsecs, tfcps, cps, oldcps;
651 #ifdef GFTIMER
652 extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
653 #else
654 extern long xfsecs;
655 #endif /* GFTIMER */
656
657 extern char filnam[], * filefile, myhost[];
658 extern char * snd_move, * rcv_move, * snd_rename, * rcv_rename;
659 extern int g_skipbup, skipbup, sendmode;
660 extern int g_displa, fdispla, displa;
661
662 #ifdef LOCUS
663 extern int locus, autolocus;
664 #endif /* LOCUS */
665
666 #ifndef NOCSETS
667 extern int nfilc, dcset7, dcset8, fileorder;
668 extern struct csinfo fcsinfo[];
669 extern struct keytab fcstab[];
670 extern int fcharset;
671 #endif /* NOCSETS */
672
673 extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */
674 extern char sndnbefore[], sndnafter[], *rcvexcept[];
675 extern CHAR feol;
676 extern long sendstart, sndsmaller, sndlarger, rs_len;
677
678 extern char * remdest;
679 extern int remfile, remappd, rempipe;
680
681 #ifndef NOSPL
682 extern int cmd_quoting;
683 #ifdef PUTARRAY
684 extern int sndxlo, sndxhi, sndxin;
685 extern char sndxnam[];
686 extern char **a_ptr[];                  /* Array pointers */
687 extern int a_dim[];                     /* Array dimensions */
688 #endif /* PUTARRAY */
689 #endif /* NOSPL */
690
691 #ifndef NOMSEND                         /* MPUT and ADD SEND-LIST lists */
692 extern char *msfiles[];
693 extern int filesinlist;
694 extern struct filelist * filehead;
695 extern struct filelist * filetail;
696 extern struct filelist * filenext;
697 extern int addlist;
698 extern char fspec[];                    /* Most recent filespec */
699 extern int fspeclen;                    /* Length of fspec[] buffer */
700 #endif /* NOMSEND */
701
702 extern int pipesend;
703 #ifdef PIPESEND
704 extern char * sndfilter, * rcvfilter;
705 #endif /* PIPESEND */
706
707 #ifdef CKROOT
708 extern int ckrooterr;
709 #endif /* CKROOT */
710
711 #ifdef KRB4
712 extern int krb4_autoget;
713 _PROTOTYP(char * ck_krb4_realmofhost,(char *));
714 #endif /* KRB4 */
715
716 #ifdef KRB5
717 extern int krb5_autoget;
718 extern int krb5_d_no_addresses;
719 _PROTOTYP(char * ck_krb5_realmofhost,(char *));
720 #endif /* KRB5 */
721
722 #ifdef DCMDBUF
723 extern char *atmbuf;                    /* Atom buffer (malloc'd) */
724 extern char *cmdbuf;                    /* Command buffer (malloc'd) */
725 extern char *line;                      /* Big string buffer #1 */
726 extern char *tmpbuf;                    /* Big string buffer #2 */
727 #else
728 extern char atmbuf[];                   /* The same, but static */
729 extern char cmdbuf[];
730 extern char line[];
731 extern char tmpbuf[];
732 #endif /* DCMDBUF */
733
734 extern char * cmarg, * cmarg2, ** cmlist; /* For setting up file lists */
735
736 /* Public variables declared here */
737
738 #ifdef NOXFER
739 int ftpget  =  1;                       /* GET/PUT/REMOTE orientation FTP */
740 #else
741 int ftpget  =  2;                       /* GET/PUT/REMOTE orientation AUTO */
742 #endif /* NOXFER */
743 int ftpcode = -1;                       /* Last FTP response code */
744 int ftp_cmdlin = 0;                     /* FTP invoked from command line */
745 int ftp_fai = 0;                        /* FTP failure count */
746 int ftp_deb = 0;                        /* FTP debugging */
747 int ftp_dis = -1;                       /* FTP display style */
748 int ftp_log = 1;                        /* FTP Auto-login */
749 int sav_log = -1;
750 int ftp_action = 0;                     /* FTP action from command line */
751 int ftp_dates = 1;                      /* Set file dates from server */
752
753 char ftp_reply_str[FTP_BUFSIZ] = "";    /* Last line of previous reply */
754 char ftp_srvtyp[SRVNAMLEN] = { NUL, NUL }; /* Server's system type */
755 char ftp_user_host[MAX_DNS_NAMELEN]= ""; /* FTP hostname specified by user */
756 char * ftp_host = NULL;                 /* FTP hostname */
757 char * ftp_logname = NULL;              /* FTP username */
758 char * ftp_rdir = NULL;                 /* Remote directory from cmdline */
759 char * ftp_apw = NULL;                  /* Anonymous password */
760
761 /* Definitions and typedefs needed for prototypes */
762
763 #define sig_t my_sig_t
764 #define sigtype SIGTYP
765 typedef sigtype (*sig_t)();
766
767 /* Static global variables */
768
769 static char ftpsndbuf[FTP_BUFSIZ+64];
770
771 static char * fts_sto = NULL;
772
773 static int ftpsndret = 0;
774 static struct _ftpsnd {
775     sig_t oldintr, oldintp;
776     int            reply;
777     int            incs,
778                    outcs;
779     char *         cmd, * local, * remote;
780     int            bytes;
781     int            restart;
782     int            xlate;
783     char *         lmode;
784 } ftpsnd;
785
786 /*
787   This is just a first stab -- these strings should match how the
788   corresponding FTP servers identify themselves.
789 */
790 #ifdef UNIX
791 static char * myostype = "UNIX";
792 #else
793 #ifdef VMS
794 /* not yet... */
795 static char * myostype = "VMS";
796 #else
797 #ifdef OS2
798 #ifdef NT
799 static char * myostype = "WIN32";
800 #else
801 static char * myostype = "OS/2";
802 #endif /* NT */
803 #else
804 static char * myostype = "UNSUPPORTED";
805 #endif /* OS2  */
806 #endif /* VMS */
807 #endif /* UNIX */
808
809 static int noinit = 0;                  /* Don't send REST, STRU, MODE */
810 static int alike = 0;                   /* Client/server like platforms */
811 static int local = 1;                   /* Shadows Kermit global 'local' */
812 static int dout = -1;                   /* Data connection file descriptor */
813 static int dpyactive = 0;               /* Data transfer is active */
814 static int globaldin = -1;              /* Data connection f.d. */
815 static int out2screen = 0;              /* GET output is to screen */
816 static int forcetype = 0;               /* Force text or binary mode */
817 static int cancelfile = 0;              /* File canceled */
818 static int cancelgroup = 0;             /* Group canceled */
819 static int anonymous = 0;               /* Logging in as anonymous */
820 static int loggedin = 0;                /* Logged in (or not) */
821 static int puterror = 0;                /* What to do on PUT error */
822 static int geterror = 0;                /* What to do on GET error */
823 static int rfrc = 0;                    /* remote_files() return code */
824 static int okrestart = 0;               /* Server understands REST */
825 static int printlines = 0;              /* getreply()should print data lines */
826 static int haveurl = 0;                 /* Invoked by command-line FTP URL */
827 static int mdtmok = 1;                  /* Server supports MDTM */
828 static int sizeok = 1;
829 static int featok = 1;
830 static int mlstok = 1;
831 static int stouarg = 1;
832 static int typesent = 0;
833 static int havesigint = 0;
834 static long havetype =  0;
835 static long havesize = -1L;
836 static char * havemdtm = NULL;
837 static int mgetmethod = 0;              /* NLST or MLSD */
838 static int mgetforced = 0;
839
840 static int i, /* j, k, */ x, y, z;      /* Volatile temporaries */
841 static int c0, c1;                      /* Temp variables for characters */
842
843 static char putpath[CKMAXPATH+1] = { NUL, NUL };
844 static char asnambuf[CKMAXPATH+1] = { NUL, NUL };
845
846 #define RFNBUFSIZ 4096                  /* Remote filename buffer size */
847
848 static unsigned int maxbuf = 0, actualbuf = 0;
849 static CHAR *ucbuf = NULL;
850 static int ucbufsiz = 0;
851 static unsigned int nout = 0;           /* Number of chars in ucbuf */
852
853 static jmp_buf recvcancel;
854 static jmp_buf sendcancel;
855 static jmp_buf ptcancel;
856 static jmp_buf jcancel;
857 static int ptabflg = 0;
858
859 /* Protection level symbols */
860
861 #define FPL_CLR 1                       /* Clear */
862 #define FPL_SAF 2                       /* Safe */
863 #define FPL_PRV 3                       /* Private */
864 #define FPL_CON 4                       /* Confidential */
865
866 /* Symbols for file types returned by MLST/MLSD */
867
868 #define FTYP_FILE 1                     /* Regular file */
869 #define FTYP_DIR  2                     /* Directory */
870 #define FTYP_CDIR 3                     /* Current directory */
871 #define FTYP_PDIR 4                     /* Parent directory */
872
873 /* File type symbols keyed to the file-type symbols from ckcker.h */
874
875 #define FTT_ASC XYFT_T                  /* ASCII (text) */
876 #define FTT_BIN XYFT_B                  /* Binary (image) */
877 #define FTT_TEN XYFT_X                  /* TENEX (TOPS-20) */
878
879 /* Server feature table - sfttab[0] > 0 means server supports FEAT and OPTS */
880
881 static int sfttab[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
882
883 #define SFT_AUTH  1                     /* FTP server feature codes */
884 #define SFT_LANG  2
885 #define SFT_MDTM  3
886 #define SFT_MLST  4
887 #define SFT_PBSZ  5
888 #define SFT_PROT  6
889 #define SFT_REST  7
890 #define SFT_SIZE  8
891 #define SFT_TVFS  9
892 #define SFT_UTF8 10
893
894 #define CNV_AUTO  2                     /* FTP filename conversion */
895 #define CNV_CNV   1
896 #define CNV_LIT   0
897
898 /* SET FTP values */
899
900 static int                              /* SET FTP values... */
901   ftp_aut = 1,                          /* Auto-authentication */
902 #ifdef FTP_SECURITY
903   ftp_cry = 1,                          /* Auto-encryption */
904   ftp_cfw = 0,                          /* Credential forwarding */
905 #endif /* FTP_SECURITY */
906   ftp_cpl = FPL_CLR,                    /* Command protection level */
907   ftp_dpl = FPL_CLR,                    /* Data protection level */
908 #ifdef FTP_PROXY
909   ftp_prx = 0,                          /* Use proxy */
910 #endif /* FTP_PROXY */
911   sav_psv = -1,                         /* For saving passive mode */
912   ftp_psv = 1,                          /* Passive mode */
913   ftp_spc = 1,                          /* Send port commands */
914   ftp_typ = FTT_ASC,                    /* Type */
915   get_auto = 1,                         /* Automatic type switching for GET */
916   tenex = 0,                            /* Type is Tenex */
917   ftp_usn = 0,                          /* Unique server names */
918   ftp_prm = 0,                          /* Permissions */
919   ftp_cnv = CNV_AUTO,                   /* Filename conversion (2 = auto) */
920   ftp_vbm = DEF_VBM,                    /* Verbose mode */
921   ftp_vbx = DEF_VBM,                    /* Sticky version of same */
922   ftp_err = 0,                          /* Error action */
923   ftp_fnc = -1;                         /* Filename collision action */
924
925 #ifdef CK_SSL
926 static int ftp_bug_use_ssl_v2 = 0;      /* use SSLv2 for AUTH SSL */
927 #endif /* CK_SSL */
928
929 static int
930 #ifdef NOCSETS
931   ftp_csr = -1,                         /* Remote (server) character set */
932 #else
933   ftp_csr = FC_UTF8,
934 #endif /* NOCSETS */
935   ftp_xla = 0;                          /* Character-set translation on/off */
936 int
937   ftp_csx = -1,                         /* Remote charset currently in use */
938   ftp_csl = -1;                         /* Local charset currently in use */
939
940 static int g_ftp_typ = FTT_ASC;         /* For saving and restoring ftp_typ */
941
942 char * ftp_nml = NULL;                  /* /NAMELIST */
943 char * ftp_tmp = NULL;                  /* Temporary string */
944 static char * ftp_acc = NULL;           /* Account string */
945 static char * auth_type = NULL;         /* Authentication type */
946 static char * srv_renam = NULL;         /* Server-rename string */
947 FILE * fp_nml = NULL;                   /* Namelist file pointer */
948
949 static int csocket = -1;                /* Control socket */
950 static int connected = 0;               /* Connected to FTP server */
951 static short ftp_port = 0;              /* FTP port */
952 #ifdef FTPHOST
953 static int hostcmd = 0;                 /* Has HOST command been sent */
954 #endif /* FTPHOST */
955 static int form, mode, stru, bytesize, curtype = FTT_ASC;
956 static char bytename[8];
957
958 /* For parsing replies to FTP server command */
959 static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
960
961 #ifdef FTP_PROXY
962 static int proxy, unix_proxy
963 #endif /* FTP_PROXY */
964
965 static char pasv[64];                   /* Passive-mode port */
966 static int passivemode = 0;
967 static int sendport = 0;
968 static int servertype = 0;              /* FTP server's OS type */
969
970 static int testing = 0;
971 static char ftpcmdbuf[FTP_BUFSIZ];
972
973 /* Macro definitions */
974
975 #define UC(b) ckitoa(((int)b)&0xff)
976 #define nz(x) ((x) == 0 ? 1 : (x))
977
978 /* Command tables and definitions */
979
980 #define FTP_ACC  1                      /* FTP command keyword codes */
981 #define FTP_APP  2
982 #define FTP_CWD  3
983 #define FTP_CHM  4
984 #define FTP_CLS  5
985 #define FTP_DEL  6
986 #define FTP_DIR  7
987 #define FTP_GET  8
988 #define FTP_IDL  9
989 #define FTP_MDE 10
990 #define FTP_MDI 11
991 #define FTP_MGE 12
992 #define FTP_MKD 13
993 #define FTP_MOD 14
994 #define FTP_MPU 15
995 #define FTP_OPN 16
996 #define FTP_PUT 17
997 #define FTP_PWD 18
998 #define FTP_RGE 19
999 #define FTP_REN 20
1000 #define FTP_RES 21
1001 #define FTP_HLP 22
1002 #define FTP_RMD 23
1003 #define FTP_STA 24
1004 #define FTP_SIT 25
1005 #define FTP_SIZ 26
1006 #define FTP_SYS 27
1007 #define FTP_UMA 28
1008 #define FTP_GUP 29
1009 #define FTP_USR 30
1010 #define FTP_QUO 31
1011 #define FTP_TYP 32
1012 #define FTP_FEA 33
1013 #define FTP_OPT 34
1014 #define FTP_CHK 35
1015 #define FTP_VDI 36
1016 #define FTP_ENA 37
1017 #define FTP_DIS 38
1018
1019 struct keytab gprtab[] = {              /* GET-PUT-REMOTE keywords */
1020     { "auto",    2, 0 },
1021     { "ftp",     1, 0 },
1022     { "kermit",  0, 0  }
1023 };
1024
1025 static struct keytab qorp[] = {         /* QUIT or PROCEED keywords */
1026     { "proceed", 0, 0 },                /* 0 = proceed */
1027     { "quit",    1, 0 }                 /* 1 = quit */
1028 };
1029
1030 static struct keytab ftpcmdtab[] = {    /* FTP command table */
1031     { "account",   FTP_ACC, 0 },
1032     { "append",    FTP_APP, 0 },
1033     { "bye",       FTP_CLS, 0 },
1034     { "cd",        FTP_CWD, 0 },
1035     { "cdup",      FTP_GUP, 0 },
1036     { "check",     FTP_CHK, 0 },
1037     { "chmod",     FTP_CHM, 0 },
1038     { "close",     FTP_CLS, 0 },
1039     { "cwd",       FTP_CWD, CM_INV },
1040     { "delete",    FTP_MDE, 0 },
1041     { "directory", FTP_DIR, 0 },
1042     { "disable",   FTP_DIS, 0 },
1043     { "enable",    FTP_ENA, 0 },
1044     { "features",  FTP_FEA, 0 },
1045     { "get",       FTP_GET, 0 },
1046     { "help",      FTP_HLP, 0 },
1047     { "idle",      FTP_IDL, 0 },
1048     { "login",     FTP_USR, CM_INV },
1049     { "mdelete",   FTP_MDE, CM_INV },
1050     { "mget",      FTP_MGE, 0 },
1051     { "mkdir",     FTP_MKD, 0 },
1052     { "modtime",   FTP_MOD, 0 },
1053     { "mput",      FTP_MPU, 0 },
1054     { "open",      FTP_OPN, 0 },
1055     { "opt",       FTP_OPT, CM_INV|CM_ABR },
1056     { "opts",      FTP_OPT, CM_INV },
1057     { "options",   FTP_OPT, 0 },
1058     { "put",       FTP_PUT, 0 },
1059     { "pwd",       FTP_PWD, 0 },
1060     { "quit",      FTP_CLS, CM_INV },
1061     { "quote",     FTP_QUO, 0 },
1062     { "reget",     FTP_RGE, 0 },
1063     { "rename",    FTP_REN, 0 },
1064     { "reset",     FTP_RES, 0 },
1065     { "rmdir",     FTP_RMD, 0 },
1066     { "send",      FTP_PUT, CM_INV },
1067     { "site",      FTP_SIT, 0 },
1068     { "size",      FTP_SIZ, 0 },
1069     { "status",    FTP_STA, 0 },
1070     { "system",    FTP_SYS, 0 },
1071     { "type",      FTP_TYP, 0 },
1072     { "umask",     FTP_UMA, 0 },
1073     { "up",        FTP_GUP, CM_INV },
1074     { "user",      FTP_USR, 0 },
1075     { "vdirectory",FTP_VDI, 0 },
1076     { "", 0, 0 }
1077 };
1078 static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1;
1079
1080 #define OPN_ANO 1                       /* FTP OPEN switch codes */
1081 #define OPN_PSW 2
1082 #define OPN_USR 3
1083 #define OPN_ACC 4
1084 #define OPN_ACT 5
1085 #define OPN_PSV 6
1086 #define OPN_TLS 7
1087 #define OPN_NIN 8
1088 #define OPN_NOL 9
1089
1090 #ifdef FTP_SECURITY
1091 #ifdef CK_SSL
1092 #define USETLSTAB
1093 static struct keytab tlstab[] = {       /* FTP SSL/TLS switches */
1094     { "/ssl",       OPN_TLS, 0    },
1095     { "/tls",       OPN_TLS, 0    },
1096     { "", 0, 0 }
1097 };
1098 static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1;
1099 #endif /* CK_SSL */
1100 #endif /* FTP_SECURITY */
1101
1102 static struct keytab ftpswitab[] = {    /* FTP command switches */
1103     { "/account",   OPN_ACC, CM_ARG },
1104     { "/active",    OPN_ACT, 0      },
1105     { "/anonymous", OPN_ANO, 0      },
1106     { "/noinit",    OPN_NIN, 0      },
1107     { "/nologin",   OPN_NOL, 0      },
1108     { "/passive",   OPN_PSV, 0      },
1109     { "/password",  OPN_PSW, CM_ARG },
1110     { "/user",      OPN_USR, CM_ARG },
1111     { "", 0, 0 }
1112 };
1113 static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1;
1114
1115 /* FTP { ENABLE, DISABLE } items */
1116
1117 #define ENA_FEAT 1
1118 #define ENA_MDTM 2
1119 #define ENA_MLST 3
1120 #define ENA_SIZE 4
1121 #define ENA_AUTH 5
1122
1123 static struct keytab ftpenatab[] = {
1124     { "AUTH",  ENA_AUTH, 0 },
1125     { "FEAT",  ENA_FEAT, 0 },
1126     { "MDTM",  ENA_MDTM, 0 },
1127     { "ML",    ENA_MLST, CM_INV|CM_ABR },
1128     { "MLS",   ENA_MLST, CM_INV|CM_ABR },
1129     { "MLSD",  ENA_MLST, CM_INV },
1130     { "MLST",  ENA_MLST, 0 },
1131     { "SIZE",  ENA_SIZE, 0 },
1132     { "", 0, 0 }
1133 };
1134 static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1;
1135
1136 /* SET FTP command keyword indices */
1137
1138 #define FTS_AUT  1                      /* Autoauthentication */
1139 #define FTS_CRY  2                      /* Encryption */
1140 #define FTS_LOG  3                      /* Autologin */
1141 #define FTS_CPL  4                      /* Command protection level */
1142 #define FTS_CFW  5                      /* Credentials forwarding */
1143 #define FTS_DPL  6                      /* Data protection level */
1144 #define FTS_DBG  7                      /* Debugging */
1145 #define FTS_PSV  8                      /* Passive mode */
1146 #define FTS_SPC  9                      /* Send port commands */
1147 #define FTS_TYP 10                      /* (file) Type */
1148 #define FTS_USN 11                      /* Unique server names (for files) */
1149 #define FTS_VBM 12                      /* Verbose mode */
1150 #define FTS_ATP 13                      /* Authentication type */
1151 #define FTS_CNV 14                      /* Filename conversion */
1152 #define FTS_TST 15                      /* Test (progress) messages */
1153 #define FTS_PRM 16                      /* (file) Permissions */
1154 #define FTS_XLA 17                      /* Charset translation */
1155 #define FTS_CSR 18                      /* Server charset */
1156 #define FTS_ERR 19                      /* Error action */
1157 #define FTS_FNC 20                      /* Collision */
1158 #define FTS_SRP 21                      /* SRP options */
1159 #define FTS_GFT 22                      /* GET automatic file-type switching */
1160 #define FTS_DAT 23                      /* Set file dates */
1161 #define FTS_STO 24                      /* Server time offset */
1162 #define FTS_APW 25                      /* Anonymous password */
1163 #define FTS_DIS 26                      /* File-transfer display style */
1164 #define FTS_BUG 27                      /* Bug(s) */
1165
1166 /* FTP BUGS */
1167
1168 #define FTB_SV2  1                      /* use SSLv2 */
1169
1170 static struct keytab ftpbugtab[] = {
1171     { "use-ssl-v2",     FTB_SV2,        0 }
1172 };
1173 static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab));
1174
1175 /* FTP PUT options (mutually exclusive, not a bitmask) */
1176
1177 #define PUT_UPD 1                       /* Update */
1178 #define PUT_RES 2                       /* Restart */
1179 #define PUT_SIM 4                       /* Simulation */
1180 #define PUT_DIF 8                       /* Dates Differ */
1181
1182 static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */
1183 #ifndef MAC
1184     { "append",    XYFX_A, 0 },         /* append to old file */
1185 #endif /* MAC */
1186 #ifdef COMMENT
1187     { "ask",       XYFX_Q, 0 },         /* ask what to do (not implemented) */
1188 #endif
1189     { "backup",    XYFX_B, 0 },         /* rename old file */
1190 #ifndef MAC
1191     { "dates-differ", XYFX_M, 0 },      /* accept if dates differ */
1192     { "discard",   XYFX_D, 0 },         /* don't accept new file */
1193     { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
1194 #endif /* MAC */
1195     { "overwrite", XYFX_X, 0 },         /* overwrite the old file */
1196     { "rename",    XYFX_R, 0 },         /* rename the incoming file */
1197 #ifndef MAC                             /* This crashes Mac Kermit. */
1198     { "update",    XYFX_U, 0 },         /* replace if newer */
1199 #endif /* MAC */
1200     { "", 0, 0 }
1201 };
1202 static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1;
1203
1204
1205 #ifdef FTP_SECURITY
1206 /* FTP authentication options */
1207
1208 #define FTA_AUTO 0                      /* Auto */
1209 #define FTA_SRP  1                      /* SRP */
1210 #define FTA_GK5  2                      /* Kerberos 5 */
1211 #define FTA_K4   3                      /* Kerberos 4 */
1212 #define FTA_SSL  4                      /* SSL */
1213 #define FTA_TLS  5                      /* TLS */
1214
1215 /* FTP authentication types */
1216
1217 #define FTPATYPS 8
1218 static int ftp_auth_type[FTPATYPS] = {
1219 #ifdef FTP_GSSAPI
1220     FTA_GK5,                            /* GSSAPI Kerberos 5 */
1221 #endif /* FTP_GK5 */
1222 #ifdef FTP_SRP
1223     FTA_SRP,                            /* SRP */
1224 #endif /* FTP_SRP */
1225 #ifdef FTP_KRB4
1226     FTA_K4,                             /* Kerberos 4 */
1227 #endif /* FTP_KRB4 */
1228 #ifdef CK_SSL
1229     FTA_TLS,                            /* TLS */
1230     FTA_SSL,                            /* SSL */
1231 #endif /* CK_SSL */
1232     0
1233 };
1234
1235 static struct keytab ftpauth[] = {      /* SET FTP AUTHTYPE cmd table */
1236     { "automatic", FTA_AUTO,  CM_INV },
1237 #ifdef FTP_GSSAPI
1238     { "gssapi-krb5", FTA_GK5, 0 },
1239 #endif /* FTP_GSSAPI */
1240 #ifdef FTP_KRB4
1241     { "k4",       FTA_K4,     CM_INV },
1242 #endif /* FTP_KRB4 */
1243 #ifdef FTP_GSSAPI
1244     { "k5",        FTA_GK5,   CM_INV },
1245 #endif /* FTP_GSSAPI */
1246 #ifdef FTP_KRB4
1247     { "kerberos4", FTA_K4,    0 },
1248 #endif /* FTP_KRB4 */
1249 #ifdef FTP_GSSAPI
1250     { "kerberos5", FTA_GK5,   CM_INV },
1251 #endif /* FTP_GSSAPI */
1252 #ifdef FTP_KRB4
1253     { "kerberos_iv",FTA_K4,   CM_INV },
1254 #endif /* FTP_KRB4 */
1255 #ifdef FTP_GSSAPI
1256     { "kerberos_v", FTA_GK5,  CM_INV },
1257 #endif /* FTP_GSSAPI */
1258 #ifdef FTP_KRB4
1259     { "krb4",     FTA_K4,     CM_INV },
1260 #endif /* FTP_KRB4 */
1261 #ifdef FTP_GSSAPI
1262     { "krb5",     FTA_GK5,    CM_INV },
1263 #endif /* FTP_GSSAPI */
1264 #ifdef FTP_SRP
1265     { "srp",      FTA_SRP,     0 },
1266 #endif /* FTP_SRP */
1267 #ifdef CK_SSL
1268     { "ssl",      FTA_SSL,     0 },
1269     { "tls",      FTA_TLS,     0 },
1270 #endif /* CK_SSL */
1271     { "", 0, 0 }
1272 };
1273 static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1;
1274
1275 #ifdef FTP_SRP
1276 #define SRP_CIPHER 1
1277 #define SRP_HASH   2
1278 static struct keytab ftpsrp[] = {      /* SET FTP SRP command table */
1279     { "cipher",   SRP_CIPHER,     0 },
1280     { "hash",     SRP_HASH,       0 },
1281     { "", 0, 0 }
1282 };
1283 static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1;
1284 #endif /* FTP_SRP */
1285 #endif /* FTP_SECURITY */
1286
1287 static struct keytab ftpset[] = {       /* SET FTP commmand table */
1288     { "anonymous-password",       FTS_APW, 0 },
1289 #ifdef FTP_SECURITY
1290     { "authtype",                 FTS_ATP, 0 },
1291     { "autoauthentication",       FTS_AUT, 0 },
1292     { "autoencryption",           FTS_CRY, 0 },
1293 #endif /* FTP_SECURITY */
1294     { "autologin",                FTS_LOG, 0 },
1295     { "bug",                      FTS_BUG, 0 },
1296 #ifndef NOCSETS
1297     { "character-set-translation",FTS_XLA, 0 },
1298 #endif /* NOCSETS */
1299     { "collision",                FTS_FNC, 0 },
1300 #ifdef FTP_SECURITY
1301     { "command-protection-level", FTS_CPL, 0 },
1302     { "cpl",                      FTS_CPL, CM_INV },
1303     { "credential-forwarding",    FTS_CFW, 0 },
1304     { "da",                       FTS_DAT, CM_INV|CM_ABR },
1305     { "data-protection-level",    FTS_DPL, 0 },
1306 #endif /* FTP_SECURITY */
1307     { "dates",                    FTS_DAT, 0 },
1308     { "debug",                    FTS_DBG, 0 },
1309     { "display",                  FTS_DIS, 0 },
1310 #ifdef FTP_SECURITY
1311     { "dpl",                      FTS_DPL, CM_INV },
1312 #endif /* FTP_SECURITY */
1313     { "error-action",             FTS_ERR, 0 },
1314     { "filenames",                FTS_CNV, 0 },
1315     { "get-filetype-switching",   FTS_GFT, 0 },
1316     { "passive-mode",             FTS_PSV, 0 },
1317     { "pasv",                     FTS_PSV, CM_INV },
1318     { "permissions",              FTS_PRM, 0 },
1319     { "progress-messages",        FTS_TST, 0 },
1320     { "send-port-commands",       FTS_SPC, 0 },
1321 #ifndef NOCSETS
1322     { "server-character-set",     FTS_CSR, 0 },
1323 #endif /* NOCSETS */
1324     { "server-time-offset",       FTS_STO, 0 },
1325 #ifdef FTP_SRP
1326     { "srp",                      FTS_SRP, 0 },
1327 #else
1328     { "srp",                      FTS_SRP, CM_INV },
1329 #endif /* FTP_SRP */
1330     { "type",                     FTS_TYP, 0 },
1331     { "unique-server-names",      FTS_USN, 0 },
1332     { "verbose-mode",             FTS_VBM, 0 },
1333     { "", 0, 0 }
1334 };
1335 static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1;
1336
1337 /*
1338   GET and PUT switches are approximately the same as Kermit GET and SEND,
1339   and use the same SND_xxx definitions, but hijack a couple for FTP use.
1340   Don't just make up new ones, since the number of SND_xxx options must be
1341   known in advance for the switch-parsing arrays.
1342 */
1343 #define SND_USN SND_PRO                 /* /UNIQUE instead of /PROTOCOL */
1344 #define SND_PRM SND_PIP                 /* /PERMISSIONS instead of /PIPES */
1345 #define SND_TEN SND_CAL                 /* /TENEX instead of /CALIBRATE */
1346
1347 static struct keytab putswi[] = {       /* FTP PUT switch table */
1348     { "/after",                SND_AFT, CM_ARG },
1349 #ifdef PUTARRAY
1350     { "/array",                SND_ARR, CM_ARG },
1351 #endif /* PUTARRAY */
1352     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1353     { "/as-name",              SND_ASN, CM_ARG },
1354     { "/ascii",                SND_TXT, CM_INV },
1355     { "/b",                    SND_BIN, CM_INV|CM_ABR },
1356     { "/before",               SND_BEF, CM_ARG },
1357     { "/binary",               SND_BIN, 0 },
1358 #ifdef PUTPIPE
1359     { "/command",              SND_CMD, CM_PSH },
1360 #endif /* PUTPIPE */
1361 #ifdef COMMENT
1362 /* This works but it's dangerous */
1363 #ifdef DOUPDATE
1364     { "/dates-differ",         SND_DIF, CM_INV },
1365 #endif /* DOUPDATE */
1366 #endif /* COMMENT */
1367     { "/delete",               SND_DEL, 0 },
1368 #ifdef UNIXOROSK
1369     { "/dotfiles",             SND_DOT, 0 },
1370 #endif /* UNIXOROSK */
1371     { "/error-action",         SND_ERR, CM_ARG },
1372     { "/except",               SND_EXC, CM_ARG },
1373     { "/filenames",            SND_NAM, CM_ARG },
1374 #ifdef PIPESEND
1375 #ifndef NOSPL
1376     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1377 #endif /* NOSPL */
1378 #endif /* PIPESEND */
1379 #ifdef CKSYMLINK
1380     { "/followlinks",          SND_LNK, 0 },
1381 #endif /* CKSYMLINK */
1382 #ifdef VMS
1383     { "/image",                SND_IMG, 0 },
1384 #else
1385     { "/image",                SND_BIN, CM_INV },
1386 #endif /* VMS */
1387     { "/larger-than",          SND_LAR, CM_ARG },
1388     { "/listfile",             SND_FIL, CM_ARG },
1389 #ifndef NOCSETS
1390     { "/local-character-set",  SND_CSL, CM_ARG },
1391 #endif /* NOCSETS */
1392 #ifdef CK_TMPDIR
1393     { "/move-to",              SND_MOV, CM_ARG },
1394 #endif /* CK_TMPDIR */
1395     { "/nobackupfiles",        SND_NOB, 0 },
1396 #ifdef UNIXOROSK
1397     { "/nodotfiles",           SND_NOD, 0 },
1398 #endif /* UNIXOROSK */
1399 #ifdef CKSYMLINK
1400     { "/nofollowlinks",        SND_NLK, 0 },
1401 #endif /* CKSYMLINK */
1402
1403     { "/not-after",            SND_NAF, CM_ARG },
1404     { "/not-before",           SND_NBE, CM_ARG },
1405 #ifdef UNIX
1406     { "/permissions",          SND_PRM, CM_ARG },
1407 #else
1408     { "/permissions",          SND_PRM, CM_ARG|CM_INV },
1409 #endif /* UNIX */
1410     { "/quiet",                SND_SHH, 0 },
1411 #ifdef FTP_RESTART
1412     { "/recover",              SND_RES, 0 },
1413 #endif /* FTP_RESTART */
1414 #ifdef RECURSIVE
1415     { "/recursive",            SND_REC, 0 },
1416 #endif /* RECURSIVE */
1417     { "/rename-to",            SND_REN, CM_ARG },
1418 #ifdef FTP_RESTART
1419     { "/restart",              SND_RES, CM_INV },
1420 #endif /* FTP_RESTART */
1421 #ifndef NOCSETS
1422     { "/server-character-set", SND_CSR, CM_ARG },
1423 #endif /* NOCSETS */
1424     { "/server-rename-to",     SND_SRN, CM_ARG },
1425     { "/simulate",             SND_SIM, 0 },
1426     { "/since",                SND_AFT, CM_INV|CM_ARG },
1427     { "/smaller-than",         SND_SMA, CM_ARG },
1428 #ifdef COMMENT
1429     { "/starting-at",          SND_STA, CM_ARG },
1430 #endif /* COMMENT */
1431 #ifdef RECURSIVE
1432     { "/subdirectories",       SND_REC, CM_INV },
1433 #endif /* RECURSIVE */
1434     { "/tenex",                SND_TEN, 0 },
1435     { "/text",                 SND_TXT, 0 },
1436 #ifndef NOCSETS
1437     { "/transparent",          SND_XPA, 0 },
1438 #endif /* NOCSETS */
1439     { "/type",                 SND_TYP, CM_ARG },
1440 #ifdef DOUPDATE
1441     { "/update",               SND_UPD, 0 },
1442 #endif /* DOUPDATE */
1443     { "/unique-server-names",  SND_USN, 0 },
1444     { "", 0, 0 }
1445 };
1446 static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1;
1447
1448 static struct keytab getswi[] = {       /* FTP [M]GET switch table */
1449     { "/after",                SND_AFT, CM_INV },
1450     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1451     { "/as-name",              SND_ASN, CM_ARG },
1452     { "/ascii",                SND_TXT, CM_INV },
1453     { "/before",               SND_BEF, CM_INV },
1454     { "/binary",               SND_BIN, 0 },
1455     { "/collision",            SND_COL, CM_ARG },
1456 #ifdef PUTPIPE
1457     { "/command",              SND_CMD, CM_PSH },
1458 #endif /* PUTPIPE */
1459     { "/delete",               SND_DEL, 0 },
1460     { "/error-action",         SND_ERR, CM_ARG },
1461     { "/except",               SND_EXC, CM_ARG },
1462     { "/filenames",            SND_NAM, CM_ARG },
1463 #ifdef PIPESEND
1464 #ifndef NOSPL
1465     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1466 #endif /* NOSPL */
1467 #endif /* PIPESEND */
1468 #ifdef VMS
1469     { "/image",                SND_IMG, 0 },
1470 #else
1471     { "/image",                SND_BIN, CM_INV },
1472 #endif /* VMS */
1473     { "/larger-than",          SND_LAR, CM_ARG },
1474     { "/listfile",             SND_FIL, CM_ARG },
1475 #ifndef NOCSETS
1476     { "/local-character-set",  SND_CSL, CM_ARG },
1477 #endif /* NOCSETS */
1478     { "/match",                SND_PAT, CM_ARG },
1479     { "/ml",                   SND_MLS, CM_INV|CM_ABR },
1480     { "/mls",                  SND_MLS, CM_INV|CM_ABR },
1481     { "/mlsd",                 SND_MLS, 0 },
1482     { "/mlst",                 SND_MLS, CM_INV },
1483 #ifdef CK_TMPDIR
1484     { "/move-to",              SND_MOV, CM_ARG },
1485 #endif /* CK_TMPDIR */
1486     { "/namelist",             SND_NML, CM_ARG },
1487     { "/nlst",                 SND_NLS, 0 },
1488     { "/nobackupfiles",        SND_NOB, 0 },
1489     { "/nodotfiles",           SND_NOD, 0 },
1490 #ifdef DOUPDATE
1491     { "/dates-differ",         SND_DIF, CM_INV },
1492 #endif /* DOUPDATE */
1493     { "/not-after",            SND_NAF, CM_INV },
1494     { "/not-before",           SND_NBE, CM_INV },
1495     { "/permissions",          SND_PRM, CM_INV },
1496     { "/quiet",                SND_SHH, 0 },
1497 #ifdef FTP_RESTART
1498     { "/recover",              SND_RES, 0 },
1499 #endif /* FTP_RESTART */
1500 #ifdef RECURSIVE
1501     { "/recursive",            SND_REC, 0 },
1502 #endif /* RECURSIVE */
1503     { "/rename-to",            SND_REN, CM_ARG },
1504 #ifdef FTP_RESTART
1505     { "/restart",              SND_RES, CM_INV },
1506 #endif /* FTP_RESTART */
1507 #ifndef NOCSETS
1508     { "/server-character-set", SND_CSR, CM_ARG },
1509 #endif /* NOCSETS */
1510     { "/server-rename-to",     SND_SRN, CM_ARG },
1511     { "/smaller-than",         SND_SMA, CM_ARG },
1512 #ifdef RECURSIVE
1513     { "/subdirectories",       SND_REC, CM_INV },
1514 #endif /* RECURSIVE */
1515     { "/text",                 SND_TXT, 0 },
1516     { "/tenex",                SND_TEN, 0 },
1517 #ifndef NOCSETS
1518     { "/transparent",          SND_XPA, 0 },
1519 #endif /* NOCSETS */
1520     { "/to-screen",            SND_MAI, 0 },
1521 #ifdef DOUPDATE
1522     { "/update",               SND_UPD, CM_INV },
1523 #endif /* DOUPDATE */
1524     { "", 0, 0 }
1525 };
1526 static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1;
1527
1528 static struct keytab delswi[] = {       /* FTP [M]DELETE switch table */
1529     { "/error-action",         SND_ERR, CM_ARG },
1530     { "/except",               SND_EXC, CM_ARG },
1531     { "/filenames",            SND_NAM, CM_ARG },
1532     { "/larger-than",          SND_LAR, CM_ARG },
1533     { "/nobackupfiles",        SND_NOB, 0 },
1534 #ifdef UNIXOROSK
1535     { "/nodotfiles",           SND_NOD, 0 },
1536 #endif /* UNIXOROSK */
1537     { "/quiet",                SND_SHH, 0 },
1538 #ifdef RECURSIVE
1539     { "/recursive",            SND_REC, 0 },
1540 #endif /* RECURSIVE */
1541     { "/smaller-than",         SND_SMA, CM_ARG },
1542 #ifdef RECURSIVE
1543     { "/subdirectories",       SND_REC, CM_INV },
1544 #endif /* RECURSIVE */
1545     { "", 0, 0 }
1546 };
1547 static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1;
1548
1549 static struct keytab fntab[] = {        /* Filename conversion keyword table */
1550     { "automatic",    2, CNV_AUTO },
1551     { "converted",    1, CNV_CNV  },
1552     { "literal",      0, CNV_LIT  }
1553 };
1554 static int nfntab = (sizeof(fntab) / sizeof(struct keytab));
1555
1556 static struct keytab ftptyp[] = {       /* SET FTP TYPE table */
1557     { "ascii",        FTT_ASC, 0 },
1558     { "binary",       FTT_BIN, 0 },
1559     { "tenex",        FTT_TEN, 0 },
1560     { "text",         FTT_ASC, CM_INV },
1561     { "", 0, 0 }
1562 };
1563 static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1;
1564
1565 #ifdef FTP_SECURITY
1566 static struct keytab ftppro[] = {       /* SET FTP PROTECTION-LEVEL table */
1567     { "clear",        FPL_CLR, 0 },
1568     { "confidential", FPL_CON, 0 },
1569     { "private",      FPL_PRV, 0 },
1570     { "safe",         FPL_SAF, 0 },
1571     { "", 0, 0 }
1572 };
1573 static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1;
1574 #endif /* FTP_SECURITY */
1575
1576 /* Definitions for FTP from RFC765. */
1577
1578 /* Reply codes */
1579
1580 #define REPLY_PRELIM    1               /* Positive preliminary */
1581 #define REPLY_COMPLETE  2               /* Positive completion */
1582 #define REPLY_CONTINUE  3               /* Positive intermediate */
1583 #define REPLY_TRANSIENT 4               /* Transient negative completion */
1584 #define REPLY_ERROR     5               /* Permanent negative completion */
1585 #define REPLY_SECURE    6               /* Security encoded message */
1586
1587 /* Form codes and names */
1588
1589 #define FORM_N 1                        /* Non-print */
1590 #define FORM_T 2                        /* Telnet format effectors */
1591 #define FORM_C 3                        /* Carriage control (ASA) */
1592
1593 /* Structure codes and names */
1594
1595 #define STRU_F 1                        /* File (no record structure) */
1596 #define STRU_R 2                        /* Record structure */
1597 #define STRU_P 3                        /* Page structure */
1598
1599 /* Mode types and names */
1600
1601 #define MODE_S 1                        /* Stream */
1602 #define MODE_B 2                        /* Block */
1603 #define MODE_C 3                        /* Compressed */
1604
1605 /* Protection levels and names */
1606
1607 #define PROT_C 1                        /* Clear */
1608 #define PROT_S 2                        /* Safe */
1609 #define PROT_P 3                        /* Private */
1610 #define PROT_E 4                        /* Confidential */
1611
1612 #ifdef COMMENT                          /* Not used */
1613 #ifdef FTP_NAMES
1614 char *strunames[]  =  {"0", "File",     "Record", "Page" };
1615 char *formnames[]  =  {"0", "Nonprint", "Telnet", "Carriage-control" };
1616 char *modenames[]  =  {"0", "Stream",   "Block",  "Compressed" };
1617 char *levelnames[] =  {"0", "Clear",    "Safe",   "Private",  "Confidential" };
1618 #endif /* FTP_NAMES */
1619
1620 /* Record Tokens */
1621
1622 #define REC_ESC '\377'                  /* Record-mode Escape */
1623 #define REC_EOR '\001'                  /* Record-mode End-of-Record */
1624 #define REC_EOF '\002'                  /* Record-mode End-of-File */
1625
1626 /* Block Header */
1627
1628 #define BLK_EOR           0x80          /* Block is End-of-Record */
1629 #define BLK_EOF           0x40          /* Block is End-of-File */
1630 #define BLK_REPLY_ERRORS  0x20          /* Block might have errors */
1631 #define BLK_RESTART       0x10          /* Block is Restart Marker */
1632 #define BLK_BYTECOUNT 2                 /* Bytes in this block */
1633 #endif /* COMMENT */
1634
1635 #define RADIX_ENCODE 0                  /* radix_encode() function codes */
1636 #define RADIX_DECODE 1
1637
1638 /*
1639   The default setpbsz() value in the Unix FTP client is 1<<20 (1MB).  This
1640   results in a serious performance degradation due to the increased number
1641   of page faults and the inability to overlap encrypt/decrypt, file i/o, and
1642   network i/o.  So instead we set the value to 1<<13 (8K), about half the size
1643   of the typical TCP window.  Maybe we should add a command to allow the value
1644   to be changed.
1645 */
1646 #define DEFAULT_PBSZ 1<<13
1647
1648 /* Prototypes */
1649
1650 _PROTOTYP(int remtxt, (char **) );
1651 _PROTOTYP(char * gskreason, (int) );
1652 _PROTOTYP(static int ftpclose,(void));
1653 _PROTOTYP(static int zzsend, (int, CHAR));
1654 _PROTOTYP(static int getreply,(int,int,int,int,int));
1655 _PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int));
1656 _PROTOTYP(static int setpbsz,(unsigned int));
1657 _PROTOTYP(static int recvrequest,(char *,char *,char *,char *,
1658   int,int,char *,int,int,int));
1659 _PROTOTYP(static int ftpcmd,(char *,char *,int,int,int));
1660 _PROTOTYP(static int fts_cpl,(int));
1661 _PROTOTYP(static int fts_dpl,(int));
1662 #ifdef FTP_SECURITY
1663 _PROTOTYP(static int ftp_auth, (void));
1664 #endif /* FTP_SECURITY */
1665 _PROTOTYP(static int ftp_user, (char *, char *, char *));
1666 _PROTOTYP(static int ftp_login, (char *));
1667 _PROTOTYP(static int ftp_reset, (void));
1668 _PROTOTYP(static int ftp_rename, (char *, char *));
1669 _PROTOTYP(static int ftp_umask, (char *));
1670 _PROTOTYP(static int secure_flush, (int));
1671 #ifdef COMMENT
1672 _PROTOTYP(static int secure_putc, (char, int));
1673 #endif /* COMMENT */
1674 _PROTOTYP(static int secure_write, (int, CHAR *, unsigned int));
1675 _PROTOTYP(static int scommand, (char *));
1676 _PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int));
1677 _PROTOTYP(static int secure_getc, (int, int));
1678 _PROTOTYP(static int secure_getbyte, (int, int));
1679 _PROTOTYP(static int secure_read, (int, char *, int));
1680 _PROTOTYP(static int initconn, (void));
1681 _PROTOTYP(static int dataconn, (char *));
1682 _PROTOTYP(static int setprotbuf,(unsigned int));
1683 _PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int));
1684
1685 _PROTOTYP(static char * radix_error,(int));
1686 _PROTOTYP(static char * ftp_hookup,(char *, int, int));
1687 _PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int));
1688
1689 _PROTOTYP(static VOID mlsreset, (void));
1690 _PROTOTYP(static VOID secure_error, (char *fmt, ...));
1691 _PROTOTYP(static VOID lostpeer, (void));
1692 _PROTOTYP(static VOID cancel_remote, (int));
1693 _PROTOTYP(static VOID changetype, (int, int));
1694
1695 _PROTOTYP(static sigtype cmdcancel, (int));
1696
1697 #ifdef FTP_SRP
1698 _PROTOTYP(static int srp_reset, ());
1699 _PROTOTYP(static int srp_ftp_auth, (char *,char *,char *));
1700 _PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *));
1701 _PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *));
1702 _PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int));
1703 _PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int));
1704 _PROTOTYP(static int srp_selcipher, (char *));
1705 _PROTOTYP(static int srp_selhash, (char *));
1706 #endif /* FTP_SRP */
1707
1708 #ifdef FTP_GSSAPI
1709 _PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *));
1710 #endif /* FTP_GSSAPI */
1711
1712 /*  D O F T P A R G  --  Do an FTP command-line argument.  */
1713
1714 #ifdef FTP_SECURITY
1715 #ifndef NOICP
1716 #define FT_NOGSS   1
1717 #define FT_NOK4    2
1718 #define FT_NOSRP   3
1719 #define FT_NOSSL   4
1720 #define FT_NOTLS   5
1721 #define FT_CERTFI  6
1722 #define FT_OKCERT  7
1723 #define FT_DEBUG   8
1724 #define FT_KEY     9
1725 #define FT_SECURE 10
1726 #define FT_VERIFY 11
1727
1728 static struct keytab ftpztab[] = {
1729     { "!gss",    FT_NOGSS,  0 },
1730     { "!krb4",   FT_NOK4,   0 },
1731     { "!srp",    FT_NOSRP,  0 },
1732     { "!ssl",    FT_NOSSL,  0 },
1733     { "!tls",    FT_NOTLS,  0 },
1734     { "cert",    FT_CERTFI, CM_ARG },
1735     { "certsok", FT_OKCERT, 0 },
1736     { "debug",   FT_DEBUG,  0 },
1737     { "key",     FT_KEY,    CM_ARG },
1738     { "nogss",   FT_NOGSS,  0 },
1739     { "nokrb4",  FT_NOK4,   0 },
1740     { "nosrp",   FT_NOSRP,  0 },
1741     { "nossl",   FT_NOSSL,  0 },
1742     { "notls",   FT_NOTLS,  0 },
1743 #ifdef COMMENT
1744     { "secure",  FT_SECURE, 0 },
1745 #endif /* COMMENT */
1746     { "verify",  FT_VERIFY, CM_ARG },
1747     { "", 0, 0 }
1748 };
1749 static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1;
1750
1751 /*
1752   The following cipher and hash tables should be replaced with
1753   dynamicly created versions based upon the linked library.
1754 */
1755 #define SRP_BLOWFISH_ECB    1
1756 #define SRP_BLOWFISH_CBC    2
1757 #define SRP_BLOWFISH_CFB64  3
1758 #define SRP_BLOWFISH_OFB64  4
1759 #define SRP_CAST5_ECB       5
1760 #define SRP_CAST5_CBC       6
1761 #define SRP_CAST5_CFB64     7
1762 #define SRP_CAST5_OFB64     8
1763 #define SRP_DES_ECB         9
1764 #define SRP_DES_CBC        10
1765 #define SRP_DES_CFB64      11
1766 #define SRP_DES_OFB64      12
1767 #define SRP_DES3_ECB       13
1768 #define SRP_DES3_CBC       14
1769 #define SRP_DES3_CFB64     15
1770 #define SRP_DES3_OFB64     16
1771
1772 static struct keytab ciphertab[] = {
1773     { "blowfish_ecb",   SRP_BLOWFISH_ECB,   0 },
1774     { "blowfish_cbc",   SRP_BLOWFISH_CBC,   0 },
1775     { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 },
1776     { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 },
1777     { "cast5_ecb",      SRP_CAST5_ECB,      0 },
1778     { "cast5_cbc",      SRP_CAST5_CBC,      0 },
1779     { "cast5_cfb64",    SRP_CAST5_CFB64,    0 },
1780     { "cast5_ofb64",    SRP_CAST5_OFB64,    0 },
1781     { "des_ecb",        SRP_DES_ECB,        0 },
1782     { "des_cbc",        SRP_DES_CBC,        0 },
1783     { "des_cfb64",      SRP_DES_CFB64,      0 },
1784     { "des_ofb64",      SRP_DES_OFB64,      0 },
1785     { "des3_ecb",       SRP_DES3_ECB,       0 },
1786     { "des3_cbc",       SRP_DES3_CBC,       0 },
1787     { "des3_cfb64",     SRP_DES3_CFB64,     0 },
1788     { "des3_ofb64",     SRP_DES3_OFB64,     0 },
1789     { "none",           0, 0 },
1790     { "", 0, 0 }
1791 };
1792 static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1;
1793
1794 #define SRP_MD5  1
1795 #define SRP_SHA  2
1796 static struct keytab hashtab[] = {
1797     { "md5",              SRP_MD5,        0 },
1798     { "none",             0,              0 },
1799     { "sha",              SRP_SHA,        0 },
1800     { "", 0, 0 }
1801 };
1802 static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1;
1803 #endif /* NOICP */
1804 #endif /* FTP_SECURITY */
1805
1806 static char *
1807 strval(s1,s2) char * s1, * s2; {
1808     if (!s1) s1 = "";
1809     if (!s2) s2 = "";
1810     return(*s1 ? s1 : (*s2 ? s2 : "(none)"));
1811 }
1812
1813 #ifndef NOCSETS
1814 static char * rfnptr = NULL;
1815 static int rfnlen = 0;
1816 static char rfnbuf[RFNBUFSIZ];          /* Remote filename translate buffer */
1817 static char * xgnbp = NULL;
1818
1819 static int
1820 strgetc() {                             /* Helper function for xgnbyte() */
1821     int c;
1822     if (!xgnbp)
1823       return(-1);
1824     if (!*xgnbp)
1825       return(-1);
1826     c = (unsigned) *xgnbp++;
1827     return(((unsigned) c) & 0xff);
1828 }
1829
1830 static int                              /* Helper function for xpnbyte() */
1831 #ifdef CK_ANSIC
1832 strputc(char c)
1833 #else
1834 strputc(c) char c;
1835 #endif /* CK_ANSIC */
1836 {
1837     rfnlen = rfnptr - rfnbuf;
1838     if (rfnlen >= (RFNBUFSIZ - 1))
1839       return(-1);
1840     *rfnptr++ = c;
1841     *rfnptr = NUL;
1842     return(0);
1843 }
1844
1845 static int
1846 #ifdef CK_ANSIC
1847 xprintc(char c)
1848 #else
1849 xprintc(c) char c;
1850 #endif /* CK_ANSIC */
1851 {
1852     printf("%c",c);
1853     return(0);
1854 }
1855
1856 static VOID
1857 bytswap(c0,c1) int * c0, * c1; {
1858     int t;
1859     t = *c0;
1860     *c0 = *c1;
1861     *c1 = t;
1862 }
1863 #endif /* NOCSETS */
1864
1865 #ifdef CKLOGDIAL
1866 char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */
1867 int ftplogactive = 0;
1868 long ftplogprev = 0L;
1869
1870 VOID
1871 ftplogend() {
1872     extern int dialog;
1873     extern char diafil[];
1874     long d1, d2, t1, t2;
1875     char buf[32], * p;
1876
1877     debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive);
1878     debug(F110,"ftp cx log buf",ftplogbuf,0);
1879
1880     if (!ftplogactive || !ftplogbuf[0]) /* No active record */
1881       return;
1882
1883     ftplogactive = 0;                   /* Record is not active */
1884
1885     d1 = mjd((char *)ftplogbuf);        /* Get start date of this session */
1886     ckstrncpy(buf,ckdate(),31);         /* Get current date */
1887     d2 = mjd(buf);                      /* Convert them to mjds */
1888     p = ftplogbuf;                      /* Get start time */
1889     p[11] = NUL;
1890     p[14] = NUL;                        /* Convert to seconds */
1891     t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
1892     p[11] = ':';
1893     p[14] = ':';
1894     p = buf;                            /* Get end time */
1895     p[11] = NUL;
1896     p[14] = NUL;
1897     t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
1898     t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */
1899     if (t2 > -1L) {
1900         ftplogprev = t2;
1901         p = hhmmss(t2);
1902         strncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */
1903         strncat(ftplogbuf,p,CXLOGBUFL);
1904     } else
1905       ftplogprev = 0L;
1906     debug(F101,"ftp cx log dialog","",dialog);
1907     if (dialog) {                       /* If logging */
1908         int x;
1909         x = diaopn(diafil,1,1);         /* Open log in append mode */
1910         if (x > 0) {
1911             debug(F101,"ftp cx log open","",x);
1912             x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */
1913             debug(F101,"ftp cx log write","",x);
1914             x = zclose(ZDIFIL);         /* Close the log */
1915             debug(F101,"ftp cx log close","",x);
1916         }
1917     }
1918 }
1919
1920 VOID
1921 dologftp() {
1922     ftplogend();                        /* Previous session not closed out? */
1923     ftplogprev = 0L;
1924     ftplogactive = 1;                   /* Record is active */
1925
1926     ckmakxmsg(ftplogbuf,CXLOGBUFL,
1927               ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(),
1928               " T=FTP N=", strval(ftp_host,NULL)," H=",myhost," ",NULL,NULL);
1929     debug(F110,"ftp cx log begin",ftplogbuf,0);
1930 }
1931 #endif /* CKLOGDIAL */
1932
1933 static char * dummy[2] = { NULL, NULL };
1934
1935 static struct keytab modetab[] = {
1936     { "active",  0, 0 },
1937     { "passive", 1, 0 }
1938 };
1939
1940 #ifndef NOCMDL
1941 int                                     /* Called from ckuusy.c */
1942 #ifdef CK_ANSIC
1943 doftparg(char c)
1944 #else
1945 doftparg(c) char c;
1946 #endif /* CK_ANSIC */
1947 /* doftparg */ {
1948     int x, z;
1949     char *xp;
1950     extern char **xargv, *xarg0;
1951     extern int xargc, stayflg, haveftpuid;
1952     extern char uidbuf[];
1953
1954     xp = *xargv+1;                      /* Pointer for bundled args */
1955     while (c) {
1956         if (ckstrchr("MuDPkcHzm",c)) {  /* Options that take arguments */
1957             if (*(xp+1)) {
1958                 fatal("?Invalid argument bundling");
1959             }
1960             xargv++, xargc--;
1961             if ((xargc < 1) || (**xargv == '-')) {
1962                 fatal("?Required argument missing");
1963             }
1964         }
1965         switch (c) {                    /* Big switch on arg */
1966           case 'h':                     /* help */
1967            printf("C-Kermit's FTP client command-line personality.  Usage:\n");
1968             printf("  %s [ options ] host [ port ] [-pg files ]\n\n",xarg0);
1969             printf("Options:\n");
1970             printf("  -h           = help (this message)\n");
1971             printf("  -m mode      = \"passive\" (default) or \"active\"\n");
1972             printf("  -u name      = username for autologin (or -M)\n");
1973             printf("  -P password  = password for autologin (RISKY)\n");
1974             printf("  -A           = autologin anonymously\n");
1975             printf("  -D directory = cd after autologin\n");
1976             printf("  -b           = force binary mode\n");
1977             printf("  -a           = force text (\"ascii\") mode (or -T)\n");
1978             printf("  -d           = debug (double to add timestamps)\n");
1979             printf("  -n           = no autologin\n");
1980             printf("  -v           = verbose (default)\n");
1981             printf("  -q           = quiet\n");
1982             printf("  -S           = Stay (issue command prompt when done)\n");
1983             printf("  -Y           = do not execute Kermit init file\n");
1984             printf("  -p files     = files to put after autologin (or -s)\n");
1985             printf("  -g files     = files to get after autologin\n");
1986             printf("  -R           = recursive (for use with -p)\n");
1987
1988 #ifdef FTP_SECURITY
1989             printf("\nSecurity options:\n");
1990             printf("  -k realm     = Kerberos 4 realm\n");
1991             printf("  -f           = Kerboros 5 credentials forwarding\n");
1992             printf("  -x           = autoencryption mode\n");
1993             printf("  -c cipher    = SRP cipher type\n");
1994             printf("  -H hash      = SRP encryption hash\n");
1995             printf("  -z option    = Security options\n");
1996 #endif /* FTP_SECURITY */
1997
1998             printf("\n-p or -g, if given, should be last.  Example:\n");
1999             printf("  ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n");
2000
2001             doexit(GOOD_EXIT,-1);
2002             break;
2003
2004           case 'R':                     /* Recursive */
2005             recursive = 1;
2006             break;
2007
2008           case 'd':                     /* Debug */
2009 #ifdef DEBUG
2010             if (deblog) {
2011                 extern int debtim;
2012                 debtim = 1;
2013             } else {
2014                 deblog = debopn("debug.log",0);
2015                 debok = 1;
2016             }
2017 #endif /* DEBUG */
2018             /* fall thru on purpose */
2019
2020           case 't':                     /* Trace */
2021             ftp_deb++;
2022             break;
2023
2024           case 'n':                     /* No autologin */
2025             ftp_log = 0;
2026             break;
2027
2028           case 'i':                     /* No prompt */
2029           case 'v':                     /* Verbose */
2030             break;                      /* (ignored) */
2031
2032           case 'q':                     /* Quiet */
2033             quiet = 1;
2034             break;
2035
2036           case 'S':                     /* Stay */
2037             stayflg = 1;
2038             break;
2039
2040           case 'M':
2041           case 'u':                     /* My User Name */
2042             if ((int)strlen(*xargv) > 63) {
2043                 fatal("username too long");
2044             }
2045             ckstrncpy(uidbuf,*xargv,UIDBUFLEN);
2046             haveftpuid = 1;
2047             break;
2048
2049           case 'A':
2050             ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2051             haveftpuid = 1;
2052             break;
2053
2054           case 'T':                     /* Text */
2055           case 'a':                     /* "ascii" */
2056           case 'b':                     /* Binary */
2057             binary = (c == 'b') ? FTT_BIN : FTT_ASC;
2058             xfermode = XMODE_M;
2059             filepeek = 0;
2060             patterns = 0;
2061             break;
2062
2063           case 'g':                     /* Get */
2064           case 'p':                     /* Put */
2065           case 's': {                   /* Send (= Put) */
2066               int havefiles, rc;
2067               if (ftp_action) {
2068                   fatal("Only one FTP action at a time please");
2069               }
2070               if (*(xp+1)) {
2071                   fatal("invalid argument bundling after -s");
2072               }
2073               nfils = 0;                /* Initialize file counter */
2074               havefiles = 0;            /* Assume nothing to send  */
2075               cmlist = xargv + 1;       /* Remember this pointer */
2076
2077               while (++xargv, --xargc > 0) { /* Traverse the list */
2078                   if (c == 'g') {
2079                       havefiles++;
2080                       nfils++;
2081                       continue;
2082                   }
2083 #ifdef RECURSIVE
2084                   if (!strcmp(*xargv,".")) {
2085                       havefiles = 1;
2086                       nfils++;
2087                       recursive = 1;
2088                   } else
2089 #endif /* RECURSIVE */
2090                     if ((rc = zchki(*xargv)) > -1 || (rc == -2)) {
2091                         if  (rc != -2)
2092                           havefiles = 1;
2093                         nfils++;
2094                     } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) {
2095                         havefiles = 1;
2096                         nfils++;
2097                     }
2098               }
2099               xargc++, xargv--;         /* Adjust argv/argc */
2100               if (!havefiles) {
2101                   if (c == 'g') {
2102                       fatal("No files to put");
2103                   } else {
2104                       fatal("No files to get");
2105                   }
2106               }
2107               ftp_action = c;
2108               break;
2109           }
2110           case 'D':                     /* Directory */
2111             makestr(&ftp_rdir,*xargv);
2112             break;
2113
2114           case 'm':                     /* Mode (Active/Passive */
2115             ftp_psv = lookup(modetab,*xargv,2,NULL);
2116             if (ftp_psv < 0) fatal("Invalid mode");
2117             break;
2118
2119           case 'P':
2120             makestr(&ftp_tmp,*xargv);   /* You-Know-What */
2121             break;
2122
2123           case 'Y':                     /* No initialization file */
2124             break;                      /* (already done in prescan) */
2125
2126 #ifdef CK_URL
2127           case 'U': {                   /* URL */
2128               /* These are set by urlparse() - any not set are NULL */
2129               if (g_url.hos) {
2130 /*
2131   Kermit has accepted host:port notation since many years before URLs were
2132   invented.  Unfortunately, URLs conflict with this notation.  Thus "ftp
2133   host:449" looks like a URL and results in service = host and host = 449.
2134   Here we try to catch this situation transparently to the user.
2135 */
2136                   if (ckstrcmp(g_url.svc,"ftp",-1,0)
2137 #ifdef CK_SSL
2138                        && ckstrcmp(g_url.svc,"ftps",-1,0)
2139 #endif /* CK_SSL */
2140                        ) {
2141                       if (!g_url.usr &&
2142                           !g_url.psw &&
2143                           !g_url.por &&
2144                           !g_url.pth) {
2145                           g_url.por = g_url.hos;
2146                           g_url.hos = g_url.svc;
2147                           g_url.svc = "ftp";
2148                       } else {
2149                           ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=",
2150                                    g_url.svc," host=",g_url.hos);
2151                           fatal(tmpbuf);
2152                       }
2153                   }
2154                   makestr(&ftp_host,g_url.hos);
2155                   if (g_url.usr) {
2156                       haveftpuid = 1;
2157                       ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
2158                       makestr(&ftp_logname,uidbuf);
2159                   }
2160                   if (g_url.psw) {
2161                       makestr(&ftp_tmp,g_url.psw);
2162                   }
2163                   if (g_url.pth) {
2164                       if (!g_url.usr) {
2165                           haveftpuid = 1;
2166                           ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2167                           makestr(&ftp_logname,uidbuf);
2168                       }
2169                       if (ftp_action) {
2170                           fatal("Only one FTP action at a time please");
2171                       }
2172                       if (!stayflg)
2173                         quiet = 1;
2174                       nfils = 1;
2175                       dummy[0] = g_url.pth;
2176                       cmlist = dummy;
2177                       ftp_action = 'g';
2178                   }
2179                   xp = NULL;
2180                   haveurl = 1;
2181               }
2182               break;
2183           }
2184 #endif /* CK_URL */
2185
2186 #ifdef FTP_SECURITY
2187           case 'k': {                   /* K4 Realm */
2188 #ifdef FTP_KRB4
2189               ckstrncpy(ftp_realm,*xargv, REALM_SZ);
2190 #endif /* FTP_KRB4 */
2191               if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv);
2192               break;
2193           }
2194           case 'f': {
2195 #ifdef FTP_GSSAPI
2196               ftp_cfw = 1;
2197               if (ftp_deb) printf("K5 Credentials Forwarding\n");
2198 #else /* FTP_GSSAPI */
2199               printf("K5 Credentials Forwarding not supported\n");
2200 #endif /* FTP_GSSAPI */
2201               break;
2202           }
2203           case 'x': {
2204               ftp_cry = 1;
2205               if (ftp_deb) printf("Autoencryption\n");
2206               break;
2207           }
2208           case 'c': {                   /* Cipher */
2209 #ifdef FTP_SRP
2210               if (!srp_selcipher(*xargv)) {
2211                   if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv);
2212               } else
2213                 printf("?Invalid SRP cipher type: \"%s\"\n",*xargv);
2214 #else /* FTP_SRP */
2215               printf("?SRP not supported\n");
2216 #endif /* FTP_SRP */
2217               break;
2218           }
2219           case 'H': {
2220 #ifdef FTP_SRP
2221               if (!srp_selhash(*xargv)) {
2222                   if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv);
2223               } else
2224                 printf("?Invalid SRP hash type: \"%s\"\n",*xargv);
2225 #else /* FTP_SRP */
2226               printf("?SRP not supported\n");
2227 #endif /* FTP_SRP */
2228               break;
2229           }
2230           case 'z': {
2231               /* *xargv contains a value of the form tag=value */
2232               /* we need to lookup the tag and save the value  */
2233               char * p = NULL, * q = NULL;
2234               makestr(&p,*xargv);
2235               y = ckindex("=",p,0,0,1);
2236               if (y > 0)
2237                 p[y-1] = '\0';
2238               x = lookup(ftpztab,p,nftpztab,&z);
2239               if (x < 0) {
2240                   printf("?Invalid security option: \"%s\"\n",p);
2241               } else {
2242                   if (ftp_deb)
2243                     printf("Security option: \"%s",p);
2244                   if (ftpztab[z].flgs & CM_ARG) {
2245                       if (y <= 0)
2246                         fatal("?Missing required value");
2247                       q = &p[y];
2248                       if (!*q)
2249                         fatal("?Missing required value");
2250                       if (ftp_deb)
2251                         printf("=%s\"",q);
2252                   }
2253                   switch (ftpztab[z].kwval) { /* -z options w/args */
2254                     case FT_NOGSS:
2255 #ifdef FTP_GSSAPI
2256                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2257                           if (ftp_auth_type[z] == FTA_GK5) {
2258                               for (y = z;
2259                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2260                                    y++
2261                                    )
2262                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2263                               ftp_auth_type[FTPATYPS-1] = 0;
2264                               break;
2265                           }
2266                       }
2267 #endif /* FTP_GSSAPI */
2268                       break;
2269                     case FT_NOK4:
2270 #ifdef FTP_KRB4
2271                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2272                           if (ftp_auth_type[z] == FTA_K4) {
2273                               for (y = z;
2274                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2275                                    y++
2276                                    )
2277                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2278                               ftp_auth_type[FTPATYPS-1] = 0;
2279                               break;
2280                           }
2281                       }
2282 #endif /* FTP_KRB4 */
2283                       break;
2284                     case FT_NOSRP:
2285 #ifdef FTP_SRP
2286                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2287                           if (ftp_auth_type[z] == FTA_SRP) {
2288                               for (y = z;
2289                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2290                                    y++
2291                                    )
2292                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2293                               ftp_auth_type[FTPATYPS-1] = 0;
2294                               break;
2295                           }
2296                       }
2297 #endif /* FTP_SRP */
2298                       break;
2299                     case FT_NOSSL:
2300 #ifdef CK_SSL
2301                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2302                           if (ftp_auth_type[z] == FTA_SSL) {
2303                               for (y = z;
2304                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2305                                    y++
2306                                    )
2307                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2308                               ftp_auth_type[FTPATYPS-1] = 0;
2309                               break;
2310                           }
2311                       }
2312 #endif /* CK_SSL */
2313                       break;
2314                     case FT_NOTLS:
2315 #ifdef CK_SSL
2316                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2317                           if (ftp_auth_type[z] == FTA_TLS) {
2318                               for (y = z;
2319                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2320                                    y++
2321                                    )
2322                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2323                               ftp_auth_type[FTPATYPS-1] = 0;
2324                               break;
2325                           }
2326                       }
2327 #endif /* CK_SSL */
2328                       break;
2329                     case FT_CERTFI:
2330 #ifdef CK_SSL
2331                       makestr(&ssl_rsa_cert_file,q);
2332 #endif /* CK_SSL */
2333                       break;
2334                     case FT_OKCERT:
2335 #ifdef CK_SSL
2336                       ssl_certsok_flag = 1;
2337 #endif /* CK_SSL */
2338                       break;
2339                     case FT_DEBUG:
2340 #ifdef DEBUG
2341                       if (deblog) {
2342                           extern int debtim;
2343                           debtim = 1;
2344                       } else {
2345                           deblog = debopn("debug.log",0);
2346                       }
2347 #endif /* DEBUG */
2348                       break;
2349                     case FT_KEY:
2350 #ifdef CK_SSL
2351                       makestr(&ssl_rsa_key_file,q);
2352 #endif /* CK_SSL */
2353                       break;
2354                     case FT_SECURE:
2355                       /* no equivalent */
2356                       break;
2357                     case FT_VERIFY:
2358 #ifdef CK_SSL
2359                       if (!rdigits(q))
2360                         printf("?Bad number: %s\n",q);
2361                       ssl_verify_flag = atoi(q);
2362 #endif /* CK_SSL */
2363                       break;
2364                   }
2365               }
2366               if (ftp_deb) printf("\"\n");
2367               free(p);
2368               break;
2369           }
2370 #endif /* FTP_SECURITY */
2371
2372           default:
2373             fatal2(*xargv,
2374                    "unknown command-line option, type \"ftp -h\" for help"
2375                    );
2376         }
2377         if (!xp) break;
2378         c = *++xp;                      /* See if options are bundled */
2379     }
2380     return(0);
2381 }
2382 #endif /* NOCMDL */
2383
2384 int
2385 ftpisconnected() {
2386     return(connected);
2387 }
2388
2389 int
2390 ftpisloggedin() {
2391     return(connected ? loggedin : 0);
2392 }
2393
2394 int
2395 ftpissecure() {
2396     return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1);
2397 }
2398
2399 static VOID
2400 ftscreen(n, c, z, s) int n; char c; long z; char * s; {
2401     if (displa && fdispla && !backgrd && !quiet && !out2screen) {
2402         if (!dpyactive) {
2403             ckscreen(SCR_PT,'S',0L,"");
2404             dpyactive = 1;
2405         }
2406         ckscreen(n,c,z,s);
2407     }
2408 }
2409
2410 #ifndef OS2
2411 /*  g m s t i m e r  --  Millisecond timer */
2412
2413 long
2414 gmstimer() {
2415 #ifdef HAVE_MSECS
2416     /* For those versions of ztime() that also set global ztmsec. */
2417     char *p = NULL;
2418     long z;
2419     ztime(&p);
2420     if (!p) return(0L);
2421     if (!*p) return(0L);
2422     z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
2423     return(z * 1000 + ztmsec);
2424 #else
2425     return((long)time(NULL) * 1000L);
2426 #endif /* HAVE_MSECS */
2427 }
2428 #endif /* OS2 */
2429
2430 /*  d o s e t f t p  --  The SET FTP command  */
2431
2432 int
2433 dosetftp() {
2434     int cx;
2435     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */
2436       return(cx);
2437     switch (cx) {
2438
2439       case FTS_FNC:                     /* Filename collision action */
2440         if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
2441           return(x);
2442         if ((y = cmcfm()) < 0)
2443           return(y);
2444         ftp_fnc = x;
2445         return(1);
2446
2447       case FTS_CNV:                     /* Filename conversion */
2448         if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
2449           return(x);
2450         if ((y = cmcfm()) < 0)
2451           return(y);
2452         ftp_cnv = x;
2453         return(1);
2454
2455       case FTS_DBG:                     /* Debug messages */
2456         return(seton(&ftp_deb));
2457
2458       case FTS_LOG:                     /* Auto-login */
2459         return(seton(&ftp_log));
2460
2461       case FTS_PSV:                     /* Passive mode */
2462         return(dosetftppsv());
2463
2464       case FTS_SPC:                     /* Send port commands */
2465         x = seton(&ftp_spc);
2466         if (x > 0) sendport = ftp_spc;
2467         return(x);
2468
2469       case FTS_TYP:                     /* Type */
2470         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
2471           return(x);
2472         if ((y = cmcfm()) < 0) return(y);
2473         ftp_typ = x;
2474         g_ftp_typ = x;
2475         tenex = (ftp_typ == FTT_TEN);
2476         return(1);
2477
2478       case FTS_USN:                     /* Unique server names */
2479         return(seton(&ftp_usn));
2480
2481       case FTS_VBM:                     /* Verbose mode */
2482         if ((x = seton(&ftp_vbm)) < 0)  /* Per-command copy */
2483           return(x);
2484         ftp_vbx = ftp_vbm;              /* Global sticky copy */
2485         return(x);
2486
2487       case FTS_TST:                     /* "if (testing)" messages */
2488         return(seton(&testing));
2489
2490       case FTS_PRM:                     /* Send permissions */
2491         return(setonaut(&ftp_prm));
2492
2493       case FTS_AUT:                     /* Auto-authentication */
2494         return(seton(&ftp_aut));
2495
2496       case FTS_ERR:                     /* Error action */
2497         if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
2498           return(x);
2499         if ((y = cmcfm()) < 0)
2500           return(y);
2501         ftp_err = x;
2502         return(success = 1);
2503
2504 #ifndef NOCSETS
2505       case FTS_XLA:                     /* Translation */
2506         return(seton(&ftp_xla));
2507
2508       case FTS_CSR:                     /* Server charset */
2509         if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0)
2510           return(x);
2511         if ((y = cmcfm()) < 0)
2512           return(y);
2513         ftp_csr = x;
2514         ftp_xla = 1;                    /* Also enable translation */
2515         return(success = 1);
2516 #endif /* NOCSETS */
2517
2518       case FTS_GFT:
2519         return(seton(&get_auto));       /* GET-filetype-switching */
2520
2521       case FTS_DAT:
2522         return(seton(&ftp_dates));      /* Set file dates */
2523
2524       case FTS_STO: {                   /* Server time offset */
2525           char * s, * p = NULL;
2526           int k;
2527           if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0)
2528             return(x);
2529           if (!strcmp(s,"+0")) {
2530               s = NULL;
2531           } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */
2532               printf("?Invalid time offset\n");
2533               return(-9);
2534           }
2535           makestr(&p,s);                /* Make a safe copy the string */
2536           if ((x = cmcfm()) < 0) {      /* Get confirmation */
2537               if (p)
2538                 makestr(&p,NULL);
2539               return(x);
2540           }
2541           fts_sto = p;                  /* Confirmed - set the string. */
2542           return(success = 1);
2543       }
2544       case FTS_APW: {
2545           char * s;
2546           if ((x = cmtxt("Text", "", &s, xxstring)) < 0)
2547             return(x);
2548           makestr(&ftp_apw, *s ? s : NULL);
2549           return(success = 1);
2550       }
2551
2552       case FTS_BUG: {
2553           if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0) 
2554             return(x);
2555           switch (x) {
2556 #ifdef CK_SSL
2557           case FTB_SV2:
2558             return seton(&ftp_bug_use_ssl_v2);
2559 #endif /* CK_SSL */
2560           default:
2561             return(-2);
2562           }
2563       }
2564
2565 #ifdef FTP_SECURITY
2566       case FTS_CRY:                     /* Auto-encryption */
2567         return(seton(&ftp_cry));
2568
2569       case FTS_CFW:                     /* Credential-forwarding */
2570         return(seton(&ftp_cfw));
2571
2572       case FTS_CPL:                     /* Command protection level */
2573         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2574         if ((y = cmcfm()) < 0) return(y);
2575         success = fts_cpl(x);
2576         return(success);
2577
2578       case FTS_DPL:                     /* Data protection level */
2579         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2580         if ((y = cmcfm()) < 0) return(y);
2581           success = fts_dpl(x);
2582           return(success);
2583
2584       case FTS_ATP: {                   /* FTP Auth Type */
2585           int i, j, atypes[8];
2586
2587           for (i = 0; i < 8; i++) {
2588               if ((y = cmkey(ftpauth,nftpauth,"",
2589                              (i == 0) ? "automatic" : "",
2590                              xxstring)) < 0) {
2591                   if (y == -3)
2592                     break;
2593                   return(y);
2594               }
2595               if (i > 0 && (y == FTA_AUTO)) {
2596                   printf("?Choice may only be used in first position.\r\n");
2597                   return(-9);
2598               }
2599               for (j = 0; j < i; j++) {
2600                   if (atypes[j] == y) {
2601                       printf("\r\n?Choice has already been used.\r\n");
2602                       return(-9);
2603                   }
2604               }
2605               atypes[i] = y;
2606               if (y == FTA_AUTO) {
2607                   i++;
2608                   break;
2609               }
2610           }
2611           if (i < 8)
2612             atypes[i] = 0;
2613           if ((z = cmcfm()) < 0)
2614             return(z);
2615           if (atypes[0] == FTA_AUTO) {
2616               i = 0;
2617 #ifdef FTP_GSSAPI
2618               ftp_auth_type[i++] = FTA_GK5;
2619 #endif /* FTP_GSSAPI */
2620 #ifdef FTP_SRP
2621               ftp_auth_type[i++] = FTA_SRP;
2622 #endif /* FTP_SRP */
2623 #ifdef FTP_KRB4
2624               ftp_auth_type[i++] = FTA_K4;
2625 #endif /* FTP_KRB4 */
2626 #ifdef CK_SSL
2627               ftp_auth_type[i++] = FTA_TLS;
2628               ftp_auth_type[i++] = FTA_SSL;
2629 #endif /* CK_SSL */
2630               ftp_auth_type[i] = 0;
2631           } else {
2632               for (i = 0; i < 8; i++)
2633                 ftp_auth_type[i] = atypes[i];
2634           }
2635           return(success = 1);
2636       }
2637
2638       case FTS_SRP:
2639 #ifdef FTP_SRP
2640         if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0)
2641           return(x);
2642         switch (x) {
2643           case SRP_CIPHER:
2644             if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0)
2645               return(x);
2646             if ((z = cmcfm()) < 0)
2647               return(z);
2648             success = !srp_selcipher(ciphertab[x].kwd);
2649             return(success);
2650           case SRP_HASH:
2651             if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0)
2652               return(x);
2653             if ((z = cmcfm()) < 0)
2654               return(z);
2655             success = !srp_selhash(hashtab[x].kwd);
2656             return(success = 1);
2657           default:
2658             if ((z = cmcfm()) < 0)
2659               return(z);
2660             return(-2);
2661         }
2662 #else /* FTP_SRP */
2663         if ((z = cmcfm()) < 0)
2664           return(z);
2665         return(-2);
2666 #endif /* FTP_SRP */
2667 #endif /* FTP_SECURITY */
2668
2669       case FTS_DIS:
2670         doxdis(2);                      /* 2 == ftp */
2671         return(success = 1);
2672
2673       default:
2674         return(-2);
2675     }
2676 }
2677
2678 int
2679 ftpbye() {
2680     int x;
2681     if (!connected)
2682       return(1);
2683     if (testing)
2684       printf(" ftp closing %s...\n",ftp_host);
2685     x = ftpclose();
2686     return((x > -1) ? 1 : 0);
2687 }
2688
2689 /*  o p e n f t p  --  Parse FTP hostname & port and open */
2690
2691 static int
2692 openftp(s,opn_tls) char * s; int opn_tls; {
2693     char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL;
2694     int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0;
2695     int haveuser = 0;
2696     struct FDB sw, fl, cm;
2697     extern int nnetdir;                 /* Network services directory */
2698     extern int nhcount;                 /* Lookup result */
2699     extern char *nh_p[];                /* Network directory entry pointers */
2700     extern char *nh_p2[];               /* Network directory entry nettype */
2701
2702     if (!s) return(-2);
2703     if (!*s) return(-2);
2704
2705     makestr(&hostname,s);
2706     hostsave = hostname;
2707     makestr(&ftp_logname,NULL);
2708     anonymous = 0;
2709     noinit = 0;
2710
2711     debug(F110,"ftp open",hostname,0);
2712
2713     if (sav_psv > -1) {                 /* Restore prevailing active/passive */
2714         ftp_psv = sav_psv;              /* selection in case it was */
2715         sav_psv = -1;                   /* temporarily overriden by a switch */
2716     }
2717     if (sav_log > -1) {                 /* Ditto for autologin */
2718         ftp_log = sav_log;
2719         sav_log = -1;
2720     }
2721     cmfdbi(&sw,                         /* Switches */
2722            _CMKEY,
2723            "Service name or port;\n or switch",
2724            "",                          /* default */
2725            "",                          /* addtl string data */
2726            nftpswi,                     /* addtl numeric data 1: tbl size */
2727            4,                           /* addtl numeric data 2: none */
2728            xxstring,                    /* Processing function */
2729            ftpswitab,                   /* Keyword table */
2730            &fl                          /* Pointer to next FDB */
2731            );
2732     cmfdbi(&fl,                         /* A host name or address */
2733            _CMFLD,                      /* fcode */
2734            "",                          /* help */
2735            "xYzBoo",                    /* default */
2736            "",                          /* addtl string data */
2737            0,                           /* addtl numeric data 1 */
2738            0,                           /* addtl numeric data 2 */
2739            xxstring,
2740            NULL,
2741            &cm
2742            );
2743     cmfdbi(&cm,                         /* Command confirmation */
2744            _CMCFM,
2745            "",
2746            "",
2747            "",
2748            0,
2749            0,
2750            NULL,
2751            NULL,
2752            NULL
2753            );
2754
2755     for (n = 0;; n++) {
2756         rc = cmfdb(&sw);                /* Parse a service name or a switch */
2757         if (rc < 0)
2758           goto xopenftp;
2759
2760         if (cmresult.fcode == _CMCFM) { /* Done? */
2761             break;
2762         } else if (cmresult.fcode == _CMFLD) {  /* Port */
2763             if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1))
2764               makestr(&service,cmresult.sresult);
2765             else
2766               makestr(&service,opn_tls?"ftps":"ftp");
2767         } else if (cmresult.fcode == _CMKEY) { /* Have a switch */
2768             c = cmgbrk();               /* get break character */
2769             getval = (c == ':' || c == '=');
2770             rc = -9;
2771             if (getval && !(cmresult.kflags & CM_ARG)) {
2772                 printf("?This switch does not take arguments\n");
2773                 goto xopenftp;
2774             }
2775             if (!getval && (cmresult.kflags & CM_ARG)) {
2776                 printf("?This switch requires an argument\n");
2777                 goto xopenftp;
2778             }
2779             switch (cmresult.nresult) { /* Switch */
2780               case OPN_ANO:             /* /ANONYMOUS */
2781                 anonymous++;
2782                 nologin = 0;
2783                 break;
2784               case OPN_NIN:             /* /NOINIT */
2785                 noinit++;
2786                 break;
2787               case OPN_NOL:             /* /NOLOGIN */
2788                 nologin++;
2789                 anonymous = 0;
2790                 makestr(&ftp_logname,NULL);
2791                 break;
2792               case OPN_PSW:             /* /PASSWORD */
2793                 if (!anonymous)         /* Don't log real passwords */
2794                   debok = 0;
2795                 rc = cmfld("Password for FTP server","",&p,xxstring);
2796                 if (rc == -3) {
2797                     makestr(&ftp_tmp,NULL);
2798                 } else if (rc < 0) {
2799                     goto xopenftp;
2800                 } else {
2801                     makestr(&ftp_tmp,brstrip(p));
2802                     nologin = 0;
2803                 }
2804                 break;
2805               case OPN_USR:             /* /USER */
2806                 rc = cmfld("Username for FTP server","",&p,xxstring);
2807                 if (rc == -3) {
2808                     makestr(&ftp_logname,NULL);
2809                 } else if (rc < 0) {
2810                     goto xopenftp;
2811                 } else {
2812                     nologin = 0;
2813                     anonymous = 0;
2814                     haveuser = 1;
2815                     makestr(&ftp_logname,brstrip(p));
2816                 }
2817                 break;
2818               case OPN_ACC:
2819                 rc = cmfld("Account for FTP server","",&p,xxstring);
2820                 if (rc == -3) {
2821                     makestr(&ftp_acc,NULL);
2822                 } else if (rc < 0) {
2823                     goto xopenftp;
2824                 } else {
2825                     makestr(&ftp_acc,brstrip(p));
2826                 }
2827                 break;
2828               case OPN_ACT:
2829                 opn_psv = 0;
2830                 break;
2831               case OPN_PSV:
2832                 opn_psv = 1;
2833                 break;
2834               case OPN_TLS:
2835                 opn_tls = 1;
2836                 break;
2837               default:
2838                 break;
2839             }
2840         }
2841         if (n == 0) {                   /* After first time through */
2842             cmfdbi(&sw,                 /* accept only switches */
2843                    _CMKEY,
2844                    "\nCarriage return to confirm to command, or switch",
2845                    "",
2846                    "",
2847                    nftpswi,
2848                    4,
2849                    xxstring,
2850                    ftpswitab,
2851                    &cm
2852                    );
2853         }
2854     }
2855 #ifdef COMMENT
2856     debug(F100,"ftp openftp while exit","",0);
2857     rc = cmcfm();
2858     debug(F101,"ftp openftp cmcfm rc","",rc);
2859     if (rc < 0)
2860       goto xopenftp;
2861 #endif /* COMMENT */
2862
2863     if (opn_psv > -1) {                 /* /PASSIVE or /ACTIVE switch given */
2864         sav_psv = ftp_psv;
2865         ftp_psv = opn_psv;
2866     }
2867     if (nologin || haveuser) {          /* /NOLOGIN or /USER switch given */
2868         sav_log = ftp_log;
2869         ftp_log = haveuser ? 1 : 0;
2870     }
2871     if (*hostname == '=') {             /* Bypass directory lookup */
2872         hostname++;                     /* if hostname starts with '=' */
2873         havehost++;
2874     } else if (isdigit(*hostname)) {    /* or if it starts with a digit */
2875         havehost++;
2876     }
2877     if (!service)
2878       makestr(&service,opn_tls?"ftps":"ftp");
2879
2880 #ifndef NODIAL
2881     if (!havehost && nnetdir > 0) {     /* If there is a networks directory */
2882         lunet(hostname);                /* Look up the name */
2883         debug(F111,"ftp openftp lunet",hostname,nhcount);
2884         if (nhcount == 0) {
2885             if (testing)
2886               printf(" ftp open trying \"%s %s\"...\n",hostname,service);
2887             success = ftpopen(hostname,service,opn_tls);
2888             debug(F101,"ftp openftp A ftpopen success","",success);
2889             rc = success;
2890         } else {
2891             int found = 0;
2892             for (i = 0; i < nhcount; i++) {
2893                 if (nh_p2[i])           /* If network type specified */
2894                   if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0))
2895                     continue;
2896                 found++;
2897                 makestr(&hostname,nh_p[i]);
2898                 debug(F111,"ftpopen lunet substitution",hostname,i);
2899                 if (testing)
2900                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
2901                 success = ftpopen(hostname,service,opn_tls);
2902                 debug(F101,"ftp openftp B ftpopen success","",success);
2903                 rc = success;
2904                 if (success)
2905                   break;
2906             }
2907             if (!found) {               /* E.g. if no network types match */
2908                 if (testing)
2909                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
2910                 success = ftpopen(hostname,service,opn_tls);
2911                 debug(F101,"ftp openftp C ftpopen success","",success);
2912                 rc = success;
2913             }
2914         }
2915     } else {
2916 #endif /* NODIAL */
2917         if (testing)
2918           printf(" ftp open trying \"%s %s\"...\n",hostname,service);
2919         success = ftpopen(hostname,service,opn_tls);
2920         debug(F111,"ftp openftp D ftpopen success",hostname,success);
2921         debug(F111,"ftp openftp D ftpopen connected",hostname,connected);
2922         rc = success;
2923 #ifndef NODIAL
2924     }
2925 #endif /* NODIAL */
2926
2927   xopenftp:
2928     debug(F101,"ftp openftp xopenftp rc","",rc);
2929     if (hostsave) free(hostsave);
2930     if (service) free(service);
2931     if (rc < 0 && ftp_logname) {
2932         free(ftp_logname);
2933         ftp_logname = NULL;
2934     }
2935     if (ftp_tmp) {
2936         free(ftp_tmp);
2937         ftp_tmp = NULL;
2938     }
2939     return(rc);
2940 }
2941
2942 int
2943 doftpacct() {
2944     int x;
2945     char * s;
2946     if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
2947       return(x);
2948     CHECKCONN();
2949     makestr(&ftp_acc,brstrip(s));
2950     if (testing)
2951       printf(" ftp account: \"%s\"\n",ftp_acc);
2952     success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
2953     return(success);
2954 }
2955
2956 int
2957 doftpusr() {                            /* Log in as USER */
2958     int x;
2959     char *s, * acct = "";
2960
2961     debok = 0;                          /* Don't log */
2962     if ((x = cmfld("Remote username or ID","",&s,xxstring)) < 0)
2963       return(x);
2964     ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */
2965     if ((x = cmfld("Remote password","",&s,xxstring)) < 0)
2966       if (x != -3)
2967         return(x);
2968     ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
2969     if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command",
2970                    "", &s, xxstring)) < 0)
2971       return(x);
2972     CHECKCONN();
2973     if (*s) {
2974         x = strlen(tmpbuf);
2975         if (x > 0) {
2976             acct = &tmpbuf[x+2];
2977             ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2);
2978         }
2979     }
2980     if (testing)
2981       printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf);
2982     success = ftp_user(line,tmpbuf,acct);
2983 #ifdef CKLOGDIAL
2984     dologftp();
2985 #endif /* CKLOGDIAL */
2986     return(success);
2987 }
2988
2989 /* DO (various FTP commands)... */
2990
2991 int
2992 doftptyp(type) int type; {              /* TYPE */
2993     CHECKCONN();
2994     ftp_typ = type;
2995     changetype(ftp_typ,ftp_vbm);
2996     return(1);
2997 }
2998
2999 static int
3000 doftpxmkd(s,vbm) char * s; int vbm; {   /* MKDIR action */
3001     int lcs = -1, rcs = -1;
3002 #ifndef NOCSETS
3003     if (ftp_xla) {
3004         lcs = ftp_csl;
3005         if (lcs < 0) lcs = fcharset;
3006         rcs = ftp_csx;
3007         if (rcs < 0) rcs = ftp_csr;
3008     }
3009 #endif /* NOCSETS */
3010     debug(F110,"ftp doftpmkd",s,0);
3011     if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3012       return(success = 1);
3013     if (ftpcode == 500 || ftpcode == 502) {
3014         if (!quiet)
3015           printf("MKD command not recognized, trying XMKD\n");
3016         if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3017           return(success = 1);
3018     }
3019     return(success = 0);
3020 }
3021
3022 static int
3023 doftpmkd() {                            /* MKDIR parse */
3024     int x;
3025     char * s;
3026     if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0)
3027       return(x);
3028     CHECKCONN();
3029     ckstrncpy(line,s,LINBUFSIZ);
3030     if (testing)
3031       printf(" ftp mkdir \"%s\"...\n",line);
3032     return(success = doftpxmkd(line,-1));
3033 }
3034
3035 static int
3036 doftprmd() {                            /* RMDIR */
3037     int x, lcs = -1, rcs = -1;
3038     char * s;
3039     if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
3040       return(x);
3041     CHECKCONN();
3042     ckstrncpy(line,s,LINBUFSIZ);
3043     if (testing)
3044       printf(" ftp rmdir \"%s\"...\n",line);
3045 #ifndef NOCSETS
3046     if (ftp_xla) {
3047         lcs = ftp_csl;
3048         if (lcs < 0) lcs = fcharset;
3049         rcs = ftp_csx;
3050         if (rcs < 0) rcs = ftp_csr;
3051     }
3052 #endif /* NOCSETS */
3053     if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE)
3054       return(success = 1);
3055     if (ftpcode == 500 || ftpcode == 502) {
3056         if (!quiet)
3057           printf("RMD command not recognized, trying XMKD\n");
3058         success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
3059     } else
3060       success = 0;
3061     return(success);
3062 }
3063
3064 static int
3065 doftpren() {                            /* RENAME */
3066     int x;
3067     char * s;
3068     if ((x = cmfld("Remote filename","",&s,xxstring)) < 0)
3069       return(x);
3070     ckstrncpy(line,s,LINBUFSIZ);
3071     if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0)
3072       return(x);
3073     ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3074     if ((x = cmcfm()) < 0)
3075       return(x);
3076     CHECKCONN();
3077     if (testing)
3078       printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf);
3079     success = ftp_rename(line,tmpbuf);
3080     return(success);
3081 }
3082
3083 int
3084 doftpres() {                            /* RESET (log out without close) */
3085     int x;
3086     if ((x = cmcfm()) < 0)
3087       return(x);
3088     CHECKCONN();
3089     if (testing)
3090       printf(" ftp reset...\n");
3091     return(success = ftp_reset());
3092 }
3093
3094 static int
3095 doftpxhlp() {                           /* HELP */
3096     int x;
3097     char * s;
3098     if ((x = cmtxt("Command name", "", &s, xxstring)) < 0)
3099       return(x);
3100     CHECKCONN();
3101     ckstrncpy(line,s,LINBUFSIZ);
3102     if (testing)
3103       printf(" ftp help \"%s\"...\n",line);
3104     /* No need to translate -- all FTP commands are ASCII */
3105     return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE));
3106 }
3107
3108 static int
3109 doftpdir(cx) int cx; {                  /* [V]DIRECTORY */
3110     int x, lcs = 0, rcs = 0, xlate = 0;
3111     char * p, * s, * m = "";
3112     if (cx == FTP_VDI) {
3113         switch (servertype) {
3114           case SYS_VMS:
3115           case SYS_DOS:
3116           case SYS_TOPS10:
3117           case SYS_TOPS20:
3118             m = "*.*";
3119             break;
3120           default:
3121             m = "*";
3122         }
3123     }
3124     if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0)
3125       return(x);
3126     if ((x = remtxt(&s)) < 0)
3127       return(x);
3128 #ifdef NOCSETS
3129     xlate = 0;
3130 #else
3131     xlate = ftp_xla;
3132 #endif /* NOCSETS */
3133     line[0] = NUL;
3134     ckstrncpy(line,s,LINBUFSIZ);
3135     s = line;
3136     CHECKCONN();
3137
3138 #ifndef NOCSETS
3139     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
3140         lcs = ftp_csl;                  /* Local charset */
3141         if (lcs < 0) lcs = fcharset;
3142         if (lcs < 0) xlate = 0;
3143     }
3144     if (xlate) {                        /* Still ON? */
3145         rcs = ftp_csx;                  /* Remote (Server) charset */
3146         if (rcs < 0) rcs = ftp_csr;
3147         if (rcs < 0) xlate = 0;
3148     }
3149 #endif /* NOCSETS */
3150
3151     if (testing) {
3152         p = s;
3153         if (!p) p = "";
3154         if (*p)
3155           printf("Directory of files %s at %s:\n", line, ftp_host);
3156         else
3157           printf("Directory of files at %s:\n", ftp_host);
3158     }
3159     debug(F111,"doftpdir",s,cx);
3160
3161     if (cx == FTP_DIR) {
3162         /* Translation of line[] is done inside recvrequest() */
3163         /* when it calls ftpcmd(). */
3164         return(success =
3165           (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0));
3166     }
3167     success = 1;                        /* VDIR - one file at a time... */
3168     p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */
3169     cancelgroup = 0;
3170     if (!ftp_vbm && !quiet)
3171       printlines = 1;
3172     while (p && !cancelfile && !cancelgroup) { /* STAT one file */
3173         if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) {
3174             success = 0;
3175             break;
3176         }
3177         p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */
3178         debug(F110,"ftp vdir file",s,0);
3179     }
3180     return(success);
3181 }
3182
3183 static int
3184 doftppwd() {                            /* PWD */
3185     int x, lcs = -1, rcs = -1;
3186 #ifndef NOCSETS
3187     if (ftp_xla) {
3188         lcs = ftp_csl;
3189         if (lcs < 0) lcs = fcharset;
3190         rcs = ftp_csx;
3191         if (rcs < 0) rcs = ftp_csr;
3192     }
3193 #endif /* NOCSETS */
3194     if ((x = cmcfm()) < 0)
3195       return(x);
3196     CHECKCONN();
3197     if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) {
3198         success = 1;
3199     } else if (ftpcode == 500 || ftpcode == 502) {
3200         if (ftp_deb)
3201           printf("PWD command not recognized, trying XPWD\n");
3202         success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE);
3203     }
3204     return(success);
3205 }
3206
3207 static int
3208 doftpcwd(s,vbm) char * s; int vbm; {    /* CD (CWD) */
3209     int lcs = -1, rcs = -1;
3210 #ifndef NOCSETS
3211     if (ftp_xla) {
3212         lcs = ftp_csl;
3213         if (lcs < 0) lcs = fcharset;
3214         rcs = ftp_csx;
3215         if (rcs < 0) rcs = ftp_csr;
3216     }
3217 #endif /* NOCSETS */
3218
3219     debug(F110,"ftp doftpcwd",s,0);
3220     if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3221       return(success = 1);
3222     if (ftpcode == 500 || ftpcode == 502) {
3223         if (!quiet)
3224           printf("CWD command not recognized, trying XCWD\n");
3225         if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3226           return(success = 1);
3227     }
3228     return(success = 0);
3229 }
3230
3231 static int
3232 doftpcdup() {                           /* CDUP */
3233     debug(F100,"ftp doftpcdup","",0);
3234     if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE)
3235       return(success = 1);
3236     if (ftpcode == 500 || ftpcode == 502) {
3237         if (!quiet)
3238           printf("CDUP command not recognized, trying XCUP\n");
3239         if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE)
3240           return(success = 1);
3241     }
3242     return(success = 0);
3243 }
3244
3245 /* s y n c d i r  --  Synchronizes client & server directories */
3246
3247 /* Used with recursive PUTs; Returns 0 on failure, 1 on success */
3248
3249 static int cdlevel = 0, cdsimlvl = 0;
3250
3251 static int
3252 syncdir(local,sim) char * local; int sim; {
3253     char buf[CKMAXPATH+1];
3254     char tmp[CKMAXPATH+1];
3255     char msgbuf[CKMAXPATH+64];
3256     char c, * p = local, * s = buf, * q = buf;
3257     int i, k = 0, done = 0, itsadir = 0, saveq;
3258
3259     debug(F110,"ftp syncdir local (new)",local,0);
3260     debug(F110,"ftp syncdir putpath (old)",putpath,0);
3261
3262     itsadir = isdir(local);
3263     saveq = quiet;
3264
3265     while ((*s = *p)) {                 /* Copy the argument filename */
3266         if (++k == CKMAXPATH)           /* so we can poke it. */
3267           return(-1);
3268         if (*s == '/')                  /* Pointer to rightmost dirsep */
3269           q = s;
3270         s++;
3271         p++;
3272     }
3273     if (!itsadir)
3274       *q = NUL;                         /* Keep just the path part */
3275
3276     debug(F110,"ftp syncdir buf",buf,0);
3277     if (!strcmp(buf,putpath)) {         /* Same as for previous file? */
3278         if (itsadir) {                  /* It's a directory? */
3279             if (doftpcwd(local,0)) {    /* Try to CD to it */
3280                 doftpcdup();            /* Worked - CD back up */
3281             } else if (sim) {           /* Simulating... */
3282                 if (fdispla == XYFD_B) {
3283                     printf("WOULD CREATE DIRECTORY %s\n",local);
3284                 } else if (fdispla) {
3285                     ckmakmsg(msgbuf,CKMAXPATH,
3286                              "WOULD CREATE DIRECTORY",local,NULL,NULL);
3287                     ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
3288                 }
3289                 /* See note above */
3290                 return(0);
3291             } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */
3292                 return(0);
3293             } else {
3294                 if (fdispla == XYFD_B) {
3295                     printf("CREATED DIRECTORY %s\n",local);
3296                 } else if (fdispla) {
3297                     ckmakmsg(msgbuf,CKMAXPATH+64,
3298                              "CREATED DIRECTORY ",local,NULL,NULL);
3299                     ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
3300                 }
3301             }
3302         }
3303         debug(F110,"ftp syncdir no change",buf,0);
3304         return(1);                      /* Yes, done. */
3305     }
3306     ckstrncpy(tmp,buf,CKMAXPATH+1);     /* Make a safe (pre-poked) copy */
3307     debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */
3308
3309     p = buf;                            /* New */
3310     s = putpath;                        /* Old */
3311
3312     debug(F110,"ftp syncdir A p",p,0);
3313     debug(F110,"ftp syncdir A s",s,0);
3314
3315     while (*p != NUL && *s != NUL && *p == *s) p++,s++;
3316
3317     if (*s == '/' && !*p) s++;          /* Don't count initial slash */
3318
3319     debug(F110,"ftp syncdir B p",p,0);
3320     debug(F110,"ftp syncdir B s",s,0);
3321
3322     /* p and s now point to the leftmost spot where they differ */
3323
3324     if (*s) {                           /* We have to back up */
3325         k = 1;                          /* How many levels */
3326         while ((c = *s++)) {            /* Count dirseps */
3327             if (c == '/' && *s)
3328               k++;
3329         }
3330         for (i = 0; i < k; i++) {       /* Do that many CDUPs */
3331             debug(F111,"ftp syncdir up",p,i+1);
3332             if (sim && cdsimlvl) {
3333                 cdsimlvl--;
3334             } else {
3335                 if (!doftpcdup()) {
3336                     quiet = saveq;
3337                     return(0);
3338                 }
3339             }
3340             cdlevel--;
3341         }
3342         if (!*p)                        /* If we don't have to go down */
3343           goto xcwd;                    /* we're done. */
3344     }
3345     while (p > buf && *p && *p != '/')  /* If in middle of segment */
3346       p--;                              /* back up to beginning */
3347     if (*p == '/')                      /* and terminate there */
3348       p++;
3349
3350     s = p;                              /* Point to start of new down path. */
3351     while (1) {                         /* Loop through characters. */
3352         if (*s == '/' || !*s) {         /* Have a segment. */
3353             if (!*s)                    /* If end of string, */
3354               done++;                   /* after this segment we're done. */
3355             else
3356               *s = NUL;                 /* NUL out the separator. */
3357             if (*p) {                   /* If segment is not empty */
3358                 debug(F110,"ftp syncdir down segment",p,0);
3359                 if (!doftpcwd(p,0)) {   /* Try to CD to it */
3360                     if (sim) {
3361                         if (fdispla == XYFD_B) {
3362                             printf("WOULD CREATE DIRECTORY %s\n",local);
3363                         } else if (fdispla) {
3364                             ckmakmsg(msgbuf,CKMAXPATH,"WOULD CREATE DIRECTORY",
3365                                      local,NULL,NULL);
3366                             ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
3367                         }
3368                         cdsimlvl++;
3369                     } else {
3370                         if (!doftpxmkd(p,0)) { /* Can't CD - try to create */
3371 /*
3372   Suppose we are executing SEND /RECURSIVE.  Locally we have a directory
3373   FOO but the remote has a regular file with the same name.  We can't CD
3374   to it, can't MKDIR it either.  There's no way out but to fail and let
3375   the user handle the problem.
3376 */
3377                             quiet = saveq;
3378                             return(0);
3379                         }
3380                         if (fdispla == XYFD_B) {
3381                             printf("CREATED DIRECTORY %s\n",p);
3382                         } else if (fdispla) {
3383                             ckmakmsg(msgbuf,CKMAXPATH,
3384                                      "CREATED DIRECTORY ",p,NULL,NULL);
3385                             ftscreen(SCR_ST,ST_MSG,0l,msgbuf);
3386                         }
3387                         if (!doftpcwd(p,0)) { /* Try again to CD */
3388                             quiet = saveq;
3389                             return(0);
3390                         }
3391                     }
3392                 }
3393                 cdlevel++;
3394             }
3395             if (done)                   /* Quit if no next segment */
3396               break;
3397             p = s+1;                    /* Point to next segment */
3398         }
3399         s++;                            /* Point to next source char */
3400     }
3401
3402   xcwd:
3403     ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */
3404     quiet = saveq;
3405     return(1);
3406 }
3407
3408 #ifdef DOUPDATE
3409 #ifdef DEBUG
3410 static VOID
3411 dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */
3412     if (deblog) {
3413         debug(F111,"ftp year ",s,xx->tm_year);
3414         debug(F111,"ftp month",s,xx->tm_mon);
3415         debug(F111,"ftp day  ",s,xx->tm_mday);
3416         debug(F111,"ftp hour ",s,xx->tm_hour);
3417         debug(F111,"ftp min  ",s,xx->tm_min);
3418         debug(F111,"ftp sec  ",s,xx->tm_sec);
3419     }
3420 }
3421 #endif /* DEBUG */
3422
3423 /*  t m c o m p a r e  --  Compare two struct tm's */
3424
3425 /*  Like strcmp() but for struct tm's  */
3426 /*  Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */
3427
3428 static int
3429 tmcompare(xx,yy) struct tm * xx, * yy; {
3430
3431     if (xx->tm_year < yy->tm_year)      /* First year less than second */
3432       return(-1);
3433     if (xx->tm_year > yy->tm_year)      /* First year greater than second */
3434       return(1);
3435
3436     /* Years are equal so compare months */
3437
3438     if (xx->tm_mon  < yy->tm_mon)       /* And so on... */
3439       return(-1);
3440     if (xx->tm_mon  > yy->tm_mon)
3441       return(1);
3442
3443     if (xx->tm_mday < yy->tm_mday)
3444       return(-1);
3445     if (xx->tm_mday > yy->tm_mday)
3446       return(1);
3447
3448     if (xx->tm_hour < yy->tm_hour)
3449       return(-1);
3450     if (xx->tm_hour > yy->tm_hour)
3451       return(1);
3452
3453     if (xx->tm_min  < yy->tm_min)
3454       return(-1);
3455     if (xx->tm_min  > yy->tm_min)
3456       return(1);
3457
3458     if (xx->tm_sec  < yy->tm_sec)
3459       return(-1);
3460     if (xx->tm_sec  > yy->tm_sec)
3461       return(1);
3462
3463     return(0);
3464 }
3465 #endif /* DOUPDATE */
3466
3467 #ifndef HAVE_TIMEGM             /* For platforms that do not have timegm() */
3468 static CONST int MONTHDAYS[] = { /* Number of days in each month. */
3469     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3470 };
3471
3472 /* Macro for whether a given year is a leap year. */
3473 #define ISLEAP(year) \
3474 (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
3475 #endif /* HAVE_TIMEGM */
3476
3477 /*  m k u t i m e  --  Like mktime() but argument is already UTC */
3478
3479 static time_t
3480 #ifdef CK_ANSIC
3481 mkutime(struct tm * tm)
3482 #else
3483 mkutime(tm) struct tm * tm;
3484 #endif /* CK_ANSIC */
3485 /* mkutime */ {
3486 #ifdef HAVE_TIMEGM
3487     return(timegm(tm));                 /* Have system service, use it. */
3488 #else
3489 /*
3490   Contributed by Russ Allbery (rra@stanford.edu), used by permission.
3491   Given a struct tm representing a calendar time in UTC, convert it to
3492   seconds since epoch.  Returns (time_t) -1 if the time is not
3493   convertable.  Note that this function does not canonicalize the provided
3494   struct tm, nor does it allow out-of-range values or years before 1970.
3495   Result should be identical with timegm().
3496 */
3497     time_t result = 0;
3498     int i;
3499     /*
3500       We do allow some ill-formed dates, but we don't do anything special
3501       with them and our callers really shouldn't pass them to us.  Do
3502       explicitly disallow the ones that would cause invalid array accesses
3503       or other algorithm problems.
3504     */
3505 #ifdef DEBUG
3506     if (deblog) {
3507         debug(F101,"mkutime tm_mon","",tm->tm_mon);
3508         debug(F101,"mkutime tm_year","",tm->tm_year);
3509     }
3510 #endif /* DEBUG */
3511     if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70)
3512       return((time_t) -1);
3513
3514     /* Convert to time_t. */
3515     for (i = 1970; i < tm->tm_year + 1900; i++)
3516       result += 365 + ISLEAP(i);
3517     for (i = 0; i < tm->tm_mon; i++)
3518       result += MONTHDAYS[i];
3519     if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900))
3520       result++;
3521     result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
3522     result = 60 * result + tm->tm_min;
3523     result = 60 * result + tm->tm_sec;
3524     debug(F101,"mkutime result","",result);
3525     return(result);
3526 #endif /* HAVE_TIMEGM */
3527 }
3528
3529
3530 /*
3531   s e t m o d t i m e  --  Set file modification time.
3532
3533   f = char * filename;
3534   t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local)
3535
3536   UNIX-specific; isolates mainline code from hideous #ifdefs.
3537   Returns:
3538     0 on success,
3539    -1 on error.
3540
3541 */
3542 static int
3543 #ifdef CK_ANSIC
3544 setmodtime(char * f, time_t t)
3545 #else
3546 setmodtime(f,t) char * f; time_t t;
3547 #endif /* CK_ANSIC */
3548 /* setmodtime */ {
3549 #ifdef NT
3550     struct _stat sb;
3551 #else /* NT */
3552     struct stat sb;
3553 #endif /* NT */
3554     int x, rc = 0;
3555 #ifdef BSD44
3556     struct timeval tp[2];
3557 #else
3558 #ifdef V7
3559     struct utimbuf {
3560         time_t timep[2];
3561     } tp;
3562 #else
3563 #ifdef SYSUTIMEH
3564 #ifdef NT
3565     struct _utimbuf tp;
3566 #else /* NT */
3567     struct utimbuf tp;
3568 #endif /* NT */
3569 #else
3570     struct utimbuf {
3571         time_t atime;
3572         time_t mtime;
3573     } tp;
3574 #endif /* SYSUTIMEH */
3575 #endif /* V7 */
3576 #endif /* BSD44 */
3577
3578     if (stat(f,&sb) < 0) {
3579         debug(F111,"setmodtime stat failure",f,errno);
3580         return(-1);
3581     }
3582 #ifdef BSD44
3583     tp[0].tv_sec = sb.st_atime;         /* Access time first */
3584     tp[1].tv_sec = t;                   /* Update time second */
3585     debug(F111,"setmodtime BSD44",f,t);
3586 #else
3587 #ifdef V7
3588     tp.timep[0] = t;                    /* Set modif. time to creation date */
3589     tp.timep[1] = sb.st_atime;          /* Don't change the access time */
3590     debug(F111,"setmodtime V7",f,t);
3591 #else
3592 #ifdef SYSUTIMEH
3593     tp.modtime = t;                     /* Set modif. time to creation date */
3594     tp.actime = sb.st_atime;            /* Don't change the access time */
3595     debug(F111,"setmodtime SYSUTIMEH",f,t);
3596 #else
3597     tp.mtime = t;                       /* Set modif. time to creation date */
3598     tp.atime = sb.st_atime;             /* Don't change the access time */
3599     debug(F111,"setmodtime (other)",f,t);
3600 #endif /* SYSUTIMEH */
3601 #endif /* V7 */
3602 #endif /* BSD44 */
3603
3604     /* Try to set the file date */
3605
3606 #ifdef BSD44
3607     x = utimes(f,tp);
3608     debug(F111,"setmodtime utimes()","BSD44",x);
3609 #else
3610 #ifdef IRIX65
3611     {
3612       /*
3613         The following produces the nonsensical warning:
3614         Argument  of type "const struct utimbuf *" is incompatible with
3615         parameter of type "const struct utimbuf *".  If you can make it
3616         go away, be my guest.
3617       */
3618         const struct utimbuf * t2 = &tp;
3619         x = utime(f,t2);
3620     }
3621 #else
3622     x = utime(f,&tp);
3623     debug(F111,"setmodtime utime()","other",x);
3624 #endif /* IRIX65 */
3625 #endif /* BSD44 */
3626     if (x)
3627       rc = -1;
3628
3629     debug(F101,"setmodtime result","",rc);
3630     return(rc);
3631 }
3632
3633
3634 /*
3635   c h k m o d t i m e  --  Check/Set file modification time.
3636
3637   fc = function code:
3638     0 = Check; returns:
3639       -1 on error,
3640        0 if local older than remote,
3641        1 if modtimes are equal,
3642        2 if local newer than remote.
3643     1 = Set (local file's modtime from remote's); returns:
3644       -1 on error,
3645        0 on success.
3646 */
3647 static int
3648 chkmodtime(local,remote,fc) char * local, * remote; int fc; {
3649 #ifdef NT
3650     struct _stat statbuf;
3651 #else /* NT */
3652     struct stat statbuf;
3653 #endif /* NT */
3654     struct tm * tmlocal = NULL;
3655     struct tm tmremote;
3656     int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0;
3657     char * s, timebuf[64];
3658
3659     debug(F111,"chkmodtime",local,mdtmok);
3660     if (!mdtmok)                        /* Server supports MDTM? */
3661       return(-1);                       /* No don't bother. */
3662
3663 #ifndef NOCSETS
3664     if (ftp_xla) {
3665         lcs = ftp_csl;
3666         if (lcs < 0) lcs = fcharset;
3667         rcs = ftp_csx;
3668         if (rcs < 0) rcs = ftp_csr;
3669     }
3670 #endif /* NOCSETS */
3671
3672     if (fc == 0) {
3673         rc = stat(local,&statbuf);
3674         if (rc == 0) {                  /* Get local file's mod time */
3675             tmlocal = gmtime(&statbuf.st_mtime); /* Convert to struct tm */
3676 #ifdef DEBUG
3677             if (tmlocal) {
3678                 dbtime(local,tmlocal);
3679             }
3680 #endif /* DEBUG */
3681         }
3682     }
3683     /* Get remote file's mod time as yyyymmddhhmmss */
3684
3685     if (havemdtm) {                     /* Already got it from MLSD? */
3686         s = havemdtm;
3687         flag++;
3688     } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) {
3689         char c;
3690         bzero((char *)&tmremote, sizeof(struct tm));
3691         s = ftp_reply_str;
3692         while ((c = *s++)) {            /* Skip past response code */
3693             if (c == SP) {
3694                 flag++;
3695                 break;
3696             }
3697         }
3698     }
3699     if (flag) {
3700         debug(F111,"ftp chkmodtime string",s,flag);
3701         if (fts_sto) {                  /* User gave server time offset? */
3702             char * p;
3703             debug(F110,"ftp chkmodtime offset",fts_sto,0);
3704             ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */
3705             if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */
3706                 ckstrncpy(timebuf,p,64);      /* Convert to MDTM format */
3707                 timebuf[8]  = timebuf[9];  /* h */
3708                 timebuf[9]  = timebuf[10]; /* h */
3709                 timebuf[10] = timebuf[12]; /* m */
3710                 timebuf[11] = timebuf[13]; /* m */
3711                 timebuf[12] = timebuf[12]; /* s */
3712                 timebuf[13] = timebuf[13]; /* s */
3713                 timebuf[14] = NUL;
3714                 s = timebuf;
3715                 debug(F110,"ftp chkmodtime adjust",s,0);
3716             }
3717         }
3718         if (flag) {                     /* Convert to struct tm */
3719             char * pat;
3720             int y2kbug = 0;             /* Seen in Kerberos 4 FTP servers */
3721             if (!ckstrcmp(s,"191",3,0)) {
3722                 pat = "%05d%02d%02d%02d%02d%02d";
3723                 y2kbug++;
3724                 debug(F110,"ftp chkmodtime Y2K BUG detected",s,0);
3725             } else {
3726                 pat = "%04d%02d%02d%02d%02d%02d";
3727             }
3728             if (sscanf(s,               /* Parse into struct tm */
3729                        pat,
3730                        &(tmremote.tm_year),
3731                        &(tmremote.tm_mon),
3732                        &(tmremote.tm_mday),
3733                        &(tmremote.tm_hour),
3734                        &(tmremote.tm_min),
3735                        &(tmremote.tm_sec)
3736                        ) == 6) {
3737                 tmremote.tm_year -= (y2kbug ? 19000 : 1900);
3738                 debug(F101,"ftp chkmodtime year","",tmremote.tm_year);
3739                 tmremote.tm_mon--;
3740
3741 #ifdef DEBUG
3742                 debug(F100,"SERVER TIME FOLLOWS:","",0);
3743                 dbtime(remote,&tmremote);
3744 #endif /* DEBUG */
3745
3746                 if (havedate > -1)
3747                   havedate = 1;
3748             }
3749         }
3750     } else {                            /* Failed */
3751         debug(F101,"ftp chkmodtime ftpcode","",ftpcode);
3752         if (ftpcode == 500 ||           /* Command unrecognized */
3753             ftpcode == 502 ||           /* Command not implemented */
3754             ftpcode == 202)             /* Command superfluous */
3755           mdtmok = 0;                   /* Don't ask this server again */
3756         return(-1);
3757     }
3758     if (fc == 0) {                      /* Compare */
3759         if (havedate == 1) {            /* Only if we have both file dates */
3760             /*
3761               Compare with local file's time.  We don't use
3762               clock time (time_t) here in case of signed/unsigned
3763               confusion, etc.
3764             */
3765             int xx;
3766 #ifdef COMMENT
3767 #ifdef DEBUG        
3768             if (deblog) {
3769                 dbtime("LOCAL",tmlocal);
3770                 dbtime("REMOT",&tmremote);
3771             }
3772 #endif /* DEBUG */
3773 #endif /* COMMENT */
3774             xx = tmcompare(tmlocal,&tmremote);
3775             debug(F101,"chkmodtime tmcompare","",xx);
3776             return(xx + 1);
3777         }
3778     } else if (ftp_dates) {             /* Set */
3779         /*
3780           Here we must convert struct tm to time_t
3781           without applying timezone conversion, for which
3782           there is no portable API.  The method is hidden
3783           in mkutime(), defined above.
3784         */
3785         time_t utc;
3786         utc = mkutime(&tmremote);
3787         debug(F111,"ftp chkmodtime mkutime",remote,utc);
3788         if (utc != (time_t)-1)
3789           return(setmodtime(local,utc));
3790     }
3791     return(-1);
3792 }
3793
3794 /* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */
3795
3796 static int
3797 getfile(remote,local,recover,append,pipename,xlate,fcs,rcs)
3798     char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs;
3799 /* getfile */ {
3800     int rc = -1;
3801     ULONG t0, t1;
3802
3803 #ifdef GFTIMER
3804     CKFLOAT sec;
3805 #else
3806     int sec = 0;
3807 #endif /* GFTIMER */
3808     char fullname[CKMAXPATH+1];
3809
3810     debug(F110,"ftp getfile remote A",remote,0);
3811     debug(F110,"ftp getfile local A",local,0);
3812     debug(F110,"ftp getfile pipename",pipename,0);
3813     if (!remote) remote = "";
3814
3815 #ifdef PATTERNS
3816     /* Automatic type switching? */
3817     if (xfermode == XMODE_A && patterns && get_auto && !forcetype) {
3818         int x;
3819         x = matchname(remote,0,servertype);
3820         debug(F111,"ftp getfile matchname",remote,x);
3821         switch (x) {
3822           case 0: ftp_typ = FTT_ASC; break;
3823           case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break;
3824           default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ;
3825         }
3826         changetype(ftp_typ,ftp_vbm);
3827         binary = ftp_typ;               /* For file-transfer display */
3828     }
3829 #endif /* PATTERNS */
3830
3831 #ifndef NOCSETS
3832     ftp_csx = -1;                       /* For file-transfer display */
3833     ftp_csl = -1;                       /* ... */
3834
3835     if (rcs > -1)                       /* -1 means no translation */
3836       if (ftp_typ == FTT_ASC)           /* File type is "ascii"? */
3837         if (fcs < 0)                    /* File charset not forced? */
3838           fcs = fcharset;               /* use prevailing FILE CHARACTER-SET */
3839     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
3840         debug(F110,"ftp getfile","initxlate",0);
3841         initxlate(rcs,fcs);             /* NB: opposite order of PUT */
3842         ftp_csx = rcs;
3843         ftp_csl = fcs;
3844     } else
3845       xlate = 0;
3846 #endif /* NOCSETS */
3847
3848     if (!pipename && (!local || !local[0]))
3849       local = remote;
3850
3851     out2screen = !strcmp(local,"-");
3852
3853     fullname[0] = NUL;
3854     if (pipename) {
3855         ckstrncpy(fullname,pipename,CKMAXPATH+1);
3856     } else {
3857         zfnqfp(local,CKMAXPATH,fullname);
3858         if (!fullname[0])
3859           ckstrncpy(fullname,local,CKMAXPATH+1);
3860     }
3861     if (!out2screen && displa && fdispla) { /* Screen */
3862         ftscreen(SCR_FN,'F',(long)pktnum,remote);
3863         ftscreen(SCR_AN,0,0L,fullname);
3864         ftscreen(SCR_FS,0,fsize,"");
3865     }
3866     tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0);
3867     tlog(F110," as",fullname,0);
3868     debug(F111,"ftp getfile size",remote,fsize);
3869     debug(F111,"ftp getfile local",local,out2screen);
3870
3871     ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH);
3872
3873     t0 = gmstimer();                    /* Start time */
3874     debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */
3875     rc = recvrequest("RETR",
3876                      local,
3877                      remote,
3878                      append ? "ab" : "wb",
3879                      0,
3880                      recover,
3881                      pipename,
3882                      xlate,
3883                      fcs,
3884                      rcs
3885                      );
3886     t1 = gmstimer();                    /* End time */
3887     debug(F111,"ftp getfile t1",remote,t1);
3888     debug(F111,"ftp getfile sec",remote,(t1-t0)/1000);
3889 #ifdef GFTIMER
3890     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
3891     fpxfsecs = sec;                     /* (for doxlog()) */
3892 #else
3893     sec = (t1 - t0) / 1000;
3894     xfsecs = (int)sec;
3895 #endif /* GFTIMER */
3896     debug(F111,"ftp recvrequest rc",remote,rc);
3897     if (cancelfile || cancelgroup) {
3898         debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup);
3899         ftscreen(SCR_ST,ST_INT,0l,"");
3900     } else if (rc > 0) {
3901         debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup);
3902         ftscreen(SCR_ST,ST_SKIP,0l,cmarg);
3903     } else if (rc < 0) {
3904         switch (ftpcode) {
3905           case -4:                      /* Network error */
3906           case -2:                      /* File error */
3907             ftscreen(SCR_ST,ST_MSG,0l,ck_errstr());
3908             break;
3909           case -3:
3910             ftscreen(SCR_ST,ST_MSG,0l,"Failure to make data connection");
3911             break;
3912           case -1:
3913             ftscreen(SCR_ST,ST_INT,0l,""); /* (should be covered above) */
3914             break;
3915           default:
3916             ftscreen(SCR_ST,ST_MSG,0l,&ftp_reply_str[4]);
3917         }
3918     } else {                            /* Tudo bem */
3919         ftscreen(SCR_PT,'Z',0L,"");
3920         if (rc == 0) {
3921             ftscreen(SCR_ST,ST_OK,0L,""); /* For screen */
3922             makestr(&rrfspec,remote);     /* For WHERE command */
3923             makestr(&rfspec,fullname);
3924         }
3925     }
3926     if (ftp_dates)                      /* If FTP DATES ON... */
3927       if (!pipename && !out2screen)     /* and it's a real file */
3928         if (rc < 1 && rc != -3)         /* and it wasn't skipped */
3929           if (connected)                /* and we still have a connection */
3930             if (zchki(local) > -1) {    /* and the file wasn't discarded */
3931                 chkmodtime(local,remote,1); /* set local file date */
3932                 debug(F110,"ftp get set date",local,0);
3933             }
3934     filcnt++;                           /* Used by \v(filenum) */
3935 #ifdef TLOG
3936     if (tralog) {
3937         if (rc > 0) {
3938             tlog(F100," recovery skipped","",0);
3939         } else if (rc == 0) {
3940             tlog(F101," complete, size", "", fsize);
3941         } else if (cancelfile) {
3942             tlog(F100," canceled by user","",0);
3943         } else {
3944             tlog(F110," failed:",ftp_reply_str,0);
3945         }
3946         if (!tlogfmt)
3947           doxlog(what,local,fsize,ftp_typ,rc,"");
3948     }
3949 #endif /* TLOG */
3950     return(rc);
3951 }
3952
3953 /* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */
3954 /* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */
3955
3956 static int
3957 putfile(cx,
3958     local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg)
3959     char * local, * remote, * mvto, *rnto, *srvrn;
3960     int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg;
3961
3962 /* putfile */ {
3963
3964     char asname[CKMAXPATH+1];
3965     char fullname[CKMAXPATH+1];
3966     int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0;
3967     int xlate = 0, restart = 0, mt = -1;
3968     char * s = NULL, * cmd = NULL;
3969     ULONG t0 = 0, t1 = 0;               /* Times for stats */
3970     int ofcs = 0, orcs = 0;
3971
3972 #ifdef GFTIMER
3973     CKFLOAT sec = 0.0;
3974 #else
3975     int sec = 0;
3976 #endif /* GFTIMER */
3977     debug(F111,"ftp putfile flg",local,flg);
3978     debug(F110,"ftp putfile srv_renam",srvrn,0);
3979     debug(F101,"ftp putfile fcs","",fcs);
3980     debug(F101,"ftp putfile rcs","",rcs);
3981
3982     ofcs = fcs;                         /* Save charset args */
3983     orcs = rcs;
3984
3985     sendstart = 0L;
3986     restart = flg & PUT_RES;
3987     if (!remote)
3988       remote = "";
3989
3990     /* FTP protocol command to send to server */
3991     cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR");
3992
3993     if (x_cnv == SET_AUTO) {            /* Name conversion is auto */
3994         if (alike) {                    /* If server & client are alike */
3995             nc = 0;                     /* no conversion */
3996         } else {                        /* If they are different */
3997             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
3998               nc = -1;                  /* only minimal conversions needed */
3999             else                        /* otherwise */
4000               nc = 1;                   /* full conversion */
4001         }
4002     } else                              /* Not auto - do what user said */
4003       nc = x_cnv;
4004
4005     /* If Transfer Mode is Automatic, determine file type */
4006     if (xfermode == XMODE_A && filepeek && !pipesend) {
4007         if (isdir(local)) {             /* If it's a directory */
4008             k = FT_BIN;                 /* skip the file scan */
4009         } else {
4010             debug(F110,"FTP PUT calling scanfile",local,0);
4011             k = scanfile(local,&o,nscanfile); /* Scan the file */
4012         }
4013         debug(F111,"FTP PUT scanfile",local,k);
4014         if (k > -1 && !forcetype) {
4015             ftp_typ = (k == FT_BIN) ? 1 : 0;
4016             if (xft > -1 && ftp_typ != xft) {
4017                 if (flg & PUT_SIM)
4018                   tlog(F110,"ftp put SKIP (Type):", local, 0);
4019                 return(SKP_TYP);
4020             }
4021             if (ftp_typ == 1 && tenex)  /* User said TENEX? */
4022               ftp_typ = FTT_TEN;
4023         }
4024     }
4025 #ifndef NOCSETS
4026     ftp_csx = -1;                       /* For file-transfer display */
4027     ftp_csl = -1;                       /* ... */
4028
4029     if (rcs > -1) {                     /* -1 means no translation */
4030         if (ftp_typ == 0) {             /* File type is "ascii"? */
4031             if (fcs < 0) {              /* File charset not forced? */
4032                 if (k < 0) {            /* If we didn't scan */
4033                     fcs = fcharset;     /* use prevailing FILE CHARACTER-SET */
4034                 } else {                /* If we did scan, use scan result */
4035                     switch (k) {
4036                       case FT_TEXT:     /* Unknown text */
4037                         fcs = fcharset;
4038                         break;
4039                       case FT_7BIT:     /* 7-bit text */
4040                         fcs = dcset7;
4041                         break;
4042                       case FT_8BIT:     /* 8-bit text */
4043                         fcs = dcset8;
4044                         break;
4045                       case FT_UTF8:     /* UTF-8 */
4046                         fcs = FC_UTF8;
4047                         break;
4048                       case FT_UCS2:     /* UCS-2 */
4049                         fcs = FC_UCS2;
4050                         if (o > -1)     /* Input file byte order */
4051                           fileorder = o;
4052                         break;
4053                       default:
4054                         rcs = -1;
4055                     }
4056                 }
4057             }
4058         }
4059     }
4060     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4061         debug(F110,"ftp putfile","initxlate",0);
4062         initxlate(fcs,rcs);
4063         debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs);
4064         xlate = 1;
4065         ftp_csx = rcs;
4066         ftp_csl = fcs;
4067     }
4068 #endif /* NOCSETS */
4069
4070     binary = ftp_typ;                   /* For file-transfer display */
4071     asname[0] = NUL;
4072
4073     if (recursive) {                    /* If sending recursively, */
4074         if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */
4075           return(-1);                   /* Don't PUT if it fails. */
4076         else if (isdir(local))          /* It's a directory */
4077           return(0);                    /* Don't send it! */
4078     }
4079     if (*remote) {                      /* If an as-name template was given */
4080 #ifndef NOSPL
4081         if (cmd_quoting) {              /* and COMMAND QUOTING is ON */
4082             y = CKMAXPATH;              /* evaluate it for this file */
4083             s = asname;
4084             zzstring(remote,&s,&y);
4085         } else
4086 #endif /* NOSPL */
4087           ckstrncpy(asname,remote,CKMAXPATH);   /* (or take it literally) */
4088     } else {                                    /* No as-name */
4089         nzltor(local,asname,nc,0,CKMAXPATH);    /* use local name strip path */
4090         debug(F110,"FTP PUT nzltor",asname,0);
4091     }
4092     /* Preliminary messages and log entries */
4093
4094     fullname[0] = NUL;
4095     zfnqfp(local,CKMAXPATH,fullname);
4096     if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1);
4097     fullname[CKMAXPATH] = NUL;
4098
4099     if (displa && fdispla) {            /* Screen */
4100         ftscreen(SCR_FN,'F',(long)pktnum,local);
4101         ftscreen(SCR_AN,0,0L,asname);
4102         ftscreen(SCR_FS,0,fsize,"");
4103     }
4104 #ifdef DOUPDATE
4105     if (flg & (PUT_UPD|PUT_DIF)) {      /* Date-checking modes... */
4106         mt = chkmodtime(fullname,asname,0);
4107         debug(F111,"ftp putfile chkmodtime",asname,mt);
4108         if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */
4109             tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0);
4110             ftscreen(SCR_ST,ST_SKIP,SKP_DAT,fullname); /* Skip this one */
4111             filcnt++;
4112             return(SKP_DAT);
4113         } else if (mt == 1) {           /* Times are equal */
4114             tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0);
4115             ftscreen(SCR_ST,ST_SKIP,SKP_EQU,fullname); /* Skip it */
4116             filcnt++;
4117             return(SKP_DAT);
4118         }
4119         /* Local file is newer */
4120         tlog(F110,ftp_typ ? "ftp put /update BINARY:" :
4121              "ftp put /update TEXT:", fullname, 0);
4122     } else if (flg & PUT_RES) {
4123         tlog(F110,ftp_typ ? "ftp put /recover BINARY:" :
4124              "ftp put /recover TEXT:", fullname, 0);
4125     } else {
4126         tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4127     }
4128 #else
4129     tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4130 #endif /* DOUPDATE */
4131     tlog(F110," as",asname,0);
4132
4133 #ifndef NOCSETS
4134     if (xlate) {
4135         debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs);
4136         tlog(F110," file character set:",fcsinfo[fcs].keyword,0);
4137         tlog(F110," server character set:",fcsinfo[rcs].keyword,0);
4138     } else if (!ftp_typ) {
4139         tlog(F110," character sets:","no conversion",0);
4140         fcs = ofcs;                     /* Binary file but we still must */
4141         rcs = orcs;                     /* translate its name */
4142     }
4143 #endif /* NOCSETS */
4144
4145     /* PUT THE FILE */
4146
4147     t0 = gmstimer();                    /* Start time */
4148     if (flg & PUT_SIM) {                /* rc > 0 is a skip reason code */
4149         if (flg & (PUT_UPD|PUT_DIF)) {  /* (see SKP_xxx in ckcker.h) */
4150             rc = (mt < 0) ?             /* Update mode... */
4151               SKP_XNX :                 /* Remote file doesn't exist */
4152                 SKP_XUP;                /* Remote file is older */
4153         } else {
4154             rc = SKP_SIM;               /* "Would be sent", period. */
4155         }
4156     } else {
4157         rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart);
4158     }
4159     t1 = gmstimer();                    /* End time */
4160     filcnt++;                           /* File number */
4161
4162 #ifdef GFTIMER
4163     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4164     fpxfsecs = sec;                     /* (for doxlog()) */
4165 #else
4166     sec = (t1 - t0) / 1000;
4167     xfsecs = (int)sec;
4168 #endif /* GFTIMER */
4169
4170     debug(F111,"ftp sendrequest rc",local,rc);
4171
4172     if (cancelfile || cancelgroup) {
4173         debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup);
4174         ftscreen(SCR_ST,ST_INT,0l,"");
4175     } else if (rc > 0) {
4176         debug(F101,"ftp put skipped",local,rc);
4177         ftscreen(SCR_ST,ST_SKIP,rc,fullname);
4178     } else if (rc < 0) {
4179         debug(F111,"ftp put error",local,ftpcode);
4180         ftscreen(SCR_ST,ST_MSG,0L,&ftp_reply_str[4]);
4181     } else {
4182         debug(F111,"ftp put not canceled",ckitoa(displa),fdispla);
4183         ftscreen(SCR_PT,'Z',0L,"");
4184         debug(F111,"ftp put ST_OK",local,rc);
4185         ftscreen(SCR_ST,ST_OK,0L,"");
4186         debug(F110,"ftp put old sfspec",sfspec,0);
4187         makestr(&sfspec,fullname);      /* For WHERE command */
4188         debug(F110,"ftp put new sfspec",sfspec,0);
4189         debug(F110,"ftp put old srfspec",srfspec,0);
4190         makestr(&srfspec,asname);
4191         debug(F110,"ftp put new srfspec",srfspec,0);
4192     }
4193
4194     /* Final log entries */
4195
4196 #ifdef TLOG
4197     if (tralog) {
4198         if (rc > 0) {
4199             if (rc == SKP_XNX)
4200               tlog(F100," /simulate: WOULD BE SENT:","no remote file",0);
4201             else if (rc == SKP_XUP)
4202               tlog(F100," /simulate: WOULD BE SENT:","remote file older",0);
4203             else if (rc == SKP_SIM)
4204               tlog(F100," /simulate: WOULD BE SENT","",0);
4205             else
4206               tlog(F110," skipped:",gskreason(rc),0);
4207         } else if (rc == 0) {
4208             tlog(F101," complete, size", "", fsize);
4209         } else if (cancelfile) {
4210             tlog(F100," canceled by user","",0);
4211         } else {
4212             tlog(F110," failed:",ftp_reply_str,0);
4213         }
4214         if (!tlogfmt)
4215           doxlog(what,local,fsize,ftp_typ,rc,"");
4216     }
4217 #endif /* TLOG */
4218
4219     if (rc < 0)                         /* PUT did not succeed */
4220       return(-1);                       /* so done. */
4221
4222     if (flg & PUT_SIM)                  /* Simulating, skip the rest. */
4223       return(SKP_SIM);
4224
4225 #ifdef UNIX
4226     /* Set permissions too? */
4227
4228     if (prm) {                          /* Change permissions? */
4229         s = zgperm(local);              /* Get perms of local file */
4230         if (!s) s = "";
4231         x = strlen(s);
4232         if (x > 3) s += (x - 3);
4233         if (rdigits(s)) {
4234             ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL);
4235             x =
4236               ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE;
4237             tlog(F110, x ? " chmod" : " chmod failed",
4238                  s,
4239                  0
4240                  );
4241             if (!x)
4242               return(-1);
4243         }
4244     }
4245 #endif /* UNIX */
4246
4247     /* Disposition of source file */
4248
4249     if (moving) {
4250         x = zdelet(local);
4251         tlog(F110, (x > -1) ?
4252              " deleted" : " failed to delete",
4253              local,
4254              0
4255              );
4256         if (x < 0)
4257           return(-1);
4258     } else if (mvto) {
4259         x = zrename(local,mvto);
4260         tlog(F110, (x > -1) ?
4261              " moved source to" : " failed to move source to",
4262              mvto,
4263              0
4264              );
4265         if (x < 0)
4266           return(-1);
4267         /* ftscreen(SCR_ST,ST_MSG,0L,mvto); */
4268
4269     } else if (rnto) {
4270         char * s = rnto;
4271 #ifndef NOSPL
4272         int y;                          /* Pass it thru the evaluator */
4273         extern int cmd_quoting;         /* for \v(filename) */
4274         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4275             y = CKMAXPATH;
4276             s = (char *)asname;
4277             zzstring(rnto,&s,&y);
4278             s = (char *)asname;
4279         }
4280 #endif /* NOSPL */
4281         if (s) if (*s) {
4282             int x;
4283             x = zrename(local,s);
4284             tlog(F110, (x > -1) ?
4285                  " renamed source file to" :
4286                  " failed to rename source file to",
4287                  s,
4288                  0
4289                  );
4290             if (x < 0)
4291               return(-1);
4292             /* ftscreen(SCR_ST,ST_MSG,0L,s); */
4293         }
4294     }
4295
4296     /* Disposition of destination file */
4297
4298     if (srvrn) {                        /* /SERVER-RENAME: */
4299         char * s = srvrn;
4300 #ifndef NOSPL
4301         int y;                          /* Pass it thru the evaluator */
4302         extern int cmd_quoting; /* for \v(filename) */
4303         debug(F111,"ftp putfile srvrn",s,1);
4304
4305         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4306             y = CKMAXPATH;
4307             s = (char *)fullname;       /* We can recycle this buffer now */
4308             zzstring(srvrn,&s,&y);
4309             s = (char *)fullname;
4310         }
4311 #endif /* NOSPL */
4312         debug(F111,"ftp putfile srvrn",s,2);
4313         if (s) if (*s) {
4314             int x;
4315             x = ftp_rename(asname,s);
4316             debug(F111,"ftp putfile ftp_rename",asname,x);
4317             tlog(F110, (x > 0) ?
4318                  " renamed destination file to" :
4319                  " failed to rename destination file to",
4320                  s,
4321                  0
4322                  );
4323             if (x < 1)
4324               return(-1);
4325         }
4326     }
4327     return(0);
4328 }
4329
4330 /* xxout must only be used for ASCII transfers */
4331 static int
4332 #ifdef CK_ANSIC
4333 xxout(char c)
4334 #else
4335 xxout(c) char c;
4336 #endif /* CK_ANSIC */
4337 {
4338 #ifndef OS2
4339 #ifndef VMS
4340 #ifndef MAC
4341 #ifndef OSK
4342     /* For Unix, DG, Stratus, Amiga, Gemdos, other */
4343     if (c == '\012') {
4344         if (zzout(dout,(CHAR)'\015') < 0)
4345           return(-1);
4346         ftpsnd.bytes++;
4347     }
4348 #else /* OSK */
4349     if (c == '\015') {
4350         c = '\012';
4351         if (zzout(dout,(CHAR)'\015') < 0)
4352           return(-1);
4353         ftpsnd.bytes++;
4354     }
4355 #endif /* OSK */
4356 #else /* MAC */
4357     if (c == '\015') {
4358         c = '\012';
4359         if (zzout(dout,(CHAR)'\015') < 0)
4360           return(-1);
4361         ftpsnd.bytes++;
4362     }
4363 #endif /* MAC */
4364 #endif /* VMS */
4365 #endif /* OS2 */
4366     if (zzout(dout,(CHAR)c) < 0)
4367       return(-1);
4368     ftpsnd.bytes++;
4369     return(0);
4370 }
4371
4372 static int
4373 #ifdef CK_ANSIC
4374 scrnout(char c)
4375 #else
4376 scrnout(c) char c;
4377 #endif /* CK_ANSIC */
4378 {
4379     return(putchar(c));
4380 }
4381
4382 static int
4383 #ifdef CK_ANSIC
4384 pipeout(char c)
4385 #else
4386 pipeout(c) char c;
4387 #endif /* CK_ANSIC */
4388 {
4389     return(zmchout(c));
4390 }
4391
4392 static int
4393 ispathsep(c) int c; {
4394     switch (servertype) {
4395       case SYS_VMS:
4396       case SYS_TOPS10:
4397       case SYS_TOPS20:
4398         return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0);
4399       case SYS_OS2:
4400       case SYS_WIN32:
4401       case SYS_DOS:
4402         return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0);
4403       case SYS_VOS:
4404         return((c == '>') ? 1 : 0);
4405       default:
4406         return((c == '/') ? 1 : 0);
4407     }
4408 }
4409
4410 static int
4411 iscanceled() {
4412 #ifdef CK_CURSES
4413     extern int ck_repaint();
4414 #endif /* CK_CURSES */
4415     int x, rc = 0;
4416     char c = 0;
4417     if (cancelfile)
4418       return(1);
4419     x = conchk();                       /* Any chars waiting at console? */
4420     if (x-- > 0) {                      /* Yes...  */
4421         c = coninc(5);                  /* Get one */
4422         switch (c) {
4423           case 032:                     /* Ctrl-X or X */
4424           case 'z':
4425           case 'Z': cancelgroup++;      /* fall thru on purpose */
4426           case 030:                     /* Ctrl-Z or Z */
4427           case 'x':
4428           case 'X': cancelfile++; rc++; break;
4429 #ifdef CK_CURSES
4430           case 'L':
4431           case 'l':
4432           case 014:                     /* Ctrl-L or L or Ctrl-W */
4433           case 027:
4434             ck_repaint();               /* Refresh screen */
4435 #endif /* CK_CURSES */
4436         }
4437     }
4438     while (x-- > 0)                     /* Soak up any rest */
4439       c = coninc(1);
4440     return(rc);
4441 }
4442
4443 /* zzsend - used by buffered output macros. */
4444
4445 static int
4446 #ifdef CK_ANSIC
4447 zzsend(int fd, CHAR c)
4448 #else
4449 zzsend(fd,c) int fd; CHAR c;
4450 #endif /* CK_ANSIC */
4451 {
4452     int rc;
4453
4454     debug(F101,"zzsend ucbufsiz","",ucbufsiz);
4455     debug(F101,"zzsend nout","",nout);
4456     debug(F111,"zzsend","secure?",ftpissecure());
4457
4458     if (iscanceled())                   /* Check for cancellation */
4459       return(-9);
4460     rc = (!ftpissecure()) ?
4461       send(fd, (SENDARG2TYPE)ucbuf, nout, 0) :
4462         secure_putbuf(fd, ucbuf, nout);
4463     ucbuf[nout] = NUL;
4464     nout = 0;
4465     ucbuf[nout++] = c;
4466     spackets++;
4467     pktnum++;
4468     if (rc > -1 && fdispla != XYFD_B) {
4469         spktl = nout;
4470         ftscreen(SCR_PT,'D',spackets,NULL);
4471     }
4472     return(rc);
4473 }
4474
4475 /* c m d l i n p u t  --  Command-line PUT */
4476
4477 int
4478 cmdlinput(stay) int stay; {
4479     int x, rc = 0, done = 0, good = 0, status = 0;
4480     ULONG t0, t1;                       /* Times for stats */
4481 #ifdef GFTIMER
4482     CKFLOAT sec;
4483 #else
4484     int sec = 0;
4485 #endif /* GFTIMER */
4486
4487     if (quiet) {                        /* -q really means quiet */
4488         displa = 0;
4489         fdispla = 0;
4490     } else {
4491         displa = 1;
4492         fdispla = XYFD_B;
4493     }
4494     testing = 0;
4495     out2screen = 0;
4496     dpyactive = 0;
4497     what = W_FTP|W_SEND;
4498
4499 #ifndef NOSPL
4500     cmd_quoting = 0;
4501 #endif /* NOSPL */
4502     sndsrc = nfils;
4503
4504     t0 = gmstimer();                    /* Record starting time */
4505
4506     while (!done && !cancelgroup) {     /* Loop for all files */
4507
4508         cancelfile = 0;
4509         x = gnfile();                   /* Get next file from list(s) */
4510         if (x == 0)                     /* (see gnfile() comments...) */
4511           x = gnferror;
4512
4513         switch (x) {
4514           case 1:                       /* File to send */
4515             rc = putfile(FTP_PUT,       /* Function (PUT, APPEND) */
4516                          filnam,        /* Local file to send */
4517                          filnam,        /* Remote name for file */
4518                          forcetype,     /* Text/binary mode forced */
4519                          0,             /* Not moving */
4520                          NULL,          /* No move-to */
4521                          NULL,          /* No rename-to */
4522                          NULL,          /* No server-rename */
4523                          ftp_cnv,       /* Filename conversion */
4524                          0,             /* Unique-server-names */
4525                          -1,            /* All file types */
4526                          0,             /* No permissions */
4527                          -1,            /* No character sets */
4528                          -1,            /* No character sets */
4529                          0              /* No update or restart */
4530                          );
4531             if (rc > -1) {
4532                 good++;
4533                 status = 1;
4534             }
4535             if (cancelfile) {
4536                 continue;               /* Or break? */
4537             }
4538             if (rc < 0) {
4539                 ftp_fai++;
4540             }
4541             continue;                   /* Or break? */
4542
4543           case 0:                       /* No more files, done */
4544             done++;
4545             continue;
4546
4547           case -2:
4548           case -1:
4549             printf("?%s: file not found - \"%s\"\n",
4550                    puterror ? "Fatal" : "Warning",
4551                    filnam
4552                    );
4553             continue;                   /* or break? */
4554           case -3:
4555             printf("?Warning access denied - \"%s\"\n", filnam);
4556             continue;                   /* or break? */
4557           case -5:
4558             printf("?Too many files match\n");
4559             done++;
4560             break;
4561           case -6:
4562             if (good < 1)
4563               printf("?No files selected\n");
4564             done++;
4565             break;
4566           default:
4567             printf("?getnextfile() - unknown failure\n");
4568             done++;
4569         }
4570     }
4571     if (status > 0) {
4572         if (cancelgroup)
4573           status = 0;
4574         else if (cancelfile && good < 1)
4575           status = 0;
4576     }
4577     success = status;
4578     x = success;
4579     if (x > -1) {
4580         lastxfer = W_FTP|W_SEND;
4581         xferstat = success;
4582     }
4583     t1 = gmstimer();                    /* End time */
4584 #ifdef GFTIMER
4585     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4586     if (!sec) sec = 0.001;
4587     fptsecs = sec;
4588 #else
4589     sec = (t1 - t0) / 1000;
4590     if (!sec) sec = 1;
4591 #endif /* GFTIMER */
4592     tfcps = (long) (tfc / sec);
4593     tsecs = (int)sec;
4594     lastxfer = W_FTP|W_SEND;
4595     xferstat = success;
4596     if (dpyactive)
4597       ftscreen(SCR_TC,0,0L,"");
4598
4599     if (!stay)
4600       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
4601     return(success);
4602 }
4603
4604
4605 /*  d o f t p p u t  --  Parse and execute PUT, MPUT, and APPEND  */
4606
4607 int
4608 #ifdef CK_ANSIC
4609 doftpput(int cx, int who)               /* who == 1 for ftp, 0 for kermit */
4610 #else
4611 doftpput(cx,who) int cx, who;
4612 #endif /* CK_ANSIC */
4613 {
4614     struct FDB sf, fl, sw, cm;
4615     int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0;
4616     int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0;
4617     char * s, * s2;
4618
4619     int x_csl, x_csr = -1;              /* Local and remote charsets */
4620     int x_xla = 0;
4621     int x_recurse = 0;
4622     char c, * p;                        /* Workers */
4623 #ifdef PUTARRAY
4624     int range[2];                       /* Array range */
4625     char ** ap = NULL;                  /* Array pointer */
4626     int arrayx = -1;                    /* Array index */
4627 #endif /* PUTARRAY */
4628     ULONG t0 = 0L, t1 = 0L;             /* Times for stats */
4629 #ifdef GFTIMER
4630     CKFLOAT sec;
4631 #else
4632     int sec = 0;
4633 #endif /* GFTIMER */
4634
4635     struct stringint {                  /* Temporary array for switch values */
4636         char * sval;
4637         int ival;
4638     } pv[SND_MAX+1];
4639
4640     success = 0;                        /* Assume failure */
4641     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
4642     out2screen = 0;                     /* Not outputting file to screen */
4643     putflags = 0;                       /* PUT options */
4644     x_cnv = ftp_cnv;                    /* Filename conversion */
4645     x_usn = ftp_usn;                    /* Unique server names */
4646     x_prm = ftp_prm;                    /* Permissions */
4647     if (x_prm == SET_AUTO)              /* Permissions AUTO */
4648       x_prm = alike;
4649
4650 #ifndef NOCSETS
4651     x_csr = ftp_csr;                    /* Inherit global server charset */
4652     x_csl = ftp_csl;
4653     if (x_csl < 0)
4654       x_csl = fcharset;
4655     x_xla = ftp_xla;
4656 #endif /* NOCSETS */
4657
4658     makestr(&filefile,NULL);            /* No filename list file yet. */
4659     makestr(&srv_renam,NULL);           /* Clear /SERVER-RENAME: */
4660     makestr(&snd_rename,NULL);          /*  PUT /RENAME */
4661     makestr(&snd_move,NULL);            /*  PUT /MOVE */
4662     putpath[0] = NUL;                   /* Initialize for syncdir(). */
4663     puterror = ftp_err;                 /* Inherit global error action. */
4664     what = W_SEND|W_FTP;                /* What we're doing (sending w/FTP) */
4665     asnambuf[0] = NUL;                  /* Clear as-name buffer */
4666
4667     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
4668         ftp_typ = g_ftp_typ;
4669         /* g_ftp_typ = -1; */
4670     }
4671     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
4672         pv[i].sval = NULL;              /* to null pointers */
4673         pv[i].ival = -1;                /* and -1 int values */
4674     }
4675     if (who == 0) {                     /* Called with unprefixed command */
4676         switch (cx) {
4677           case XXRSEN:  pv[SND_RES].ival = 1; break;
4678           case XXCSEN:  pv[SND_CMD].ival = 1; break;
4679           case XXMOVE:  pv[SND_DEL].ival = 1; break;
4680           case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */
4681           case XXMSE:   mput++; break;
4682         }
4683     } else {
4684         if (cx == FTP_MPU)
4685           mput++;
4686     }
4687     cmfdbi(&sw,                         /* First FDB - command switches */
4688            _CMKEY,                      /* fcode */
4689            "Filename, or switch",       /* hlpmsg */
4690            "",                          /* default */
4691            "",                          /* addtl string data */
4692            nputswi,                     /* addtl numeric data 1: tbl size */
4693            4,                           /* addtl numeric data 2: 4 = cmswi */
4694            xxstring,                    /* Processing function */
4695            putswi,                      /* Keyword table */
4696            &sf                          /* Pointer to next FDB */
4697            );
4698     cmfdbi(&fl,                         /* 3rd FDB - local filespec */
4699            _CMFLD,                      /* fcode */
4700            "",                          /* hlpmsg */
4701            "",                          /* default */
4702            "",                          /* addtl string data */
4703            0,                           /* addtl numeric data 1 */
4704            0,                           /* addtl numeric data 2 */
4705            xxstring,
4706            NULL,
4707            &cm
4708            );
4709     cmfdbi(&cm,                         /* 4th FDB - Confirmation */
4710            _CMCFM,                      /* fcode */
4711            "",                          /* hlpmsg */
4712            "",                          /* default */
4713            "",                          /* addtl string data */
4714            0,                           /* addtl numeric data 1 */
4715            0,                           /* addtl numeric data 2 */
4716            NULL,
4717            NULL,
4718            NULL
4719            );
4720
4721   again:
4722     cmfdbi(&sf,                         /* 2nd FDB - file to send */
4723            _CMIFI,                      /* fcode */
4724            "",                          /* hlpmsg */
4725            "",                          /* default */
4726            "",                          /* addtl string data */
4727            /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */
4728            nolinks | x_recurse,         /* addtl numeric data 1 */
4729            0,                           /* dirflg 0 means "not dirs only" */
4730            xxstring,
4731            NULL,
4732 #ifdef COMMENT
4733            mput ? &cm : &fl
4734 #else
4735            &fl
4736 #endif /* COMMENT */
4737            );
4738
4739     while (1) {                         /* Parse zero or more switches */
4740         x = cmfdb(&sw);                 /* Parse something */
4741         debug(F101,"ftp put cmfdb A","",x);
4742         debug(F101,"ftp put fcode A","",cmresult.fcode);
4743         if (x < 0)                      /* Error */
4744           goto xputx;                   /* or reparse needed */
4745         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
4746           break;
4747         c = cmgbrk();                   /* Get break character */
4748         getval = (c == ':' || c == '='); /* to see how they ended the switch */
4749         if (getval && !(cmresult.kflags & CM_ARG)) {
4750             printf("?This switch does not take arguments\n");
4751             x = -9;
4752             goto xputx;
4753         }
4754         if (!getval && (cmgkwflgs() & CM_ARG)) {
4755             printf("?This switch requires an argument\n");
4756             x = -9;
4757             goto xputx;
4758         }
4759         n = cmresult.nresult;           /* Numeric result = switch value */
4760         debug(F101,"ftp put switch","",n);
4761
4762         switch (n) {                    /* Process the switch */
4763           case SND_AFT:                 /* Send /AFTER:date-time */
4764           case SND_BEF:                 /* Send /BEFORE:date-time */
4765           case SND_NAF:                 /* Send /NOT-AFTER:date-time */
4766           case SND_NBE:                 /* Send /NOT-BEFORE:date-time */
4767             if (!getval) break;
4768             if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) {
4769                 if (x == -3) {
4770                     printf("?Date-time required\n");
4771                     x = -9;
4772                 }
4773                 goto xputx;
4774             }
4775             pv[n].ival = 1;
4776             makestr(&(pv[n].sval),s);
4777             break;
4778
4779           case SND_ASN:                 /* /AS-NAME: */
4780             debug(F101,"ftp put /as-name getval","",getval);
4781             if (!getval) break;
4782             if ((x = cmfld("Name to send under","",&s,NULL)) < 0) {
4783                 if (x == -3) {
4784                     printf("?name required\n");
4785                     x = -9;
4786                 }
4787                 goto xputx;
4788             }
4789             makestr(&(pv[n].sval),brstrip(s));
4790             debug(F110,"ftp put /as-name 1",pv[n].sval,0);
4791             if (pv[n].sval) pv[n].ival = 1;
4792             break;
4793
4794 #ifdef PUTARRAY
4795           case SND_ARR:                 /* /ARRAY */
4796             if (!getval) break;
4797             ap = NULL;
4798             if ((x = cmfld("Array name (a single letter will do)",
4799                            "",
4800                            &s,
4801                            NULL
4802                            )) < 0) {
4803                 if (x == -3)
4804                   break;
4805                 else
4806                   return(x);
4807             }
4808             if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) {
4809                 printf("?Bad array: %s\n",s);
4810                 return(-9);
4811             }
4812             if (!(ap = a_ptr[x])) {
4813                 printf("?No such array: %s\n",s);
4814                 return(-9);
4815             }
4816             pv[n].ival = 1;
4817             pv[SND_CMD].ival = 0;       /* Undo any conflicting ones... */
4818             pv[SND_RES].ival = 0;
4819             pv[SND_FIL].ival = 0;
4820             arrayx = x;
4821             break;
4822 #endif /* PUTARRAY */
4823
4824           case SND_BIN:                 /* /BINARY */
4825           case SND_TXT:                 /* /TEXT or /ASCII */
4826           case SND_TEN:                 /* /TENEX */
4827             pv[SND_BIN].ival = 0;
4828             pv[SND_TXT].ival = 0;
4829             pv[SND_TEN].ival = 0;
4830             pv[n].ival = 1;
4831             break;
4832
4833 #ifdef PUTPIPE
4834           case SND_CMD:                 /* These take no args */
4835             if (nopush) {
4836                 printf("?Sorry, system command access is disabled\n");
4837                 x = -9;
4838                 goto xputx;
4839             }
4840 #ifdef PIPESEND
4841             else if (sndfilter) {
4842                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
4843                 x = -9;
4844                 goto xputx;
4845             }
4846 #endif /* PIPESEND */
4847             sw.hlpmsg = "Command, or switch"; /* Change help message */
4848             pv[n].ival = 1;             /* Just set the flag */
4849             pv[SND_ARR].ival = 0;
4850             break;
4851 #endif /* PUTPIPE */
4852
4853 #ifdef CKSYMLINK
4854           case SND_LNK:
4855             nolinks = 0;
4856             goto again;                 /* Because CMIFI params changed... */
4857           case SND_NLK:
4858             nolinks = 2;
4859             goto again;
4860 #endif /* CKSYMLINK */
4861
4862 #ifdef FTP_RESTART
4863           case SND_RES:                 /* /RECOVER (resend) */
4864             pv[SND_ARR].ival = 0;       /* fall thru on purpose... */
4865 #endif /* FTP_RESTART */
4866
4867           case SND_NOB:
4868           case SND_DEL:                 /* /DELETE */
4869           case SND_SHH:                 /* /QUIET */
4870           case SND_UPD:                 /* /UPDATE */
4871           case SND_SIM:                 /* /UPDATE */
4872           case SND_USN:                 /* /UNIQUE */
4873             pv[n].ival = 1;             /* Just set the flag */
4874             break;
4875
4876           case SND_REC:                 /* /RECURSIVE */
4877             recursive = 2;              /* Must be set before cmifi() */
4878             x_recurse = 1;
4879             goto again;                 /* Because CMIFI params changed... */
4880             break;
4881
4882 #ifdef UNIXOROSK
4883           case SND_DOT:                 /* /DOTFILES */
4884             matchdot = 1;
4885             break;
4886           case SND_NOD:                 /* /NODOTFILES */
4887             matchdot = 0;
4888             break;
4889 #endif /* UNIXOROSK */
4890
4891           case SND_ERR:                 /* /ERROR-ACTION */
4892             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
4893               goto xputx;
4894             pv[n].ival = x;
4895             break;
4896
4897           case SND_EXC:                 /* Excludes */
4898             if (!getval) break;
4899             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
4900                 if (x == -3) {
4901                     printf("?Pattern required\n");
4902                     x = -9;
4903                 }
4904                 goto xputx;
4905             }
4906             if (s) if (!*s) s = NULL;
4907             makestr(&(pv[n].sval),s);
4908             if (pv[n].sval)
4909               pv[n].ival = 1;
4910             break;
4911
4912           case SND_PRM:                 /* /PERMISSIONS */
4913             if (!getval)
4914               x = 1;
4915             else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
4916               goto xputx;
4917             pv[SND_PRM].ival = x;
4918             break;
4919
4920 #ifdef PIPESEND
4921           case SND_FLT:                 /* /FILTER */
4922             debug(F101,"ftp put /filter getval","",getval);
4923             if (!getval) break;
4924             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
4925                 if (x == -3)
4926                   s = "";
4927                 else
4928                   goto xputx;
4929             }
4930             if (*s) s = brstrip(s);
4931             y = strlen(s);
4932             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
4933                 if (s[x] != '\\') continue;
4934                 if (s[x+1] == 'v') break;
4935             }
4936             if (x == y) {
4937                 printf(
4938                 "?Filter must contain a replacement variable for filename.\n"
4939                        );
4940                 x = -9;
4941                 goto xputx;
4942             }
4943             if (s) if (!*s) s = NULL;
4944             makestr(&(pv[n].sval),s);
4945             if (pv[n].sval)
4946               pv[n].ival = 1;
4947             break;
4948 #endif /* PIPESEND */
4949
4950           case SND_NAM:                 /* /FILENAMES */
4951             if (!getval) break;
4952             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
4953               goto xputx;
4954             debug(F101,"ftp put /filenames","",x);
4955             pv[n].ival = x;
4956             break;
4957
4958           case SND_SMA:                 /* Smaller / larger than */
4959           case SND_LAR:
4960             if (!getval) break;
4961             if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0)
4962               goto xputx;
4963             pv[n].ival = y;
4964             break;
4965
4966           case SND_FIL:                 /* Name of file containing filenames */
4967             if (!getval) break;
4968             if ((x = cmifi("Name of file containing list of filenames",
4969                                "",&s,&y,xxstring)) < 0) {
4970                 if (x == -3) {
4971                     printf("?Filename required\n");
4972                     x = -9;
4973                 }
4974                 goto xputx;
4975             } else if (y && iswild(s)) {
4976                 printf("?Wildcards not allowed\n");
4977                 x = -9;
4978                 goto xputx;
4979             }
4980             if (s) if (!*s) s = NULL;
4981             makestr(&(pv[n].sval),s);
4982             if (pv[n].sval) {
4983                 pv[n].ival = 1;
4984                 pv[SND_ARR].ival = 0;
4985             } else {
4986                 pv[n].ival = 0;
4987             }
4988             mput = 0;
4989             break;
4990
4991           case SND_MOV:                 /* MOVE after */
4992           case SND_REN:                 /* RENAME after */
4993           case SND_SRN: {               /* SERVER-RENAME after */
4994               char * m = "";
4995               switch (n) {
4996                 case SND_MOV:
4997                   m = "device and/or directory for source file after sending";
4998                   break;
4999                 case SND_REN:
5000                   m = "new name for source file after sending";
5001                   break;
5002                 case SND_SRN:
5003                   m = "new name for destination file after sending";
5004                   break;
5005               }
5006               if (!getval) break;
5007               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
5008                   if (x == -3) {
5009                       printf("%s\n", n == SND_MOV ?
5010                              "?Destination required" :
5011                              "?New name required"
5012                              );
5013                       x = -9;
5014                   }
5015                   goto xputx;
5016               }
5017               if (s) if (!*s) s = NULL;
5018               makestr(&(pv[n].sval),s ? brstrip(s) : NULL);
5019               pv[n].ival = (pv[n].sval) ? 1 : 0;
5020               break;
5021           }
5022           case SND_STA:                 /* Starting position (= PSEND) */
5023             if (!getval) break;
5024             if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0)
5025               goto xputx;
5026             pv[n].ival = y;
5027             break;
5028
5029           case SND_TYP:                 /* /TYPE */
5030             if (!getval) break;
5031             if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0)
5032               goto xputx;
5033             pv[n].ival = (x == 2) ? -1 : x;
5034             break;
5035
5036 #ifndef NOCSETS
5037           case SND_CSL:                 /* Local character set */
5038           case SND_CSR:                 /* Remote (server) charset */
5039             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) {
5040                 return((x == -3) ? -2 : x);
5041             }
5042             if (n == SND_CSL)
5043               x_csl = x;
5044             else
5045               x_csr = x;
5046             x_xla = 1;                  /* Overrides global OFF setting */
5047             break;
5048
5049           case SND_XPA:                 /* Transparent */
5050             x_xla = 0;
5051             x_csr = -1;
5052             x_csl = -1;
5053             break;
5054 #endif /* NOCSETS */
5055         }
5056     }
5057 #ifdef PIPESEND
5058     if (pv[SND_RES].ival > 0) { /* /RECOVER */
5059         if (sndfilter || pv[SND_FLT].ival > 0) {
5060             printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n");
5061             x = -9;
5062             goto xputx;
5063         }
5064         if (sfttab[0] > 0 && sfttab[SFT_REST] == 0)
5065           printf("WARNING: Server says it doesn't support REST.\n");
5066     }
5067 #endif /* PIPESEND */
5068
5069     cmarg = "";
5070     cmarg2 = asnambuf;
5071     line[0] = NUL;
5072     s = line;
5073     wild = 0;
5074
5075     switch (cmresult.fcode) {           /* How did we get out of switch loop */
5076       case _CMIFI:                      /* Input filename */
5077         if (pv[SND_FIL].ival > 0) {
5078             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5079             x = -9;
5080             goto xputx;
5081         }
5082         ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */
5083         if (pv[SND_ARR].ival > 0)
5084           ckstrncpy(asnambuf,line,CKMAXPATH);
5085         else
5086           wild = cmresult.nresult;      /* Wild flag */
5087         debug(F111,"ftp put wild",line,wild);
5088         if (!wild && !recursive && !mput)
5089           nolinks = 0;
5090         break;
5091       case _CMFLD:                      /* Field */
5092         /* Only allowed with /COMMAND and /ARRAY */
5093         if (pv[SND_FIL].ival > 0) {
5094             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5095             x = -9;
5096             goto xputx;
5097         }
5098         /* For MPUT it's OK to have filespecs that don't match any files */
5099         if (mput)
5100           break;
5101         if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) {
5102 #ifdef CKROOT
5103             if (ckrooterr)
5104               printf("?Off limits: %s\n",cmresult.sresult);
5105             else
5106 #endif /* CKROOT */
5107               printf("?%s - \"%s\"\n",
5108                    iswild(cmresult.sresult) ?
5109                    "No files match" : "File not found",
5110                    cmresult.sresult
5111                    );
5112             x = -9;
5113             goto xputx;
5114         }
5115         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
5116         if (pv[SND_ARR].ival > 0)
5117           ckstrncpy(asnambuf,line,CKMAXPATH);
5118         break;
5119       case _CMCFM:                      /* Confirmation */
5120         confirmed = 1;
5121         break;
5122       default:
5123         printf("?Unexpected function code: %d\n",cmresult.fcode);
5124         x = -9;
5125         goto xputx;
5126     }
5127     debug(F110,"ftp put string",s,0);
5128     debug(F101,"ftp put confirmed","",confirmed);
5129
5130     /* Save and change protocol and transfer mode */
5131     /* Global values are restored in main parse loop */
5132
5133     g_displa = fdispla;
5134     if (ftp_dis > -1)
5135       fdispla = ftp_dis;
5136     g_skipbup = skipbup;
5137
5138     if (pv[SND_NOB].ival > -1) {        /* /NOBACKUP (skip backup file) */
5139         g_skipbup = skipbup;
5140         skipbup = 1;
5141     }
5142     if (pv[SND_TYP].ival > -1) {        /* /TYPE */
5143         xfiletype = pv[SND_TYP].ival;
5144         if (xfiletype == 2)
5145           xfiletype = -1;
5146     }
5147     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
5148         forcetype = 1;                  /* So skip file scan */
5149         ftp_typ = FTT_BIN;              /* Set binary */
5150     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
5151         forcetype = 1;
5152         ftp_typ = FTT_ASC;
5153     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
5154         forcetype = 1;
5155         ftp_typ = FTT_TEN;
5156     } else if (ftp_cmdlin && xfermode == XMODE_M) {
5157         forcetype = 1;
5158         ftp_typ = binary;
5159         g_ftp_typ = binary;
5160     }
5161
5162 #ifdef PIPESEND
5163     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
5164         debug(F110,"PUT /COMMAND before stripping",s,0);
5165         s = brstrip(s);
5166         debug(F110,"PUT /COMMAND after stripping",s,0);
5167         if (!*s) {
5168             printf("?Sorry, a command to send from is required\n");
5169             x = -9;
5170             goto xputx;
5171         }
5172         cmarg = s;
5173     }
5174 #endif /* PIPESEND */
5175
5176 /* Set up /MOVE and /RENAME */
5177
5178     if (pv[SND_DEL].ival > 0 &&
5179         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
5180         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
5181         x = -9;
5182         goto xputx;
5183     }
5184 #ifdef CK_TMPDIR
5185     if (pv[SND_MOV].ival > 0) {
5186         int len;
5187         char * p = pv[SND_MOV].sval;
5188         len = strlen(p);
5189         if (!isdir(p)) {                /* Check directory */
5190 #ifdef CK_MKDIR
5191             char * s = NULL;
5192             s = (char *)malloc(len + 4);
5193             if (s) {
5194                 strcpy(s,p);            /* safe */
5195 #ifdef datageneral
5196                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
5197 #else
5198                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
5199 #endif /* datageneral */
5200                 s[len++] = 'X';
5201                 s[len] = NUL;
5202 #ifdef NOMKDIR
5203                 x = -1;
5204 #else
5205                 x = zmkdir(s);
5206 #endif /* NOMKDIR */
5207                 free(s);
5208                 if (x < 0) {
5209                     printf("?Can't create \"%s\"\n",p);
5210                     x = -9;
5211                     goto xputx;
5212                 }
5213             }
5214 #else
5215             printf("?Directory \"%s\" not found\n",p);
5216             x = -9;
5217             goto xputx;
5218 #endif /* CK_MKDIR */
5219         }
5220         makestr(&snd_move,p);
5221     }
5222 #endif /* CK_TMPDIR */
5223
5224     if (pv[SND_REN].ival > 0) {         /* /RENAME */
5225         char * p = pv[SND_REN].sval;
5226         if (!p) p = "";
5227         if (!*p) {
5228             printf("?New name required for /RENAME\n");
5229             x = -9;
5230             goto xputx;
5231         }
5232         p = brstrip(p);
5233 #ifndef NOSPL
5234     /* If name given is wild, rename string must contain variables */
5235         if (wild) {
5236             char * s = tmpbuf;
5237             x = TMPBUFSIZ;
5238             zzstring(p,&s,&x);
5239             if (!strcmp(tmpbuf,p)) {
5240                 printf(
5241     "?/RENAME for file group must contain variables such as \\v(filename)\n"
5242                        );
5243                 x = -9;
5244                 goto xputx;
5245             }
5246         }
5247 #endif /* NOSPL */
5248         makestr(&snd_rename,p);
5249         debug(F110,"FTP snd_rename",snd_rename,0);
5250     }
5251     if (pv[SND_SRN].ival > 0) {         /* /SERVER-RENAME */
5252         char * p = pv[SND_SRN].sval;
5253         if (!p) p = "";
5254         if (!*p) {
5255             printf("?New name required for /SERVER-RENAME\n");
5256             x = -9;
5257             goto xputx;
5258         }
5259         p = brstrip(p);
5260 #ifndef NOSPL
5261         if (wild) {
5262             char * s = tmpbuf;
5263             x = TMPBUFSIZ;
5264             zzstring(p,&s,&x);
5265             if (!strcmp(tmpbuf,p)) {
5266                 printf(
5267 "?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n"
5268                        );
5269                 x = -9;
5270                 goto xputx;
5271             }
5272         }
5273 #endif /* NOSPL */
5274         makestr(&srv_renam,p);
5275         debug(F110,"ftp put srv_renam",srv_renam,0);
5276     }
5277     if (!confirmed) {                   /* CR not typed yet, get more fields */
5278         char * lp;
5279         if (mput) {                     /* MPUT or MMOVE */
5280             nfils = 0;                  /* We already have the first one */
5281 #ifndef NOMSEND
5282             if (cmresult.fcode == _CMIFI) {
5283                 /* First filespec is valid */
5284                 msfiles[nfils++] = line;    /* Store pointer */
5285                 lp = line + (int)strlen(line) + 1; /* Point past it */
5286                 debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1);
5287             } else {
5288                 /* First filespec matches no files */
5289                 debug(F110,"ftp put mput skipping first filespec",
5290                       cmresult.sresult,
5291                       0
5292                       );
5293                 lp = line;
5294             }
5295             /* Parse a filespec, a "field", or confirmation */
5296
5297             cmfdbi(&sf,                 /* 1st FDB - file to send */
5298                    _CMIFI,              /* fcode */
5299                    "",                  /* hlpmsg */
5300                    "",                  /* default */
5301                    "",                  /* addtl string data */
5302                    nolinks | x_recurse, /* addtl numeric data 1 */
5303                    0,                   /* dirflg 0 means "not dirs only" */
5304                    xxstring,
5305                    NULL,
5306                    &fl
5307                    );
5308             cmfdbi(&fl,                 /* 2nd FDB - local filespec */
5309                    _CMFLD,              /* fcode */
5310                    "",                  /* hlpmsg */
5311                    "",                  /* default */
5312                    "",                  /* addtl string data */
5313                    0,                   /* addtl numeric data 1 */
5314                    0,                   /* addtl numeric data 2 */
5315                    xxstring,
5316                    NULL,
5317                    &cm
5318                    );
5319             cmfdbi(&cm,                 /* 3rd FDB - Confirmation */
5320                    _CMCFM,              /* fcode */
5321                    "",
5322                    "",
5323                    "",
5324                    0,
5325                    0,
5326                    NULL,
5327                    NULL,
5328                    NULL
5329                    );
5330
5331             while (!confirmed) {        /* Get more filenames */
5332                 x = cmfdb(&sf);         /* Parse something */
5333                 debug(F101,"ftp put cmfdb B","",x);
5334                 debug(F101,"ftp put fcode B","",cmresult.fcode);
5335                 if (x < 0)              /* Error */
5336                   goto xputx;           /* or reparse needed */
5337                 switch (cmresult.fcode) {
5338                   case _CMCFM:          /* End of command */
5339                     confirmed++;
5340                     if (nfils < 1) {
5341                         debug(F100,"ftp put mput no files match","",0);
5342                         printf("?No files match MPUT list\n");
5343                         x = -9;
5344                         goto xputx;
5345                     }
5346                     break;
5347                   case _CMFLD:          /* No match */
5348                     debug(F110,"ftp put mput skipping",cmresult.sresult,0);
5349                     continue;
5350                   case _CMIFI:          /* Good match */
5351                     s = cmresult.sresult;
5352                     msfiles[nfils++] = lp; /* Got one, count, point to it, */
5353                     p = lp;                /* remember pointer, */
5354                     while ((*lp++ = *s++)) /* and copy it into buffer */
5355                       if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */
5356                           printf("?MPUT list too long\n");
5357                           line[0] = NUL;
5358                           x = -9;
5359                           goto xputx;
5360                       }
5361                     debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1);
5362                     if (nfils == 1)     /* Take care of \v(filespec) */
5363                       fspec[0] = NUL;
5364 #ifdef ZFNQFP
5365                     zfnqfp(p,TMPBUFSIZ,tmpbuf);
5366                     p = tmpbuf;
5367 #endif /* ZFNQFP */
5368                     if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) {
5369                         strcat(fspec,p);    /* safe */
5370                         strcat(fspec," ");  /* safe */
5371                     } else {
5372 #ifdef COMMENT
5373                         printf("WARNING - \\v(filespec) buffer overflow\n");
5374 #else
5375                         debug(F101,"doxput filespec buffer overflow","",0);
5376 #endif /* COMMENT */
5377                     }
5378                 }
5379             }
5380 #endif /* NOMSEND */
5381         } else {                        /* Regular PUT */
5382             nfils = -1;
5383             if ((x = cmtxt(wild ?
5384 "\nOptional as-name template containing replacement variables \
5385 like \\v(filename)" :
5386                            "Optional name to send it with",
5387                            "",&p,NULL)) < 0)
5388               goto xputx;
5389
5390             if (p) if (!*p) p = NULL;
5391             p = brstrip(p);
5392
5393             if (p && *p) {
5394                 makestr(&(pv[SND_ASN].sval),p);
5395                 if (pv[SND_ASN].sval)
5396                   pv[SND_ASN].ival = 1;
5397                 debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0);
5398             }
5399         }
5400     }
5401     /* Set cmarg2 from as-name, however we got it. */
5402
5403     CHECKCONN();
5404     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
5405         char * p;
5406         p = brstrip(pv[SND_ASN].sval);
5407         ckstrncpy(asnambuf,p,CKMAXPATH+1);
5408     }
5409     debug(F110,"ftp put asnambuf",asnambuf,0);
5410
5411     if (pv[SND_FIL].ival > 0) {
5412         if (confirmed) {
5413             if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
5414                 debug(F110,"ftp put can't open",pv[SND_FIL].sval,0);
5415                 printf("?Failure to open %s\n",pv[SND_FIL].sval);
5416                 x = -9;
5417                 goto xputx;
5418             }
5419             makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */
5420             debug(F110,"ftp PUT /LISTFILE opened",filefile,0);
5421             wild = 1;
5422         }
5423     }
5424     if (confirmed && !line[0] && !filefile) {
5425 #ifndef NOMSEND
5426         if (filehead) {                 /* OK if we have a SEND-LIST */
5427             nfils = filesinlist;
5428             sndsrc = nfils;             /* Like MSEND */
5429             addlist = 1;                /* But using a different list... */
5430             filenext = filehead;
5431             goto doput;
5432         }
5433 #endif /* NOMSEND */
5434         printf("?Filename required but not given\n");
5435         x = -9;
5436         goto xputx;
5437     }
5438 #ifndef NOMSEND
5439     addlist = 0;                        /* Don't use SEND-LIST. */
5440 #endif /* NOMSEND */
5441
5442     if (mput) {                         /* MPUT (rather than PUT) */
5443 #ifndef NOMSEND
5444         cmlist = msfiles;               /* List of filespecs */
5445         sndsrc = nfils;                 /* rather filespec and as-name */
5446 #endif /* NOMSEND */
5447         pipesend = 0;
5448     } else if (filefile) {              /* File contains list of filenames */
5449         s = "";
5450         cmarg = "";
5451         line[0] = NUL;
5452         nfils = 1;
5453         sndsrc = 1;
5454
5455     } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) {
5456
5457         /* Not MSEND, MMOVE, /LIST, or /ARRAY */
5458         nfils = sndsrc = -1;
5459         if (!wild) {
5460             y = zchki(s);
5461             if (y < 0) {
5462                 printf("?Read access denied - \"%s\"\n", s);
5463                 x = -9;
5464                 goto xputx;
5465             }
5466         }
5467         if (s != line)                  /* We might already have done this. */
5468           ckstrncpy(line,s,LINBUFSIZ);  /* Copy of string just parsed. */
5469 #ifdef DEBUG
5470         else
5471           debug(F110,"doxput line=s",line,0);
5472 #endif /* DEBUG */
5473         cmarg = line;                   /* File to send */
5474     }
5475 #ifndef NOMSEND
5476     zfnqfp(cmarg,fspeclen,fspec);       /* Get full name */
5477 #endif /* NOMSEND */
5478
5479     if (!mput) {                        /* For all but MPUT... */
5480 #ifdef PIPESEND
5481         if (pv[SND_CMD].ival > 0)       /* /COMMAND sets pipesend flag */
5482           pipesend = 1;
5483         debug(F101,"ftp put /COMMAND pipesend","",pipesend);
5484         if (pipesend && filefile) {
5485             printf("?Invalid switch combination\n");
5486             x = -9;
5487             goto xputx;
5488         }
5489 #endif /* PIPESEND */
5490
5491 #ifndef NOSPL
5492     /* If as-name given and filespec is wild, as-name must contain variables */
5493         if ((wild || mput) && asnambuf[0]) {
5494             char * s = tmpbuf;
5495             x = TMPBUFSIZ;
5496             zzstring(asnambuf,&s,&x);
5497             if (!strcmp(tmpbuf,asnambuf)) {
5498                 printf(
5499     "?As-name for file group must contain variables such as \\v(filename)\n"
5500                        );
5501                 x = -9;
5502                 goto xputx;
5503             }
5504         }
5505 #endif /* NOSPL */
5506     }
5507
5508   doput:
5509
5510     if (pv[SND_SHH].ival > 0) {         /* SEND /QUIET... */
5511         fdispla = 0;
5512         debug(F101,"ftp put display","",fdispla);
5513     } else {
5514         displa = 1;
5515         if (ftp_deb)
5516           fdispla = XYFD_B;
5517     }
5518
5519 #ifdef PUTARRAY                         /* SEND /ARRAY... */
5520     if (pv[SND_ARR].ival > 0) {
5521         if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */
5522         if (range[0] == -1)             /* If low end of range not specified */
5523           range[0] = 1;                 /* default to 1 */
5524         if (range[1] == -1)             /* If high not specified */
5525           range[1] = a_dim[arrayx];     /* default to size of array */
5526         if ((range[0] < 0) ||           /* Check range */
5527             (range[0] > a_dim[arrayx]) ||
5528             (range[1] < range[0]) ||
5529             (range[1] > a_dim[arrayx])) {
5530             printf("?Bad array range - [%d:%d]\n",range[0],range[1]);
5531             x = -9;
5532             goto xputx;
5533         }
5534         sndarray = ap;                  /* Array pointer */
5535         sndxin = arrayx;                /* Array index */
5536         sndxlo = range[0];              /* Array range */
5537         sndxhi = range[1];
5538         sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE);
5539         if (!asnambuf[0])
5540           ckstrncpy(asnambuf,sndxnam,CKMAXPATH);
5541         cmarg = "";
5542     }
5543 #endif /* PUTARRAY */
5544
5545     moving = 0;
5546
5547     if (pv[SND_ARR].ival < 1) {         /* File selection & disposition... */
5548         if (pv[SND_DEL].ival > 0)       /* /DELETE was specified */
5549           moving = 1;
5550         if (pv[SND_AFT].ival > 0)       /* Copy SEND criteria */
5551           ckstrncpy(sndafter,pv[SND_AFT].sval,19);
5552         if (pv[SND_BEF].ival > 0)
5553           ckstrncpy(sndbefore,pv[SND_BEF].sval,19);
5554         if (pv[SND_NAF].ival > 0)
5555           ckstrncpy(sndnafter,pv[SND_NAF].sval,19);
5556         if (pv[SND_NBE].ival > 0)
5557           ckstrncpy(sndnbefore,pv[SND_NBE].sval,19);
5558         if (pv[SND_EXC].ival > 0)
5559           makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT);
5560         if (pv[SND_SMA].ival > -1)
5561           sndsmaller = pv[SND_SMA].ival;
5562         if (pv[SND_LAR].ival > -1)
5563           sndlarger = pv[SND_LAR].ival;
5564         if (pv[SND_NAM].ival > -1)
5565           x_cnv = pv[SND_NAM].ival;
5566         if (pv[SND_USN].ival > -1)
5567           x_usn = pv[SND_USN].ival;
5568         if (pv[SND_ERR].ival > -1)
5569           puterror = pv[SND_ERR].ival;
5570
5571 #ifdef DOUPDATE
5572         if (pv[SND_UPD].ival > 0) {
5573             if (x_usn) {
5574                 printf("?Conflicting switches: /UPDATE /UNIQUE\n");
5575                 x = -9;
5576                 goto xputx;
5577             }
5578             putflags |= PUT_UPD;
5579             ftp_dates |= 2;
5580         }
5581 #ifdef COMMENT
5582         /* This works but it's useless, maybe dangerous */
5583         if (pv[SND_DIF].ival > 0) {
5584             if (x_usn) {
5585                 printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n");
5586                 x = -9;
5587                 goto xputx;
5588             }
5589             putflags |= PUT_DIF;
5590             ftp_dates |= 2;
5591         }
5592 #endif /* COMMENT */
5593 #endif /* DOUPDATE */
5594
5595         if (pv[SND_SIM].ival > 0)
5596           putflags |= PUT_SIM;
5597
5598         if (pv[SND_PRM].ival > -1) {
5599 #ifdef UNIX
5600             if (x_usn) {
5601                 printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n");
5602                 x = -9;
5603                 goto xputx;
5604             }
5605             x_prm = pv[SND_PRM].ival;
5606 #else /* UNIX */
5607             printf("?/PERMISSIONS switch is not supported\n");
5608 #endif /* UNIX */
5609         }
5610 #ifdef FTP_RESTART
5611         if (pv[SND_RES].ival > 0) {
5612             if (!sizeok) {
5613                 printf("?PUT /RESTART can't be used because SIZE disabled.\n");
5614                 x = -9;
5615                 goto xputx;
5616             }
5617             if (x_usn || putflags) {
5618                 printf("?Conflicting switches: /RECOVER %s\n",
5619                        x_usn && putflags ? "/UNIQUE /UPDATE" :
5620                        (x_usn ? "/UNIQUE" : "/UPDATE")
5621                        );
5622                 x = -9;
5623                 goto xputx;
5624             }
5625 #ifndef NOCSETS
5626             if (x_xla &&
5627                 (x_csl == FC_UCS2 ||
5628                  x_csl == FC_UTF8 ||
5629                  x_csr == FC_UCS2 ||
5630                  x_csr == FC_UTF8)) {
5631                 printf("?/RECOVER can not be used with Unicode translation\n");
5632                 x = -9;
5633                 goto xputx;
5634             }
5635 #endif /* NOCSETS */
5636             putflags = PUT_RES;
5637         }
5638 #endif /* FTP_RESTART */
5639     }
5640     debug(F101,"ftp PUT restart","",putflags & PUT_RES);
5641     debug(F101,"ftp PUT update","",putflags & PUT_UPD);
5642
5643 #ifdef PIPESEND
5644     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
5645         if (!pv[SND_FLT].sval) {
5646             sndfilter = NULL;
5647         } else {
5648             sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1);
5649             if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */
5650         }
5651         debug(F110,"ftp put /FILTER", sndfilter, 0);
5652     }
5653     if (sndfilter || pipesend)          /* No /UPDATE or /RESTART */
5654       if (putflags)                     /* with pipes or filters */
5655         putflags = 0;
5656 #endif /* PIPESEND */
5657
5658     tfc = 0L;                           /* Initialize stats and counters */
5659     filcnt = 0;
5660     pktnum = 0;
5661     spackets = 0L;
5662
5663     if (wild)                           /* (is this necessary?) */
5664       cx = FTP_MPU;
5665
5666     t0 = gmstimer();                    /* Record starting time */
5667
5668     done = 0;                           /* Loop control */
5669     cancelgroup = 0;
5670
5671     cdlevel = 0;
5672     cdsimlvl = 0;
5673     while (!done && !cancelgroup) {     /* Loop for all files */
5674                                         /* or until canceled. */
5675 #ifdef FTP_PROXY
5676         /*
5677            If we are using a proxy, we don't use the local file list;
5678            instead we use the list on the remote machine which we want
5679            sent to someone else, and we use remglob() to get the names.
5680            But in that case we shouldn't even be executing this routine;
5681            see ftp_mput().
5682         */
5683 #endif /* FTP_PROXY */
5684
5685         cancelfile = 0;
5686         x = gnfile();                   /* Get next file from list(s) */
5687
5688         if (x == 0)                     /* (see gnfile() comments...) */
5689           x = gnferror;
5690         debug(F111,"FTP PUT gnfile",filnam,x);
5691
5692         switch (x) {
5693           case 1:                       /* File to send */
5694             s2 = asnambuf;
5695 #ifndef NOSPL
5696             if (asnambuf[0]) {          /* As-name */
5697                 int n; char *p;         /* to be evaluated... */
5698                 n = TMPBUFSIZ;
5699                 p = tmpbuf;
5700                 zzstring(asnambuf,&p,&n);
5701                 s2 = tmpbuf;
5702                 debug(F110,"ftp put asname",s2,0);
5703             }
5704 #endif /* NOSPL */
5705             rc = putfile(cx,            /* Function (PUT, APPEND) */
5706                     filnam, s2,         /* Name to send, as-name */
5707                     forcetype, moving,  /* Parameters from switches... */
5708                     snd_move, snd_rename, srv_renam,
5709                     x_cnv, x_usn, xfiletype, x_prm,
5710 #ifndef NOCSETS
5711                     x_csl, (!x_xla ? -1 : x_csr),
5712 #else
5713                     -1, -1,
5714 #endif /* NOCSETS */
5715                     putflags
5716                     );
5717             debug(F111,"ftp put putfile rc",filnam,rc);
5718             debug(F111,"ftp put putfile cancelfile",filnam,cancelfile);
5719             debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup);
5720             if (rc > -1) {
5721                 good++;
5722                 status = 1;
5723             }
5724             if (cancelfile)
5725               continue;
5726             if (rc < 0) {
5727                 ftp_fai++;
5728                 if (puterror) {
5729                     status = 0;
5730                     printf("?Fatal upload error: %s\n",filnam);
5731                     done++;
5732                 }
5733             }
5734             continue;
5735           case 0:                       /* No more files, done */
5736             done++;
5737             continue;
5738           case -1:
5739             printf("?%s: file not found - \"%s\"\n",
5740                    puterror ? "Fatal" : "Warning",
5741                    filnam
5742                    );
5743             if (puterror) {
5744                 status = 0;
5745                 done++;
5746                 break;
5747             }
5748             continue;
5749           case -2:
5750             if (puterror) {
5751                 printf("?Fatal: file not found - \"%s\"\n", filnam);
5752                 status = 0;
5753                 done++;
5754                 break;
5755             }
5756             continue;                   /* Not readable, keep going */
5757           case -3:
5758             if (puterror) {
5759                 printf("?Fatal: Read access denied - \"%s\"\n", filnam);
5760                 status = 0;
5761                 done++;
5762                 break;
5763             }
5764             printf("?Warning access denied - \"%s\"\n", filnam);
5765             continue;
5766 #ifdef COMMENT
5767           case -4:                      /* Canceled */
5768             done++;
5769             break;
5770 #endif /* COMMENT */
5771           case -5:
5772             printf("?Too many files match\n");
5773             done++;
5774             break;
5775           case -6:
5776             if (good < 1)
5777               printf("?No files selected\n");
5778             done++;
5779             break;
5780           default:
5781             printf("?getnextfile() - unknown failure\n");
5782             done++;
5783         }
5784     }
5785     if (cdlevel > 0) {
5786         while (cdlevel--) {
5787             if (cdsimlvl) {
5788                 cdsimlvl--;
5789             } else if (!doftpcdup())
5790               break;
5791         }
5792     }
5793     if (status > 0) {
5794         if (cancelgroup)
5795           status = 0;
5796         else if (cancelfile && good < 1)
5797           status = 0;
5798     }
5799     success = status;
5800     x = success;
5801
5802   xputx:
5803     if (x > -1) {
5804 #ifdef GFTIMER
5805         t1 = gmstimer();                /* End time */
5806         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
5807         if (!sec) sec = 0.001;
5808         fptsecs = sec;
5809 #else
5810         sec = (t1 - t0) / 1000;
5811         if (!sec) sec = 1;
5812 #endif /* GFTIMER */
5813         tfcps = (long) (tfc / sec);
5814         tsecs = (int)sec;
5815         lastxfer = W_FTP|W_SEND;
5816         xferstat = success;
5817         if (dpyactive)
5818           ftscreen(SCR_TC,0,0L,"");
5819     }
5820     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
5821         if (pv[i].sval)
5822           free(pv[i].sval);
5823     }
5824     ftreset();                          /* Undo switch effects */
5825     dpyactive = 0;
5826     return(x);
5827 }
5828
5829
5830 static char ** mgetlist = NULL;         /* For MGET */
5831 static int mgetn = 0, mgetx = 0;
5832 static char xtmpbuf[4096];
5833
5834 /*
5835   c m d l i n g e t
5836
5837   Get files specified by -g command-line option.
5838   File list is set up in cmlist[] by ckuusy.c; nfils is length of list.
5839 */
5840 int
5841 cmdlinget(stay) int stay; {
5842     int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0;
5843     int lcs = -1, rcs = -1, xlate = 0;
5844     int first = 1;
5845     int mget = 1;
5846     int nc;
5847     char * s, * s2, * s3;
5848     ULONG t0, t1;                       /* Times for stats */
5849 #ifdef GFTIMER
5850     CKFLOAT sec;
5851 #else
5852     int sec = 0;
5853 #endif /* GFTIMER */
5854
5855     if (quiet) {                        /* -q really means quiet */
5856         displa = 0;
5857         fdispla = 0;
5858     } else {
5859         displa = 1;
5860         fdispla = XYFD_B;
5861     }
5862     testing = 0;
5863     dpyactive = 0;
5864     out2screen = 0;
5865     what = W_FTP|W_RECV;
5866     mgetmethod = 0;
5867     mgetforced = 0;
5868
5869     havetype = 0;
5870     havesize = -1L;
5871     makestr(&havemdtm,NULL);
5872
5873     if (ftp_fnc < 0)
5874       ftp_fnc = fncact;
5875
5876 #ifndef NOSPL
5877     cmd_quoting = 0;
5878 #endif /* NOSPL */
5879     debug(F101,"ftp cmdlinget nfils","",nfils);
5880
5881     if (ftp_cnv == CNV_AUTO) {          /* Name conversion is auto */
5882         if (alike) {                    /* If server & client are alike */
5883             nc = 0;                     /* no conversion */
5884         } else {                        /* If they are different */
5885             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
5886               nc = -1;                  /* only minimal conversions needed */
5887             else                        /* otherwise */
5888               nc = 1;                   /* full conversion */
5889         }
5890     } else                              /* Not auto - do what user said */
5891       nc = ftp_cnv;
5892
5893     if (nfils < 1)
5894       doexit(BAD_EXIT,-1);
5895
5896     t0 = gmstimer();                    /* Starting time for this batch */
5897
5898 #ifndef NOCSETS
5899     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
5900         lcs = ftp_csl;                  /* Local charset */
5901         if (lcs < 0) lcs = fcharset;
5902         if (lcs < 0) xlate = 0;
5903     }
5904     if (xlate) {                        /* Still ON? */
5905         rcs = ftp_csx;                  /* Remote (Server) charset */
5906         if (rcs < 0) rcs = ftp_csr;
5907         if (rcs < 0) xlate = 0;
5908     }
5909 #endif /* NOCSETS */
5910     /*
5911       If we have only one file and it is a directory, then we ask for a
5912       listing of its contents, rather than retrieving the directory file
5913       itself.  This is what (e.g.) Netscape does.
5914     */
5915     if (nfils == 1) {
5916         if (doftpcwd((char *)cmlist[mgetx],-1)) {
5917             /* If we can CD to it, it must be a directory */
5918             if (recursive) {
5919                 cmlist[mgetx] = "*";
5920             } else {
5921                 status =
5922                   (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0);
5923                 done = 1;
5924             }
5925         }
5926     }
5927 /*
5928   The following is to work around UNIX servers which, when given a command
5929   like "NLST path/blah" (not wild) returns the basename without the path.
5930 */
5931     if (!done && servertype == SYS_UNIX && nfils == 1) {
5932         mget = iswild(cmlist[mgetx]);
5933     }
5934     if (!mget && !done) {               /* Invoked by command-line FTP URL */
5935         if (ftp_deb)
5936           printf("DOING GET...\n");
5937         done++;
5938         cancelfile = 0;                 /* This file not canceled yet */
5939         s = cmlist[mgetx];
5940         rc = 0;                         /* Initial return code */
5941         fsize = -1L;
5942         if (sizeok) {
5943             x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */
5944             if (x == REPLY_COMPLETE)
5945               fsize = atol(&ftp_reply_str[4]);
5946         }
5947         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
5948         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
5949
5950         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
5951         s2 = tmpbuf;
5952
5953         /* If local file already exists, take collision action */
5954
5955         x = zchki(s2);
5956         if (x > -1) {
5957             switch (ftp_fnc) {
5958               case XYFX_A:              /* Append */
5959                 append = 1;
5960                 break;
5961               case XYFX_R:              /* Rename */
5962               case XYFX_B: {            /* Backup */
5963                   char * p = NULL;
5964                   int x = -1;
5965                   znewn(s2,&p);         /* Make unique name */
5966                   debug(F110,"ftp cmdlinget znewn",p,0);
5967                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
5968                       x = zrename(s2,p);
5969                       debug(F111,"ftp cmdlinget backup zrename",p,x);
5970                   } else {              /* Rename incoming file */
5971                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
5972                       s2 = tmpbuf;
5973                       debug(F111,"ftp cmdlinget rename incoming",p,x);
5974                   }
5975                   if (x < 0) {
5976                       printf("?Backup/Rename failed\n");
5977                       return(success = 0);
5978                   }
5979                   break;
5980               }
5981               case XYFX_D:              /* Discard */
5982                 ftscreen(SCR_FN,'F',0L,s);
5983                 ftscreen(SCR_ST,ST_SKIP,SKP_NAM,s);
5984                 tlog(F100," refused: name","",0);
5985                 debug(F110,"ftp cmdlinget skip name",s2,0);
5986                 goto xclget;
5987
5988               case XYFX_X:              /* Overwrite */
5989               case XYFX_U:              /* Update (already handled above) */
5990               case XYFX_M:              /* ditto */
5991                 break;
5992             }
5993         }
5994         rc = getfile(s,                 /* Remote name */
5995                      s2,                /* Local name */
5996                      0,                 /* Recover/Restart */
5997                      append,            /* Append */
5998                      NULL,              /* Pipename */
5999                      0,                 /* Translate charsets */
6000                      -1,                /* File charset (none) */
6001                      -1                 /* Server charset (none) */
6002                      );
6003         debug(F111,"ftp cmdlinget rc",s,rc);
6004         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6005         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6006
6007         if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */
6008             rc = getfile(&s[1],         /* Remote name without leading '/' */
6009                          s2,            /* Local name */
6010                          0,             /* Recover/Restart */
6011                          append,        /* Append */
6012                          NULL,          /* Pipename */
6013                          0,             /* Translate charsets */
6014                          -1,            /* File charset (none) */
6015                          -1             /* Server charset (none) */
6016                          );
6017         if (rc > -1) {
6018             good++;
6019             status = 1;
6020         }
6021         if (cancelfile)
6022           goto xclget;
6023         if (rc < 0) {
6024             ftp_fai++;
6025             if (geterror) {
6026                 status = 0;
6027                 done++;
6028             }
6029         }
6030     }
6031     if (ftp_deb && !done)
6032       printf("DOING MGET...\n");
6033     while (!done && !cancelgroup) {
6034         cancelfile = 0;                 /* This file not canceled yet */
6035         s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6036         if (!s) s = "";
6037         if (!*s) {
6038             first = 1;
6039             mgetx++;
6040             if (mgetx < nfils)
6041               s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6042             else
6043               s = NULL;
6044             debug(F111,"ftp cmdlinget remote_files B",s,0);
6045             if (!s) {
6046                 done = 1;
6047                 break;
6048             }
6049         }
6050         /*
6051           The semantics of NLST are ill-defined.  Suppose we have just sent
6052           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
6053           /path/bar, etc.  But some send back only foo and bar, and subsequent
6054           RETR commands based on the pathless names are not going to work.
6055         */
6056         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
6057             if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) {
6058                 int len, left = 4096;
6059                 char * tmp = xtmpbuf;
6060                 len = s3 - cmlist[mgetx] + 1;
6061                 ckstrncpy(tmp,cmlist[mgetx],left);
6062                 tmp += len;
6063                 left -= len;
6064                 ckstrncpy(tmp,s,left);
6065                 s = xtmpbuf;
6066                 debug(F111,"ftp cmdlinget remote_files X",s,0);
6067             }
6068         }
6069         first = 0;                      /* Not first any more */
6070
6071         debug(F111,"ftp cmdlinget havetype",s,havetype);
6072         if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */
6073             debug(F110,"ftp cmdlinget not-a-file",s,0);
6074             continue;
6075         }
6076         rc = 0;                         /* Initial return code */
6077         if (havesize > -1L) {           /* Already have file size? */
6078             fsize = havesize;
6079         } else {                        /* No - must ask server */
6080             /*
6081               Prior to sending the NLST command we necessarily put the
6082               server into ASCII mode.  We must now put it back into the
6083               the requested mode so the upcoming SIZE command returns
6084               right kind of size; this is especially important for
6085               GET /RECOVER; otherwise the server returns the "ASCII" size
6086               of the file, rather than its true size.
6087             */
6088             changetype(ftp_typ,0);      /* Change to requested type */
6089             fsize = -1L;
6090             if (sizeok) {
6091                 x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm);
6092                 if (x == REPLY_COMPLETE)
6093                   fsize = atol(&ftp_reply_str[4]);
6094             }
6095         }
6096         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6097         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6098
6099         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6100         s2 = tmpbuf;
6101
6102         /* If local file already exists, take collision action */
6103
6104         x = zchki(s2);
6105         if (x > -1) {
6106             switch (ftp_fnc) {
6107               case XYFX_A:              /* Append */
6108                 append = 1;
6109                 break;
6110               case XYFX_R:              /* Rename */
6111               case XYFX_B: {            /* Backup */
6112                   char * p = NULL;
6113                   int x = -1;
6114                   znewn(s2,&p);         /* Make unique name */
6115                   debug(F110,"ftp cmdlinget znewn",p,0);
6116                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6117                       x = zrename(s2,p);
6118                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6119                   } else {              /* Rename incoming file */
6120                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6121                       s2 = tmpbuf;
6122                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6123                   }
6124                   if (x < 0) {
6125                       printf("?Backup/Rename failed\n");
6126                       return(success = 0);
6127                   }
6128                   break;
6129               }
6130               case XYFX_D:      /* Discard */
6131                 ftscreen(SCR_FN,'F',0L,s);
6132                 ftscreen(SCR_ST,ST_SKIP,SKP_NAM,s);
6133                 tlog(F100," refused: name","",0);
6134                 debug(F110,"ftp cmdlinget skip name",s2,0);
6135                 continue;
6136               case XYFX_X:              /* Overwrite */
6137               case XYFX_U:              /* Update (already handled above) */
6138               case XYFX_M:              /* ditto */
6139                 break;
6140             }
6141         }
6142                                         /* ^^^ ADD CHARSET STUFF HERE ^^^ */
6143         rc = getfile(s,                 /* Remote name */
6144                      s2,                /* Local name */
6145                      0,                 /* Recover/Restart */
6146                      append,            /* Append */
6147                      NULL,              /* Pipename */
6148                      0,                 /* Translate charsets */
6149                      -1,                /* File charset (none) */
6150                      -1                 /* Server charset (none) */
6151                      );
6152         debug(F111,"ftp cmdlinget rc",s,rc);
6153         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6154         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6155
6156         if (rc > -1) {
6157             good++;
6158             status = 1;
6159         }
6160         if (cancelfile)
6161           continue;
6162         if (rc < 0) {
6163             ftp_fai++;
6164             if (geterror) {
6165                 status = 0;
6166                 done++;
6167             }
6168         }
6169     }
6170
6171   xclget:
6172     if (cancelgroup)
6173       mlsreset();
6174     if (status > 0) {
6175         if (cancelgroup)
6176           status = 0;
6177         else if (cancelfile && good < 1)
6178           status = 0;
6179     }
6180     success = status;
6181
6182 #ifdef GFTIMER
6183     t1 = gmstimer();                    /* End time */
6184     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6185     if (!sec) sec = 0.001;
6186     fptsecs = sec;
6187 #else
6188     sec = (t1 - t0) / 1000;
6189     if (!sec) sec = 1;
6190 #endif /* GFTIMER */
6191
6192     tfcps = (long) (tfc / sec);
6193     tsecs = (int)sec;
6194     lastxfer = W_FTP|W_RECV;
6195     xferstat = success;
6196     if (dpyactive)
6197       ftscreen(SCR_TC,0,0L,"");
6198     if (!stay)
6199       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
6200     return(success);
6201 }
6202
6203 /*  d o f t p g e t  --  Parse and execute GET, MGET, MDELETE, ...  */
6204
6205 /*
6206   Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use
6207   zstrdat() to convert to UTC-based time_t.  But it doesn't make sense from
6208   the user-interface perspective, since the server's directory listings show
6209   its own local times and since we don't know what timezone it's in, there's
6210   no way to reconcile our local times with the server's.
6211 */
6212 int
6213 doftpget(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
6214     struct FDB fl, sw, cm;
6215     int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0;
6216     int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0;
6217     int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0;
6218     int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0;
6219     int moving = 0, deleting = 0, toscreen = 0, haspath = 0;
6220     int gotsize = 0;
6221     int matchdot = 0;
6222     long getlarger = -1, getsmaller = -1;
6223     char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL;
6224     char * src = "", * local = "";
6225     char * pat = "";
6226
6227     int x_csl = -1, x_csr = -1;         /* Local and remote charsets */
6228     int x_xla = 0;
6229     char c;                             /* Worker char */
6230     ULONG t0 = 0L, t1;                  /* Times for stats */
6231 #ifdef GFTIMER
6232     CKFLOAT sec;
6233 #else
6234     int sec = 0;
6235 #endif /* GFTIMER */
6236
6237     struct stringint {                  /* Temporary array for switch values */
6238         char * sval;
6239         int ival;
6240     } pv[SND_MAX+1];
6241
6242     success = 0;                        /* Assume failure */
6243     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
6244     restart = 0;                        /* No restart yet */
6245     out2screen = 0;                     /* No TO-SCREEN switch given yet */
6246     mgetmethod = 0;                     /* No NLST or MLSD switch yet */
6247     mgetforced = 0;
6248
6249     g_displa = fdispla;
6250     if (ftp_dis > -1)
6251       fdispla = ftp_dis;
6252
6253     x_cnv = ftp_cnv;                    /* Filename conversion */
6254     if (x_cnv == CNV_AUTO) {            /* Name conversion is auto */
6255         if (alike) {                    /* If server & client are alike */
6256             x_cnv = 0;                  /* no conversion */
6257         } else {                        /* If they are different */
6258             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6259               x_cnv = -1;               /* only minimal conversions needed */
6260             else                        /* otherwise */
6261               x_cnv = 1;                /* full conversion */
6262         }
6263     } else                              /* Not auto - do what user said */
6264       x_cnv = ftp_cnv;
6265
6266     x_prm = ftp_prm;                    /* Permissions */
6267     if (x_prm == SET_AUTO)              /* Permissions AUTO */
6268       x_prm = alike;
6269
6270 #ifndef NOCSETS
6271     x_csr = ftp_csr;                    /* Inherit global server charset */
6272     x_csl = ftp_csl;                    /* Inherit global local charset */
6273     if (x_csl < 0)                      /* If none, use current */
6274       x_csl = fcharset;                 /* file character-set. */
6275     x_xla = ftp_xla;                    /* Translation On/Off */
6276 #endif /* NOCSETS */
6277
6278     geterror = ftp_err;                 /* Inherit global error action. */
6279     asnambuf[0] = NUL;                  /* No as-name yet. */
6280     pipesave = pipesend;
6281     pipesend = 0;
6282
6283     havetype = 0;
6284     havesize = -1L;
6285     makestr(&havemdtm,NULL);
6286
6287     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
6288         ftp_typ = g_ftp_typ;
6289         /* g_ftp_typ = -1; */
6290     }
6291     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
6292         pv[i].sval = NULL;              /* to null pointers */
6293         pv[i].ival = -1;                /* and -1 int values */
6294     }
6295     zclose(ZMFILE);                     /* In case it was left open */
6296
6297     x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */
6298
6299     if (fp_nml) {                       /* Reset /NAMELIST */
6300         if (fp_nml != stdout)
6301           fclose(fp_nml);
6302         fp_nml = NULL;
6303     }
6304     makestr(&ftp_nml,NULL);
6305
6306     /* Initialize list of remote filespecs */
6307
6308     if (!mgetlist) {
6309         mgetlist = (char **)malloc(MGETMAX * sizeof(char *));
6310         if (!mgetlist) {
6311             printf("?Memory allocation failure - MGET list\n");
6312             return(-9);
6313         }
6314         for (i = 0; i < MGETMAX; i++)
6315           mgetlist[i] = NULL;
6316     }
6317     mgetn = 0;                          /* Number of mget arguments */
6318     mgetx = 0;                          /* Current arg */
6319
6320     if (who == 0) {                     /* Called with unprefixed command */
6321         if (cx == XXGET || cx == XXREGET || cx == XXRETR)
6322           getone++;
6323         switch (cx) {
6324           case XXREGET: pv[SND_RES].ival = 1; break;
6325           case XXRETR:  pv[SND_DEL].ival = 1; break;
6326           case XXGET:
6327           case XXMGET:  mget++; break;
6328         }
6329     } else {                            /* FTP command */
6330         if (cx == FTP_GET || cx == FTP_RGE)
6331           getone++;
6332         switch (cx) {
6333           case FTP_DEL:                 /* (fall thru on purpose) */
6334           case FTP_MDE: mdel++;         /* (ditto) */
6335           case FTP_GET:                 /* (ditto) */
6336           case FTP_MGE: mget++; break;
6337           case FTP_RGE: pv[SND_RES].ival = 1; break;
6338         }
6339     }
6340     cmfdbi(&sw,                         /* First FDB - command switches */
6341            _CMKEY,                      /* fcode */
6342            "Remote filename;\n or switch", /* hlpmsg */
6343            "",                          /* default */
6344            "",                          /* addtl string data */
6345            mdel ? ndelswi : ngetswi,    /* addtl numeric data 1: tbl size */
6346            4,                           /* addtl numeric data 2: 4 = cmswi */
6347            xxstring,                    /* Processing function */
6348            mdel ? delswi : getswi,      /* Keyword table */
6349            &fl                          /* Pointer to next FDB */
6350            );
6351     cmfdbi(&fl,                         /* 2nd FDB - remote filename */
6352            _CMFLD,                      /* fcode */
6353            "",                          /* hlpmsg */
6354            "",                          /* default */
6355            "",                          /* addtl string data */
6356            0,                           /* addtl numeric data 1 */
6357            0,                           /* addtl numeric data 2 */
6358            xxstring,
6359            NULL,
6360            &cm
6361            );
6362     cmfdbi(&cm,                         /* 3rd FDB - Confirmation */
6363            _CMCFM,                      /* fcode */
6364            "",                          /* hlpmsg */
6365            "",                          /* default */
6366            "",                          /* addtl string data */
6367            0,                           /* addtl numeric data 1 */
6368            0,                           /* addtl numeric data 2 */
6369            NULL,
6370            NULL,
6371            NULL
6372            );
6373
6374     while (1) {                         /* Parse 0 or more switches */
6375         x = cmfdb(&sw);                 /* Parse something */
6376         debug(F101,"ftp get cmfdb","",x);
6377         if (x < 0)                      /* Error */
6378           goto xgetx;                   /* or reparse needed */
6379         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
6380           break;
6381         c = cmgbrk();                   /* Get break character */
6382         getval = (c == ':' || c == '='); /* to see how they ended the switch */
6383         if (getval && !(cmresult.kflags & CM_ARG)) {
6384             printf("?This switch does not take arguments\n");
6385             x = -9;
6386             goto xgetx;
6387         }
6388         n = cmresult.nresult;           /* Numeric result = switch value */
6389         debug(F101,"ftp get switch","",n);
6390
6391         if (!getval && (cmgkwflgs() & CM_ARG)) {
6392             printf("?This switch requires an argument\n");
6393             x = -9;
6394             goto xgetx;
6395         }
6396         switch (n) {                    /* Process the switch */
6397           case SND_ASN:                 /* /AS-NAME: */
6398             debug(F101,"ftp get /as-name getval","",getval);
6399             if (!getval) break;
6400             if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) {
6401                 if (x == -3) {
6402                     printf("?name required\n");
6403                     x = -9;
6404                 }
6405                 goto xgetx;
6406             }
6407             s = brstrip(s);
6408             if (!*s) s = NULL;
6409             makestr(&(pv[n].sval),s);
6410             pv[n].ival = 1;
6411             break;
6412
6413           case SND_BIN:                 /* /BINARY */
6414           case SND_TXT:                 /* /TEXT or /ASCII */
6415           case SND_TEN:                 /* /TENEX */
6416             pv[SND_BIN].ival = 0;
6417             pv[SND_TXT].ival = 0;
6418             pv[SND_TEN].ival = 0;
6419             pv[n].ival = 1;
6420             break;
6421
6422 #ifdef PUTPIPE
6423           case SND_CMD:                 /* These take no args */
6424             if (nopush) {
6425                 printf("?Sorry, system command access is disabled\n");
6426                 x = -9;
6427                 goto xgetx;
6428             }
6429 #ifdef PIPESEND
6430             else if (rcvfilter) {
6431                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
6432                 x = -9;
6433                 goto xgetx;
6434             }
6435 #endif /* PIPESEND */
6436             sw.hlpmsg = "Command, or switch"; /* Change help message */
6437             pv[n].ival = 1;             /* Just set the flag */
6438             pv[SND_ARR].ival = 0;
6439             break;
6440 #endif /* PUTPIPE */
6441
6442           case SND_SHH:                 /* /QUIET */
6443           case SND_RES:                 /* /RECOVER (reget) */
6444           case SND_NOB:                 /* /NOBACKUPFILES */
6445           case SND_DEL:                 /* /DELETE */
6446           case SND_UPD:                 /* /UPDATE */
6447           case SND_USN:                 /* /UNIQUE */
6448           case SND_NOD:                 /* /NODOTFILES */
6449           case SND_REC:                 /* /RECOVER */
6450           case SND_MAI:                 /* /TO-SCREEN */
6451             pv[n].ival = 1;             /* Just set the flag */
6452             break;
6453
6454           case SND_DIF:                 /* /DATES-DIFFER */
6455             pv[SND_COL].ival = XYFX_M;  /* Now it's a collision option */
6456             pv[n].ival = 1;
6457             break;
6458
6459           case SND_COL:                 /* /COLLISION: */
6460             if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
6461               goto xgetx;
6462             if (x == XYFX_M)
6463               pv[SND_DIF].ival = 1;     /* (phase this out) */
6464             pv[n].ival = x;             /* this should be sufficient */
6465             break;
6466
6467           case SND_ERR:                 /* /ERROR-ACTION */
6468             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
6469               goto xgetx;
6470             pv[n].ival = x;
6471             break;
6472
6473           case SND_EXC:                 /* Exception list */
6474             if (!getval) break;
6475             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
6476                 if (x == -3) {
6477                     printf("?Pattern required\n");
6478                     x = -9;
6479                 }
6480                 goto xgetx;
6481             }
6482             if (s) if (!*s) s = NULL;
6483             makestr(&(pv[n].sval),s);
6484             if (pv[n].sval)
6485               pv[n].ival = 1;
6486             break;
6487
6488 #ifdef PIPESEND
6489           case SND_FLT:
6490             debug(F101,"ftp get /filter getval","",getval);
6491             if (!getval) break;
6492             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
6493                 if (x == -3)
6494                   s = "";
6495                 else
6496                   goto xgetx;
6497             }
6498             s = brstrip(s);
6499             if (pv[SND_MAI].ival < 1) {
6500                 y = strlen(s);
6501                 /* Make sure they included "\v(...)" */
6502                 for (x = 0; x < y; x++) {
6503                     if (s[x] != '\\') continue;
6504                     if (s[x+1] == 'v') break;
6505                 }
6506                 if (x == y) {
6507                     printf(
6508                 "?Filter must contain a replacement variable for filename.\n"
6509                            );
6510                     x = -9;
6511                     goto xgetx;
6512                 }
6513             }
6514             if (*s) {
6515                 pv[n].ival = 1;
6516                 makestr(&(pv[n].sval),s);
6517             } else {
6518                 pv[n].ival = 0;
6519                 makestr(&(pv[n].sval),NULL);
6520             }
6521             break;
6522 #endif /* PIPESEND */
6523
6524           case SND_NAM:
6525             if (!getval) break;
6526             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
6527               goto xgetx;
6528             debug(F101,"ftp get /filenames","",x);
6529             pv[n].ival = x;
6530             break;
6531
6532           case SND_SMA:                 /* Smaller / larger than */
6533           case SND_LAR:
6534             if (!getval) break;
6535             if ((x = cmnum("Size in bytes","0",10,&y,xxstring)) < 0)
6536               goto xgetx;
6537             pv[n].ival = y;
6538             break;
6539
6540           case SND_FIL:                 /* Name of file containing filnames */
6541             if (!getval) break;
6542             if ((x = cmifi("Name of file containing list of filenames",
6543                                "",&s,&y,xxstring)) < 0) {
6544                 if (x == -3) {
6545                     printf("?Filename required\n");
6546                     x = -9;
6547                 }
6548                 goto xgetx;
6549             } else if (y && iswild(s)) {
6550                 printf("?Wildcards not allowed BBB\n");
6551                 x = -9;
6552                 goto xgetx;
6553             }
6554             if (s) if (!*s) s = NULL;
6555             makestr(&(pv[n].sval),s);
6556             if (pv[n].sval)
6557               pv[n].ival = 1;
6558             break;
6559
6560           case SND_MOV:                 /* MOVE after */
6561           case SND_REN:                 /* RENAME after */
6562           case SND_SRN: {               /* SERVER-RENAME */
6563               char * m = "";
6564               switch (n) {
6565                 case SND_MOV:
6566                   m =
6567                    "Device and/or directory for incoming file after reception";
6568                   break;
6569                 case SND_REN:
6570                   m = "New name for incoming file after reception";
6571                   break;
6572                 case SND_SRN:
6573                   m = "New name for source file on server after reception";
6574                   break;
6575               }
6576               if (!getval) break;
6577               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
6578                   if (x == -3) {
6579                       printf("%s\n", n == SND_MOV ?
6580                              "?Destination required" :
6581                              "?New name required"
6582                              );
6583                       x = -9;
6584                   }
6585                   goto xgetx;
6586               }
6587               makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6588               pv[n].ival = (pv[n].sval) ? 1 : 0;
6589               break;
6590           }
6591 #ifndef NOCSETS
6592           case SND_CSL:                 /* Local character set */
6593           case SND_CSR:                 /* Remote (server) charset */
6594             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0)
6595               return((x == -3) ? -2 : x);
6596             if (n == SND_CSL)
6597               x_csl = x;
6598             else
6599               x_csr = x;
6600             x_xla = 1;                  /* Overrides global OFF setting */
6601             break;
6602
6603           case SND_XPA:                 /* Transparent */
6604             x_xla =  0;
6605             x_csr = -1;
6606             x_csl = -1;
6607             break;
6608 #endif /* NOCSETS */
6609
6610           case SND_NML:
6611             if ((x = cmofi("Local filename","-",&s,xxstring)) < 0)
6612               goto xgetx;
6613             makestr(&ftp_nml,s);
6614             break;
6615
6616           case SND_PAT:                 /* /PATTERN: */
6617             if (!getval) break;
6618             if ((x = cmfld("Pattern","*", &s, xxstring)) < 0)
6619               goto xgetx;
6620             makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6621             pv[n].ival = (pv[n].sval) ? 1 : 0;
6622             break;
6623
6624           case SND_NLS:                 /* /NLST */
6625             pv[n].ival = 1;             /* Use NLST */
6626             pv[SND_MLS].ival = 0;       /* Don't use MLSD */
6627             break;
6628
6629           case SND_MLS:                 /* /MLSD */
6630             pv[n].ival = 1;             /* Use MLSD */
6631             pv[SND_NLS].ival = 0;       /* Don't use NLST */
6632             break;
6633
6634           default:                      /* /AFTER, /PERMISSIONS, etc... */
6635             printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf);
6636             x = -9;
6637             goto xgetx;
6638         }
6639     }
6640     line[0] = NUL;
6641     cmarg = line;
6642     cmarg2 = asnambuf;
6643     s = line;
6644 /*
6645   For GET, we want to parse an optional as-name, like with PUT.
6646   For MGET, we must parse a list of names, and then send NLST or MLSD
6647   commands for each name separately.
6648 */
6649     switch (cmresult.fcode) {           /* How did we get out of switch loop */
6650       case _CMFLD:                      /* Field */
6651         if (!getone) {
6652             s = brstrip(cmresult.sresult);
6653             makestr(&(mgetlist[mgetn++]),s);
6654             while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) {
6655                 if (x < 0)
6656                   goto xgetx;
6657                 makestr(&(mgetlist[mgetn++]),brstrip(s));
6658                 if (mgetn >= MGETMAX) {
6659                     printf("?Too many items in MGET list\n");
6660                     goto xgetx;
6661                 }
6662             }
6663             if ((x = cmcfm()) < 0)
6664               goto xgetx;
6665         } else {
6666             s = brstrip(cmresult.sresult);
6667             ckstrncpy(line,s,LINBUFSIZ);
6668             if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0)
6669               if (x != -3)
6670                 goto xgetx;
6671             s = brstrip(s);
6672             ckstrncpy(asnambuf,s,CKMAXPATH+1);
6673             if ((x = cmcfm()) < 0)
6674               goto xgetx;
6675         }
6676         break;
6677       case _CMCFM:                      /* Confirmation */
6678         break;
6679       default:
6680         printf("?Unexpected function code: %d\n",cmresult.fcode);
6681         x = -9;
6682         goto xgetx;
6683     }
6684     if (pv[SND_REC].ival > 0)           /* /RECURSIVE */
6685       recursive = 2;
6686
6687     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
6688         forcetype = 1;                  /* So skip the name-pattern match */
6689         ftp_typ = XYFT_B;               /* Set binary */
6690     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
6691         forcetype = 1;
6692         ftp_typ = XYFT_T;
6693     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
6694         forcetype = 1;
6695         ftp_typ = FTT_TEN;
6696     } else if (ftp_cmdlin && xfermode == XMODE_M) {
6697         forcetype = 1;
6698         ftp_typ = binary;
6699         g_ftp_typ = binary;
6700     }
6701     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
6702         char * p;
6703         p = brstrip(pv[SND_ASN].sval);  /* As-name */
6704         ckstrncpy(asnambuf,p,CKMAXPATH+1);
6705     }
6706     debug(F110,"ftp get asnambuf",asnambuf,0);
6707
6708 #ifdef PIPESEND
6709     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
6710         char * p;
6711         p = asnambuf;
6712         debug(F110,"GET /COMMAND before stripping",p,0);
6713         p = brstrip(p);
6714         debug(F110,"GET /COMMAND after stripping",p,0);
6715         if (!*p) {
6716             printf("?Sorry, a command to write to is required\n");
6717             x = -9;
6718             goto xgetx;
6719         }
6720         pipename = p;
6721         pipesend = 1;
6722     }
6723 #endif /* PIPESEND */
6724
6725 /* Set up /MOVE and /RENAME */
6726
6727     if (pv[SND_DEL].ival > 0 &&
6728         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
6729         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
6730         x = -9;
6731         goto xgetx;
6732     }
6733 #ifdef CK_TMPDIR
6734     if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) {
6735         int len;
6736         char * p = pv[SND_MOV].sval;
6737         len = strlen(p);
6738         if (!isdir(p)) {                /* Check directory */
6739 #ifdef CK_MKDIR
6740             char * s = NULL;
6741             s = (char *)malloc(len + 4);
6742             if (s) {
6743                 strcpy(s,p);            /* safe */
6744 #ifdef datageneral
6745                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
6746 #else
6747                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
6748 #endif /* datageneral */
6749                 s[len++] = 'X';
6750                 s[len] = NUL;
6751 #ifdef NOMKDIR
6752                 x = -1;
6753 #else
6754                 x = zmkdir(s);
6755 #endif /* NOMKDIR */
6756                 free(s);
6757                 if (x < 0) {
6758                     printf("?Can't create \"%s\"\n",p);
6759                     x = -9;
6760                     goto xgetx;
6761                 }
6762             }
6763 #else
6764             printf("?Directory \"%s\" not found\n",p);
6765             x = -9;
6766             goto xgetx;
6767 #endif /* CK_MKDIR */
6768         }
6769         makestr(&rcv_move,p);
6770         moving = 1;
6771     }
6772 #endif /* CK_TMPDIR */
6773
6774     if (pv[SND_REN].ival > 0) {         /* /RENAME */
6775         char * p = pv[SND_REN].sval;
6776         if (!p) p = "";
6777         if (!*p) {
6778             printf("?New name required for /RENAME\n");
6779             x = -9;
6780             goto xgetx;
6781         }
6782         p = brstrip(p);
6783 #ifndef NOSPL
6784     /* If name given is wild, rename string must contain variables */
6785         if (mget && !getone) {
6786             char * s = tmpbuf;
6787             x = TMPBUFSIZ;
6788             zzstring(p,&s,&x);
6789             if (!strcmp(tmpbuf,p)) {
6790                 printf(
6791     "?/RENAME for file group must contain variables such as \\v(filename)\n"
6792                        );
6793                 x = -9;
6794                 goto xgetx;
6795             }
6796         }
6797 #endif /* NOSPL */
6798         renaming = 1;
6799         makestr(&rcv_rename,p);
6800         debug(F110,"FTP rcv_rename",rcv_rename,0);
6801     }
6802     if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) {
6803         printf("?Filename required but not given\n");
6804         x = -9;
6805         goto xgetx;
6806     } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) {
6807         printf("?You can't give both /LISTFILE and a remote filename\n");
6808         x = -9;
6809         goto xgetx;
6810     }
6811     CHECKCONN();                        /* Check connection */
6812
6813     if (pv[SND_COL].ival > -1)
6814       x_fnc = pv[SND_COL].ival;
6815
6816 #ifndef NOSPL
6817     /* If as-name given for MGET, as-name must contain variables */
6818     if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) {
6819         char * s = tmpbuf;
6820         x = TMPBUFSIZ;
6821         zzstring(asnambuf,&s,&x);
6822         if (!strcmp(tmpbuf,asnambuf)) {
6823             printf(
6824     "?As-name for MGET must contain variables such as \\v(filename)\n"
6825                    );
6826             x = -9;
6827             goto xgetx;
6828         }
6829     }
6830 #endif /* NOSPL */
6831
6832 /* doget: */
6833
6834     if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */
6835         fdispla = 0;
6836     } else {
6837         displa = 1;
6838         if (mdel || ftp_deb)
6839           fdispla = XYFD_B;
6840     }
6841     deleting = 0;
6842     if (pv[SND_DEL].ival > 0)           /* /DELETE was specified */
6843       deleting = 1;
6844     if (pv[SND_EXC].ival > 0)
6845       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
6846     if (pv[SND_SMA].ival > -1)
6847       getsmaller = pv[SND_SMA].ival;
6848     if (pv[SND_LAR].ival > -1)
6849       getlarger = pv[SND_LAR].ival;
6850     if (pv[SND_NAM].ival > -1)
6851       x_cnv = pv[SND_NAM].ival;
6852     if (pv[SND_ERR].ival > -1)
6853       geterror = pv[SND_ERR].ival;
6854     if (pv[SND_MAI].ival > -1)
6855       toscreen = 1;
6856
6857     if (pv[SND_NLS].ival > 0) {         /* Force NLST or MLSD? */
6858         mgetmethod = SND_NLS;
6859         mgetforced = 1;
6860     } else if (pv[SND_MLS].ival > 0) {
6861         mgetmethod = SND_MLS;
6862         mgetforced = 1;
6863     }
6864
6865 #ifdef FTP_RESTART
6866     if (pv[SND_RES].ival > 0) {
6867         if (!ftp_typ) {
6868             printf("?Sorry, GET /RECOVER requires binary mode\n");
6869             x = -9;
6870             goto xgetx;
6871 #ifdef COMMENT
6872         /* Not true - the fact that the initial REST fails does not mean */
6873         /* it will fail here.  */
6874         } else if (!okrestart) {
6875             printf("WARNING: Server might not support restart...\n");
6876 #endif /* COMMENT */
6877         }
6878         restart = 1;
6879     }
6880 #endif /* FTP_RESTART */
6881
6882 #ifdef PIPESEND
6883     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
6884         if (pipesend) {
6885             printf("?Switch conflict: /FILTER and /COMMAND\n");
6886             x = -9;
6887             goto xgetx;
6888         }
6889         makestr(&rcvfilter,pv[SND_FLT].sval);
6890         debug(F110,"ftp get /FILTER", rcvfilter, 0);
6891     }
6892     if (rcvfilter || pipesend) {        /* /RESTART */
6893 #ifdef FTP_RESTART
6894         if (restart) {                  /* with pipes or filters */
6895             printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n");
6896             x = -9;
6897             goto xgetx;
6898         }
6899 #endif /* FTP_RESTART */
6900         if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) {
6901             printf(
6902                 "?Switch conflict: /FILTER or /COMMAND and Date Checking\n");
6903             x = -9;
6904             goto xgetx;
6905         }
6906     }
6907 #endif /* PIPESEND */
6908
6909     tfc = 0L;                           /* Initialize stats and counters */
6910     filcnt = 0;
6911     pktnum = 0;
6912     rpackets = 0L;
6913
6914     if (pv[SND_FIL].ival > 0) {
6915         if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
6916             debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno);
6917             printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval);
6918             x = -9;
6919             goto xgetx;
6920         }
6921         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */
6922             zclose(ZMFILE);                       /* Failed */
6923             debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0);
6924             printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval);
6925             x = -9;
6926             goto xgetx;
6927         }
6928         listfile = 1;
6929         debug(F110,"ftp get listfile first",tmpbuf,0);
6930         makestr(&(mgetlist[0]),tmpbuf);
6931     }
6932     t0 = gmstimer();                    /* Record starting time */
6933
6934     updating = 0;                       /* Checking dates? */
6935     if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U))
6936       updating = 1;
6937     if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M)
6938       updating = 2;
6939     if (updating)                       /* These switches force FTP DATES ON */
6940       ftp_dates |= 2;
6941
6942     what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */
6943
6944     cancelgroup = 0;                    /* Group not canceled yet */
6945     if (!(xfermode == XMODE_A && patterns && get_auto && !forcetype))
6946       changetype(ftp_typ,0);            /* Change to requested type */
6947     binary = ftp_typ;                   /* For file-transfer display */
6948     first = 1;                          /* For MGET list */
6949     done = 0;                           /* Loop control */
6950
6951 #ifdef CK_TMPDIR
6952     if (dldir && !f_tmpdir) {           /* If they have a download directory */
6953         if ((s = zgtdir())) {           /* Get current directory */
6954             if (zchdir(dldir)) {        /* Change to download directory */
6955                 ckstrncpy(savdir,s,TMPDIRLEN);
6956                 f_tmpdir = 1;           /* Remember that we did this */
6957             }
6958         }
6959     }
6960 #endif /* CK_TMPDIR */
6961
6962     if (ftp_nml) {                      /* /NAMELIST */
6963         debug(F110,"ftp GET ftp_nml",ftp_nml,0);
6964         if (ftp_nml[0] == '-' && ftp_nml[1] == 0)
6965           fp_nml = stdout;
6966         else
6967           fp_nml = fopen(ftp_nml, "wb");
6968         if (!fp_nml) {
6969             printf("?%s: %s\n",ftp_nml,ck_errstr());
6970             goto xgetx;
6971         }
6972     }
6973     while (!done && !cancelgroup) {     /* Loop for all files */
6974                                         /* or until canceled. */
6975 #ifdef FTP_PROXY
6976         /* do something here if proxy */
6977 #endif /* FTP_PROXY */
6978
6979         rs_len = 0L;                    /* REGET position */
6980         cancelfile = 0;                 /* This file not canceled yet */
6981         haspath = 0;                    /* Recalculate this each time thru */
6982
6983         if (getone) {                   /* GET */
6984             char * p;
6985             s = line;
6986             src = line;                 /* Server name */
6987             done = 1;
6988             debug(F111,"ftp get file",s,0);
6989         } else if (mget) {              /* MGET */
6990             src = mgetlist[mgetx];
6991             debug(F111,"ftp mget remote_files A",src,first);
6992             s = (char *)remote_files(first,
6993                                      (CHAR *)mgetlist[mgetx],
6994                                      (CHAR *)pv[SND_PAT].sval,
6995                                      0
6996                                      );
6997             debug(F110,"ftp mget remote_files B",s,0);
6998             if (!s) s = "";
6999             if (!*s) {
7000                 first = 1;
7001                 if (listfile) {         /* Names from listfile */
7002                   again:
7003                     tmpbuf[0] = NUL;
7004                     while (!tmpbuf[0]) {
7005                         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) {
7006                             zclose(ZMFILE);
7007                             debug(F110,"ftp get listfile EOF",
7008                                   pv[SND_FIL].sval,0);
7009                             makestr(&(mgetlist[0]),NULL);
7010                             s = NULL;
7011                             done = 1;
7012                             break;
7013                         }
7014                     }
7015                     if (done)
7016                       continue;
7017
7018                     makestr(&(mgetlist[0]),tmpbuf);
7019                     debug(F110,"ftp get listfile next",tmpbuf,0);
7020                     s = (char *)remote_files(first,
7021                                              (CHAR *)mgetlist[0],
7022                                              (CHAR *)pv[SND_PAT].sval,
7023                                              0
7024                                              );
7025                     debug(F110,"ftp mget remote_files C",s,0);
7026                     if (!s) {
7027                         ftscreen(SCR_FN,'F',0L,s);
7028                         ftscreen(SCR_ST,ST_MSG,0L,"File not found");
7029                         tlog(F110,"ftp get file not found:",s,0);
7030                         goto again;
7031                     }
7032                 } else {                /* Names from command line */
7033                     mgetx++;
7034                     if (mgetx < mgetn)
7035                       s = (char *)remote_files(first,
7036                                                (CHAR *)mgetlist[mgetx],
7037                                                (CHAR *)pv[SND_PAT].sval,
7038                                                0
7039                                                );
7040                     else
7041                       s = NULL;
7042                     if (!s) mgetx++;
7043                     debug(F111,"ftp mget remote_files D",s,mgetx);
7044                 }
7045                 if (!s) {
7046                     if (!first || mgetx >= mgetn) {
7047                         done = 1;
7048                         break;
7049                     } else if (geterror) {
7050                         status = 0;
7051                         done = 1;
7052                         break;
7053                     } else {
7054                         continue;
7055                     }
7056                 }
7057             }
7058         }
7059         debug(F111,"ftp mget remote_files E",s,0);
7060         /*
7061           The semantics of NLST are ill-defined.  Suppose we have just sent
7062           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
7063           /path/bar, etc.  But some send back only foo and bar, and subsequent
7064           RETR commands based on the pathless names are not going to work.
7065         */
7066         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
7067             char * s3;
7068             if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) {
7069                 int len, left = 4096;
7070                 char * tmp = xtmpbuf;
7071                 len = s3 - mgetlist[mgetx] + 1;
7072                 ckstrncpy(tmp,mgetlist[mgetx],left);
7073                 tmp += len;
7074                 left -= len;
7075                 ckstrncpy(tmp,s,left);
7076                 s = xtmpbuf;
7077                 debug(F111,"ftp mget remote_files F",s,0);
7078             }
7079         }
7080         first = 0;
7081         skipthis = 0;                   /* File selection... */
7082         msg = "";
7083         nam = s;                        /* Filename (without path) */
7084         rc = 0;                         /* Initial return code */
7085         s2 = "";
7086
7087         if (!getone && !skipthis) {     /* For MGET and MDELETE... */
7088             char c, * p = s;
7089             int srvpath = 0;
7090             int usrpath = 0;
7091             int i, k = 0;
7092
7093             debug(F111,"ftp mget havetype",s,havetype);
7094             if (havetype > 0 && havetype != FTYP_FILE) {
7095                 /* Server says it's not file... */
7096                 debug(F110,"ftp mget not-a-file",s,0);
7097                 continue;
7098             }
7099 /*
7100   Explanation: Some ftp servers (such as wu-ftpd) return a recursive list.
7101   But if the client did not ask for a recursive list, we have to ignore any
7102   server files that include a pathname that extends beyond any path that
7103   was included in the user's request.
7104
7105   User's filespec is blah or path/blah (or other non-UNIX syntax).  We need to
7106   get the user's path segment.  Then, for each incoming file, if it begins
7107   with the same path segment, we must strip it (point past it).
7108 */
7109             src = mgetlist[mgetx];      /* In case it moved! */
7110             if (src) {
7111                 for (i = 0; src[i]; i++) { /* Find rightmost path separator */
7112                     if (ispathsep(src[i])) /* in user's pathname */
7113                       k = i + 1;
7114                 }
7115             } else {
7116                 src = "";
7117             }
7118             usrpath = k;                /* User path segment length */
7119             debug(F111,"ftp get usrpath",src,usrpath);
7120
7121             p = s;                      /* Server filename */
7122             while ((c = *p++)) {        /* Look for path in server filename */
7123                 if (ispathsep(c)) {
7124                     /* haspath++; */
7125                     nam = p;            /* Pathless name (for ckmatch) */
7126                     srvpath = p - s;    /* Server path segment length */
7127                 }
7128             }
7129             debug(F111,"ftp get srvpath",s,srvpath);
7130
7131             if (usrpath == 0) {
7132 /*
7133   Here we handle the case where the user said "mget foo" where foo is a
7134   directory name, and the server is sending back names like "foo/file1",
7135   "foo/file2", etc.  This is a nasty trick but it's necessary because the
7136   user can't compensate by typing "mget foo/" because then the server is
7137   likely to send back "foo//file1, foo//file2" etc, and we still won't
7138   get a match...
7139 */
7140                 int srclen = 0, srvlen = 0;
7141                 if (src) srclen = strlen(src);
7142                 if (s) srvlen = strlen(s);
7143                 if (src && (srvlen > srclen)) {
7144                     if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) {
7145                         char * tmpsrc = NULL;
7146                         tmpsrc = (char *)malloc(srclen + 2);
7147                         strncpy(tmpsrc,src,srclen);
7148                         tmpsrc[srclen] = s[srclen];
7149                         tmpsrc[srclen+1] = NUL;
7150                         free(mgetlist[mgetx]);
7151                         mgetlist[mgetx] = tmpsrc;
7152                         tmpsrc = NULL;
7153                         src = mgetlist[mgetx];
7154                         usrpath = srclen+1;
7155                     }                         
7156                 }
7157             }
7158 /*
7159   If as-name not given and server filename includes path that matches
7160   the pathname from the user's file specification, we must trim the common
7161   path prefix from the server's name when constructing the local name.
7162 */
7163             if (src &&                  /* Wed Sep 25 17:27:48 2002 */
7164                 !asnambuf[0] &&
7165                 !recursive &&           /* Thu Sep 19 16:11:59 2002 */
7166                 (srvpath > 0) &&
7167                 !strncmp(src,s,usrpath)) {
7168                 s2 = s + usrpath;       /* Local name skips past remote path */
7169             }
7170 #ifdef COMMENT
7171             /* This doesn't work if the path prefix contains wildcards! */
7172             haspath = (srvpath > usrpath);
7173 #else
7174             {                           /* Count path segments instead */
7175                 int x1 = 0, x2 = 0;
7176                 char *p;
7177                 for (p = s; *p; p++)
7178                   if (ispathsep(*p)) x1++;
7179                 for (p = src; *p; p++) {
7180                     if (ispathsep(*p)) x2++;
7181                 }
7182                 haspath = recursive ? x1 || x2 : x1 > x2;
7183                 debug(F111,"ftp get server path segments",s,x1);
7184                 debug(F111,"ftp get user   path segments",src,x2);
7185             }
7186
7187 #endif /* COMMENT */
7188             debug(F111,"ftp get haspath",s+usrpath,haspath);
7189
7190             if (haspath) {              /* Server file has path segments? */
7191                 if (!recursive) {       /* [M]GET /RECURSIVE? */
7192 /*
7193   We did not ask for a recursive listing, but the server is sending us one
7194   anyway (as wu-ftpd is wont to do).  We get here if the current filename
7195   includes a path segment beyond any path segment we asked for in our
7196   non-recursive [M]GET command.  We MUST skip this file.
7197 */
7198                     debug(F111,"ftp get skipping because of path",s,0);
7199                     continue;
7200                 }
7201             }
7202         } else if (getone && !skipthis) { /* GET (not MGET) */
7203             char * p = nam;
7204             while ((c = *p++)) {        /* Handle path in local name */
7205                 if (ispathsep(c)) {
7206                     if (recursive) {    /* If recursive, keep it */
7207                         haspath = 1;
7208                         break;
7209                     } else {            /* Otherwise lose it. */
7210                       nam = p;
7211                     }
7212                 }
7213             }
7214             s2 = nam;
7215         }
7216         if (!*nam)                      /* Name without path */
7217           nam = s;
7218
7219         if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */
7220             if (nam[0] == '.')
7221               continue;
7222         }
7223         if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */
7224             int xx;
7225             for (i = 0; i < NSNDEXCEPT; i++) {
7226                 if (!rcvexcept[i]) {
7227                     break;
7228                 }
7229                 xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1);
7230                 debug(F111,"ftp mget /except match",rcvexcept[i],xx);
7231                 if (xx) {
7232                     tlog(F100," refused: exception list","",0);
7233                     msg = "Refused: Exception List";
7234                     skipthis++;
7235                     break;
7236                 }
7237             }
7238         }
7239         if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */
7240             if (ckmatch(
7241 #ifdef CKREGEX
7242                         "*.~[0-9]*~"
7243 #else
7244                         "*.~*~"
7245 #endif /* CKREGEX */
7246                         ,nam,0,1) > 0)
7247               continue;
7248         }
7249         if (!x_xla) {                   /* If translation is off */
7250             x_csl = -2;                 /* unset the charsets */
7251             x_csr = -2;
7252         }
7253         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
7254         if (!*s2)                       /* Local name */
7255           s2 = asnambuf;                /* As-name */
7256
7257         if (!*s2)                       /* Sat Nov 16 19:19:39 2002 */
7258           s2 = recursive ? s : nam;     /* Fri Jan 10 13:15:19 2003 */
7259
7260         debug(F110,"ftp get filnam  ",s,0);
7261         debug(F110,"ftp get asname A",s2,0);
7262
7263         /* Receiving to real file */
7264         if (!pipesend &&
7265 #ifdef PIPESEND
7266             !rcvfilter &&
7267 #endif /* PIPESEND */
7268             !toscreen) {
7269 #ifndef NOSPL
7270             /* Do this here so we can decide whether to skip */
7271             if (cmd_quoting && !skipthis && asnambuf[0]) {
7272                 int n; char *p;
7273                 n = TMPBUFSIZ;
7274                 p = tmpbuf;
7275                 zzstring(asnambuf,&p,&n);
7276                 s2 = tmpbuf;
7277                 debug(F111,"ftp get asname B",s2,updating);
7278             }
7279 #endif /* NOSPL */
7280
7281             local = *s2 ? s2 : s;
7282
7283             if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */
7284                 int x;
7285                 x = zchki(local);
7286                 debug(F111,"ftp get DISCARD zchki",local,x);
7287                 if (x > -1) {
7288                     skipthis++;
7289                     debug(F110,"ftp get skip name",local,0);
7290                     tlog(F100," refused: name","",0);
7291                     msg = "Refused: Name";
7292                 }
7293             }
7294
7295 #ifdef DOUPDATE
7296             if (!skipthis && updating) { /* If updating and not yet skipping */
7297                 if (zchki(local) > -1) {
7298                     x = chkmodtime(local,s,0);
7299 #ifdef DEBUG
7300                     if (deblog) {
7301                         if (updating == 2)
7302                           debug(F111,"ftp get /dates-diff chkmodtime",local,x);
7303                         else
7304                           debug(F111,"ftp get /update chkmodtime",local,x);
7305                     }
7306 #endif /* DEBUG */
7307                     if ((updating == 1 && x > 0) ||  /* /UPDATE */
7308                         (updating == 2 && x == 1)) { /* /DATES-DIFFER */
7309                         skipthis++;
7310                         tlog(F100," refused: date","",0);
7311                         msg = "Refused: Date";
7312                         debug(F110,"ftp get skip date",local,0);
7313                     }
7314                 }
7315             }
7316 #endif /* DOUPDATE */
7317         }
7318         /* Initialize file size to -1 in case server doesn't understand */
7319         /* SIZE command, so xxscreen() will know we don't know the size */
7320
7321         fsize = -1L;
7322
7323         /* Ask for size now only if we need it for selection */
7324         /* because if you're going thru a list 100,000 files to select */
7325         /* a small subset, 100,000 SIZE commands can take hours... */
7326
7327         gotsize = 0;
7328         if (!mdel && !skipthis &&        /* Don't need size for DELE... */
7329             (getsmaller > -1L || getlarger > -1L)) {
7330             if (havesize > -1L) {       /* Already have file size? */
7331                 fsize = havesize;
7332                 gotsize = 1;
7333             } else {                    /* No - must ask server */
7334                 /*
7335                   Prior to sending the NLST command we necessarily put the
7336                   server into ASCII mode.  We must now put it back into the
7337                   the requested mode so the upcoming SIZE command returns
7338                   right kind of size; this is especially important for
7339                   GET /RECOVER; otherwise the server returns the "ASCII" size
7340                   of the file, rather than its true size.
7341                 */
7342                 changetype(ftp_typ,0);  /* Change to requested type */
7343                 fsize = -1L;
7344                 if (sizeok) {
7345                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7346                     if (x == REPLY_COMPLETE) {
7347                         fsize = atol(&ftp_reply_str[4]);
7348                         gotsize = 1;
7349                     }
7350                 }
7351             }
7352             if (gotsize) {
7353                 if (getsmaller > -1L && fsize >= getsmaller)
7354                   skipthis++;
7355                 if (getlarger > -1L && fsize <= getlarger)
7356                   skipthis++;
7357                 if (skipthis) {
7358                     debug(F111,"ftp get skip size",s,fsize);
7359                     tlog(F100," refused: size","",0);
7360                     msg = "Refused: Size";
7361                 }
7362 #ifdef COMMENT
7363             } else if (getone) {
7364                 /* SIZE can fail for many reasons.  Does the file exist? */
7365                 x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm);
7366                 if (x != REPLY_COMPLETE) {
7367                     printf(">>> FILE NOT FOUND: %s\n",s);
7368                     break;
7369                 }
7370 #endif /* COMMENT */
7371             }
7372         }
7373         if (skipthis) {                 /* Skipping this file? */
7374             ftscreen(SCR_FN,'F',0L,s);
7375             if (msg)
7376               ftscreen(SCR_ST,ST_ERR,0L,msg);
7377             else
7378               ftscreen(SCR_ST,ST_SKIP,0L,s);
7379             continue;
7380         }
7381         if (fp_nml) {                   /* /NAMELIST only - no transfer */
7382             fprintf(fp_nml,"%s\n",s);
7383             continue;
7384         }
7385         if (recursive && haspath && !pipesend
7386 #ifdef PIPESEND
7387             && !rcvfilter
7388 #endif /* PIPESEND */
7389             ) {
7390             int x;
7391
7392 #ifdef NOMKDIR
7393             x = -1;
7394 #else
7395             x = zmkdir(s);              /* Try to make the directory */
7396 #endif /* NOMKDIR */
7397
7398             if (x < 0) {
7399                 rc = -1;                /* Failure is fatal */
7400                 if (geterror) {
7401                     status = 0;
7402                     ftscreen(SCR_EM,0,0L,"Directory creation failure");
7403                     break;
7404                 }
7405             }
7406         }
7407
7408         /* Not skipping */
7409
7410         selected++;                     /* Count this file as selected */
7411         pn = NULL;
7412
7413         if (!gotsize && !mdel) {        /* Didn't get size yet */
7414             if (havesize > -1L) {       /* Already have file size? */
7415                 fsize = havesize;
7416                 gotsize = 1;
7417             } else {                    /* No - must ask server */
7418                 fsize = -1L;
7419                 if (sizeok) {
7420                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7421                     if (x == REPLY_COMPLETE) {
7422                         fsize = atol(&ftp_reply_str[4]);
7423                         gotsize = 1;
7424                     }
7425                 }
7426             }
7427         }
7428         if (mdel) {                     /* [M]DELETE */
7429             if (displa && !ftp_vbm)
7430               printf(" %s...",s);
7431             rc =
7432              (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1;
7433             if (rc > -1) {
7434                 tlog(F110,"ftp mdelete",s,0);
7435                 if (displa && !ftp_vbm)
7436                   printf("OK\n");
7437             } else {
7438                 tlog(F110,"ftp mdelete failed:",s,0);
7439                 if (displa)
7440                   printf("Failed\n");
7441             }
7442 #ifndef NOSPL
7443 #ifdef PIPESEND
7444         } else if (rcvfilter) {         /* [M]GET with filter */
7445             int n; char * p;
7446             n = CKMAXPATH;
7447             p = tmpbuf;                 /* Safe - no asname with filter */
7448             zzstring(rcvfilter,&p,&n);
7449             if (n > -1)
7450               pn = tmpbuf;
7451             debug(F111,"ftp get rcvfilter",pn,n);
7452 #endif /* PIPESEND */
7453 #endif /* NOSPL */
7454             if (toscreen) s2 = "-";
7455         } else if (pipesend) {          /* [M]GET /COMMAND */
7456             int n; char * p;
7457             n = CKMAXPATH;
7458             p = tmpbuf;                 /* Safe - no asname with filter */
7459             zzstring(pipename,&p,&n);
7460             if (n > -1)
7461               pn = tmpbuf;
7462             debug(F111,"ftp get pipename",pipename,n);
7463             if (toscreen) s2 = "-";
7464         } else {                        /* [M]GET with no pipes or filters */
7465             debug(F111,"ftp get s2 A",s2,x_cnv);
7466             if (toscreen) {
7467                 s2 = "-";               /* (hokey convention for stdout) */
7468             } else if (!*s2) {          /* No asname? */
7469                 if (x_cnv) {            /* If converting */
7470                     nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */
7471                     s2 = tmpbuf;
7472                     debug(F110,"ftp get nzrtol",s2,0);
7473                 } else                  /* otherwise */
7474                   s2 = s;               /* use incoming file's name */
7475             }
7476             debug(F110,"ftp get s2 B",s2,0);
7477
7478             /* If local file already exists, take collision action */
7479
7480             if (!pipesend &&
7481 #ifdef PIPESEND
7482                 !rcvfilter &&
7483 #endif /* PIPESEND */
7484                 !toscreen) {
7485                 x = zchki(s2);
7486                 debug(F111,"ftp get zchki",s2,x);
7487                 debug(F111,"ftp get x_fnc",s2,x_fnc);
7488
7489                 if (x > -1 && !restart) {
7490                     int x = -1;
7491                     char * newname = NULL;
7492
7493                     switch (x_fnc) {
7494                       case XYFX_A:      /* Append */
7495                         append = 1;
7496                         break;
7497                       case XYFX_R:      /* Rename */
7498                       case XYFX_B:      /* Backup */
7499                         znewn(s2,&newname); /* Make unique name */
7500                         debug(F110,"ftp get znewn",newname,0);
7501                         if (x_fnc == XYFX_B) { /* Backup existing file */
7502                             x = zrename(s2,newname);
7503                             debug(F111,"ftp get backup zrename",newname,x);
7504                         } else {      /* Rename incoming file */
7505                             x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1);
7506                             s2 = tmpbuf;
7507                             debug(F111,"ftp get rename incoming",newname,x);
7508                         }
7509                         if (x < 0) {
7510                             ftscreen(SCR_EM,0,0L,"Backup/Rename failed");
7511                             x = 0;
7512                             goto xgetx;
7513                         }
7514                         break;
7515                       case XYFX_D:      /* Discard (already handled above) */
7516                       case XYFX_U:      /* Update (ditto) */
7517                       case XYFX_M:      /* Update (ditto) */
7518                       case XYFX_X:      /* Overwrite */
7519                         break;
7520                     }
7521                 }
7522             }
7523         }
7524         if (!mdel) {
7525 #ifdef PIPESEND
7526             debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0);
7527 #endif /* PIPESEND */
7528             if (pipesend && !toscreen)
7529               s2 = NULL;
7530 #ifdef DEBUG
7531             if (deblog) {
7532                 debug(F101,"ftp get x_xla","",x_xla);
7533                 debug(F101,"ftp get x_csl","",x_csl);
7534                 debug(F101,"ftp get x_csr","",x_csr);
7535                 debug(F101,"ftp get append","",append);
7536             }
7537 #endif /* DEBUG */
7538
7539             rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr);
7540
7541 #ifdef DEBUG
7542             if (deblog) {
7543                 debug(F111,"ftp get rc",s,rc);
7544                 debug(F111,"ftp get cancelfile",s,cancelfile);
7545                 debug(F111,"ftp get cancelgroup",s,cancelgroup);
7546                 debug(F111,"ftp get renaming",s,renaming);
7547             }
7548 #endif /* DEBUG */
7549         }
7550         if (rc > -1) {
7551             good++;
7552             status = 1;
7553             if (!cancelfile) {
7554                 if (deleting) {         /* GET /DELETE (source file) */
7555                     rc =
7556                       (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE)
7557                         ? 1 : -1;
7558                     tlog(F110, (rc > -1) ?
7559                          " deleted" : " failed to delete", s, 0);
7560                 } else if (renaming && rcv_rename && !toscreen) {
7561                     char *p;            /* Rename downloaded file */
7562 #ifndef NOSPL
7563                     char tmpbuf[CKMAXPATH+1];
7564                     int n;
7565                     n = CKMAXPATH;
7566                     p = tmpbuf;
7567                     debug(F111,"ftp get /rename",rcv_rename,0);
7568                     zzstring(rcv_rename,&p,&n);
7569                     debug(F111,"ftp get /rename",rcv_rename,0);
7570                     p = tmpbuf;
7571 #else
7572                     p = rcv_rename;
7573 #endif /* NOSPL */
7574                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7575                     debug(F111,"doftpget /RENAME zrename",p,rc);
7576                     tlog(F110, (rc > -1) ?
7577                          " renamed to" :
7578                          " failed to rename to",
7579                          p,
7580                          0
7581                          );
7582                 } else if (moving && rcv_move && !toscreen) {
7583                     char *p;            /* Move downloaded file */
7584 #ifndef NOSPL
7585                     char tmpbuf[CKMAXPATH+1];
7586                     int n;
7587                     n = TMPBUFSIZ;
7588                     p = tmpbuf;
7589                     debug(F111,"ftp get /move-to",rcv_move,0);
7590                     zzstring(rcv_move,&p,&n);
7591                     p = tmpbuf;
7592 #else
7593                     p = rcv_move;
7594 #endif /* NOSPL */
7595                     debug(F111,"ftp get /move-to",p,0);
7596                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7597                     debug(F111,"doftpget /MOVE zrename",p,rc);
7598                     tlog(F110, (rc > -1) ?
7599                          " moved to" : " failed to move to", p, 0);
7600                 }
7601                 if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) {
7602                     char * s = pv[SND_SRN].sval;
7603                     char * srvrn = pv[SND_SRN].sval;
7604                     char tmpbuf[CKMAXPATH+1];
7605 #ifndef NOSPL
7606                     int y;              /* Pass it thru the evaluator */
7607                     extern int cmd_quoting; /* for \v(filename) */
7608                     debug(F111,"ftp get srv_renam",s,1);
7609
7610                     if (cmd_quoting) {
7611                         y = CKMAXPATH;
7612                         s = (char *)tmpbuf;
7613                         zzstring(srvrn,&s,&y);
7614                         s = (char *)tmpbuf;
7615                     }
7616 #endif /* NOSPL */
7617                     debug(F111,"ftp get srv_renam",s,1);
7618                     if (s) if (*s) {
7619                         int x;
7620                         x = ftp_rename(s2,s);
7621                         debug(F111,"ftp get ftp_rename",s2,x);
7622                         tlog(F110, (x > 0) ?
7623                              " renamed source file to" :
7624                              " failed to rename source file to",
7625                              s,
7626                              0
7627                              );
7628                         if (x < 1)
7629                           return(-1);
7630                     }
7631                 }
7632             }
7633         }
7634         if (cancelfile)
7635           continue;
7636         if (rc < 0) {
7637             ftp_fai++;
7638             if (geterror) {
7639                 status = 0;
7640                 ftscreen(SCR_EM,0,0L,"Fatal download error");
7641                 done++;
7642             }
7643         }
7644     }
7645 #ifdef DEBUG
7646     if (deblog) {
7647         debug(F101,"ftp get status","",status);
7648         debug(F101,"ftp get cancelgroup","",cancelgroup);
7649         debug(F101,"ftp get cancelfile","",cancelfile);
7650         debug(F101,"ftp get selected","",selected);
7651         debug(F101,"ftp get good","",good);
7652     }
7653 #endif /* DEBUG */
7654
7655     if (selected == 0) {                /* No files met selection criteria */
7656         status = 1;                     /* which is a kind of success. */
7657     } else if (status > 0) {            /* Some files were selected */
7658         if (cancelgroup)                /* but MGET was canceled */
7659           status = 0;                   /* so MGET failed */
7660         else if (cancelfile && good < 1) /* If file was canceled */
7661           status = 0;                   /* MGET failed if it got no files */
7662     }
7663     success = status;
7664     x = success;
7665     debug(F101,"ftp get success","",success);
7666
7667   xgetx:
7668     pipesend = pipesave;                /* Restore global pipe selection */
7669     if (fp_nml) {                       /* Close /NAMELIST */
7670         if (fp_nml != stdout)
7671           fclose(fp_nml);
7672         fp_nml = NULL;
7673     }
7674     if (x > -1) {                       /* Download successful */
7675 #ifdef GFTIMER
7676         t1 = gmstimer();                /* End time */
7677         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
7678         if (!sec) sec = 0.001;
7679         fptsecs = sec;
7680 #else
7681         sec = (t1 - t0) / 1000;
7682         if (!sec) sec = 1;
7683 #endif /* GFTIMER */
7684         tfcps = (long) (tfc / sec);
7685         tsecs = (int)sec;
7686         lastxfer = W_FTP|W_RECV;
7687         xferstat = success;
7688     }
7689     if (dpyactive)
7690       ftscreen(SCR_TC,0,0L,"");
7691 #ifdef CK_TMPDIR
7692     if (f_tmpdir) {                     /* If we changed to download dir */
7693         zchdir((char *) savdir);        /* Go back where we came from */
7694         f_tmpdir = 0;
7695     }
7696 #endif /* CK_TMPDIR */
7697
7698     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
7699         if (pv[i].sval)
7700           free(pv[i].sval);
7701     }
7702     for (i = 0; i < mgetn; i++)         /* MGET list too */
7703       makestr(&(mgetlist[i]),NULL);
7704
7705     if (cancelgroup)                    /* Clear temp-file stack */
7706       mlsreset();
7707
7708     ftreset();                          /* Undo switch effects */
7709     dpyactive = 0;
7710     return(x);
7711 }
7712
7713 static struct keytab ftprmt[] = {
7714     { "cd",        XZCWD, 0 },
7715     { "cdup",      XZCDU, 0 },
7716     { "cwd",       XZCWD, CM_INV },
7717     { "delete",    XZDEL, 0 },
7718     { "directory", XZDIR, 0 },
7719     { "exit",      XZXIT, 0 },
7720     { "help",      XZHLP, 0 },
7721     { "login",     XZLGI, 0 },
7722     { "logout",    XZLGO, 0 },
7723     { "mkdir",     XZMKD, 0 },
7724     { "pwd",       XZPWD, 0 },
7725     { "rename",    XZREN, 0 },
7726     { "rmdir",     XZRMD, 0 },
7727     { "type",      XZTYP, 0 },
7728     { "", 0, 0 }
7729 };
7730 static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1;
7731
7732 int
7733 doftpsite() {                           /* Send a SITE command */
7734     int reply;
7735     char * s;
7736     int lcs = -1, rcs = -1;
7737 #ifndef NOCSETS
7738     if (ftp_xla) {
7739         lcs = ftp_csl;
7740         if (lcs < 0) lcs = fcharset;
7741         rcs = ftp_csx;
7742         if (rcs < 0) rcs = ftp_csr;
7743     }
7744 #endif /* NOCSETS */
7745     if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
7746       return(x);
7747     CHECKCONN();
7748     ckstrncpy(line,s,LINBUFSIZ);
7749     if (testing) printf(" ftp site \"%s\"...\n",line);
7750     if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) {
7751         do {
7752             reply = getreply(0,lcs,rcs,ftp_vbm,0);
7753         } while (reply == REPLY_PRELIM);
7754     }
7755     return(success = (reply == REPLY_COMPLETE));
7756 }
7757
7758
7759 int
7760 dosetftppsv() {                         /* Passive mode */
7761     x = seton(&ftp_psv);
7762     if (x > 0) passivemode = ftp_psv;
7763     return(x);
7764 }
7765
7766 /*  d o f t p r m t  --  Parse and execute REMOTE commands  */
7767
7768 int
7769 doftprmt(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
7770     /* cx == 0 means REMOTE */
7771     /* cx != 0 is a XZxxx value */
7772     char * s;
7773
7774     if (who != 0)
7775       return(0);
7776
7777     if (cx == 0) {
7778         if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0)
7779           return(x);
7780         cx = x;
7781     }
7782     switch (cx) {
7783       case XZCDU:                       /* CDUP */
7784         if ((x = cmcfm()) < 0) return(x);
7785         return(doftpcdup());
7786
7787       case XZCWD:                       /* RCD */
7788         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
7789           return(x);
7790         ckstrncpy(line,s,LINBUFSIZ);
7791         return(doftpcwd((char *)line,1));
7792       case XZPWD:                       /* RPWD */
7793         return(doftppwd());
7794       case XZDEL:                       /* RDEL */
7795         return(doftpget(FTP_MDE,1));
7796       case XZDIR:                       /* RDIR */
7797         return(doftpdir(FTP_DIR));
7798       case XZHLP:                       /* RHELP */
7799         return(doftpxhlp());
7800       case XZMKD:                       /* RMKDIR */
7801         return(doftpmkd());
7802       case XZREN:                       /* RRENAME */
7803         return(doftpren());
7804       case XZRMD:                       /* RRMDIR */
7805         return(doftprmd());
7806       case XZLGO:                       /* LOGOUT */
7807         return(doftpres());
7808       case XZXIT:                       /* EXIT */
7809         return(ftpbye());
7810     }
7811     printf("?Not usable with FTP - \"%s\"\n", atmbuf);
7812     return(-9);
7813 }
7814
7815 int
7816 doxftp() {                              /* Command parser for built-in FTP */
7817     int cx, n;
7818     struct FDB kw, fl;
7819     char * s;
7820     int usetls = 0;
7821     int lcs = -1, rcs = -1;
7822
7823 #ifndef NOCSETS
7824     if (ftp_xla) {
7825         lcs = ftp_csl;
7826         if (lcs < 0) lcs = fcharset;
7827         rcs = ftp_csx;
7828         if (rcs < 0) rcs = ftp_csr;
7829     }
7830 #endif /* NOCSETS */
7831
7832     if (inserver)                       /* FTP not allowed in IKSD. */
7833       return(-2);
7834
7835     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
7836         ftp_typ = g_ftp_typ;
7837         /* g_ftp_typ = -1; */
7838     }
7839 #ifdef COMMENT
7840 /*
7841   We'll set the collision action locally in doftpget() based on whether
7842   ftp_fnc was ever set to a value.  if not, we'll use the fncact value.
7843 */
7844     if (ftp_fnc < 0)                    /* Inherit global collision action */
7845       ftp_fnc = fncact;                 /* if none specified for FTP */
7846 #endif /* COMMENT */
7847
7848     /* Restore global verbose mode */
7849     if (ftp_deb)
7850       ftp_vbm = 1;
7851     else if (quiet)
7852       ftp_vbm = 0;
7853     else
7854       ftp_vbm = ftp_vbx;
7855
7856     ftp_dates &= 1;                     /* Undo any previous /UPDATE switch */
7857
7858     dpyactive = 0;                      /* Reset global transfer-active flag */
7859     printlines = 0;                     /* Reset printlines */
7860
7861     if (fp_nml) {                       /* Reset /NAMELIST */
7862         if (fp_nml != stdout)
7863           fclose(fp_nml);
7864         fp_nml = NULL;
7865     }
7866     makestr(&ftp_nml,NULL);
7867
7868     cmfdbi(&kw,                         /* First FDB - commands */
7869            _CMKEY,                      /* fcode */
7870            "Hostname; or FTP command",  /* help */
7871            "",                          /* default */
7872            "",                          /* addtl string data */
7873            nftpcmd,                     /* addtl numeric data 1: tbl size */
7874            0,                           /* addtl numeric data 2: none */
7875            xxstring,                    /* Processing function */
7876            ftpcmdtab,                   /* Keyword table */
7877            &fl                          /* Pointer to next FDB */
7878            );
7879     cmfdbi(&fl,                         /* A host name or address */
7880            _CMFLD,                      /* fcode */
7881            "Hostname or address",       /* help */
7882            "",                          /* default */
7883            "",                          /* addtl string data */
7884            0,                           /* addtl numeric data 1 */
7885            0,                           /* addtl numeric data 2 */
7886            xxstring,
7887            NULL,
7888            NULL
7889            );
7890     x = cmfdb(&kw);                     /* Parse a hostname or a keyword */
7891     if (x == -3) {
7892         printf("?ftp what? \"help ftp\" for hints\n");
7893         return(-9);
7894     }
7895     if (x < 0)
7896       return(x);
7897     if (cmresult.fcode == _CMFLD) {     /* If hostname */
7898         return(openftp(cmresult.sresult,0)); /* go open the connection */
7899     } else {
7900         cx = cmresult.nresult;
7901     }
7902     switch (cx) {
7903       case FTP_ACC:                     /* ACCOUNT */
7904         if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
7905           return(x);
7906         CHECKCONN();
7907         makestr(&ftp_acc,s);
7908         if (testing)
7909           printf(" ftp account: \"%s\"\n",ftp_acc);
7910         success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
7911         return(success);
7912
7913       case FTP_GUP:                     /* Go UP */
7914         if ((x = cmcfm()) < 0) return(x);
7915         CHECKCONN();
7916         if (testing) printf(" ftp cd: \"(up)\"\n");
7917         return(success = doftpcdup());
7918
7919       case FTP_CWD:                     /* CD */
7920         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
7921           return(x);
7922         CHECKCONN();
7923         ckstrncpy(line,s,LINBUFSIZ);
7924         if (testing)
7925           printf(" ftp cd: \"%s\"\n", line);
7926         return(success = doftpcwd(line,1));
7927
7928       case FTP_CHM:                     /* CHMOD */
7929         if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0)
7930           return(x);
7931         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
7932         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
7933           return(x);
7934         CHECKCONN();
7935         ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL);
7936         if (testing)
7937           printf(" ftp chmod: %s\n",ftpcmdbuf);
7938         success =
7939           (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
7940         return(success);
7941
7942       case FTP_CLS:                     /* CLOSE FTP connection */
7943         if ((y = cmcfm()) < 0)
7944           return(y);
7945         CHECKCONN();
7946         if (testing)
7947           printf(" ftp closing...\n");
7948         ftpclose();
7949         return(success = 1);
7950
7951       case FTP_DIR:                     /* DIRECTORY of remote files */
7952       case FTP_VDI:
7953         return(doftpdir(cx));
7954
7955       case FTP_GET:                     /* GET a remote file */
7956       case FTP_RGE:                     /* REGET */
7957       case FTP_MGE:                     /* MGET */
7958       case FTP_MDE:                     /* MDELETE */
7959         return(doftpget(cx,1));
7960
7961       case FTP_IDL:                     /* IDLE */
7962         if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0)
7963           return(x);
7964         if ((y = cmcfm()) < 0)
7965           return(y);
7966         CHECKCONN();
7967         if (z < 0)  {                   /* Display idle timeout */
7968             if (testing)
7969               printf(" ftp query idle timeout...\n");
7970             success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE);
7971         } else {                        /* Set idle timeout */
7972             if (testing)
7973               printf(" ftp idle timeout set: %d...\n",z);
7974             success =
7975               (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE);
7976         }
7977         return(success);
7978
7979       case FTP_MKD:                     /* MKDIR */
7980         return(doftpmkd());
7981
7982       case FTP_MOD:                     /* MODTIME */
7983         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
7984           return(x);
7985         CHECKCONN();
7986         ckstrncpy(line,s,LINBUFSIZ);
7987         if (testing)
7988           printf(" ftp modtime \"%s\"...\n",line);
7989         success = 0;
7990         if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) {
7991             success = 1;
7992             mdtmok = 1;
7993             if (!quiet) {
7994                 int flag = 0;
7995                 char c, * s;
7996                 struct tm tmremote;
7997
7998                 bzero((char *)&tmremote, sizeof(struct tm));
7999                 s = ftp_reply_str;
8000                 while ((c = *s++)) {
8001                     if (c == SP) {
8002                         flag++;
8003                         break;
8004                     }
8005                 }
8006                 if (flag) {
8007                     if (sscanf(s, "%04d%02d%02d%02d%02d%02d",
8008                                &tmremote.tm_year,
8009                                &tmremote.tm_mon,
8010                                &tmremote.tm_mday,
8011                                &tmremote.tm_hour,
8012                                &tmremote.tm_min,
8013                                &tmremote.tm_sec
8014                                ) == 6) {
8015                         printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n",
8016                                line,
8017                                tmremote.tm_year,
8018                                tmremote.tm_mon,
8019                                tmremote.tm_mday,
8020                                tmremote.tm_hour,
8021                                tmremote.tm_min,
8022                                tmremote.tm_sec
8023                                );
8024                     } else {
8025                         success = 0;
8026                     }
8027                 }
8028             }
8029         }
8030         return(success);
8031
8032       case FTP_OPN:                     /* OPEN connection */
8033 #ifdef COMMENT
8034         x = cmfld("IP hostname or address","",&s,xxstring);
8035         if (x < 0) {
8036             success = 0;
8037             return(x);
8038         }
8039         ckstrncpy(line,s,LINBUFSIZ);
8040         s = line;
8041         return(openftp(s,0));
8042 #else
8043         {                               /* OPEN connection */
8044             char name[TTNAMLEN+1], *p;
8045             extern int network;
8046             extern char ttname[];
8047             if (network)                /* If we have a current connection */
8048               ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */
8049             else
8050               *name = '\0';             /* as default host */
8051             for (p = name; *p; p++)     /* Remove ":service" from end. */
8052               if (*p == ':') { *p = '\0'; break; }
8053 #ifndef USETLSTAB
8054             x = cmfld("IP hostname or address",name,&s,xxstring);
8055 #else
8056             cmfdbi(&kw,                 /* First FDB - commands */
8057                    _CMKEY,              /* fcode */
8058                    "Hostname or switch", /* help */
8059                    "",                  /* default */
8060                    "",                  /* addtl string data */
8061                    ntlstab,             /* addtl numeric data 1: tbl size */
8062                    0,                   /* addtl numeric data 2: none */
8063                    xxstring,            /* Processing function */
8064                    tlstab,              /* Keyword table */
8065                    &fl                  /* Pointer to next FDB */
8066                    );
8067             cmfdbi(&fl,                 /* A host name or address */
8068                    _CMFLD,              /* fcode */
8069                    "Hostname or address", /* help */
8070                    "",                  /* default */
8071                    "",                  /* addtl string data */
8072                    0,                   /* addtl numeric data 1 */
8073                    0,                   /* addtl numeric data 2 */
8074                    xxstring,
8075                    NULL,
8076                    NULL
8077                    );
8078
8079             for (n = 0;; n++) {
8080                 x = cmfdb(&kw);         /* Parse a hostname or a keyword */
8081                 if (x == -3) {
8082                   printf("?ftp open what? \"help ftp\" for hints\n");
8083                   return(-9);
8084                 }
8085                 if (x < 0)
8086                   break;
8087                 if (cmresult.fcode == _CMFLD) { /* Hostname */
8088                     s = cmresult.sresult;
8089                     break;
8090                 } else if (cmresult.nresult == OPN_TLS) {
8091                     usetls = 1;
8092                 }
8093             }
8094 #endif /* USETLSTAB */
8095             if (x < 0) {
8096                 success = 0;
8097                 return(x);
8098             }
8099             ckstrncpy(line,s,LINBUFSIZ);
8100             s = line;
8101             return(openftp(s,usetls));
8102         }
8103 #endif /* COMMENT */
8104
8105       case FTP_PUT:                     /* PUT */
8106       case FTP_MPU:                     /* MPUT */
8107       case FTP_APP:                     /* APPEND */
8108         return(doftpput(cx,1));
8109
8110       case FTP_PWD:                     /* PWD */
8111         x = doftppwd();
8112         if (x > -1) success = x;
8113         return(x);
8114
8115       case FTP_REN:                     /* RENAME */
8116         return(doftpren());
8117
8118       case FTP_RES:                     /* RESET */
8119         return(doftpres());
8120
8121       case FTP_HLP:                     /* (remote) HELP */
8122         return(doftpxhlp());
8123
8124       case FTP_RMD:                     /* RMDIR */
8125         return(doftprmd());
8126
8127       case FTP_STA:                     /* STATUS */
8128         if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8129           return(x);
8130         CHECKCONN();
8131         ckstrncpy(line,s,LINBUFSIZ);
8132         if (testing) printf(" ftp status \"%s\"...\n",line);
8133         success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE);
8134         return(success);
8135
8136       case FTP_SIT: {                   /* SITE */
8137           return(doftpsite());
8138       }
8139
8140       case FTP_SIZ:                     /* (ask for) SIZE */
8141         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8142           return(x);
8143         CHECKCONN();
8144         ckstrncpy(line,s,LINBUFSIZ);
8145         if (testing)
8146           printf(" ftp size \"%s\"...\n",line);
8147         success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE);
8148         if (success)
8149           sizeok = 1;
8150         return(success);
8151
8152       case FTP_SYS:                     /* Ask for server's SYSTEM type */
8153         if ((x = cmcfm()) < 0) return(x);
8154         CHECKCONN();
8155         if (testing)
8156           printf(" ftp system...\n");
8157         success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE);
8158         return(success);
8159
8160       case FTP_UMA:                     /* Set/query UMASK */
8161         if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0)
8162           if (x != -3)
8163             return(x);
8164         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8165         if ((x = cmcfm()) < 0) return(x);
8166         CHECKCONN();
8167         if (testing) {
8168             if (tmpbuf[0])
8169               printf(" ftp umask \"%s\"...\n",tmpbuf);
8170             else
8171               printf(" ftp query umask...\n");
8172         }
8173         success = ftp_umask(tmpbuf);
8174         return(success);
8175
8176       case FTP_USR:
8177         return(doftpusr());
8178
8179       case FTP_QUO:
8180         if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0)
8181           return(x);
8182         CHECKCONN();
8183         success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
8184         return(success);
8185
8186       case FTP_TYP:                     /* Type */
8187         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
8188           return(x);
8189         if ((y = cmcfm()) < 0) return(y);
8190         CHECKCONN();
8191         ftp_typ = x;
8192         g_ftp_typ = x;
8193         tenex = (ftp_typ == FTT_TEN);
8194         changetype(ftp_typ,ftp_vbm);
8195         return(1);
8196
8197       case FTP_CHK:                     /* Check if remote file(s) exist(s) */
8198         if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0)
8199           return(x);
8200         CHECKCONN();
8201         success = remote_files(1,(CHAR *)s,NULL,0) ? 1 : 0;
8202         return(success);
8203
8204       case FTP_FEA:                     /* RFC2389 */
8205         if ((y = cmcfm()) < 0)
8206           return(y);
8207         CHECKCONN();
8208         success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE);
8209         if (success) {
8210             if (sfttab[0] > 0) {
8211                 ftp_aut = sfttab[SFT_AUTH];
8212                 sizeok  = sfttab[SFT_SIZE];
8213                 mdtmok  = sfttab[SFT_MDTM];
8214                 mlstok  = sfttab[SFT_MLST];
8215             }
8216         }
8217         return(success);
8218
8219       case FTP_OPT:                     /* RFC2389 */
8220         /* Perhaps this should be a keyword list... */
8221         if ((x = cmfld("FTP command","",&s,xxstring)) < 0)
8222           return(x);
8223         CHECKCONN();
8224         ckstrncpy(line,s,LINBUFSIZ);
8225         if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0)
8226           return(x);
8227         success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8228         return(success);
8229
8230       case FTP_ENA:                     /* FTP ENABLE */
8231       case FTP_DIS:                     /* FTP DISABLE */
8232         if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0)
8233           return(x);
8234         if ((y = cmcfm()) < 0) return(y);
8235         switch (x) {
8236           case ENA_AUTH:                /* OK to use autoauthentication */
8237             ftp_aut = (cx == FTP_ENA) ? 1 : 0;
8238             sfttab[SFT_AUTH] = ftp_aut;
8239             break;
8240           case ENA_FEAT:                /* OK to send FEAT command */
8241             featok = (cx == FTP_ENA) ? 1 : 0;
8242             break;
8243           case ENA_MLST:                /* OK to use MLST/MLSD */
8244             mlstok = (cx == FTP_ENA) ? 1 : 0;
8245             sfttab[SFT_MLST] = mlstok;
8246             break;
8247           case ENA_MDTM:                /* OK to use MDTM */
8248             mdtmok = (cx == FTP_ENA) ? 1 : 0;
8249             sfttab[SFT_MDTM] = mdtmok;
8250             break;
8251           case ENA_SIZE:                /* OK to use SIZE */
8252             sizeok = (cx == FTP_ENA) ? 1 : 0;
8253             sfttab[SFT_SIZE] = sizeok;
8254             break;
8255         }
8256         return(success = 1);
8257     }
8258     return(-2);
8259 }
8260
8261 #ifndef NOSHOW
8262 static char *
8263 shopl(x) int x; {
8264     switch (x) {
8265       case FPL_CLR: return("clear");
8266       case FPL_PRV: return("private");
8267       case FPL_SAF: return("safe");
8268       case 0:  return("(not set)");
8269       default: return("(unknown)");
8270     }
8271 }
8272
8273 int
8274 shoftp(brief) {
8275     char * s = "?";
8276     int n, x;
8277
8278     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8279         ftp_typ = g_ftp_typ;
8280         /* g_ftp_typ = -1; */
8281     }
8282     printf("\n");
8283     printf("FTP connection:                 %s\n",connected ?
8284            ftp_host :
8285            "(none)"
8286            );
8287     n = 2;
8288     if (connected) {
8289         n++;
8290         printf("FTP server type:                %s\n",
8291                ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)");
8292     }
8293     if (loggedin)
8294       printf("Logged in as:                   %s\n",
8295              strval(ftp_logname,"(unknown)"));
8296     else
8297       printf("Not logged in\n");
8298     n++;
8299     if (brief) return(0);
8300
8301     printf("\nSET FTP values:\n\n");
8302     n += 3;
8303
8304     printf(" ftp anonymous-password:        %s\n",
8305            ftp_apw ? ftp_apw : "(default)"
8306            );
8307     printf(" ftp auto-login:                %s\n",showoff(ftp_log));
8308     printf(" ftp auto-authentication:       %s\n",showoff(ftp_aut));
8309     switch (ftp_typ) {
8310       case FTT_ASC: s = "text"; break;
8311       case FTT_BIN: s = "binary"; break;
8312       case FTT_TEN: s = "tenex"; break;
8313     }
8314     printf(" ftp type:                      %s\n",s);
8315     printf(" ftp get-filetype-switching:    %s\n",showoff(get_auto));
8316     printf(" ftp dates:                     %s\n",showoff(ftp_dates));
8317     printf(" ftp error-action:              %s\n",ftp_err ? "quit":"proceed");
8318     printf(" ftp filenames:                 %s\n",
8319            ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal")
8320            );
8321     printf(" ftp debug                      %s\n",showoff(ftp_deb));
8322
8323     printf(" ftp passive-mode:              %s\n",showoff(ftp_psv));
8324     printf(" ftp permissions:               %s\n",showooa(ftp_prm));
8325     printf(" ftp verbose-mode:              %s\n",showoff(ftp_vbx));
8326     printf(" ftp send-port-commands:        %s\n",showoff(ftp_psv));
8327     printf(" ftp unique-server-names:       %s\n",showoff(ftp_usn));
8328 #ifdef COMMENT
8329     /* See note in doxftp() */
8330     if (ftp_fnc < 0)
8331       ftp_fnc = fncact;
8332 #endif /* COMMENT */
8333     printf(" ftp collision:                 %s\n",
8334            fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]);
8335     printf(" ftp server-time-offset:        %s\n",
8336            fts_sto ? fts_sto : "(none)");
8337     n += 15;
8338
8339 #ifndef NOCSETS
8340     printf(" ftp character-set-translation: %s\n",showoff(ftp_xla));
8341     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8342
8343     printf(" ftp server-character-set:      %s\n",fcsinfo[ftp_csr].keyword);
8344     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8345
8346     printf(" file character-set:            %s\n",fcsinfo[fcharset].keyword);
8347     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8348 #endif /* NOCSETS */
8349
8350     x = ftp_dis;
8351     if (x < 0)
8352       x = fdispla;
8353     switch (x) {
8354       case XYFD_N: s = "none"; break;
8355       case XYFD_R: s = "serial"; break;
8356       case XYFD_C: s = "fullscreen"; break;
8357       case XYFD_S: s = "crt"; break;
8358       case XYFD_B: s = "brief"; break;
8359     }
8360     printf(" ftp display:                   %s\n",s);
8361     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8362
8363     if (mlstok || featok || mdtmok || sizeok || ftp_aut) {
8364         printf(" enabled:                      ");
8365         if (ftp_aut) printf(" AUTH");
8366         if (featok)  printf(" FEAT");
8367         if (mdtmok)  printf(" MDTM");
8368         if (mlstok)  printf(" MLST");
8369         if (sizeok)  printf(" SIZE");
8370         printf("\n");
8371         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8372     }
8373     if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) {
8374         printf(" disabled:                     ");
8375         if (!ftp_aut) printf(" AUTH");
8376         if (!featok)  printf(" FEAT");
8377         if (!mdtmok)  printf(" MDTM");
8378         if (!mlstok)  printf(" MLST");
8379         if (!sizeok)  printf(" SIZE");
8380         printf("\n");
8381         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8382     }
8383     switch (ftpget) {
8384       case 0: s = "kermit"; break;
8385       case 1: s = "ftp"; break;
8386       case 2: s = "auto"; break;
8387       default: s = "?";
8388     }
8389     printf(" get-put-remote:                %s\n",s);
8390     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8391
8392     printf("\n");
8393     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8394
8395 #ifdef FTP_SECURITY
8396     printf("Available security methods:    ");
8397 #ifdef FTP_GSSAPI
8398     printf("GSSAPI ");
8399 #endif /* FTP_GSSAPI */
8400 #ifdef FTP_KRB4
8401     printf("Kerberos4 ");
8402 #endif /* FTP_KRB4 */
8403 #ifdef FTP_SRP
8404     printf("SRP ");
8405 #endif /* FTP_SRP */
8406 #ifdef FTP_SSL
8407     printf("SSL ");
8408 #endif /* FTP_SSL */
8409
8410     n++;
8411     printf("\n\n");
8412     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8413     printf(" ftp authtype:                  %s\n",strval(auth_type,NULL));
8414     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8415     printf(" ftp auto-encryption:           %s\n",showoff(ftp_cry));
8416     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8417     printf(" ftp credential-forwarding:     %s\n",showoff(ftp_cfw));
8418     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8419     printf(" ftp command-protection-level:  %s\n",shopl(ftp_cpl));
8420     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8421     printf(" ftp data-protection-level:     %s\n",shopl(ftp_dpl));
8422     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8423     printf(" ftp secure proxy:              %s\n",shopl(ssl_ftp_proxy));
8424     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8425 #else
8426     printf("Available security methods:     (none)\n");
8427     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8428 #endif /* FTP_SECURITY */
8429
8430     if (n <= cmd_rows - 3)
8431       printf("\n");
8432     return(0);
8433 }
8434 #endif /* NOSHOW */
8435
8436 #ifndef NOHELP
8437 /* FTP HELP text strings */
8438
8439 static char * fhs_ftp[] = {
8440     "Syntax: FTP subcommand [ operands ]",
8441     "  Makes an FTP connection, or sends a command to the FTP server.",
8442     "  To see a list of available FTP subcommands, type \"ftp ?\".",
8443     "  and then use HELP FTP xxx to get help about subcommand xxx.",
8444     "  Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.",
8445     ""
8446 };
8447
8448 static char * fhs_acc[] = {             /* ACCOUNT */
8449     "Syntax: FTP ACCOUNT text",
8450     "  Sends an account designator to an FTP server that needs one.",
8451     "  Most FTP servers do not use accounts; some use them for other",
8452     "  other purposes, such as disk-access passwords.",
8453     ""
8454 };
8455 static char * fhs_app[] = {             /* APPEND */
8456     "Syntax: FTP APPEND filname",
8457     "  Equivalent to [ FTP ] PUT /APPEND.  See HELP FTP PUT.",
8458     ""
8459 };
8460 static char * fhs_cls[] = {             /* BYE, CLOSE */
8461     "Syntax: [ FTP ] BYE",
8462     "  Logs out from the FTP server and closes the FTP connection.",
8463     "  Also see HELP SET GET-PUT-REMOTE.  Synonym: [ FTP ] CLOSE.",
8464     ""
8465 };
8466 static char * fhs_cwd[] = {             /* CD, CWD */
8467     "Syntax: [ FTP ] CD directory",
8468     "  Asks the FTP server to change to the given directory.",
8469     "  Also see HELP SET GET-PUT-REMOTE.  Synonyms: [ FTP ] CWD, RCD, RCWD.",
8470     ""
8471 };
8472 static char * fhs_gup[] = {             /* CDUP, UP */
8473     "Syntax: FTP CDUP",
8474     "  Asks the FTP server to change to the parent directory of its current",
8475     "  directory.  Also see HELP SET GET-PUT-REMOTE.  Synonym: FTP UP.",
8476     ""
8477 };
8478 static char * fhs_chm[] = {             /* CHMOD */
8479     "Syntax: FTP CHMOD filename permissions",
8480     "  Asks the FTP server to change the permissions, protection, or mode of",
8481     "  the given file.  The given permissions must be in the syntax of the",
8482     "  the server's file system, e.g. an octal number for UNIX.  Also see",
8483     "  FTP PUT /PERMISSIONS",
8484     ""
8485 };
8486 static char * fhs_mde[] = {             /* DELETE */
8487     "Syntax: FTP DELETE [ switches ] filespec",
8488     "  Asks the FTP server to delete the given file or files.",
8489     "  Synonym: MDELETE (Kermit makes no distinction between single and",
8490     "  multiple file deletion).  Optional switches:",
8491     " ",
8492     "  /ERROR-ACTION:{PROCEED,QUIT}",
8493     "  /EXCEPT:pattern",
8494     "  /FILENAMES:{AUTO,CONVERTED,LITERAL}",
8495     "  /LARGER-THAN:number",
8496 #ifdef UNIXOROSK
8497     "  /NODOTFILES",
8498 #endif /* UNIXOROSK */
8499     "  /QUIET",
8500 #ifdef RECURSIVE
8501     "  /RECURSIVE (depends on server)",
8502     "  /SUBDIRECTORIES",
8503 #endif /* RECURSIVE */
8504     "  /SMALLER-THAN:number",
8505     ""
8506 };
8507 static char * fhs_dir[] = {             /* DIRECTORY */
8508     "Syntax: FTP DIRECTORY [ filespec ]",
8509     "  Asks the server to send a directory listing of the files that match",
8510     "  the given filespec, or if none is given, all the files in its current",
8511     "  directory.  The filespec, including any wildcards, must be in the",
8512     "  syntax of the server's file system.  Also see HELP SET GET-PUT-REMOTE.",
8513     "  Synonym: RDIRECTORY.",
8514     ""
8515 };
8516 static char * fhs_vdi[] = {             /* VDIRECTORY */
8517     "Syntax: FTP VDIRECTORY [ filespec ]",
8518     "  Asks the server to send a directory listing of the files that match",
8519     "  the given filespec, or if none is given, all the files in its current",
8520     "  directory.  VDIRECTORY is needed for getting verbose directory",
8521     "  listings from certain FTP servers, such as on TOPS-20.  Try it if",
8522     "  FTP DIRECTORY lists only filenames without details.",
8523     ""
8524 };
8525 static char * fhs_fea[] = {             /* FEATURES */
8526     "Syntax: FTP FEATURES",
8527     "  Asks the FTP server to list its special features.  Most FTP servers",
8528     "  do not recognize this command.",
8529     ""
8530 };
8531 static char * fhs_mge[] = {             /* MGET */
8532     "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]",
8533     "  Download a single file or multiple files.  Asks the FTP server to send",
8534     "  the given file or files.  Also see FTP GET.  Optional switches:",
8535     " ",
8536     "  /AS-NAME:text",
8537     "    Name under which to store incoming file.",
8538     "    Pattern required for for multiple files.",
8539     "  /BINARY",                        /* /IMAGE */
8540     "    Force binary mode.  Synonym: /IMAGE.",
8541     "  /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}",
8542    "    What to do if an incoming file has the same name as an existing file.",
8543
8544 #ifdef PUTPIPE
8545     "  /COMMAND",
8546     "    Specifies that the as-name is a command to which the incoming file",
8547     "    is to be piped as standard input.",
8548 #endif /* PUTPIPE */
8549
8550 #ifdef DOUPDATE
8551     "  /DATES-DIFFER",
8552     "    Download only those files whose modification date-times differ from",
8553     "    those of the corresponding local files, or that do not already",
8554     "    exist on the local computer.",
8555 #endif /* DOUPDATE */
8556
8557     "  /DELETE",
8558     "    Specifies that each file is to be deleted from the server after,",
8559     "    and only if, it is successfully downloaded.",
8560     "  /ERROR-ACTION:{PROCEED,QUIT}",
8561     "    When downloading a group of files, what to do upon failure to",
8562     "    transfer a file: quit or proceed to the next one.",
8563     "  /EXCEPT:pattern",
8564     "    Exception list: don't download any files that match this pattern.",
8565     "    See HELP WILDCARD for pattern syntax.",
8566     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
8567     "    Whether to convert incoming filenames to local syntax.",
8568 #ifdef PIPESEND
8569 #ifndef NOSPL
8570     "  /FILTER:command",
8571     "    Pass incoming files through the given command.",
8572 #endif /* NOSPL */
8573 #endif /* PIPESEND */
8574     "  /LARGER-THAN:number",
8575     "    Only download files that are larger than the given number of bytes.",
8576     "  /LISTFILE:filename",
8577     "    Obtain the list of files to download from the given file.",
8578 #ifndef NOCSETS
8579     "  /LOCAL-CHARACTER-SET:name",
8580     "    When downloading in text mode and character-set conversion is",
8581     "    desired, this specifies the target set.",
8582 #endif /* NOCSETS */
8583     "  /MATCH:pattern",
8584     "    Specifies a pattern to be used to select filenames locally from the",
8585     "    server's list.",
8586     "  /MLSD",
8587     "    Forces sending of MLSD (rather than NLST) to get the file list.",
8588 #ifdef CK_TMPDIR
8589     "  /MOVE-TO:directory",
8590     "    Each file that is downloaded is to be moved to the given local",
8591     "    directory immediately after, and only if, it has been received",
8592     "    successfully.",
8593 #endif /* CK_TMPDIR */
8594     "  /NAMELIST:filename",
8595     "    Instead of downloading the files, stores the list of files that",
8596     "    would be downloaded in the given local file, one filename per line.",
8597     "  /NLST",
8598     "    Forces sending of NLST (rather than MLSD) to get the file list.",
8599     "  /NOBACKUPFILES",
8600     "    Don't download any files whose names end with .~<number>~.",
8601     "  /NODOTFILES",
8602     "    Don't download any files whose names begin with period (.).",
8603     "  /QUIET",
8604     "    Suppress the file-transfer display.",
8605 #ifdef FTP_RESTART
8606     "  /RECOVER",                       /* /RESTART */
8607     "    Resume a download that was previously interrupted from the point of",
8608     "    failure.  Works only in binary mode.  Not supported by all servers.",
8609     "    Synonym: /RESTART.",
8610 #endif /* FTP_RESTART */
8611 #ifdef RECURSIVE
8612     "  /RECURSIVE",                     /* /SUBDIRECTORIES */
8613     "    Create subdirectories automatically if the server sends files",
8614     "    recursively and includes pathnames (most don't).",
8615 #endif /* RECURSIVE */
8616     "  /RENAME-TO:text",
8617     "    Each file that is downloaded is to be renamed as indicated just,",
8618     "    after, and only if, it has arrived successfully.",
8619 #ifndef NOCSETS
8620     "  /SERVER-CHARACTER-SET:name",
8621     "    When downloading in text mode and character-set conversion is desired"
8622 ,   "    this specifies the original file's character set on the server.",
8623 #endif /* NOCSETS */
8624     "  /SERVER-RENAME:text",
8625     "    Each server source file is to be renamed on the server as indicated",
8626     "    immediately after, but only if, it has arrived succesfully.",
8627     "  /SMALLER-THAN:number",
8628     "    Download only those files smaller than the given number of bytes.",
8629     "  /TEXT",                          /* /ASCII */
8630     "    Force text mode.  Synonym: /ASCII.",
8631     "  /TENEX",
8632     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
8633 #ifndef NOCSETS
8634     "  /TRANSPARENT",
8635     "    When downloading in text mode, do not convert chracter-sets.",
8636 #endif /* NOCSETS */
8637     "  /TO-SCREEN",
8638     "    The downloaded file is to be displayed on the screen.",
8639 #ifdef DOUPDATE
8640     "  /UPDATE",
8641     "    Equivalent to /COLLISION:UPDATE.  Download only those files that are",
8642     "    newer than than their local counterparts, or that do not exist on",
8643     "    the local computer.",
8644 #endif /* DOUPDATE */
8645     ""
8646 };
8647 static char * fhs_hlp[] = {             /* HELP */
8648     "Syntax: FTP HELP [ command [ subcommand... ] ]",
8649     "  Asks the FTP server for help about the given command.  First use",
8650     "  FTP HELP by itself to get a list of commands, then use HELP FTP xxx",
8651     "  to get help for command \"xxx\".  Synonyms: REMOTE HELP, RHELP.",
8652     ""
8653 };
8654 static char * fhs_idl[] = {             /* IDLE */
8655     "Syntax: FTP IDLE [ number ]",
8656     "  If given without a number, this asks the FTP server to tell its",
8657     "  current idle-time limit.  If given with a number, it asks the server",
8658     "  to change its idle-time limit to the given number of seconds.",
8659     ""
8660 };
8661 static char * fhs_usr[] = {             /* USER, LOGIN */
8662     "Syntax: FTP USER username [ password [ account ] ]",
8663     "  Log in to the FTP server.  To be used when connected but not yet",
8664     "  logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.",
8665     "  If you omit the password, and one is required by the server, you are",
8666     "  prompted for it.  If you omit the account, no account is sent.",
8667     "  Synonym: FTP LOGIN.",
8668     ""
8669 };
8670 static char * fhs_get[] = {             /* GET */
8671     "Syntax: [ FTP ] GET [ options ] filename [ as-name ]",
8672     "  Download a single file.  Asks the FTP server to send the given file.",
8673     "  The optional as-name is the name to store it under when it arrives;",
8674     "  if omitted, the file is stored with the name it arrived with, as",
8675     "  modified according to the FTP FILENAMES setting or /FILENAMES: switch",
8676     "  value.  Aside from the file list and as-name, syntax and options are",
8677     "  the same as for FTP MGET, which is used for downloading multiple files."
8678 ,   ""
8679 };
8680 static char * fhs_mkd[] = {             /* MKDIR */
8681     "Syntax: FTP MKDIR directory",
8682     "  Asks the FTP server to create a directory with the given name,",
8683     "  which must be in the syntax of the server's file system.  Synonyms:",
8684     "  REMOTE MKDIR, RMKDIR.",
8685     ""
8686 };
8687 static char * fhs_mod[] = {             /* MODTIME */
8688     "Syntax: FTP MODTIME filename",
8689     "  Asks the FTP server to send the modification time of the given file,",
8690     "  to be displayed on the screen.  The date-time format is all numeric:",
8691     "  yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating",
8692     "  fractions of seconds).",
8693     ""
8694 };
8695 static char * fhs_mpu[] = {             /* MPUT */
8696     "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]",
8697     "  Uploads files.  Sends the given file or files to the FTP server.",
8698     "  Also see FTP PUT.  Optional switches are:",
8699     " ",
8700     "  /AFTER:date-time",
8701     "    Uploads only those files newer than the given date-time.",
8702     "    HELP DATE for info about date-time formats.  Synonym: /SINCE.",
8703 #ifdef PUTARRAY
8704     "  /ARRAY:array-designator",
8705     "    Tells Kermit to upload the contents of the given array, rather than",
8706     "    a file.",
8707 #endif /* PUTARRAY */
8708     "  /AS-NAME:text",
8709     "    Name under which to send files.",
8710     "    Pattern required for for multiple files.",
8711     "  /BEFORE:date-time",
8712     "    Upload only those files older than the given date-time.",
8713     "  /BINARY",
8714     "    Force binary mode.  Synonym: /IMAGE.",
8715 #ifdef PUTPIPE
8716     "  /COMMAND",
8717     "    Specifies that the filespec is a command whose standard output is",
8718     "    to be sent.",
8719 #endif /* PUTPIPE */
8720
8721 #ifdef COMMENT
8722 #ifdef DOUPDATE
8723     "  /DATES-DIFFER",
8724     "    Upload only those files whose modification date-times differ from",
8725     "    those on the server, or that don't exist on the server at all.",
8726 #endif /* DOUPDATE */
8727 #endif /* COMMENT */
8728
8729     "  /DELETE",
8730     "    Specifies that each source file is to be deleted after, and only if,",
8731     "    it is successfully uploaded.",
8732     "  /DOTFILES",
8733     "    Include files whose names begin with period (.).",
8734     "  /ERROR-ACTION:{PROCEED,QUIT}",
8735     "    When uploading a group of files, what to do upon failure to",
8736     "    transfer a file: quit or proceed to the next one.",
8737     "  /EXCEPT:pattern",
8738     "    Exception list: don't upload any files that match this pattern.",
8739     "    See HELP WILDCARD for pattern syntax.",
8740     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
8741     "    Whether to convert outbound filenames to common syntax.",
8742 #ifdef PIPESEND
8743 #ifndef NOSPL
8744     "  /FILTER:command",
8745     "    Pass outbound files through the given command.",
8746 #endif /* NOSPL */
8747 #endif /* PIPESEND */
8748 #ifdef CKSYMLINK
8749     "  /FOLLOWINKS",
8750     "    Send files that are pointed to by symbolic links.",
8751     "  /NOFOLLOWINKS",
8752     "    Skip over symbolic links (default).",
8753 #endif /* CKSYMLINK */
8754     "  /LARGER-THAN:number",
8755     "    Only upload files that are larger than the given number of bytes.",
8756     "  /LISTFILE:filename",
8757     "    Obtain the list of files to upload from the given file.",
8758 #ifndef NOCSETS
8759     "  /LOCAL-CHARACTER-SET:name",
8760     "    When uploading in text mode and character-set conversion is",
8761     "    desired, this specifies the source-file character set.",
8762 #endif /* NOCSETS */
8763 #ifdef CK_TMPDIR
8764     "  /MOVE-TO:directory",
8765     "    Each source file that is uploaded is to be moved to the given local",
8766     "    directory when, and only if, the transfer is successful.",
8767 #endif /* CK_TMPDIR */
8768     "  /NOBACKUPFILES",
8769     "    Don't upload any files whose names end with .~<number>~.",
8770 #ifdef UNIXOROSK
8771     "  /NODOTFILES",
8772     "    Don't upload any files whose names begin with period (.).",
8773 #endif /* UNIXOROSK */
8774     "  /NOT-AFTER:date-time",
8775     "    Upload only files that are not newer than the given date-time",
8776     "  /NOT-BEFORE:date-time",
8777     "    Upload only files that are not older than the given date-time",
8778 #ifdef UNIX
8779     "  /PERMISSIONS",
8780     "    Ask the server to set the permissions of each file it receives",
8781     "    according to the source file's permissions.",
8782 #endif /* UNIX */
8783     "  /QUIET",
8784     "    Suppress the file-transfer display.",
8785 #ifdef FTP_RESTART
8786     "  /RECOVER",
8787     "    Resume an upload that was previously interrupted from the point of",
8788     "    failure.  Synonym: /RESTART.",
8789 #endif /* FTP_RESTART */
8790 #ifdef RECURSIVE
8791     "  /RECURSIVE",
8792     "    Send files from the given directory and all the directories beneath",
8793     "    it.  Synonym: /SUBDIRECTORIES.",
8794 #endif /* RECURSIVE */
8795     "  /RENAME-TO:text",
8796     "    Each source file that is uploaded is to be renamed on the local",
8797     "    local computer as indicated when and only if, the transfer completes",
8798     "    successfully.",
8799 #ifndef NOCSETS
8800     "  /SERVER-CHARACTER-SET:name",
8801     "    When uploading in text mode and character-set conversion is desired,",
8802     "    this specifies the character set to which the file should be",
8803     "    converted for storage on the server.",
8804 #endif /* NOCSETS */
8805     "  /SERVER-RENAME:text",
8806     "    Each file that is uploaded is to be renamed as indicated on the",
8807     "    server after, and only if, if arrives successfully.",
8808     "  /SIMULATE",
8809     "    Show which files would be sent without actually sending them.",
8810     "  /SMALLER-THAN:number",
8811     "    Upload only those files smaller than the given number of bytes.",
8812     "  /TEXT",
8813     "    Force text mode.  Synonym: /ASCII.",
8814     "  /TENEX",
8815     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
8816 #ifndef NOCSETS
8817     "  /TRANSPARENT",
8818     "    When uploading in text mode, do not convert chracter-sets.",
8819 #endif /* NOCSETS */
8820     "  /TYPE:{TEXT,BINARY}",
8821     "    Upload only files of the given type.",
8822 #ifdef DOUPDATE
8823     "  /UPDATE",
8824     "    If a file of the same name exists on the server, upload only if",
8825     "    the local file is newer.",
8826 #endif /* DOUPDATE */
8827     "  /UNIQUE-SERVER-NAMES",
8828     "    Ask the server to compute new names for any incoming file that has",
8829     "    the same name as an existing file.",
8830     ""
8831 };
8832 static char * fhs_opn[] = {             /* OPEN */
8833 #ifdef CK_SSL
8834     "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]",
8835     "  Opens a connection to the FTP server on the given host.  The default",
8836     "  TCP port is 21 (990 if SSL/TLS is used), but a different port number",
8837     "  can be supplied if necessary.  Optional switches are:",
8838 #else /* CK_SSL */
8839     "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]",
8840     "  Opens a connection to the FTP server on the given host.  The default",
8841     "  TCP port is 21, but a different port number can be supplied if",
8842     "  necessary.  Optional switches are:",
8843 #endif /* CK_SSL */
8844     " ",
8845     "  /ANONYMOUS",
8846     "    Logs you in anonymously.",
8847     "  /USER:text",
8848     "    Supplies the given text as your username.",
8849     "  /PASSWORD:text",
8850     "    Supplies the given text as your password.  If you include a username",
8851     "    but omit this switch and the server requires a password, you are",
8852     "    prompted for it.",
8853     "  /ACCOUNT:text",
8854     "    Supplies the given text as your account, if required by the server.",
8855     "  /ACTIVE",
8856     "    Forces an active (rather than passive) connection.",
8857     "  /PASSIVE",
8858     "    Forces a passive (rather than active) connection.",
8859     "  /NOINIT",
8860     "    Inhibits sending initial REST, STRU, and MODE commands, which are",
8861     "    well-known standard commands, but to which some servers react badly.",
8862     "  /NOLOGIN",
8863     "    Inhibits autologin for this connection only.",
8864     ""
8865 };
8866 static char * fhs_opt[] = {             /* OPTS, OPTIONS */
8867     "Syntax: FTP OPTIONS",
8868     "  Asks the FTP server to list its current options.  Advanced, new,",
8869     "  not supported by most FTP servers.",
8870     ""
8871 };
8872 static char * fhs_put[] = {             /* PUT, SEND */
8873     "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]",
8874     "  Like FTP MPUT, but only one filespec is allowed, and if it is followed",
8875     "  by an additional field, this is interpreted as the name under which",
8876     "  to send the file or files.  See HELP FTP MPUT.",
8877     ""
8878 };
8879 static char * fhs_pwd[] = {             /* PWD */
8880     "Syntax: FTP PWD",
8881     "  Asks the FTP server to reveal its current working directory.",
8882     "  Synonyms: REMOTE PWD, RPWD.",
8883     ""
8884 };
8885 static char * fhs_quo[] = {             /* QUOTE */
8886     "Syntax: FTP QUOTE text",
8887     "  Sends an FTP protocol command to the FTP server.  Use this command",
8888     "  for sending commands that Kermit might not support.",
8889     ""
8890 };
8891 static char * fhs_rge[] = {             /* REGET */
8892     "Syntax: FTP REGET",
8893     "  Synonym for FTP GET /RECOVER.",
8894     ""
8895 };
8896 static char * fhs_ren[] = {             /* RENAME */
8897     "Syntax: FTP RENAME name1 name1",
8898     "  Asks the FTP server to change the name of the file whose name is name1",
8899     "  and which resides in the FTP server's file system, to name2.  Works",
8900     "  only for single files; wildcards are not accepted.",
8901     ""
8902 };
8903 static char * fhs_res[] = {             /* RESET */
8904     "Syntax: FTP RESET",
8905     "  Asks the server to log out your session, terminating your access",
8906     "  rights, without closing the connection.",
8907     ""
8908 };
8909 static char * fhs_rmd[] = {             /* RMDIR */
8910     "Syntax: FTP RMDIR directory",
8911     "  Asks the FTP server to remove the directory whose name is given.",
8912     "  This usually requires the directory to be empty.  Synonyms: REMOTE",
8913     "  RMDIR, RRMDIR.",
8914     ""
8915 };
8916 static char * fhs_sit[] = {             /* SITE */
8917     "Syntax: FTP SITE text",
8918     "  Sends a site-specific command to the FTP server.",
8919     ""
8920 };
8921 static char * fhs_siz[] = {             /* SIZE */
8922     "Syntax: FTP SIZE filename",
8923     "  Asks the FTP server to send a numeric string representing the size",
8924     "  of the given file.",
8925     ""
8926 };
8927 static char * fhs_sta[] = {             /* STATUS */
8928     "Syntax: FTP STATUS [ filename ]",
8929     "  Asks the FTP server to report its status.  If a filename is given,",
8930     "  the FTP server should report details about the file.",
8931     ""
8932 };
8933 static char * fhs_sys[] = {             /* SYSTEM */
8934     "Syntax: FTP SYSTEM",
8935     "  Asks the FTP server to report its operating system type.",
8936     ""
8937 };
8938 static char * fhs_typ[] = {             /* TYPE */
8939     "Syntax: FTP TYPE { TEXT, BINARY, TENEX }",
8940     "  Puts the client and server in the indicated transfer mode.",
8941     "  ASCII is a synonym for TEXT.  TENEX is used only for uploading 8-bit",
8942     "  binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or",
8943     "  downloading files from TENEX or TOPS-20 that have been uploaded in",
8944     "  TENEX mode.",
8945     ""
8946 };
8947 static char * fhs_uma[] = {             /* UMASK */
8948     "Syntax: FTP UMASK number",
8949     "  Asks the FTP server to set its file creation mode mask.  Applies",
8950     "  only (or mainly) to UNIX-based FTP servers.",
8951     ""
8952 };
8953 static char * fhs_chk[] = {             /* CHECK */
8954     "Syntax: FTP CHECK remote-filespec",
8955     "  Asks the FTP server if the given file or files exist.  If the",
8956     "  remote-filespec contains wildcards, this command fails if no server",
8957     "  files match, and succeeds if at least one file matches.  If the",
8958     "  remote-filespec does not contain wildcards, this command succeeds if",
8959     "  the given file exists and fails if it does not.",
8960     ""
8961 };
8962 static char * fhs_ena[] = {             /* ENABLE */
8963     "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
8964     "  Enables the use of the given FTP protocol command in case it has been",
8965     "  disabled (but this is no guarantee that the FTP server understands it)."
8966 ,
8967     "  Use SHOW FTP to see which of these commands is enabled and disabled.",
8968     "  Also see FTP DISABLE.",
8969     ""
8970 };
8971 static char * fhs_dis[] = {             /* DISABLE */
8972     "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
8973     "  Disables the use of the given FTP protocol command.",
8974     "  Also see FTP ENABLE.",
8975     ""
8976 };
8977
8978 #endif /* NOHELP */
8979
8980 int
8981 doftphlp() {
8982     int cx;
8983     if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0)
8984       if (cx != -3)
8985         return(cx);
8986     if ((x = cmcfm()) < 0)
8987       return(x);
8988
8989 #ifdef NOHELP
8990     printf("Sorry, no help available\n");
8991 #else
8992     switch (cx) {
8993       case -3:
8994         return(hmsga(fhs_ftp));
8995       case FTP_ACC:                     /* ACCOUNT */
8996         return(hmsga(fhs_acc));
8997       case FTP_APP:                     /* APPEND */
8998         return(hmsga(fhs_app));
8999       case FTP_CLS:                     /* BYE, CLOSE */
9000         return(hmsga(fhs_cls));
9001       case FTP_CWD:                     /* CD, CWD */
9002         return(hmsga(fhs_cwd));
9003       case FTP_GUP:                     /* CDUP, UP */
9004         return(hmsga(fhs_gup));
9005       case FTP_CHM:                     /* CHMOD */
9006         return(hmsga(fhs_chm));
9007       case FTP_MDE:                     /* DELETE, MDELETE */
9008         return(hmsga(fhs_mde));
9009       case FTP_DIR:                     /* DIRECTORY */
9010         return(hmsga(fhs_dir));
9011       case FTP_VDI:                     /* VDIRECTORY */
9012         return(hmsga(fhs_vdi));
9013       case FTP_FEA:                     /* FEATURES */
9014         return(hmsga(fhs_fea));
9015       case FTP_GET:                     /* GET */
9016         return(hmsga(fhs_get));
9017       case FTP_HLP:                     /* HELP */
9018         return(hmsga(fhs_hlp));
9019       case FTP_IDL:                     /* IDLE */
9020         return(hmsga(fhs_idl));
9021       case FTP_USR:                     /* USER, LOGIN */
9022         return(hmsga(fhs_usr));
9023       case FTP_MGE:                     /* MGET */
9024         return(hmsga(fhs_mge));
9025       case FTP_MKD:                     /* MKDIR */
9026         return(hmsga(fhs_mkd));
9027       case FTP_MOD:                     /* MODTIME */
9028         return(hmsga(fhs_mod));
9029       case FTP_MPU:                     /* MPUT */
9030         return(hmsga(fhs_mpu));
9031       case FTP_OPN:                     /* OPEN */
9032         return(hmsga(fhs_opn));
9033       case FTP_OPT:                     /* OPTS, OPTIONS */
9034         return(hmsga(fhs_opt));
9035       case FTP_PUT:                     /* PUT, SEND */
9036         return(hmsga(fhs_put));
9037       case FTP_PWD:                     /* PWD */
9038         return(hmsga(fhs_pwd));
9039       case FTP_QUO:                     /* QUOTE */
9040         return(hmsga(fhs_quo));
9041       case FTP_RGE:                     /* REGET */
9042         return(hmsga(fhs_rge));
9043       case FTP_REN:                     /* RENAME */
9044         return(hmsga(fhs_ren));
9045       case FTP_RES:                     /* RESET */
9046         return(hmsga(fhs_res));
9047       case FTP_RMD:                     /* RMDIR */
9048         return(hmsga(fhs_rmd));
9049       case FTP_SIT:                     /* SITE */
9050         return(hmsga(fhs_sit));
9051       case FTP_SIZ:                     /* SIZE */
9052         return(hmsga(fhs_siz));
9053       case FTP_STA:                     /* STATUS */
9054         return(hmsga(fhs_sta));
9055       case FTP_SYS:                     /* SYSTEM */
9056         return(hmsga(fhs_sys));
9057       case FTP_TYP:                     /* TYPE */
9058         return(hmsga(fhs_typ));
9059       case FTP_UMA:                     /* UMASK */
9060         return(hmsga(fhs_uma));
9061       case FTP_CHK:                     /* CHECK */
9062         return(hmsga(fhs_chk));
9063       case FTP_ENA:
9064         return(hmsga(fhs_ena));
9065       case FTP_DIS:
9066         return(hmsga(fhs_dis));
9067       default:
9068         printf("Sorry, help available for this command.\n");
9069         break;
9070     }
9071 #endif /* NOHELP */
9072     return(success = 0);
9073 }
9074
9075 int
9076 dosetftphlp() {
9077     int cx;
9078     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0)
9079       if (cx != -3)
9080         return(cx);
9081     if (cx != -3)
9082       ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ);
9083     if ((x = cmcfm()) < 0)
9084       return(x);
9085
9086 #ifdef NOHELP
9087     printf("Sorry, no help available\n");
9088 #else
9089     switch (cx) {
9090       case -3:
9091         printf("\nSyntax: SET FTP parameter value\n");
9092         printf("  Type \"help set ftp ?\" for a list of parameters.\n");
9093         printf("  Type \"help set ftp xxx\" for information about setting\n");
9094         printf("  parameter xxx.  Type \"show ftp\" for current values.\n\n");
9095         return(0);
9096
9097       case FTS_BUG:
9098         printf("\nSyntax: SET FTP BUG <name> {ON, OFF}\n");
9099         printf(
9100             "  Activates a workaround for the named bug in the FTP server.\n");
9101         printf("  Type SET FTP BUG ? for a list of names.\n");
9102         printf("  For each bug, the default is OFF\n\n");
9103         return(0);
9104
9105 #ifdef FTP_SECURITY
9106       case FTS_ATP:                     /* "authtype" */
9107         printf("\nSyntax: SET FTP AUTHTYPE list\n");
9108         printf("  Specifies an ordered list of authentication methods to be\n"
9109                );
9110         printf("  when FTP AUTOAUTHENTICATION is ON.  The default list is:\n");
9111         printf("  GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n");
9112         return(0);
9113
9114       case FTS_AUT:                     /* "autoauthentication" */
9115         printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n");
9116         printf("  Tells whether authentication should be negotiated by the\n");
9117         printf("  FTP OPEN command.  Default is ON.\n\n");
9118         break;
9119
9120       case FTS_CRY:                     /* "autoencryption" */
9121         printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n");
9122         printf("  Tells whether encryption (privacy) should be negotiated\n");
9123         printf("  by the FTP OPEN command.  Default is ON.\n\n");
9124         break;
9125 #endif /* FTP_SECURITY */
9126
9127       case FTS_LOG:                     /* "autologin" */
9128         printf("\nSET FTP AUTOLOGIN { ON, OFF }\n");
9129         printf("  Tells Kermit whether to try to log you in automatically\n");
9130         printf("  as part of the connection process.\n\n");
9131         break;
9132
9133       case FTS_DIS:
9134         printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n");
9135         printf("  Chooses the file-transfer display style for FTP.\n");
9136         printf("  Like SET TRANSFER DISPLAY but applies only to FTP.\n\n");
9137         break;
9138
9139 #ifndef NOCSETS
9140       case FTS_XLA:                     /* "character-set-translation" */
9141         printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n");
9142         printf("  Whether to translate character sets when transferring\n");
9143         printf("  text files with FTP.  OFF by default.\n\n");
9144         break;
9145
9146 #endif /* NOCSETS */
9147       case FTS_FNC:                     /* "collision" */
9148         printf("\n");
9149         printf(
9150 "Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n"
9151                );
9152         printf("  Tells what do when an incoming file has the same name as\n");
9153         printf("  an existing file when downloading with FTP.\n\n");
9154         break;
9155
9156 #ifdef FTP_SECURITY
9157       case FTS_CPL:                     /* "command-protection-level" */
9158         printf("\n");
9159         printf(
9160 "Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9161                );
9162         printf("\n");
9163         printf(
9164 "  Tells what level of protection is applied to the FTP command channel.\n\n");
9165         break;
9166       case FTS_CFW:                     /* "credential-forwarding" */
9167         printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n");
9168         printf("  Tells whether end-user credentials are to be forwarded\n");
9169         printf("  to the server if supported by the authentication method\n");
9170         printf("  (GSSAPI-KRB5 only).\n\n");
9171         break;
9172       case FTS_DPL:                     /* "data-protection-level" */
9173         printf("\n");
9174         printf(
9175 "Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9176                );
9177         printf("\n");
9178         printf(
9179 "  Tells what level of protection is applied to the FTP data channel.\n\n");
9180         break;
9181 #endif /* FTP_SECURITY */
9182
9183       case FTS_DBG:                     /* "debug" */
9184         printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n");
9185         printf("  Whether to print FTP protocol messages.\n\n");
9186         return(0);
9187
9188       case FTS_ERR:                     /* "error-action" */
9189         printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n");
9190         printf("  What to do when an error occurs when transferring a group\n")
9191           ;
9192         printf("  of files: quit and fail, or proceed to the next file.\n\n");
9193         return(0);
9194
9195       case FTS_CNV:                     /* "filenames" */
9196         printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n");
9197         printf("  What to do with filenames: convert them, take and use them\n"
9198                );
9199         printf("  literally; or choose what to do automatically based on the\n"
9200                );
9201         printf("  OS type of the server.  The default is AUTO.\n\n");
9202         return(0);
9203
9204       case FTS_PSV:                     /* "passive-mode" */
9205         printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n");
9206         printf("  Whether to use passive mode, which helps to get through\n");
9207         printf("  firewalls.  ON by default.\n\n");
9208         return(0);
9209
9210       case FTS_PRM:                     /* "permissions" */
9211         printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n");
9212         printf("  Whether to try to send file permissions when uploading.\n");
9213         printf("  OFF by default.  AUTO means only if client and server\n");
9214         printf("  have the same OS type.\n\n");
9215         return(0);
9216
9217       case FTS_TST:                     /* "progress-messages" */
9218         printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n");
9219         printf("  Whether Kermit should print locally-generated feedback\n");
9220         printf("  messages for each non-file-transfer command.");
9221         printf("  ON by default.\n\n");
9222         return(0);
9223
9224       case FTS_SPC:                     /* "send-port-commands" */
9225         printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n");
9226         printf("  Whether Kermit should send a new PORT command for each");
9227         printf("  task.\n\n");
9228         return(0);
9229
9230 #ifndef NOCSETS
9231       case FTS_CSR:                     /* "server-character-set" */
9232         printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n");
9233         printf("  The name of the character set used for text files on the\n");
9234         printf("  server.  Enter a name of '?' for a menu.\n\n");
9235         return(0);
9236 #endif /* NOCSETS */
9237
9238       case FTS_STO:                     /* "server-time-offset */
9239         printf(
9240 "\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n");
9241         printf(
9242 "  Specifies an offset to apply to the server's file timestamps.\n");
9243         printf(
9244 "  Use this to correct for misconfigured server time or timezone.\n");
9245         printf(
9246 "  Format: must begin with + or - sign.  Hours must be given; minutes\n");
9247         printf(
9248 "  and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n");
9249         return(0);
9250
9251       case FTS_TYP:                     /* "type" */
9252         printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n");
9253         printf("  Establishes the default transfer mode.\n");
9254         printf("  TENEX is used for uploading 8-bit binary files to 36-bit\n");
9255         printf("  platforms such as TENEX and TOPS-20 and for downloading\n");
9256         printf("  them again.\n\n");
9257         return(0);
9258
9259 #ifdef PATTERNS
9260       case FTS_GFT:
9261         printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n");
9262         printf("  Tells whether GET and MGET should automatically switch\n");
9263         printf("  the appropriate file type, TEXT, BINARY, or TENEX, by\n");
9264         printf("  matching the name of each incoming file with its list of\n");
9265         printf("  FILE TEXT-PATTERNS and FILE BINARY-PATTERNS.  ON by\n");
9266         printf("  default.  SHOW PATTERNS displays the current pattern\n");
9267         printf("  list.  HELP SET FILE to see how to change it.\n");
9268         break;
9269 #endif /* PATTERNS */
9270
9271       case FTS_USN:                     /* "unique-server-names" */
9272         printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n");
9273         printf("  Tells whether to ask the server to create unique names\n");
9274         printf("  for any uploaded file that has the same name as an\n");
9275         printf("  existing file.  Default is OFF.\n\n");
9276         return(0);
9277
9278       case FTS_VBM:                     /* "verbose-mode" */
9279         printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n");
9280         printf("  Whether to display all responses from the FTP server.\n");
9281         printf("  OFF by default.\n\n");
9282         return(0);
9283
9284       case FTS_DAT:
9285         printf("\nSyntax: SET FTP DATES { ON, OFF }\n");
9286         printf("  Whether to set date of incoming files from the file date\n");
9287         printf("  on the server.  ON by default.  Note: there is no way to\n")
9288           ;
9289         printf("  set the date on files uploaded to the server.  Also note\n");
9290         printf("  that not all servers support this feature.\n\n");
9291         return(0);
9292
9293       case FTS_APW:
9294         printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n");
9295         printf("  Password to supply automatically on anonymous FTP\n");
9296         printf("  connections instead of the default user@host.\n");
9297         printf("  Omit optional text to restore default.\n\n");
9298         return(0);
9299
9300       default:
9301         printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf);
9302     }
9303 #endif /* NOHELP */
9304     return(0);
9305 }
9306
9307 #ifndef L_SET
9308 #define L_SET 0
9309 #endif /* L_SET */
9310 #ifndef L_INCR
9311 #define L_INCR 1
9312 #endif /* L_INCR */
9313
9314 #ifdef FTP_SRP
9315 char srp_user[BUFSIZ];                  /* where is BUFSIZ defined? */
9316 char *srp_pass;
9317 char *srp_acct;
9318 #endif /* FTP_SRP */
9319
9320 static int kerror;                      /* Needed for all auth types */
9321
9322 static struct   sockaddr_in hisctladdr;
9323 static struct   sockaddr_in hisdataaddr;
9324 static struct   sockaddr_in data_addr;
9325 static int      data = -1;
9326 static int      ptflag = 0;
9327 static struct   sockaddr_in myctladdr;
9328
9329 #ifdef COMMENT
9330 #ifndef OS2
9331 UID_T getuid();
9332 #endif /* OS2 */
9333 #endif /* COMMENT */
9334
9335
9336 static int cpend = 0;                   /* No pending replies */
9337
9338 #ifdef CK_SSL
9339 extern SSL *ssl_ftp_con;
9340 extern SSL_CTX *ssl_ftp_ctx;
9341 extern SSL *ssl_ftp_data_con;
9342 extern int ssl_ftp_active_flag;
9343 extern int ssl_ftp_data_active_flag;
9344 #endif /* CK_SSL */
9345
9346 /*  f t p c m d  --  Send a command to the FTP server  */
9347 /*
9348   Call with:
9349     char * cmd: The command to send.
9350     char * arg: The argument (e.g. a filename).
9351     int lcs: The local character set index.
9352     int rcs: The remote (server) character set index.
9353     int vbm: Verbose mode:
9354       0 = force verbosity off
9355      >0 = force verbosity on
9356
9357   If arg is given (not NULL or empty) and lcs != rcs and both are > -1,
9358   and neither lcs or rcs is UCS-2, the arg is translated from the local
9359   character set to the remote one before sending the result to the server.
9360
9361    Returns:
9362     0 on failure with ftpcode = -1
9363     >= 0 on success (getreply() result) with ftpcode = 0.
9364 */
9365 static char xcmdbuf[RFNBUFSIZ];
9366
9367 static int
9368 ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; {
9369     char * s = NULL;
9370     int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1;
9371     sig_t oldintr;
9372
9373     if (ftp_deb)                        /* DEBUG */
9374       vbm = 1;
9375     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
9376       vbm = 0;
9377     else if (vbm < 0)                   /* VERBOSE */
9378       vbm = ftp_vbm;
9379
9380     cancelfile = 0;
9381     if (!cmd) cmd = "";
9382     if (!arg) arg = "";
9383     cmdlen = (int)strlen(cmd);
9384     len = cmdlen + (int)strlen(arg) + 1;
9385
9386     if (ftp_deb /* && !dpyactive */ ) {
9387 #ifdef FTP_PROXY
9388         if (ftp_prx) printf("%s ", ftp_host);
9389 #endif /* FTP_PROXY */
9390         printf("---> ");
9391         if (!anonymous && strcmp("PASS",cmd) == 0)
9392           printf("PASS XXXX");
9393         else
9394           printf("%s %s",cmd,arg);
9395         printf("\n");
9396     }
9397     /* bzero(xcmdbuf,RFNBUFSIZ); */
9398     ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL);
9399
9400 #ifdef DEBUG
9401     if (deblog) {
9402         debug(F110,"ftpcmd cmd",cmd,0);
9403         debug(F110,"ftpcmd arg",arg,0);
9404         debug(F101,"ftpcmd lcs","",lcs);
9405         debug(F101,"ftpcmd rcs","",rcs);
9406     }
9407 #endif /* DEBUG */
9408
9409     if (csocket == -1) {
9410         perror("No control connection for command");
9411         ftpcode = -1;
9412         return(0);
9413     }
9414     havesigint = 0;
9415     oldintr = signal(SIGINT, cmdcancel);
9416
9417 #ifndef NOCSETS
9418     if (*arg &&                         /* If an arg was given */
9419         lcs > -1 &&                     /* and a local charset */
9420         rcs > -1 &&                     /* and a remote charset */
9421         lcs != rcs &&                   /* and the two are not the same */
9422         lcs != FC_UCS2 &&               /* and neither one is UCS-2 */
9423         rcs != FC_UCS2                  /* ... */
9424         ) {
9425         initxlate(lcs,rcs);             /* Translate arg from lcs to rcs */
9426         xgnbp = arg;                    /* Global pointer to input string */
9427         rfnptr = rfnbuf;                /* Global pointer to output buffer */
9428
9429         while (1) {
9430             if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break;
9431             if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break;
9432         }
9433         /*
9434           We have to copy here instead of translating directly into
9435           xcmdbuf[] so strputc() can check length.  Alternatively we could
9436           write yet another xpnbyte() output function.
9437         */
9438         if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) {
9439             printf("?FTP command too long: %s + arg\n",cmd);
9440             ftpcode = -1;
9441             return(0);
9442         }
9443         x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1));
9444     }
9445 #endif /* NOCSETS */
9446
9447     s = xcmdbuf;                        /* Command to send to server */
9448
9449 #ifdef DEBUG
9450     if (deblog) {                       /* Log it */
9451         if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) {
9452             /* But don't log passwords */
9453             debug(F110,"FTP SENT ","PASS XXXX",0);
9454         } else {
9455             debug(F110,"FTP SENT ",s,0);
9456         }
9457     }
9458 #endif /* DEBUG */
9459
9460 #ifdef CK_ENCRYPTION
9461   again:
9462 #endif /* CK_ENCRYPTION */
9463     if (scommand(s) == 0) {              /* Send it. */
9464       signal(SIGINT, oldintr);
9465       return(0);
9466     }
9467     cpend = 1;
9468     x = !strcmp(cmd,"QUIT");            /* Is it the QUIT command? */
9469     if (x)                              /* In case we're interrupted */
9470       connected = 0;                    /* while waiting for the reply... */
9471
9472     fc = 0;                             /* Function code for getreply() */
9473     if (!strncmp(cmd,"AUTH ",5)         /* Must parse AUTH reply */
9474 #ifdef FTPHOST
9475         && strncmp(cmd, "HOST ",5)
9476 #endif /* FTPHOST */
9477         ) {
9478         fc = GRF_AUTH;
9479     } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */
9480         fc = GRF_FEAT;                  /* But FEAT not widely understood */
9481         if (!ftp_deb)                   /* So suppress error messages */
9482           vbm = 9;
9483     }
9484     r = getreply(x,                     /* Expect connection to close */
9485                  lcs,rcs,               /* Charsets */
9486                  vbm,                   /* Verbosity */
9487                  fc                     /* Function code */
9488                  );
9489     if (q > -1)
9490       quiet = q;
9491
9492 #ifdef CK_ENCRYPTION
9493     if (ftpcode == 533 && ftp_cpl == FPL_PRV) {
9494         fprintf(stderr,
9495                "ENC command not supported at server; retrying under MIC...\n");
9496         ftp_cpl = FPL_SAF;
9497         goto again;
9498     }
9499 #endif /* CK_ENCRYPTION */
9500 #ifdef COMMENT
9501     if (cancelfile && oldintr != SIG_IGN)
9502       (*oldintr)(SIGINT);
9503 #endif /* COMMENT */
9504     signal(SIGINT, oldintr);
9505     return(r);
9506 }
9507
9508 static VOID
9509 lostpeer() {
9510     debug(F100,"lostpeer","",0);
9511     if (connected) {
9512         if (csocket != -1) {
9513 #ifdef CK_SSL
9514             if (ssl_ftp_active_flag) {
9515                 SSL_shutdown(ssl_ftp_con);
9516                 SSL_free(ssl_ftp_con);
9517                 ssl_ftp_proxy = 0;
9518                 ssl_ftp_active_flag = 0;
9519                 ssl_ftp_con = NULL;
9520             }
9521 #endif /* CK_SSL */
9522 #ifdef TCPIPLIB
9523             socket_close(csocket);
9524 #else /* TCPIPLIB */
9525 #ifdef USE_SHUTDOWN
9526             shutdown(csocket, 1+1);
9527 #endif /* USE_SHUTDOWN */
9528             close(csocket);
9529 #endif /* TCPIPLIB */
9530             csocket = -1;
9531         }
9532         if (data != -1) {
9533 #ifdef CK_SSL
9534             if (ssl_ftp_data_active_flag) {
9535                 SSL_shutdown(ssl_ftp_data_con);
9536                 SSL_free(ssl_ftp_data_con);
9537                 ssl_ftp_data_active_flag = 0;
9538                 ssl_ftp_data_con = NULL;
9539             }
9540 #endif /* CK_SSL */
9541 #ifdef TCPIPLIB
9542             socket_close(data);
9543 #else /* TCPIPLIB */
9544 #ifdef USE_SHUTDOWN
9545             shutdown(data, 1+1);
9546 #endif /* USE_SHUTDOWN */
9547             close(data);
9548 #endif /* TCPIPLIB */
9549             data = -1;
9550             globaldin = -1;
9551         }
9552         connected = 0;
9553         anonymous = 0;
9554         loggedin = 0;
9555         auth_type = NULL;
9556         ftp_cpl = ftp_dpl = FPL_CLR;
9557 #ifdef CKLOGDIAL
9558         ftplogend();
9559 #endif /* CKLOGDIAL */
9560
9561 #ifdef LOCUS
9562         if (autolocus)                  /* Auotomatic locus switching... */
9563           setlocus(1,1);                /* Switch locus to local. */
9564 #endif /* LOCUS */
9565 #ifdef OS2
9566         DialerSend(OPT_KERMIT_HANGUP, 0);
9567 #endif /* OS2 */
9568     }
9569 #ifdef FTP_PROXY
9570     pswitch(1);
9571     if (connected) {
9572         if (csocket != -1) {
9573 #ifdef TCPIPLIB
9574             socket_close(csocket);
9575 #else /* TCPIPLIB */
9576 #ifdef USE_SHUTDOWN
9577             shutdown(csocket, 1+1);
9578 #endif /* USE_SHUTDOWN */
9579             close(csocket);
9580 #endif /* TCPIPLIB */
9581             csocket = -1;
9582         }
9583         connected = 0;
9584         anonymous = 0;
9585         loggedin = 0;
9586         auth_type = NULL;
9587         ftp_cpl = ftp_dpl = FPL_CLR;
9588     }
9589     proxflag = 0;
9590     pswitch(0);
9591 #endif /* FTP_PROXY */
9592 }
9593
9594 int
9595 ftpisopen() {
9596     return(connected);
9597 }
9598
9599 static int
9600 ftpclose() {
9601     extern int quitting;
9602     if (!connected)
9603       return(0);
9604     if (!ftp_vbm && !quiet) printlines = 1;
9605     ftpcmd("QUIT",NULL,0,0,ftp_vbm);
9606     if (csocket) {
9607 #ifdef CK_SSL
9608         if (ssl_ftp_active_flag) {
9609             SSL_shutdown(ssl_ftp_con);
9610             SSL_free(ssl_ftp_con);
9611             ssl_ftp_proxy = 0;
9612             ssl_ftp_active_flag = 0;
9613             ssl_ftp_con = NULL;
9614         }
9615 #endif /* CK_SSL */
9616 #ifdef TCPIPLIB
9617         socket_close(csocket);
9618 #else /* TCPIPLIB */
9619 #ifdef USE_SHUTDOWN
9620         shutdown(csocket, 1+1);
9621 #endif /* USE_SHUTDOWN */
9622         close(csocket);
9623 #endif /* TCPIPLIB */
9624     }
9625     csocket = -1;
9626     connected = 0;
9627     anonymous = 0;
9628     loggedin = 0;
9629     mdtmok = 1;
9630     sizeok = 1;
9631     featok = 1;
9632     stouarg = 1;
9633     typesent = 0;
9634     data = -1;
9635     globaldin = -1;
9636 #ifdef FTP_PROXY
9637     if (!proxy)
9638       macnum = 0;
9639 #endif /* FTP_PROXY */
9640     auth_type = NULL;
9641     ftp_dpl = FPL_CLR;
9642 #ifdef CKLOGDIAL
9643     ftplogend();
9644 #endif /* CKLOGDIAL */
9645 #ifdef LOCUS
9646     /* Unprefixed file management commands are executed locally */
9647     if (autolocus && !ftp_cmdlin && !quitting) {
9648         setlocus(1,1);
9649     }
9650 #endif /* LOCUS */
9651 #ifdef OS2
9652     DialerSend(OPT_KERMIT_HANGUP, 0);
9653 #endif /* OS2 */
9654     return(0);
9655 }
9656
9657 int
9658 ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; {
9659     char * host;
9660
9661     if (connected) {
9662         printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host);
9663         ftpcode = -1;
9664         return(0);
9665     }
9666 #ifdef FTPHOST
9667     hostcmd = 0;
9668 #endif /* FTPHOST */
9669     alike = 0;
9670     ftp_srvtyp[0] = NUL;
9671     if (!service) service = "";
9672     if (!*service) service = use_tls ? "ftps" : "ftp";
9673
9674     if (!isdigit(service[0])) {
9675         struct servent *destsp;
9676         destsp = getservbyname(service, "tcp");
9677         if (!destsp) {
9678             if (!ckstrcmp(service,"ftp",-1,0)) {
9679                 ftp_port = 21;
9680             } else if (!ckstrcmp(service,"ftps",-1,0)) {
9681                 ftp_port = 990;
9682             } else {
9683                 printf("?Bad port name - \"%s\"\n", service);
9684                 ftpcode = -1;
9685                 return(0);
9686             }
9687         } else {
9688             ftp_port = destsp->s_port;
9689             ftp_port = ntohs(ftp_port);
9690         }
9691     } else
9692         ftp_port = atoi(service);
9693     if (ftp_port <= 0) {
9694         printf("?Bad port name - \"%s\"\n", service);
9695         ftpcode = -1;
9696         return(0);
9697     }
9698     host = ftp_hookup(remote, ftp_port, use_tls);
9699     if (host) {
9700         ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN);
9701         connected = 1;                  /* Set FTP defaults */
9702         ftp_cpl = ftp_dpl = FPL_CLR;
9703         curtype = FTT_ASC;              /* Server uses ASCII mode */
9704         form = FORM_N;
9705         mode = MODE_S;
9706         stru = STRU_F;
9707         strcpy(bytename, "8");
9708         bytesize = 8;
9709
9710 #ifdef FTP_SECURITY
9711         if (ftp_aut) {
9712             if (ftp_auth()) {
9713                 if (ftp_cry 
9714 #ifdef OS2
9715                      && ck_crypt_is_installed()
9716 #endif /* OS2 */
9717                      ) {
9718                     if (!quiet)
9719                       printf("FTP Command channel is Private (encrypted)\n");
9720                     ftp_cpl = FPL_PRV;
9721                     if (setpbsz(DEFAULT_PBSZ) < 0) {
9722                         /* a failure here is most likely caused by a mixup */
9723                         /* in the session key used by client and server    */
9724                         printf("?Protection buffer size negotiation failed\n");
9725                         return(0);
9726                     }
9727                     if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) {
9728                         if (!quiet)
9729                           printf("FTP Data channel is Private (encrypted)\n");
9730                         ftp_dpl = FPL_PRV;
9731                     } else
9732                       printf("?Unable to enable encryption on data channel\n");
9733                 } else {
9734                     ftp_cpl = FPL_SAF;
9735                 }
9736             }
9737             if (!connected)
9738               goto fail;
9739         }
9740 #endif /* FTP_SECURITY */
9741         if (ftp_log)                    /* ^^^ */
9742           ftp_login(remote);
9743
9744         if (!connected)
9745           goto fail;
9746
9747 #ifdef CKLOGDIAL
9748         dologftp();
9749 #endif /* CKLOGDIAL */
9750 #ifdef OS2
9751         DialerSend(OPT_KERMIT_CONNECT, 0);
9752 #endif /* OS2 */
9753         passivemode = ftp_psv;
9754         sendport = ftp_spc;
9755         mdtmok = 1;
9756         sizeok = 1;
9757         stouarg = 1;
9758         typesent = 0;
9759
9760         if (ucbuf == NULL) {
9761             actualbuf = DEFAULT_PBSZ;
9762             while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL)
9763               actualbuf >>= 2;
9764         }
9765         if (!maxbuf)
9766           ucbufsiz = actualbuf - FUDGE_FACTOR;
9767         debug(F101,"ftpopen ucbufsiz","",ucbufsiz);
9768         return(1);
9769     }
9770   fail:
9771     printf("?Can't FTP connect to %s:%s\n",remote,service);
9772     ftpcode = -1;
9773     return(0);
9774 }
9775
9776 #ifdef CK_SSL
9777 int
9778 ssl_auth() {
9779     int i;
9780     char* p;
9781
9782     if (ssl_debug_flag) {
9783         fprintf(stderr,"SSL DEBUG ACTIVE\n");
9784         fflush(stderr);
9785         /* for the moment I want the output on screen */
9786     }
9787     if (ssl_ftp_data_con != NULL) {
9788         SSL_free(ssl_ftp_data_con);
9789         ssl_ftp_data_con = NULL;
9790     }
9791     if (ssl_ftp_con != NULL) {
9792         SSL_free(ssl_ftp_con);
9793         ssl_ftp_con=NULL;
9794     }
9795     if (ssl_ftp_ctx != NULL) {
9796         SSL_CTX_free(ssl_ftp_ctx);
9797         ssl_ftp_ctx = NULL;
9798     }
9799
9800     /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 
9801      * was added to OpenSSL 0.9.6e and 0.9.7.  It does not exist in previous
9802      * versions
9803      */
9804 #ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
9805 #define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L
9806 #endif
9807     if (auth_type && !strcmp(auth_type,"TLS")) {
9808         ssl_ftp_ctx=SSL_CTX_new(SSLv3_client_method());
9809         if (!ssl_ftp_ctx)
9810           return(0);
9811         SSL_CTX_set_options(ssl_ftp_ctx,
9812                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
9813                             );
9814     } else {
9815         ssl_ftp_ctx = SSL_CTX_new(ftp_bug_use_ssl_v2 ? SSLv23_client_method() : 
9816                                   SSLv3_client_method());
9817         if (!ssl_ftp_ctx)
9818           return(0);
9819         SSL_CTX_set_options(ssl_ftp_ctx,
9820                             (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
9821                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
9822                             );
9823     }
9824     SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
9825                                   (pem_password_cb *)ssl_passwd_callback);
9826     SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
9827     SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
9828
9829 #ifdef OS2
9830 #ifdef NT
9831     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
9832     {
9833         char path[CKMAXPATH];
9834         extern char exedir[];
9835
9836         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
9837         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
9838             debug(F110,"ftp ssl_auth unable to load path",path,0);
9839             if (ssl_debug_flag)
9840                 printf("?Unable to load verify-dir: %s\r\n",path);
9841         }
9842
9843         ckmakmsg(path,CKMAXPATH,
9844                  (char *)GetAppData(1),"kermit 95/certs",NULL,NULL);
9845         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
9846             debug(F110,"ftp ssl_auth unable to load path",path,0);
9847             if (ssl_debug_flag)
9848                 printf("?Unable to load verify-dir: %s\r\n",path);
9849         }
9850
9851         ckmakmsg(path,CKMAXPATH,
9852                  (char *)GetAppData(0),"kermit 95/certs",NULL,NULL);
9853         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
9854             debug(F110,"ftp ssl_auth unable to load path",path,0);
9855             if (ssl_debug_flag)
9856                 printf("?Unable to load verify-dir: %s\r\n",path);
9857         }
9858
9859         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
9860         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
9861             debug(F110,"ftp ssl_auth unable to load path",path,0);
9862             if (ssl_debug_flag)
9863                 printf("?Unable to load verify-file: %s\r\n",path);
9864         }
9865
9866         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
9867                  "kermit 95/ca_certs.pem",NULL,NULL);
9868         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
9869             debug(F110,"ftp ssl_auth unable to load path",path,0);
9870             if (ssl_debug_flag)
9871                 printf("?Unable to load verify-file: %s\r\n",path);
9872         }
9873
9874         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
9875                  "kermit 95/ca_certs.pem",NULL,NULL);
9876         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
9877             debug(F110,"ftp ssl_auth unable to load path",path,0);
9878             if (ssl_debug_flag)
9879                 printf("?Unable to load verify-file: %s\r\n",path);
9880         }
9881     }
9882 #else /* NT */
9883     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
9884     {
9885
9886         char path[CKMAXPATH];
9887         extern char exedir[];
9888
9889         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
9890         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
9891             debug(F110,"ftp ssl_auth unable to load path",path,0);
9892             if (ssl_debug_flag)
9893                 printf("?Unable to load verify-dir: %s\r\n",path);
9894         }
9895         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
9896         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
9897             debug(F110,"ftp ssl_auth unable to load path",path,0);
9898             if (ssl_debug_flag)
9899                 printf("?Unable to load verify-file: %s\r\n",path);
9900         }
9901     }
9902 #endif /* NT */
9903 #else /* OS2 */
9904     SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
9905 #endif /* OS2 */
9906
9907     if (ssl_verify_file &&
9908         SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
9909         debug(F110,
9910               "ftp ssl auth unable to load ssl_verify_file",
9911               ssl_verify_file,
9912               0
9913               );
9914         if (ssl_debug_flag)
9915           printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
9916     }
9917     if (ssl_verify_dir &&
9918         SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
9919         debug(F110,
9920               "ftp ssl auth unable to load ssl_verify_dir",
9921               ssl_verify_dir,
9922               0
9923               );
9924         if (ssl_debug_flag)
9925           printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
9926     }
9927
9928     /* set up the new CRL Store */
9929     crl_store = (X509_STORE *)X509_STORE_new();
9930     if (crl_store) {
9931 #ifdef OS2
9932         char path[CKMAXPATH];
9933         extern char exedir[];
9934
9935         ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
9936         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
9937             debug(F110,"ftp ssl auth unable to load dir",path,0);
9938             if (ssl_debug_flag)
9939                 printf("?Unable to load crl-dir: %s\r\n",path);
9940         }
9941 #ifdef NT
9942         ckmakmsg(path,CKMAXPATH,
9943                  (char *)GetAppData(1),"kermit 95/crls",NULL,NULL);
9944         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
9945             debug(F110,"ftp ssl auth unable to load dir",path,0);
9946             if (ssl_debug_flag)
9947                 printf("?Unable to load crl-dir: %s\r\n",path);
9948         }
9949         ckmakmsg(path,CKMAXPATH,
9950                  (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
9951         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
9952             debug(F110,"ftp ssl auth unable to load dir",path,0);
9953             if (ssl_debug_flag)
9954                 printf("?Unable to load crl-dir: %s\r\n",path);
9955         }
9956 #endif /* NT */
9957         
9958         ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
9959         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
9960             debug(F110,"ftp ssl auth unable to load file",path,0);
9961             if (ssl_debug_flag)
9962                 printf("?Unable to load crl-file: %s\r\n",path);
9963         }
9964 #ifdef NT
9965         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
9966                  "kermit 95/ca_crls.pem",NULL,NULL);
9967         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
9968             debug(F110,"ftp ssl auth unable to load file",path,0);
9969             if (ssl_debug_flag)
9970                 printf("?Unable to load crl-file: %s\r\n",path);
9971         }
9972         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
9973                  "kermit 95/ca_crls.pem",NULL,NULL);
9974         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
9975             debug(F110,"ftp ssl auth unable to load file",path,0);
9976             if (ssl_debug_flag)
9977                 printf("?Unable to load crl-file: %s\r\n",path);
9978         }
9979 #endif /* NT */
9980 #endif /* OS2 */
9981
9982         if (ssl_crl_file || ssl_crl_dir) {
9983             if (ssl_crl_file &&
9984                 X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
9985                 debug(F110,
9986                       "ftp ssl auth unable to load ssl_crl_file",
9987                       ssl_crl_file,
9988                       0
9989                       );
9990                 if (ssl_debug_flag)
9991                   printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
9992             }
9993             if (ssl_crl_dir &&
9994                 X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
9995                 debug(F110,
9996                       "ftp ssl auth unable to load ssl_crl_dir",
9997                       ssl_crl_dir,
9998                       0
9999                       );
10000                 if (ssl_debug_flag)
10001                   printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
10002             }
10003         } else {
10004             X509_STORE_set_default_paths(crl_store);
10005         }
10006     }
10007     SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
10008                        ssl_client_verify_callback);
10009     ssl_verify_depth = -1;
10010     ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
10011     tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
10012     SSL_set_fd(ssl_ftp_con,csocket);
10013     SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
10014     if (ssl_cipher_list) {
10015         SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
10016     } else {
10017         char * p;
10018         if (p = getenv("SSL_CIPHER")) {
10019             SSL_set_cipher_list(ssl_ftp_con,p);
10020         } else {
10021             SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
10022         }
10023     }
10024     if (ssl_debug_flag) {
10025         fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
10026         fflush(stderr);
10027     }
10028     if (SSL_connect(ssl_ftp_con) <= 0) {
10029         static char errbuf[1024];
10030         ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
10031                  ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
10032         fprintf(stderr,"%s\n", errbuf);
10033         fflush(stderr);
10034         ssl_ftp_active_flag=0;
10035         SSL_free(ssl_ftp_con);
10036         ssl_ftp_con = NULL;
10037     } else {
10038         ssl_ftp_active_flag = 1;
10039
10040         if (!ssl_certsok_flag && !tls_is_krb5(1)) {
10041             char *subject = ssl_get_subject_name(ssl_ftp_con);
10042
10043             if (!subject) {
10044                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
10045                     debug(F110,"ssl_auth","[SSL - FAILED]",0);
10046                     return(ssl_ftp_active_flag = 0);
10047                 } else {
10048                     if (uq_ok("Warning: Server didn't provide a certificate\n",
10049                                "Continue? (Y/N)",3,NULL,0) <= 0) {
10050                         debug(F110, "ssl_auth","[SSL - FAILED]",0);
10051                         return(ssl_ftp_active_flag = 0);
10052                     }
10053                 }
10054             } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
10055                 debug(F110,"ssl_auth","[SSL - FAILED]",0);
10056                 return(ssl_ftp_active_flag = 0);
10057             }
10058         }
10059         debug(F110,"ssl_auth","[SSL - OK]",0);
10060         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
10061     }
10062     if (ssl_debug_flag) {
10063         fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
10064         fflush(stderr);
10065     }
10066     return(ssl_ftp_active_flag);
10067 }
10068 #endif /* CK_SSL */
10069
10070 static sigtype
10071 cmdcancel(sig) int sig; {
10072 #ifdef OS2
10073     /* In Unix we "chain" to trap(), which prints this */
10074     printf("^C...\n");
10075 #endif /* OS2 */
10076     debug(F100,"ftp cmdcancel caught SIGINT ","",0);
10077     fflush(stdout);
10078     secure_getc(0,1);                   /* Initialize net input buffers */
10079     cancelfile++;
10080     cancelgroup++;
10081     mlsreset();
10082 #ifndef OS2
10083 #ifdef FTP_PROXY
10084     if (ptflag)                         /* proxy... */
10085       longjmp(ptcancel,1);
10086 #endif /* FTP_PROXY */
10087     debug(F100,"ftp cmdcancel chain to trap()...","",0);
10088     trap(SIGINT);
10089     /* NOTREACHED */
10090     debug(F100,"ftp cmdcancel return from trap()...","",0);
10091 #else
10092     debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
10093     PostCtrlCSem();
10094 #endif /* OS2 */
10095 }
10096
10097 static int
10098 #ifdef CK_ANSIC
10099 scommand(char * s)                      /* Was secure_command() */
10100 #else
10101 scommand(s) char * s;
10102 #endif /* CK_ANSIC */
10103 {
10104     int length = 0, len2;
10105     char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
10106 #ifdef CK_SSL
10107     if (ssl_ftp_active_flag) {
10108         int error, rc;
10109         length = strlen(s) + 2;
10110         length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10111         rc = SSL_write(ssl_ftp_con,out,length);
10112         error = SSL_get_error(ssl_ftp_con,rc);
10113         switch (error) {
10114           case SSL_ERROR_NONE:
10115             return(1);
10116           case SSL_ERROR_WANT_WRITE:
10117           case SSL_ERROR_WANT_READ:
10118           case SSL_ERROR_SYSCALL:
10119 #ifdef NT
10120             {
10121                 int gle = GetLastError();
10122             }
10123 #endif /* NT */
10124           case SSL_ERROR_WANT_X509_LOOKUP:
10125           case SSL_ERROR_SSL:
10126           case SSL_ERROR_ZERO_RETURN:
10127           default:
10128             lostpeer();
10129         }
10130         return(0);
10131     }
10132 #endif /* CK_SSL */
10133
10134     if (auth_type && ftp_cpl != FPL_CLR) {
10135 #ifdef FTP_SRP
10136         if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
10137           if ((length = srp_encode(ftp_cpl == FPL_PRV,
10138                                    (CHAR *)s,
10139                                    (CHAR *)out,
10140                                    strlen(s))) < 0) {
10141               fprintf(stderr, "SRP failed to encode message\n");
10142               return(0);
10143           }
10144 #endif /* FTP_SRP */
10145 #ifdef FTP_KRB4
10146         if (ck_krb4_is_installed() &&
10147             (strcmp(auth_type, "KERBEROS_V4") == 0)) {
10148             if (ftp_cpl == FPL_PRV) {
10149                 length =
10150                   krb_mk_priv((CHAR *)s, (CHAR *)out,
10151                               strlen(s), ftp_sched,
10152 #ifdef KRB524
10153                               ftp_cred.session,
10154 #else /* KRB524 */
10155                               &ftp_cred.session,
10156 #endif /* KRB524 */
10157                               &myctladdr, &hisctladdr);
10158             } else {
10159                 length =
10160                   krb_mk_safe((CHAR *)s,
10161                               (CHAR *)out,
10162                               strlen(s),
10163 #ifdef KRB524
10164                               ftp_cred.session,
10165 #else /* KRB524 */
10166                               &ftp_cred.session,
10167 #endif /* KRB524 */
10168                               &myctladdr, &hisctladdr);
10169             }
10170             if (length == -1) {
10171                 fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
10172                         ftp_cpl == FPL_PRV ? "priv" : "safe");
10173                 return(0);
10174             }
10175         }
10176 #endif /* FTP_KRB4 */
10177 #ifdef FTP_GSSAPI
10178         /* Scommand (based on level) */
10179         if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
10180             gss_buffer_desc in_buf, out_buf;
10181             OM_uint32 maj_stat, min_stat;
10182             int conf_state;
10183             in_buf.value = s;
10184             in_buf.length = strlen(s) + 1;
10185             maj_stat = gss_seal(&min_stat, gcontext,
10186                                 (ftp_cpl==FPL_PRV), /* private */
10187                                 GSS_C_QOP_DEFAULT,
10188                                 &in_buf, &conf_state,
10189                                 &out_buf);
10190             if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */
10191                 user_gss_error(maj_stat, min_stat,
10192                                (ftp_cpl==FPL_PRV)?
10193                                "gss_seal ENC didn't complete":
10194                                "gss_seal MIC didn't complete");
10195             } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
10196                 fprintf(stderr, "GSSAPI didn't encrypt message");
10197             } else {
10198                 if (ftp_deb)
10199                   fprintf(stderr, "sealed (%s) %d bytes\n",
10200                           ftp_cpl==FPL_PRV?"ENC":"MIC",
10201                           out_buf.length);
10202                 memcpy(out, out_buf.value,
10203                        length=out_buf.length);
10204                 gss_release_buffer(&min_stat, &out_buf);
10205             }
10206         }
10207 #endif /* FTP_GSSAPI */
10208         /* Other auth types go here ... */
10209
10210         len2 = FTP_BUFSIZ;
10211         if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
10212                                    length, &len2, RADIX_ENCODE))
10213             ) {
10214             fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
10215                     radix_error(kerror));
10216             return(0);
10217         }
10218         if (ftp_deb)
10219           fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
10220         len2 = ckmakmsg(out,
10221                         FTP_BUFSIZ,
10222                         ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
10223                         in,
10224                         "\r\n",
10225                         NULL
10226                         );
10227         send(csocket,(SENDARG2TYPE)out,len2,0);
10228     } else {
10229         char out[FTP_BUFSIZ];
10230         int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10231         send(csocket,(SENDARG2TYPE)out,len,0);
10232     }
10233     return(1);
10234 }
10235
10236 static int
10237 mygetc() {
10238     static char inbuf[4096];
10239     static int bp = 0, ep = 0;
10240     int rc;
10241
10242     if (bp == ep) {
10243         bp = ep = 0;
10244 #ifdef CK_SSL
10245         if (ssl_ftp_active_flag) {
10246             int error;
10247             rc = SSL_read(ssl_ftp_con,inbuf,4096);
10248             error = SSL_get_error(ssl_ftp_con,rc);
10249             switch (error) {
10250               case SSL_ERROR_NONE:
10251                 break;
10252               case SSL_ERROR_WANT_WRITE:
10253               case SSL_ERROR_WANT_READ:
10254                 return(0);
10255               case SSL_ERROR_SYSCALL:
10256                 if (rc == 0) {          /* EOF */
10257                     break;
10258                 } else {
10259 #ifdef NT
10260                     int gle = GetLastError();
10261 #endif /* NT */
10262                     break;
10263                 }
10264               case SSL_ERROR_WANT_X509_LOOKUP:
10265               case SSL_ERROR_SSL:
10266               case SSL_ERROR_ZERO_RETURN:
10267               default:
10268                 break;
10269             }
10270         } else
10271 #endif /* CK_SSL */
10272           rc = recv(csocket,(char *)inbuf,4096,0);
10273         if (rc <= 0)
10274           return(EOF);
10275         ep = rc;
10276     }
10277     return(inbuf[bp++]);
10278 }
10279
10280 /*  x l a t e c  --  Translate a character  */
10281 /*
10282     Call with:
10283       fc    = Function code: 0 = translate, 1 = initialize.
10284       c     = Character (as int).
10285       incs  = Index of charset to translate from.
10286       outcs = Index of charset to translate to.
10287
10288     Returns:
10289       0: OK
10290      -1: Error
10291 */
10292 static int
10293 xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
10294 #ifdef NOCSETS
10295     return(c);
10296 #else
10297     static char buf[128];
10298     static int cx;
10299     int c0, c1;
10300
10301     if (fc == 1) {                      /* Initialize */
10302         cx = 0;                         /* Catch-up buffer write index */
10303         xgnbp = buf;                    /* Catch-up buffer read pointer */
10304         buf[0] = NUL;                   /* Buffer is empty */
10305         return(0);
10306     }
10307     if (cx >= 127) {                    /* Catch-up buffer full */
10308         debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
10309         printf("?Translation buffer overflow\n");
10310         return(-1);
10311     }
10312     /* Add char to buffer. */
10313     /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
10314
10315     debug(F000,"xlatec buf",ckitoa(cx),c);
10316     buf[cx++] = c;
10317     buf[cx] = NUL;
10318
10319     while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
10320         if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
10321           return(-1);
10322     }
10323     /* If we're caught up, reinitialize the buffer */
10324     return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
10325 #endif /* NOCSETS */
10326 }
10327
10328
10329 /*  p a r s e f e a t  */
10330
10331 /* Note: for convenience we align keyword values with table indices */
10332 /* If you need to insert a new keyword, adjust the SFT_xxx definitions */
10333
10334 static struct keytab feattab[] = {
10335     { "$$$$", 0,        0 },            /* Dummy for sfttab[0] */
10336     { "AUTH", SFT_AUTH, 0 },
10337     { "LANG", SFT_LANG, 0 },
10338     { "MDTM", SFT_MDTM, 0 },
10339     { "MLST", SFT_MLST, 0 },
10340     { "PBSZ", SFT_PBSZ, 0 },
10341     { "PROT", SFT_PROT, 0 },
10342     { "REST", SFT_REST, 0 },
10343     { "SIZE", SFT_SIZE, 0 },
10344     { "TVFS", SFT_TVFS, 0 },
10345     { "UTF8", SFT_UTF8, 0 }
10346 };
10347 static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
10348
10349 #define FACT_CSET  1
10350 #define FACT_CREA  2
10351 #define FACT_LANG  3
10352 #define FACT_MTYP  4
10353 #define FACT_MDTM  5
10354 #define FACT_PERM  6
10355 #define FACT_SIZE  7
10356 #define FACT_TYPE  8
10357 #define FACT_UNIQ  9
10358
10359 static struct keytab facttab[] = {
10360     { "CHARSET",    FACT_CSET, 0 },
10361     { "CREATE",     FACT_CREA, 0 },
10362     { "LANG",       FACT_LANG, 0 },
10363     { "MEDIA-TYPE", FACT_MTYP, 0 },
10364     { "MODIFY",     FACT_MDTM, 0 },
10365     { "PERM",       FACT_PERM, 0 },
10366     { "SIZE",       FACT_SIZE, 0 },
10367     { "TYPE",       FACT_TYPE, 0 },
10368     { "UNIQUE",     FACT_UNIQ, 0 }
10369 };
10370 static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
10371
10372 static struct keytab ftyptab[] = {
10373     { "CDIR", FTYP_CDIR, 0 },
10374     { "DIR",  FTYP_DIR,  0 },
10375     { "FILE", FTYP_FILE, 0 },
10376     { "PDIR", FTYP_PDIR, 0 }
10377 };
10378 static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
10379
10380 static VOID
10381 parsefeat(s) char * s; {                /* Parse a FEATURE response */
10382     char kwbuf[8];
10383     int i, x;
10384     if (!s) return;
10385     if (!*s) return;
10386     while (*s < '!')
10387       s++;
10388     for (i = 0; i < 4; i++) {
10389         if (s[i] < '!')
10390           break;
10391         kwbuf[i] = s[i];
10392     }
10393     if (s[i] && s[i] != SP)
10394       return;
10395     kwbuf[i] = NUL;
10396     /* xlookup requires a full (but case independent) match */
10397     i = xlookup(feattab,kwbuf,nfeattab,&x);
10398     debug(F111,"ftp parsefeat",s,i);
10399     if (i < 0 || i > 15)
10400       return;
10401
10402     switch (i) {
10403       case SFT_MDTM:                    /* Controlled by ENABLE/DISABLE */
10404         sfttab[i] = mdtmok;
10405         if (mdtmok) sfttab[0]++;
10406         break;
10407       case SFT_MLST:                    /* ditto */
10408         sfttab[i] = mlstok;
10409         if (mlstok) sfttab[0]++;
10410         break;
10411       case SFT_SIZE:                    /* ditto */
10412         sfttab[i] = sizeok;
10413         if (sizeok) sfttab[0]++;
10414         break;
10415       case SFT_AUTH:                    /* ditto */
10416         sfttab[i] = ftp_aut;
10417         if (ftp_aut) sfttab[0]++;
10418         break;
10419       default:                          /* Others */
10420         sfttab[0]++;
10421         sfttab[i]++;
10422     }
10423 }
10424
10425 static char *
10426 parsefacts(s) char * s; {               /* Parse MLS[DT] File Facts */
10427     char * p;
10428     int i, j, x;
10429     if (!s) return(NULL);
10430     if (!*s) return(NULL);
10431
10432     /* Maybe we should make a copy of s so we can poke it... */
10433
10434     while ((p = ckstrchr(s,'='))) {
10435         *p = NUL;                       /* s points to fact */
10436         i = xlookup(facttab,s,nfacttab,&x); 
10437         debug(F111,"ftp parsefact fact",s,i);
10438         *p = '=';
10439         s = p+1;                        /* Now s points to arg */
10440         p = ckstrchr(s,';');
10441         if (!p)
10442           p = ckstrchr(s,SP);
10443         if (!p) {
10444             debug(F110,"ftp parsefact end-of-val search fail",s,0);
10445             break;
10446         }
10447         *p = NUL;
10448         debug(F110,"ftp parsefact valu",s,0);
10449         switch (i) {
10450           case FACT_CSET:               /* Ignore these for now */
10451           case FACT_CREA:
10452           case FACT_LANG:
10453           case FACT_PERM:
10454           case FACT_MTYP:
10455           case FACT_UNIQ:
10456             break;
10457           case FACT_MDTM:               /* Modtime */
10458             makestr(&havemdtm,s);
10459             debug(F110,"ftp parsefact mdtm",havemdtm,0);
10460             break;
10461           case FACT_SIZE:               /* Size */
10462             havesize = atol(s);
10463             debug(F101,"ftp parsefact size","",havesize);
10464             break;
10465           case FACT_TYPE:               /* Type */
10466             j = xlookup(ftyptab,s,nftyptab,NULL);
10467             debug(F111,"ftp parsefact type",s,j);
10468             havetype = (j < 1) ? 0 : j;
10469             break;
10470         }
10471         *p = ';';
10472         s = p+1;                        /* s points next fact or name */
10473     }
10474     while (*s == SP)                    /* Skip past spaces. */
10475       s++;
10476     if (!*s)                            /* Make sure we still have a name */
10477       s = NULL;
10478     debug(F110,"ftp parsefact name",s,0);
10479     return(s);
10480 }
10481
10482 /*  g e t r e p l y  --  (to an FTP command sent to server)  */
10483
10484 /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
10485
10486 static int
10487 getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
10488     /* lcs, rcs, vbm parameters as in ftpcmd() */
10489     register int i, c, n;
10490     register int dig;
10491     register char *cp;
10492     int xlate = 0;
10493     int count = 0;
10494     int auth = 0;
10495     int originalcode = 0, continuation = 0;
10496     sig_t oldintr;
10497     int pflag = 0;
10498     char *pt = pasv;
10499     char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
10500     int safe = 0;
10501     int xquiet = 0;
10502
10503     auth = (fc == GRF_AUTH);
10504
10505 #ifndef NOCSETS
10506     debug(F101,"ftp getreply lcs","",lcs);
10507     debug(F101,"ftp getreply rcs","",rcs);
10508     if (lcs > -1 && rcs > -1 && lcs != rcs) {
10509         xlate = 1;
10510         initxlate(rcs,lcs);
10511         xlatec(1,0,rcs,lcs);
10512     }
10513 #endif /* NOCSETS */
10514     debug(F101,"ftp getreply fc","",fc);
10515
10516     if (quiet)
10517       xquiet = 1;
10518     if (vbm == 9) {
10519         xquiet = 1;
10520         vbm = 0;
10521     }
10522     if (ftp_deb)                        /* DEBUG */
10523       vbm = 1;
10524     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
10525       vbm = 0;
10526     else if (vbm < 0)                   /* VERBOSE */
10527       vbm = ftp_vbm;
10528
10529     ibuf[0] = '\0';
10530     if (reply_parse)
10531       reply_ptr = reply_buf;
10532     havesigint = 0;
10533     oldintr = signal(SIGINT, cmdcancel);
10534     for (count = 0;; count++) {
10535         obuf[0] = '\0';
10536         dig = n = ftpcode = i = 0;
10537         cp = ftp_reply_str;
10538         while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
10539             if (c == IAC) {             /* Handle telnet commands */
10540                 switch (c = mygetc()) {
10541                   case WILL:
10542                   case WONT:
10543                     c = mygetc();
10544                     obuf[0] = IAC;
10545                     obuf[1] = DONT;
10546                     obuf[2] = c;
10547                     obuf[3] = NUL;
10548 #ifdef CK_SSL
10549                     if (ssl_ftp_active_flag) {
10550                         int error, rc;
10551                         rc = SSL_write(ssl_ftp_con,obuf,3);
10552                         error = SSL_get_error(ssl_ftp_con,rc);
10553                         switch (error) {
10554                           case SSL_ERROR_NONE:
10555                             break;
10556                           case SSL_ERROR_WANT_WRITE:
10557                           case SSL_ERROR_WANT_READ:
10558                             return(0);
10559                           case SSL_ERROR_SYSCALL:
10560                             if (rc == 0) { /* EOF */
10561                                 break;
10562                             } else {
10563 #ifdef NT
10564                                 int gle = GetLastError();
10565 #endif /* NT */
10566                                 break;
10567                             }
10568                           case SSL_ERROR_WANT_X509_LOOKUP:
10569                           case SSL_ERROR_SSL:
10570                           case SSL_ERROR_ZERO_RETURN:
10571                           default:
10572                             break;
10573                         }
10574                     } else
10575 #endif /* CK_SSL */
10576                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10577                     break;
10578                   case DO:
10579                   case DONT:
10580                     c = mygetc();
10581                     obuf[0] = IAC;
10582                     obuf[1] = WONT;
10583                     obuf[2] = c;
10584                     obuf[3] = NUL;
10585 #ifdef CK_SSL
10586                     if (ssl_ftp_active_flag) {
10587                         int error, rc;
10588                         rc = SSL_write(ssl_ftp_con,obuf,3);
10589                         error = SSL_get_error(ssl_ftp_con,rc);
10590                         switch (error) {
10591                           case SSL_ERROR_NONE:
10592                             break;
10593                           case SSL_ERROR_WANT_WRITE:
10594                           case SSL_ERROR_WANT_READ:
10595                               signal(SIGINT,oldintr);
10596                               return(0);
10597                           case SSL_ERROR_SYSCALL:
10598                             if (rc == 0) { /* EOF */
10599                                 break;
10600                             } else {
10601 #ifdef NT
10602                                 int gle = GetLastError();
10603 #endif /* NT */
10604                                 break;
10605                             }
10606                           case SSL_ERROR_WANT_X509_LOOKUP:
10607                           case SSL_ERROR_SSL:
10608                           case SSL_ERROR_ZERO_RETURN:
10609                           default:
10610                             break;
10611                         }
10612                     } else
10613 #endif /* CK_SSL */
10614                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10615                     break;
10616                   default:
10617                     break;
10618                 }
10619                 continue;
10620             }
10621             dig++;
10622             if (c == EOF) {
10623                 if (expecteof) {
10624                     signal(SIGINT,oldintr);
10625                     ftpcode = 221;
10626                     debug(F101,"ftp getreply EOF","",ftpcode);
10627                     return(0);
10628                 }
10629                 lostpeer();
10630                 if (!xquiet) {
10631                     if (ftp_deb)
10632                       printf("421 ");
10633                     printf(
10634                       "Service not available, connection closed by server\n");
10635                     fflush(stdout);
10636                 }
10637                 signal(SIGINT,oldintr);
10638                 ftpcode = 421;
10639                 debug(F101,"ftp getreply EOF","",ftpcode);
10640                 return(4);
10641             }
10642             if (n == 0) {               /* First digit */
10643                 n = c;                  /* Save it */
10644             }
10645             if (auth_type &&
10646 #ifdef CK_SSL
10647                 !ssl_ftp_active_flag &&
10648 #endif /* CK_SSL */
10649                 !ibuf[0] && (n == '6' || continuation)) {
10650                 if (c != '\r' && dig > 4)
10651                   obuf[i++] = c;
10652             } else {
10653                 if (auth_type &&
10654 #ifdef CK_SSL
10655                     !ssl_ftp_active_flag &&
10656 #endif /* CK_SSL */
10657                     !ibuf[0] && dig == 1 && vbm)
10658                   printf("Unauthenticated reply received from server:\n");
10659                 if (reply_parse) {
10660                     *reply_ptr++ = c;
10661                     *reply_ptr = NUL;
10662                 }
10663                 if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
10664                     ftp_cmdlin < 2) {
10665                     if ((c != '\r') &&
10666                         (ftp_deb || ((vbm || (!auth && n == '5')) &&
10667                         (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
10668                         )))))
10669                     {
10670 #ifdef FTP_PROXY
10671                         if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
10672                           printf("%s:",ftp_host);
10673 #endif /* FTP_PROXY */
10674
10675                         if (!xquiet) {
10676 #ifdef NOCSETS
10677                             printf("%c",c);
10678 #else
10679                             if (xlate) {
10680                                 xlatec(0,c,rcs,lcs);
10681                             } else {
10682                                 printf("%c",c);
10683                             }
10684 #endif /* NOCSETS */
10685                         }
10686                     }
10687                 }
10688             }
10689             if (auth_type &&
10690 #ifdef CK_SSL
10691                 !ssl_ftp_active_flag &&
10692 #endif /* CK_SSL */
10693                 !ibuf[0] && n != '6')
10694               continue;
10695             if (dig < 4 && isdigit(c))
10696               ftpcode = ftpcode * 10 + (c - '0');
10697             if (!pflag && ftpcode == 227)
10698               pflag = 1;
10699             if (dig > 4 && pflag == 1 && isdigit(c))
10700               pflag = 2;
10701             if (pflag == 2) {
10702                 if (c != '\r' && c != ')')
10703                   *pt++ = c;
10704                 else {
10705                     *pt = '\0';
10706                     pflag = 3;
10707                 }
10708             }
10709             if (dig == 4 && c == '-' && n != '6') {
10710                 if (continuation)
10711                   ftpcode = 0;
10712                 continuation++;
10713             }
10714             if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
10715                 *cp++ = c;
10716                 *cp = NUL;
10717             }
10718         }
10719         if (deblog ||
10720 #ifdef COMMENT
10721 /*
10722   Sometimes we need to print the server reply.  printlines is nonzero for any
10723   command where the results are sent back on the control connection rather
10724   than the data connection, e.g. STAT.  In the TOPS-20 case, each file line
10725   has ftpcode 213.  But if you do this with a UNIX server, it sends "213-Start
10726   STAT", <line with ftpcode == 0>, "213-End" or somesuch.  So when printlines
10727   is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
10728   lines from UNIX.  Further experimentation needed with other servers.  Of
10729   course RFC959 is mute as to the format of the server reply.
10730
10731   'printlines' is also true for PWD and BYE.
10732 */
10733             (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
10734 #else
10735 /* No, we can't be that clever -- it breaks other things like RPWD... */
10736             (printlines &&
10737              (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
10738 #endif /* COMMENT */
10739             ) {
10740             char * q = cp;
10741             char *r = ftp_reply_str;
10742             *q-- = NUL;                 /* NUL-terminate */
10743             while (*q < '!' && q > r)   /* Strip CR, etc */
10744               *q-- = NUL;
10745             if (!ftp_deb && printlines) { /* If printing */
10746                 if (ftpcode != 0)       /* strip ftpcode if any */
10747                   r += 4;
10748 #ifdef NOCSETS
10749                 printf("%s\n",r);       /* and print */
10750 #else
10751                 if (!xlate) {
10752                     printf("%s\n",r);
10753                 } else {                /* Translating */
10754                     xgnbp = r;          /* Set up strgetc() */
10755                     while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
10756                         if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
10757                             signal(SIGINT,oldintr);
10758                             return(-1);
10759                         }
10760                     }
10761                     printf("\n");
10762                 }
10763 #endif /* NOCSETS */
10764             }
10765         }
10766         debug(F110,"FTP RCVD ",ftp_reply_str,0);
10767
10768         if (fc == GRF_FEAT) {           /* Parsing FEAT command response? */
10769             if (count == 0 && n == '2') {
10770                 int i;                  /* (Re)-init server FEATure table */
10771                 debug(F100,"ftp getreply clearing feature table","",0);
10772                 for (i = 0; i < 16; i++)
10773                   sfttab[i] = 0;
10774             } else {
10775                 parsefeat((char *)ftp_reply_str);
10776             }
10777         }
10778         if (auth_type &&
10779 #ifdef CK_SSL
10780             !ssl_ftp_active_flag &&
10781 #endif /* CK_SSL */
10782              !ibuf[0] && n != '6') {
10783             signal(SIGINT,oldintr);
10784             return(getreply(expecteof,lcs,rcs,vbm,auth));
10785         }
10786         ibuf[0] = obuf[i] = '\0';
10787         if (ftpcode && n == '6')
10788           if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
10789               printf("Unknown reply: %d %s\n", ftpcode, obuf);
10790               n = '5';
10791           } else safe = (ftpcode == 631);
10792         if (obuf[0]                     /* if there is a string to decode */
10793 #ifdef CK_SSL
10794             && !ssl_ftp_active_flag     /* and not SSL/TLS */
10795 #endif /* CK_SSL */
10796             ) {
10797             if (!auth_type) {
10798                 printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
10799                 n = '5';
10800             }
10801 #ifndef CK_ENCRYPTION
10802             else if (ftpcode == 632) {
10803                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
10804                 n = '5';
10805             }
10806 #endif /* CK_ENCRYPTION */
10807 #ifdef NOCONFIDENTIAL
10808             else if (ftpcode == 633) {
10809                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
10810                 n = '5';
10811             }
10812 #endif /* NOCONFIDENTIAL */
10813             else {
10814                 int len = FTP_BUFSIZ;
10815                 if ((kerror = radix_encode((CHAR *)obuf,
10816                                            (CHAR *)ibuf,
10817                                            0,
10818                                            &len,
10819                                            RADIX_DECODE))
10820                     ) {
10821                     printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
10822                            ftpcode, radix_error(kerror), obuf);
10823                     n = '5';
10824                 }
10825 #ifdef FTP_SRP
10826                 else if (strcmp(auth_type, "SRP") == 0) {
10827                     int outlen;
10828                     outlen = srp_decode(!safe, (CHAR *)ibuf,
10829                                         (CHAR *) ibuf, len);
10830                     if (outlen < 0) {
10831                         printf("Warning: %d reply %s!\n",
10832                                ftpcode, safe ? "modified" : "garbled");
10833                         n = '5';
10834                     } else {
10835                         ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
10836                         if (ftp_deb)
10837                           printf("%c:", safe ? 'S' : 'P');
10838                         continue;
10839                     }
10840                 }
10841 #endif /* FTP_SRP */
10842 #ifdef FTP_KRB4
10843                 else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
10844                     if (safe) {
10845                         kerror = krb_rd_safe((CHAR *)ibuf, len,
10846 #ifdef KRB524
10847                                              ftp_cred.session,
10848 #else /* KRB524 */
10849                                              &ftp_cred.session,
10850 #endif /* KRB524 */
10851                                              &hisctladdr,
10852                                              &myctladdr,
10853                                              &ftp_msg_data
10854                                              );
10855                     } else {
10856                         kerror = krb_rd_priv((CHAR *)ibuf, len,
10857                                              ftp_sched,
10858 #ifdef KRB524
10859                                              ftp_cred.session,
10860 #else /* KRB524 */
10861                                              &ftp_cred.session,
10862 #endif /* KRB524 */
10863                                              &hisctladdr,
10864                                              &myctladdr,
10865                                              &ftp_msg_data
10866                                              );
10867                     }
10868                     if (kerror != KSUCCESS) {
10869                         printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
10870                                safe ? "modified" : "garbled",
10871                                safe ? "safe" : "priv",
10872                                krb_get_err_text(kerror));
10873                         n = '5';
10874                     } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
10875                         kerror = KFAILURE;
10876                         n = '5';
10877                         printf("reply data too large for buffer\n");
10878                     } else {
10879                         if (ftp_deb)
10880                           printf("%c:", safe ? 'S' : 'P');
10881                         memcpy(ibuf,ftp_msg_data.app_data,
10882                                ftp_msg_data.app_length);
10883                         ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
10884                                   FTP_BUFSIZ - ftp_msg_data.app_length);
10885                         continue;
10886                     }
10887                 }
10888 #endif /* FTP_KRB4 */
10889 #ifdef FTP_GSSAPI
10890                 else if (strcmp(auth_type, "GSSAPI") == 0) {
10891                     gss_buffer_desc xmit_buf, msg_buf;
10892                     OM_uint32 maj_stat, min_stat;
10893                     int conf_state;
10894                     xmit_buf.value = ibuf;
10895                     xmit_buf.length = len;
10896                     /* decrypt/verify the message */
10897                     conf_state = safe;
10898                     maj_stat = gss_unseal(&min_stat, gcontext,
10899                                           &xmit_buf, &msg_buf,
10900                                           &conf_state, NULL);
10901                     if (maj_stat != GSS_S_COMPLETE) {
10902                         user_gss_error(maj_stat, min_stat,
10903                                        "failed unsealing reply");
10904                         n = '5';
10905                     } else {
10906                         memcpy(ibuf, msg_buf.value, msg_buf.length);
10907                         ckstrncpy(&ibuf[msg_buf.length], "\r\n",
10908                                   FTP_BUFSIZ-msg_buf.length);
10909                         gss_release_buffer(&min_stat,&msg_buf);
10910                         if (ftp_deb)
10911                           printf("%c:", safe ? 'S' : 'P');
10912                         continue;
10913                     }
10914                 }
10915 #endif /* FTP_GSSAPI */
10916                 /* Other auth types go here... */
10917             }
10918         } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
10919                    !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
10920 #ifdef NOCSETS
10921             printf("%c",c);
10922 #else
10923             if (xlate) {
10924                 xlatec(0,c,rcs,lcs);
10925             } else {
10926                 printf("%c",c);
10927             }
10928 #endif /* NOCSETS */
10929             fflush (stdout);
10930         }
10931         if (continuation && ftpcode != originalcode) {
10932             if (originalcode == 0)
10933               originalcode = ftpcode;
10934             continue;
10935         }
10936         *cp = '\0';
10937         if (n != '1')
10938           cpend = 0;
10939         signal(SIGINT,oldintr);
10940         if (ftpcode == 421 || originalcode == 421) {
10941             lostpeer();
10942             if (!xquiet && !ftp_deb)
10943               printf("%s\n",reply_buf);
10944         }
10945         if ((cancelfile != 0) &&
10946 #ifndef ULTRIX3
10947             /* Ultrix 3.0 cc objects violently to this clause */
10948             (oldintr != cmdcancel) &&
10949 #endif /* ULTRIX3 */
10950             (oldintr != SIG_IGN)) {
10951             if (oldintr)
10952               (*oldintr)(SIGINT);
10953         }
10954         if (reply_parse) {
10955             *reply_ptr = '\0';
10956             if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
10957                 reply_parse = reply_ptr + strlen(reply_parse);
10958                 if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
10959                   *reply_ptr = '\0';
10960             } else
10961               reply_parse = reply_ptr;
10962         }
10963         while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
10964           *cp-- = NUL;
10965         debug(F111,"ftp getreply",ftp_reply_str,n - '0');
10966         return(n - '0');
10967     } /* for (;;) */
10968 }
10969
10970 #ifdef BSDSELECT
10971 static int
10972 #ifdef CK_ANSIC
10973 empty(fd_set * mask, int sec)
10974 #else
10975 empty(mask, sec) fd_set * mask; int sec;
10976 #endif /* CK_ANSIC */
10977 {
10978     struct timeval t;
10979     t.tv_sec = (long) sec;
10980     t.tv_usec = 0L;
10981     debug(F100,"ftp empty calling select...","",0);
10982 #ifdef INTSELECT
10983     x = select(32, (int *)mask, NULL, NULL, &t);
10984 #else
10985     x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
10986 #endif /* INTSELECT */
10987     debug(F101,"ftp empty select","",x);
10988     return(x);
10989 }
10990 #else /* BSDSELECT */
10991 #ifdef IBMSELECT
10992 static int
10993 empty(mask, cnt, sec) int * mask, sec;
10994                       int   cnt;
10995 {
10996     return(select(mask,cnt,0,0,sec*1000));
10997 }
10998 #endif /* IBMSELECT */
10999 #endif /* BSDSELECT */
11000
11001 static sigtype
11002 cancelsend(sig) int sig; {
11003     havesigint++;
11004     cancelgroup++;
11005     cancelfile = 0;
11006     printf(" Canceled...\n");
11007     secure_getc(0,1);                   /* Initialize net input buffers */
11008     debug(F100,"ftp cancelsend caught SIGINT ","",0);
11009     fflush(stdout);
11010 #ifndef OS2
11011     longjmp(sendcancel, 1);
11012 #else
11013     PostCtrlCSem();
11014 #endif /* OS2 */
11015 }
11016
11017 static VOID
11018 #ifdef CK_ANSIC
11019 secure_error(char *fmt, ...)
11020 #else
11021 /* VARARGS1 */
11022 secure_error(fmt, p1, p2, p3, p4, p5)
11023    char *fmt; int p1, p2, p3, p4, p5;
11024 #endif /* CK_ANSIC */
11025 {
11026 #ifdef CK_ANSIC
11027     va_list ap;
11028
11029     va_start(ap, fmt);
11030     vfprintf(stderr, fmt, ap);
11031     va_end(ap);
11032 #else
11033     fprintf(stderr, fmt, p1, p2, p3, p4, p5);
11034 #endif
11035     fprintf(stderr, "\n");
11036 }
11037
11038 /*
11039  * Internal form of settype; changes current type in use with server
11040  * without changing our notion of the type for data transfers.
11041  * Used to change to and from ascii for listings.
11042  */
11043 static VOID
11044 changetype(newtype, show) int newtype, show; {
11045     int rc;
11046     char * s;
11047
11048     if ((newtype == curtype) && typesent++)
11049       return;
11050     switch (newtype) {
11051       case FTT_ASC:
11052         s = "A";
11053         break;
11054       case FTT_BIN:
11055         s = "I";
11056         break;
11057       case FTT_TEN:
11058         s = "L 8";
11059         break;
11060       default:
11061         s = "I";
11062         break;
11063     }
11064     rc = ftpcmd("TYPE",s,-1,-1,show);
11065     if (rc == REPLY_COMPLETE)
11066       curtype = newtype;
11067 }
11068
11069 /* PUT a file.  Returns -1 on error, 0 on success, 1 if file skipped */
11070
11071 static VOID
11072 #ifdef CK_ANSIC
11073 doftpsend(void * threadinfo)
11074 #else
11075 doftpsend(threadinfo) VOID * threadinfo;
11076 #endif
11077 {
11078 #ifdef NTSIG
11079     if (threadinfo) {                   /* Thread local storage... */
11080         TlsSetValue(TlsIndex,threadinfo);
11081         debug(F100, "doftpsend called with threadinfo block","", 0);
11082     } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
11083 #endif /* NTSIG */
11084 #ifdef CK_LOGIN
11085 #ifdef IKSD
11086 #ifdef NT
11087     if (inserver)
11088       setntcreds();
11089 #endif /* NT */
11090 #endif /* IKSD */
11091 #endif /* CK_LOGIN */
11092
11093     if (initconn()) {
11094 #ifndef NOHTTP
11095         int y = -1;
11096         debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy);
11097
11098        /*  If the connection failed and we are using an HTTP Proxy
11099         *  and the reason for the failure was an authentication
11100         *  error, then we need to give the user to ability to
11101         *  enter a username and password, just like a browser.
11102         *
11103         *  I tried to do all of this within the netopen() call
11104         *  but it is much too much work.
11105         */
11106         while (y != 0 && tcp_http_proxy != NULL ) {
11107
11108             if (tcp_http_proxy_errno == 401 ||
11109                  tcp_http_proxy_errno == 407 ) {
11110                 char uid[UIDBUFLEN];
11111                 char pwd[PWDSIZ];
11112                 struct txtbox tb[2];
11113                 int ok;
11114
11115                 tb[0].t_buf = uid;
11116                 tb[0].t_len = UIDBUFLEN;
11117                 tb[0].t_lbl = "Proxy Userid: ";
11118                 tb[0].t_dflt = NULL;
11119                 tb[0].t_echo = 1;
11120                 tb[1].t_buf = pwd;
11121                 tb[1].t_len = 256;
11122                 tb[1].t_lbl = "Proxy Passphrase: ";
11123                 tb[1].t_dflt = NULL;
11124                 tb[1].t_echo = 2;
11125
11126                 ok = uq_mtxt("Proxy Server Authentication Required\n",
11127                               NULL, 2, tb);
11128                 if (ok && uid[0]) {
11129                     char * proxy_user, * proxy_pwd;
11130
11131                     proxy_user = tcp_http_proxy_user;
11132                     proxy_pwd  = tcp_http_proxy_pwd;
11133
11134                     tcp_http_proxy_user = uid;
11135                     tcp_http_proxy_pwd = pwd;
11136
11137                     y = initconn();
11138
11139                     debug(F101,"doftpsend","initconn",y);
11140                     memset(pwd,0,PWDSIZ);
11141                     tcp_http_proxy_user = proxy_user;
11142                     tcp_http_proxy_pwd = proxy_pwd;
11143                 } else
11144                     break;
11145             } else
11146                 break;
11147         }
11148
11149         if ( y != 0 ) {
11150 #endif /* NOHTTP */
11151             signal(SIGINT, ftpsnd.oldintr);
11152 #ifdef SIGPIPE
11153             if (ftpsnd.oldintp)
11154               signal(SIGPIPE, ftpsnd.oldintp);
11155 #endif /* SIGPIPE */
11156             ftpcode = -1;
11157             zclose(ZIFILE);
11158             ftpsndret = -1;
11159 #ifdef NTSIG
11160             ckThreadEnd(threadinfo);
11161 #endif /* NTSIG */
11162             return;
11163 #ifndef NOHTTP
11164         }
11165 #endif /* NOHTTP */
11166     }
11167     ftpsndret = 0;
11168 #ifdef NTSIG
11169      ckThreadEnd(threadinfo);
11170 #endif /* NTSIG */
11171 }
11172
11173 static VOID
11174 #ifdef CK_ANSIC
11175 failftpsend(void * threadinfo)
11176 #else
11177 failftpsend(threadinfo) VOID * threadinfo;
11178 #endif /* CK_ANSIC */
11179 {
11180 #ifdef NTSIG
11181     if (threadinfo) {                   /* Thread local storage... */
11182         TlsSetValue(TlsIndex,threadinfo);
11183         debug(F100, "docmdfile called with threadinfo block","", 0);
11184     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11185 #endif /* NTSIG */
11186 #ifdef CK_LOGIN
11187 #ifdef IKSD
11188 #ifdef NT
11189     if (inserver)
11190       setntcreds();
11191 #endif /* NT */
11192 #endif /* IKSD */
11193 #endif /* CK_LOGIN */
11194
11195     while (cpend) {
11196         ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11197         debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
11198     }
11199     if (data >= 0) {
11200 #ifdef CK_SSL
11201         if (ssl_ftp_data_active_flag) {
11202             SSL_shutdown(ssl_ftp_data_con);
11203             SSL_free(ssl_ftp_data_con);
11204             ssl_ftp_data_active_flag = 0;
11205             ssl_ftp_data_con = NULL;
11206         }
11207 #endif /* CK_SSL */
11208 #ifdef TCPIPLIB
11209         socket_close(data);
11210 #else /* TCPIPLIB */
11211 #ifdef USE_SHUTDOWN
11212         shutdown(data, 1+1);
11213 #endif /* USE_SHUTDOWN */
11214         close(data);
11215 #endif /* TCPIPLIB */
11216         data = -1;
11217         globaldin = -1;
11218     }
11219     if (ftpsnd.oldintr)
11220         signal(SIGINT,ftpsnd.oldintr);
11221 #ifdef SIGPIPE
11222     if (ftpsnd.oldintp)
11223         signal(SIGPIPE,ftpsnd.oldintp);
11224 #endif /* SIGPIPE */
11225     ftpcode = -1;
11226 #ifndef OS2
11227     /* TEST ME IN K95 */
11228     if (havesigint) {
11229         havesigint = 0;
11230         debug(F100,"ftp failftpsend chain to trap()...","",0);
11231         if (ftpsnd.oldintr != SIG_IGN)
11232           (*ftpsnd.oldintr)(SIGINT);
11233         /* NOTREACHED (I hope!) */
11234         debug(F100,"ftp failftpsend return from trap()...","",0);
11235     }
11236 #endif /* OS2 */
11237 }
11238
11239 static VOID
11240 #ifdef CK_ANSIC
11241 failftpsend2(void * threadinfo)
11242 #else
11243 failftpsend2(threadinfo) VOID * threadinfo;
11244 #endif /* CK_ANSIC */
11245 {
11246 #ifdef NTSIG
11247     if (threadinfo) {                   /* Thread local storage... */
11248         TlsSetValue(TlsIndex,threadinfo);
11249         debug(F100, "docmdfile called with threadinfo block","", 0);
11250     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11251 #endif /* NTSIG */
11252 #ifdef CK_LOGIN
11253 #ifdef IKSD
11254 #ifdef NT
11255     if (inserver)
11256       setntcreds();
11257 #endif /* NT */
11258 #endif /* IKSD */
11259 #endif /* CK_LOGIN */
11260
11261     debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
11262     tfc += ffc;
11263 #ifdef GFTIMER
11264     fpfsecs = gftimer();
11265 #endif /* GFTIMER */
11266     zclose(ZIFILE);
11267 #ifdef PIPESEND
11268     if (sndfilter)
11269       pipesend = 0;
11270 #endif /* PIPESEND */
11271     signal(SIGINT, ftpsnd.oldintr);
11272 #ifdef SIGPIPE
11273     if (ftpsnd.oldintp)
11274       signal(SIGPIPE, ftpsnd.oldintp);
11275 #endif /* SIGPIPE */
11276     if (!cpend) {
11277         ftpcode = -1;
11278         ftpsndret = -1;
11279 #ifdef NTSIG
11280         ckThreadEnd(threadinfo);
11281 #endif /* NTSIG */
11282         return;
11283     }
11284     if (data >= 0) {
11285 #ifdef CK_SSL
11286         if (ssl_ftp_data_active_flag) {
11287             SSL_shutdown(ssl_ftp_data_con);
11288             SSL_free(ssl_ftp_data_con);
11289             ssl_ftp_data_active_flag = 0;
11290             ssl_ftp_data_con = NULL;
11291         }
11292 #endif /* CK_SSL */
11293 #ifdef TCPIPLIB
11294         socket_close(data);
11295 #else /* TCPIPLIB */
11296 #ifdef USE_SHUTDOWN
11297         shutdown(data, 1+1);
11298 #endif /* USE_SHUTDOWN */
11299         close(data);
11300 #endif /* TCPIPLIB */
11301         data = -1;
11302         globaldin = -1;
11303     }
11304     if (dout) {
11305 #ifdef TCPIPLIB
11306         socket_close(dout);
11307 #else /* TCPIPLIB */
11308 #ifdef USE_SHUTDOWN
11309         shutdown(dout, 1+1);
11310 #endif /* USE_SHUTDOWN */
11311         close(dout);
11312 #endif /* TCPIPLIB */
11313     }
11314     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11315     ftpcode = -1;
11316     ftpsndret = -1;
11317
11318 #ifndef OS2
11319     /* TEST ME IN K95 */
11320     if (havesigint) {
11321         havesigint = 0;
11322         debug(F100,"ftp failftpsend2 chain to trap()...","",0);
11323         if (ftpsnd.oldintr != SIG_IGN)
11324           (*ftpsnd.oldintr)(SIGINT);
11325         /* NOTREACHED (I hope!) */
11326         debug(F100,"ftp failftpsend2 return from trap()...","",0);
11327     }
11328 #endif /* OS2 */
11329 }
11330
11331 static VOID
11332 #ifdef CK_ANSIC
11333 doftpsend2(void * threadinfo)
11334 #else
11335 doftpsend2(threadinfo) VOID * threadinfo;
11336 #endif
11337 {
11338     register int c, d = 0;
11339     int n, t, x, notafile, unique = 0;
11340     char *buf, *bufp;
11341     
11342 #ifdef NTSIG
11343     if (threadinfo) {                   /* Thread local storage... */
11344         TlsSetValue(TlsIndex,threadinfo);
11345         debug(F100, "doftpsend2 called with threadinfo block","", 0);
11346     } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
11347 #endif /* NTSIG */
11348 #ifdef CK_LOGIN
11349 #ifdef IKSD
11350 #ifdef NT
11351     if (inserver)
11352       setntcreds();
11353 #endif /* NT */
11354 #endif /* IKSD */
11355 #endif /* CK_LOGIN */
11356
11357     buf = ftpsndbuf;                    /* (not on stack) */
11358
11359     unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
11360     notafile = sndarray || pipesend;
11361
11362 #ifdef FTP_RESTART
11363     if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
11364         char * p;
11365         changetype(FTT_BIN,0);          /* Change to binary */
11366
11367         /* Ask for remote file's size */
11368         x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
11369
11370         if (x == REPLY_COMPLETE) {      /* Have ftpsnd.reply */
11371             p = &ftp_reply_str[4];      /* Parse it */
11372             while (isdigit(*p)) {
11373                 sendstart = sendstart * 10 + (int)(*p - '0');
11374                 p++;
11375             }
11376             if (*p && *p != CR) {       /* Bad number */
11377                 debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
11378                 sendstart = 0L;
11379             } else if (sendstart > fsize) { /* Remote file bigger than local */
11380                 debug(F110,"doftpsend2 big size",ckltoa(fsize),sendstart);
11381                 sendstart = 0L;
11382             }
11383             /* Local is newer */
11384             debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
11385             if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
11386                 debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
11387                 sendstart = 0L;         /* Send the whole file */
11388             }
11389         }
11390         changetype(ftp_typ,0);          /* Change back to appropriate type */
11391         if (sendstart > 0L) {           /* Still restarting? */
11392             if (sendstart == fsize) {   /* Same size - no need to send */
11393                 debug(F111,"doftpsend2 /restart SKIP",fsize,sendstart);
11394                 zclose(ZIFILE);
11395                 ftpsndret = SKP_RES;
11396 #ifdef NTSIG
11397                 ckThreadEnd(threadinfo);
11398 #endif /* NTSIG */
11399                 return;
11400             }
11401             errno = 0;                  /* Restart needed, seek to the spot */
11402             if (zfseek((long)sendstart) < 0) {
11403                 debug(F111,"doftpsend2 zfseek fails",
11404                       ftpsnd.local,sendstart);
11405                 fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
11406                 sendstart = 0;
11407                 zclose(ZIFILE);
11408                 ftpsndret = -1;
11409 #ifdef NTSIG
11410                 ckThreadEnd(threadinfo);
11411 #endif /* NTSIG */
11412                 return;
11413             }
11414 #ifdef COMMENT
11415             debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
11416             x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
11417             if (x != REPLY_CONTINUE) {
11418                 sendstart = 0;
11419                 zclose(ZIFILE);
11420                 ftpsndret = -1;
11421 #ifdef NTSIG
11422                 ckThreadEnd(threadinfo);
11423 #endif /* NTSIG */
11424                 return;
11425             } else {
11426                 ftpsnd.cmd = "STOR";
11427             }
11428 #else
11429             sendmode = SM_RESEND;
11430             ftpsnd.cmd = "APPE";
11431 #endif /* COMMENT */
11432             /* sendstart = 0L; */
11433         }
11434     }
11435 #endif /* FTP_RESTART */
11436
11437     if (unique && !stouarg)             /* If we know STOU accepts no arg */
11438       ftpsnd.remote = NULL;             /* don't include one. */
11439
11440     x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
11441     debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
11442
11443     if (x != REPLY_PRELIM && unique) {
11444         /*
11445           RFC959 says STOU does not take an argument.  But every FTP server
11446           I've encountered but one accepts the arg and constructs the unique
11447           name from it, which is better than making up a totally random name
11448           for the file, which is what RFC959 calls for.  Especially because
11449           there is no way for the client to find out the name chosen by the
11450           server.  So we try STOU with the argument first, which works with
11451           most servers, and if it fails we retry it without the arg, for
11452           the benefit of the one picky server that is not "liberal in what
11453           it accepts" UNLESS the first STOU got a 502 code ("not implemented")
11454           which means STOU is not accepted, period.
11455         */
11456         if ((x == 5) && stouarg && (ftpcode != 502)) {
11457             x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); 
11458             if (x == REPLY_PRELIM)      /* If accepted */
11459               stouarg = 0;              /* flag no STOU arg for this server */
11460         }
11461     }
11462     if (x != REPLY_PRELIM) {
11463         signal(SIGINT, ftpsnd.oldintr);
11464 #ifdef SIGPIPE
11465         if (ftpsnd.oldintp)
11466           signal(SIGPIPE, ftpsnd.oldintp);
11467 #endif /* SIGPIPE */
11468         zclose(ZIFILE);
11469 #ifdef PIPESEND
11470         if (sndfilter)
11471           pipesend = 0;
11472 #endif /* PIPESEND */
11473         ftpsndret = -1;
11474 #ifdef NTSIG
11475         ckThreadEnd(threadinfo);
11476 #endif /* NTSIG */
11477         return;
11478     }
11479     dout = dataconn(ftpsnd.lmode);             /* Get data connection */
11480     if (dout == -1) {
11481         failftpsend2(threadinfo);
11482 #ifdef NTSIG
11483         ckThreadEnd(threadinfo);
11484 #endif /* NTSIG */
11485         return;
11486     }
11487     /* Initialize per-file stats */
11488     ffc = 0L;                           /* Character counter */
11489     cps = oldcps = 0L;                  /* Thruput */
11490 #ifdef GFTIMER
11491     rftimer();                          /* reset f.p. timer */
11492 #endif /* GFTIMER */
11493
11494 #ifdef SIGPIPE
11495     ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
11496 #endif /* SIGPIPE */
11497     switch (curtype) {
11498       case FTT_BIN:                     /* Binary mode */
11499       case FTT_TEN:
11500         errno = d = 0;
11501         while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
11502             ftpsnd.bytes += n;
11503             ffc += n;
11504             debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
11505             hexdump("doftpsend2 zxin",buf,16);
11506 #ifdef CK_SSL
11507             if (ssl_ftp_data_active_flag) {
11508                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11509                     if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
11510                       break;
11511                     spackets++;
11512                     pktnum++;
11513                     if (fdispla != XYFD_B) {
11514                         spktl = d;
11515                         ftscreen(SCR_PT,'D',spackets,NULL);
11516                     }
11517                 }
11518             } else {
11519 #endif /* CK_SSL */
11520                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11521                     if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
11522                         || iscanceled())
11523                       break;
11524                     spackets++;
11525                     pktnum++;
11526                     if (fdispla != XYFD_B) {
11527                         spktl = d;
11528                         ftscreen(SCR_PT,'D',spackets,NULL);
11529                     }
11530                 }
11531 #ifdef CK_SSL
11532             }
11533 #endif /* CK_SSL */
11534             if (d <= 0)
11535               break;
11536         }
11537         if (n < 0)
11538           fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
11539         if (d < 0 || (d = secure_flush(dout)) < 0) {
11540             if (d == -1 && errno && errno != EPIPE)
11541               perror("netout");
11542             ftpsnd.bytes = -1;
11543         }
11544         break;
11545
11546       case FTT_ASC:                     /* Text mode */
11547 #ifndef NOCSETS
11548         if (ftpsnd.xlate) {             /* With translation */
11549             initxlate(ftpsnd.incs,ftpsnd.outcs);
11550             while (!cancelfile) {
11551                 if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
11552                 if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
11553             }
11554         } else {
11555 #endif /* NOCSETS */
11556             /* Text mode, no translation */
11557             while (((c = zminchar()) > -1) && !cancelfile) {
11558                 ffc++;
11559                 if (xxout(c) < 0)
11560                   break;
11561             }
11562             d = 0;
11563 #ifndef NOCSETS
11564         }
11565 #endif /* NOCSETS */
11566         if (dout == -1 || (d = secure_flush(dout)) < 0) {
11567             if (d == -1 && errno && errno != EPIPE)
11568               perror("netout");
11569             ftpsnd.bytes = -1;
11570         }
11571         break;
11572     }
11573     tfc += ffc;                         /* Total file chars */
11574 #ifdef GFTIMER
11575     fpfsecs = gftimer();
11576 #endif /* GFTIMER */
11577     zclose(ZIFILE);                     /* Close input file */
11578 #ifdef PIPESEND
11579     if (sndfilter)                      /* Undo this (it's per file) */
11580       pipesend = 0;
11581 #endif /* PIPESEND */
11582
11583 #ifdef CK_SSL
11584         if (ssl_ftp_data_active_flag) {
11585             SSL_shutdown(ssl_ftp_data_con);
11586             SSL_free(ssl_ftp_data_con);
11587             ssl_ftp_data_active_flag = 0;
11588             ssl_ftp_data_con = NULL;
11589         }
11590 #endif /* CK_SSL */
11591
11592 #ifdef TCPIPLIB
11593     socket_close(dout);                 /* Close data connection */
11594 #else /* TCPIPLIB */
11595 #ifdef USE_SHUTDOWN
11596     shutdown(dout, 1+1);
11597 #endif /* USE_SHUTDOWN */
11598     close(dout);
11599 #endif /* TCPIPLIB */
11600     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11601     signal(SIGINT, ftpsnd.oldintr);            /* Put back interrupts */
11602 #ifdef SIGPIPE
11603     if (ftpsnd.oldintp)
11604       signal(SIGPIPE, ftpsnd.oldintp);
11605 #endif /* SIGPIPE */
11606     if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
11607         debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
11608         ftpsndret = -1;
11609 #ifdef NTSIG
11610         ckThreadEnd(threadinfo);
11611 #endif /* NTSIG */
11612         return;
11613     } else if (cancelfile) {
11614         debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
11615         ftpsndret = -1;
11616 #ifdef NTSIG
11617         ckThreadEnd(threadinfo);
11618 #endif /* NTSIG */
11619         return;
11620     }
11621     debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
11622     ftpsndret = 0;
11623 #ifdef NTSIG
11624      ckThreadEnd(threadinfo);
11625 #endif /* NTSIG */
11626 }
11627
11628 static int
11629 sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
11630     char *cmd, *local, *remote; int xlate, incs, outcs, restart;
11631 {
11632     if (!remote) remote = "";           /* Check args */
11633     if (!*remote) remote = local;
11634     if (!local) local = "";
11635     if (!*local) return(-1);
11636     if (!cmd) cmd = "";
11637     if (!*cmd) cmd = "STOR";
11638
11639     debug(F111,"ftp sendrequest restart",local,restart);
11640
11641     nout = 0;                           /* Init output buffer count */
11642     ftpsnd.bytes = 0;                   /* File input byte count */
11643     dout = -1;
11644
11645 #ifdef FTP_PROXY
11646     if (proxy) {
11647         proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
11648         return(0);
11649     }
11650 #endif /* FTP_PROXY */
11651
11652     changetype(ftp_typ,0);              /* Change type for this file */
11653
11654     ftpsnd.oldintr = NULL;              /* Set up interrupt handler */
11655     ftpsnd.oldintp = NULL;
11656     ftpsnd.restart = restart;
11657     ftpsnd.xlate = xlate;
11658     ftpsnd.lmode = "wb";
11659
11660 #ifdef PIPESEND                         /* Use Kermit API for file i/o... */
11661     if (sndfilter) {
11662         char * p = NULL, * q;
11663 #ifndef NOSPL
11664         int n = CKMAXPATH;
11665         if (cmd_quoting && (p = (char *) malloc(n + 1))) {
11666             q = p;
11667             debug(F110,"sendrequest pipesend filter",sndfilter,0);
11668             zzstring(sndfilter,&p,&n);
11669             debug(F111,"sendrequest pipename",q,n);
11670             if (n <= 0) {
11671                 printf("?Sorry, send filter + filename too long, %d max.\n",
11672                        CKMAXPATH
11673                        );
11674                 free(q);
11675                 return(-1);
11676             }
11677             ckstrncpy(filnam,q,CKMAXPATH+1);
11678             free(q);
11679             local = filnam;
11680         }
11681 #endif /* NOSPL */
11682     }
11683
11684     if (sndfilter)                      /* If sending thru a filter */
11685       pipesend = 1;                     /* set this for open and i/o */
11686 #endif /* PIPESEND */
11687     
11688     if (openi(local) == 0)              /* Try to open the input file */
11689         return(-1);
11690
11691     ftpsndret = 0;
11692     ftpsnd.incs = incs;
11693     ftpsnd.outcs = outcs;
11694     ftpsnd.cmd = cmd;
11695     ftpsnd.local = local;
11696     ftpsnd.remote = remote;
11697     ftpsnd.oldintr = signal(SIGINT, cancelsend);
11698     havesigint = 0;
11699
11700     if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
11701       return(-1);
11702     if (ftpsndret < 0)
11703       return(-1);
11704     if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
11705       return(-1);
11706
11707     return(ftpsndret);
11708 }
11709
11710 static sigtype
11711 cancelrecv(sig) int sig; {
11712     havesigint++;
11713     cancelfile = 0;
11714     cancelgroup++;
11715     secure_getc(0,1);                   /* Initialize net input buffers */
11716     printf(" Canceling...\n");
11717     debug(F100,"ftp cancelrecv caught SIGINT","",0);
11718     fflush(stdout);
11719     if (fp_nml) {
11720         if (fp_nml != stdout)
11721           fclose(fp_nml);
11722         fp_nml = NULL;
11723     }
11724 #ifndef OS2
11725     longjmp(recvcancel, 1);
11726 #else
11727     PostCtrlCSem();
11728 #endif /* OS2 */
11729 }
11730
11731 /* Argumentless front-end for secure_getc() */
11732
11733 static int
11734 netgetc() {
11735     return(secure_getc(globaldin,0));
11736 }
11737
11738 /* Returns -1 on failure, 0 on success, 1 if file skipped */
11739
11740 /*
11741   Sets ftpcode < 0 on failure if failure reason is not server reply code:
11742     -1: interrupted by user.
11743     -2: error opening or writing output file (reason in errno).
11744     -3: failure to make data connection.
11745     -4: network read error (reason in errno).
11746 */
11747
11748 struct xx_ftprecv {
11749     int reply;
11750     int fcs;
11751     int rcs;
11752     int recover;
11753     int xlate;
11754     int din;
11755     int is_retr;
11756     sig_t oldintr, oldintp;
11757     char * cmd;
11758     char * local;
11759     char * remote;
11760     char * lmode;
11761     char * pipename;
11762     int    tcrflag;
11763     long   localsize;
11764 };
11765 static struct xx_ftprecv ftprecv;
11766
11767 static int ftprecvret = 0;
11768
11769 static VOID
11770 #ifdef CK_ANSIC
11771 failftprecv(VOID * threadinfo)
11772 #else
11773 failftprecv(threadinfo) VOID * threadinfo;
11774 #endif /* CK_ANSIC */
11775 {
11776 #ifdef NTSIG
11777     if (threadinfo) {                   /* Thread local storage... */
11778         TlsSetValue(TlsIndex,threadinfo);
11779         debug(F100, "docmdfile called with threadinfo block","", 0);
11780     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11781 #endif /* NTSIG */
11782
11783 #ifdef CK_LOGIN
11784 #ifdef IKSD
11785 #ifdef NT
11786     if (inserver)
11787       setntcreds();
11788 #endif /* NT */
11789 #endif /* IKSD */
11790 #endif /* CK_LOGIN */
11791
11792     while (cpend) {
11793         ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
11794     }
11795     if (data >= 0) {
11796 #ifdef CK_SSL
11797         if (ssl_ftp_data_active_flag) {
11798             SSL_shutdown(ssl_ftp_data_con);
11799             SSL_free(ssl_ftp_data_con);
11800             ssl_ftp_data_active_flag = 0;
11801             ssl_ftp_data_con = NULL;
11802         }
11803 #endif /* CK_SSL */
11804 #ifdef TCPIPLIB
11805         socket_close(data);
11806 #else /* TCPIPLIB */
11807 #ifdef USE_SHUTDOWN
11808         shutdown(data, 1+1);
11809 #endif /* USE_SHUTDOWN */
11810         close(data);
11811 #endif /* TCPIPLIB */
11812         data = -1;
11813         globaldin = -1;
11814     }
11815     if (ftprecv.oldintr)
11816       signal(SIGINT, ftprecv.oldintr);
11817     ftpcode = -1;
11818     ftprecvret = -1;
11819
11820 #ifndef OS2
11821     /* TEST ME IN K95 */
11822     if (havesigint) {
11823         havesigint = 0;
11824         debug(F100,"ftp failftprecv chain to trap()...","",0);
11825         if (ftprecv.oldintr != SIG_IGN)
11826           (*ftprecv.oldintr)(SIGINT);
11827         /* NOTREACHED (I hope!) */
11828         debug(F100,"ftp failftprecv return from trap()...","",0);
11829     }
11830 #endif /* OS2 */
11831     return;
11832 }
11833
11834 static VOID
11835 #ifdef CK_ANSIC
11836 doftprecv(VOID * threadinfo)
11837 #else
11838 doftprecv(threadinfo) VOID * threadinfo;
11839 #endif /* CK_ANSIC */
11840 {
11841 #ifdef NTSIG
11842     if (threadinfo) {                   /* Thread local storage... */
11843         TlsSetValue(TlsIndex,threadinfo);
11844         debug(F100, "docmdfile called with threadinfo block","", 0);
11845     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11846 #endif /* NTSIG */
11847 #ifdef CK_LOGIN
11848 #ifdef IKSD
11849 #ifdef NT
11850     if (inserver)
11851       setntcreds();
11852 #endif /* NT */
11853 #endif /* IKSD */
11854 #endif /* CK_LOGIN */
11855
11856 #ifndef COMMENT
11857     if (!out2screen && !ftprecv.pipename) {
11858         int x;
11859         char * local;
11860         local = ftprecv.local;
11861         x = zchko(local);
11862         if (x < 0) {
11863             if ((!dpyactive || ftp_deb))
11864               fprintf(stderr,
11865                       "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
11866             signal(SIGINT, ftprecv.oldintr);
11867             ftpcode = -2;
11868             ftprecvret = -1;
11869 #ifdef NTSIG
11870             ckThreadEnd(threadinfo);
11871 #endif /* NTSIG */
11872             return;
11873         }
11874     }
11875 #endif /* COMMENT */
11876     changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
11877     if (initconn()) {                   /* Initialize the data connection */
11878         signal(SIGINT, ftprecv.oldintr);
11879         ftpcode = -1;
11880         ftprecvret = -3;
11881 #ifdef NTSIG
11882         ckThreadEnd(threadinfo);
11883 #endif /* NTSIG */
11884         return;
11885     }
11886     secure_getc(0,1);                   /* Initialize net input buffers */
11887     ftprecvret = 0;
11888
11889 #ifdef NTSIG
11890     ckThreadEnd(threadinfo);
11891 #endif /* NTSIG */
11892 }
11893
11894 static VOID
11895 #ifdef CK_ANSIC
11896 failftprecv2(VOID * threadinfo)
11897 #else
11898 failftprecv2(threadinfo) VOID * threadinfo;
11899 #endif /* CK_ANSIC */
11900 {
11901 #ifdef NTSIG
11902     if (threadinfo) {                   /* Thread local storage... */
11903         TlsSetValue(TlsIndex,threadinfo);
11904         debug(F100, "docmdfile called with threadinfo block","", 0);
11905     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11906 #endif /* NTSIG */
11907 #ifdef CK_LOGIN
11908 #ifdef IKSD
11909 #ifdef NT
11910     if (inserver)
11911       setntcreds();
11912 #endif /* NT */
11913 #endif /* IKSD */
11914 #endif /* CK_LOGIN */
11915
11916     /* Cancel using RFC959 recommended IP,SYNC sequence  */
11917
11918     debug(F100,"ftp recvrequest CANCEL","",0);
11919 #ifdef GFTIMER
11920     fpfsecs = gftimer();
11921 #endif /* GFTIMER */
11922 #ifdef SIGPIPE
11923     if (ftprecv.oldintp)
11924       signal(SIGPIPE, ftprecv.oldintr);
11925 #endif /* SIGPIPE */
11926     signal(SIGINT, SIG_IGN);
11927     if (!cpend) {
11928         ftpcode = -1;
11929         signal(SIGINT, ftprecv.oldintr);
11930         ftprecvret = -1;
11931 #ifdef NTSIG
11932         ckThreadEnd(threadinfo);
11933 #endif /* NTSIG */
11934         return;
11935     }
11936     cancel_remote(ftprecv.din);
11937     if (ftpcode > -1)
11938       ftpcode = -1;
11939     if (data >= 0) {
11940 #ifdef CK_SSL
11941         if (ssl_ftp_data_active_flag) {
11942             SSL_shutdown(ssl_ftp_data_con);
11943             SSL_free(ssl_ftp_data_con);
11944             ssl_ftp_data_active_flag = 0;
11945             ssl_ftp_data_con = NULL;
11946         }
11947 #endif /* CK_SSL */
11948 #ifdef TCPIPLIB
11949         socket_close(data);
11950 #else /* TCPIPLIB */
11951 #ifdef USE_SHUTDOWN
11952         shutdown(data, 1+1);
11953 #endif /* USE_SHUTDOWN */
11954         close(data);
11955 #endif /* TCPIPLIB */
11956         data = -1;
11957         globaldin = -1;
11958     }
11959     if (!out2screen) {
11960         int x = 0;
11961         debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
11962         zclose(ZOFILE);
11963         switch (keep) {                 /* which is... */
11964           case SET_AUTO:                /* AUTO */
11965             if (curtype == FTT_ASC)     /* Delete file if TYPE A. */
11966               x = 1;
11967             break;
11968           case SET_OFF:                 /* DISCARD */
11969             x = 1;                      /* Delete file, period. */
11970             break;
11971           default:                      /* KEEP */
11972             break;
11973         }
11974         if (x) {
11975             x = zdelet(ftprecv.local);
11976             debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
11977         }
11978     }
11979     if (ftprecv.din) {
11980 #ifdef TCPIPLIB
11981         socket_close(ftprecv.din);
11982 #else /* TCPIPLIB */
11983 #ifdef USE_SHUTDOWN
11984         shutdown(ftprecv.din, 1+1);
11985 #endif /* USE_SHUTDOWN */
11986         close(ftprecv.din);
11987 #endif /* TCPIPLIB */
11988     }
11989     signal(SIGINT, ftprecv.oldintr);
11990     ftprecvret = -1;
11991
11992     if (havesigint) {
11993         havesigint = 0;
11994         debug(F100,"FTP failftprecv2 chain to trap()...","",0);
11995 #ifdef OS2
11996         debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
11997         PostCtrlCSem();
11998 #else /* OS2 */
11999         if (ftprecv.oldintr != SIG_IGN)
12000           (*ftprecv.oldintr)(SIGINT);
12001         /* NOTREACHED (I hope!) */
12002         debug(F100,"ftp failftprecv2 return from trap()...","",0);
12003 #endif /* OS2 */
12004     }
12005 }
12006
12007 static VOID
12008 #ifdef CK_ANSIC
12009 doftprecv2(VOID * threadinfo)
12010 #else
12011 doftprecv2(threadinfo) VOID * threadinfo;
12012 #endif /* CK_ANSIC */
12013 {
12014     register int c, d;
12015     long bytes = 0L;
12016     int bare_lfs = 0;
12017     int blksize = 0;
12018     ULONG start = 0L, stop;
12019     char * p;
12020     static char * rcvbuf = NULL;
12021     static int rcvbufsiz = 0;
12022 #ifdef CK_URL
12023     char newname[CKMAXPATH+1];          /* For file dialog */
12024 #endif /* CK_URL */
12025     extern int adl_ask;
12026
12027     ftprecv.din = -1;
12028 #ifdef NTSIG
12029     if (threadinfo) {                   /* Thread local storage... */
12030         TlsSetValue(TlsIndex,threadinfo);
12031         debug(F100, "docmdfile called with threadinfo block","", 0);
12032     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12033 #endif /* NTSIG */
12034 #ifdef CK_LOGIN
12035 #ifdef IKSD
12036 #ifdef NT
12037     if (inserver)
12038       setntcreds();
12039 #endif /* NT */
12040 #endif /* IKSD */
12041 #endif /* CK_LOGIN */
12042
12043     if (ftprecv.recover) {                      /* Initiate recovery */
12044         x = ftpcmd("REST",ckltoa(ftprecv.localsize),-1,-1,ftp_vbm);
12045         debug(F111,"ftp reply","REST",x);
12046         if (x == REPLY_CONTINUE) {
12047             ftprecv.lmode = "ab";
12048             rs_len = ftprecv.localsize;
12049         } else {
12050             ftprecv.recover = 0;
12051         }
12052     }
12053     /* IMPORTANT: No FTP commands can come between REST and RETR! */
12054
12055     debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
12056
12057     /* Send the command and get reply */
12058     debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
12059     debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
12060
12061     if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
12062         != REPLY_PRELIM) {
12063         signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
12064         ftprecvret = -1;                /* ftpcode is set by ftpcmd() */
12065 #ifdef NTSIG
12066         ckThreadEnd(threadinfo);
12067 #endif /* NTSIG */
12068         return;
12069     }
12070     ftprecv.din = dataconn("r");        /* Good reply, open data connection */
12071     globaldin = ftprecv.din;            /* Global copy of file descriptor */
12072     if (ftprecv.din == -1) {            /* Check for failure */
12073         ftpcode = -3;                   /* Code for no data connection */
12074         ftprecvret = -1;
12075 #ifdef NTSIG
12076         ckThreadEnd(threadinfo);
12077 #endif /* NTSIG */
12078         return;
12079     }
12080 #ifdef CK_URL
12081     /* In K95 GUI put up a file box */
12082     if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
12083         int x;
12084         char * preface =
12085 "\r\nIncoming file from FTP server...\r\n\
12086 Please confirm output file specification or supply an alternative:";
12087
12088         x = uq_file(preface,            /* K95 GUI: Put up file box. */
12089                     NULL,
12090                     4,
12091                     NULL,
12092                     ftprecv.local ? ftprecv.local : ftprecv.remote,
12093                     newname,
12094                     CKMAXPATH+1
12095                     );
12096         if (x > 0) {
12097             ftprecv.local = newname;    /* Substitute user's file name */
12098             if (x == 2)                 /* And append if user said to */
12099               ftprecv.lmode = "ab";
12100         }
12101     }
12102 #endif /* CK_URL */
12103     x = 1;                              /* Output file open OK? */
12104     if (ftprecv.pipename) {             /* Command */
12105         x = zxcmd(ZOFILE,ftprecv.pipename);
12106         debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
12107     } else if (!out2screen) {           /* File */
12108         struct filinfo xx;
12109         xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
12110         xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
12111         /* Append or New */
12112         xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
12113         x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
12114         debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
12115     }
12116     if (x < 1) {                        /* Failure to open output file */
12117         if ((!dpyactive || ftp_deb))
12118           fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
12119         ftprecvret = -1;
12120 #ifdef NTSIG
12121         ckThreadEnd(threadinfo);
12122 #endif /* NTSIG */
12123         return;
12124     }
12125     blksize = FTP_BUFSIZ;               /* Allocate input buffer */
12126
12127     debug(F101,"ftp recvrequest blksize","",blksize);
12128     debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
12129
12130     if (rcvbufsiz < blksize) {          /* if necessary */
12131         if (rcvbuf) {
12132             free(rcvbuf);
12133             rcvbuf = NULL;
12134         }
12135         rcvbuf = (char *)malloc((unsigned)blksize);
12136         if (!rcvbuf) {
12137             debug(F100,"ftp get rcvbuf malloc failed","",0);
12138             ftpcode = -2;
12139 #ifdef ENOMEM
12140             errno = ENOMEM;
12141 #endif /* ENOMEM */
12142             if ((!dpyactive || ftp_deb))
12143               perror("malloc");
12144             rcvbufsiz = 0;
12145             ftprecvret = -1;
12146 #ifdef NTSIG
12147             ckThreadEnd(threadinfo);
12148 #endif /* NTSIG */
12149             return;
12150         }
12151         debug(F101,"ftp get rcvbuf malloc ok","",blksize);
12152         rcvbufsiz = blksize;
12153     }
12154     debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
12155
12156     ffc = 0L;                           /* Character counter */
12157     cps = oldcps = 0L;                  /* Thruput */
12158     start = gmstimer();                 /* Start time (msecs) */
12159 #ifdef GFTIMER
12160     rftimer();                          /* Start time (float) */
12161 #endif /* GFTIMER */
12162
12163     debug(F111,"ftp get type",ftprecv.local,curtype);
12164     debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
12165     switch (curtype) {
12166       case FTT_BIN:                     /* Binary mode */
12167       case FTT_TEN:                     /* TENEX mode */
12168         d = 0;
12169         while (1) {
12170             errno = 0;
12171             c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
12172             if (cancelfile) {
12173                 failftprecv2(threadinfo);
12174 #ifdef NTSIG
12175                 ckThreadEnd(threadinfo);
12176 #endif /* NTSIG */
12177                 return;
12178             }
12179             if (c < 1)
12180               break;
12181 #ifdef printf                           /* (What if it isn't?) */
12182             if (out2screen && !ftprecv.pipename) {
12183                 int i;
12184                 for (i = 0; i < c; i++)
12185                   printf("%c",rcvbuf[i]);
12186             } else
12187 #endif /* printf */
12188               {
12189                 register int i;
12190                 i = 0;
12191                 errno = 0;
12192                 while (i < c) {
12193                     if (zmchout(rcvbuf[i++]) < 0) {
12194                         d = i;
12195                         break;
12196                     }
12197                 }
12198             }
12199             bytes += c;
12200             ffc += c;
12201         }
12202         if (c < 0) {
12203             debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
12204             if (c == -1 && errno != EPIPE)
12205               if ((!dpyactive || ftp_deb))
12206                 perror("netin");
12207             bytes = -1;
12208             ftpcode = -4;
12209         }
12210         if (d < c) {
12211             ftpcode = -2;
12212             if ((!dpyactive || ftp_deb)) {
12213                 char * p;
12214                 p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
12215                 if (d < 0)
12216                   fprintf(stderr,
12217                           "local(3): %s: %s\n", ftprecv.local, ck_errstr());
12218                 else
12219                   fprintf(stderr,
12220                           "%s: short write\n", ftprecv.local);
12221             }
12222         }
12223         break;
12224
12225       case FTT_ASC:                     /* Text mode */
12226         debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
12227 #ifndef NOCSETS
12228         if (ftprecv.xlate) {
12229             int t;
12230 #ifdef CK_ANSIC
12231             int (*fn)(char);
12232 #else
12233             int (*fn)();
12234 #endif /* CK_ANSIC */
12235             debug(F110,"ftp recvrequest (data)","initxlate",0);
12236             initxlate(ftprecv.rcs,ftprecv.fcs);         /* (From,To) */
12237             if (ftprecv.pipename) {
12238                 fn = pipeout;
12239                 debug(F110,"ftp recvrequest ASCII","pipeout",0);
12240             } else {
12241                 fn = out2screen ? scrnout : putfil;
12242                 debug(F110,"ftp recvrequest ASCII",
12243                       out2screen ? "scrnout" : "putfil",0);
12244             }
12245             while (1) {
12246                 /* Get byte from net */
12247                 c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12248                 if (cancelfile) {
12249                     failftprecv2(threadinfo);
12250 #ifdef NTSIG
12251                     ckThreadEnd(threadinfo);
12252 #endif /* NTSIG */
12253                     return;
12254                 }
12255                 if (c0 < 0)
12256                   break;
12257                 /* Second byte from net */
12258                 c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12259                 if (cancelfile) {
12260                     failftprecv2(threadinfo);
12261 #ifdef NTSIG
12262                     ckThreadEnd(threadinfo);
12263 #endif /* NTSIG */
12264                     return;
12265                 }
12266                 if (c1 < 0)
12267                   break;
12268 #ifdef COMMENT
12269                 /* K95: Check whether we need this */
12270                 if (fileorder > 0)      /* Little Endian */
12271                   bytswap(&c0,&c1);     /* swap bytes*/
12272 #endif /* COMMENT */
12273
12274 #ifdef OS2
12275                 if ( out2screen &&            /* we're translating to UCS-2 */ 
12276                      !k95stdout && !inserver) /* for the real screen... */     
12277                 {
12278                     union {
12279                         USHORT ucs2;
12280                         UCHAR  bytes[2];
12281                     } output;
12282
12283                     output.bytes[0] = c1;
12284                     output.bytes[1] = c0;
12285
12286                     VscrnWrtUCS2StrAtt(VCMD,
12287                                        &output.ucs2,
12288                                        1,
12289                                        wherey[VCMD],
12290                                        wherex[VCMD],
12291                                        &colorcmd
12292                                        );
12293
12294                 } else 
12295 #endif /* OS2 */
12296                 {
12297                     if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12298                     if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12299                 }
12300             }
12301         } else {
12302 #endif /* NOCSETS */
12303             while (1) {
12304                 c = secure_getc(ftprecv.din,0);
12305                 if (cancelfile) {
12306                     failftprecv2(threadinfo);
12307 #ifdef NTSIG
12308                     ckThreadEnd(threadinfo);
12309 #endif /* NTSIG */
12310                     return;
12311                 }
12312                 if (c < 0 || c == EOF)
12313                   break;
12314 #ifdef UNIX
12315                 /* Record format conversion for Unix */
12316                 /* SKIP THIS FOR WINDOWS! */
12317                 if (c == '\n')
12318                   bare_lfs++;
12319                 while (c == '\r') {
12320                     bytes++;
12321                     if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
12322                         ftprecv.tcrflag) {
12323                         if (cancelfile) {
12324                             failftprecv2(threadinfo);
12325 #ifdef NTSIG
12326                             ckThreadEnd(threadinfo);
12327 #endif /* NTSIG */
12328                             return;
12329                         }
12330                         if (c < 0 || c == EOF)
12331                           goto break2;
12332                         if (c == '\0') {
12333                             bytes++;
12334                             goto contin2;
12335                         }
12336                     }
12337                 }
12338                 if (c < 0)
12339                   break;
12340 #endif /* UNX */
12341
12342                 if (out2screen && !ftprecv.pipename)
12343 #ifdef printf
12344                   printf("%c",(char)c);
12345 #else
12346                   putchar((char)c);
12347 #endif /* printf */
12348                 else
12349                   if ((d = zmchout(c)) < 0)
12350                     break;
12351                 bytes++;
12352                 ffc++;
12353               contin2:
12354                 ;
12355             }
12356           break2:
12357             if (bare_lfs && (!dpyactive || ftp_deb)) {
12358                 printf("WARNING! %d bare linefeeds received in ASCII mode\n",
12359                        bare_lfs);
12360                 printf("File might not have transferred correctly.\n");
12361             }
12362             if (ftprecv.din == -1) {
12363                 bytes = -1;
12364             }
12365             if (c == -2)
12366               bytes = -1;
12367             break;
12368 #ifndef NOCSETS
12369         }
12370 #endif /* NOCSETS */
12371     }
12372     if (ftprecv.pipename || !out2screen) {
12373         zclose(ZOFILE);                 /* Close the file */
12374         debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
12375         if (ftpcode < 0) {              /* If download failed */
12376             int x = 0;
12377             switch (keep) {             /* which is... */
12378               case SET_AUTO:            /* AUTO */
12379                 if (curtype == FTT_ASC) /* Delete file if TYPE A. */
12380                   x = 1;
12381                 break;
12382               case SET_OFF:             /* DISCARD */
12383                 x = 1;                  /* Delete file, period. */
12384                 break;
12385               default:                  /* KEEP */
12386                 break;
12387             }
12388             if (x) {
12389                 x = zdelet(ftprecv.local);
12390                 debug(F111,"ftp get delete incomplete",ftprecv.local,x);
12391             }
12392         }
12393     }
12394     signal(SIGINT, ftprecv.oldintr);
12395 #ifdef SIGPIPE
12396     if (ftprecv.oldintp)
12397       signal(SIGPIPE, ftprecv.oldintp);
12398 #endif /* SIGPIPE */
12399     stop = gmstimer();
12400 #ifdef GFTIMER
12401     fpfsecs = gftimer();
12402 #endif /* GFTIMER */
12403     tfc += ffc;
12404
12405 #ifdef TCPIPLIB
12406     socket_close(ftprecv.din);
12407 #else /* TCPIPLIB */
12408 #ifdef USE_SHUTDOWN
12409     shutdown(ftprecv.din, 1+1);
12410 #endif /* USE_SHUTDOWN */
12411     close(ftprecv.din);
12412 #endif /* TCPIPLIB */
12413     ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12414     ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || 
12415                    ftprecv.reply == REPLY_ERROR) ? -1 : 0);
12416 #ifdef NTSIG
12417      ckThreadEnd(threadinfo);
12418 #endif /* NTSIG */
12419 }
12420
12421 static int
12422 recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
12423             xlate, fcs, rcs)
12424     char *cmd, *local, *remote, *lmode, *pipename;
12425     int printnames, recover, xlate, fcs, rcs;
12426 {
12427 #ifdef NT
12428     struct _stat stbuf;
12429 #else /* NT */
12430     struct stat stbuf;
12431 #endif /* NT */
12432
12433 #ifdef DEBUG
12434     if (deblog) {
12435         debug(F111,"ftp recvrequest cmd",cmd,recover);
12436         debug(F110,"ftp recvrequest local ",local,0);
12437         debug(F111,"ftp recvrequest remote",remote,ftp_typ);
12438         debug(F110,"ftp recvrequest pipename ",pipename,0);
12439         debug(F101,"ftp recvrequest xlate","",xlate);
12440         debug(F101,"ftp recvrequest fcs","",fcs);
12441         debug(F101,"ftp recvrequest rcs","",rcs);
12442     }
12443 #endif /* DEBUG */
12444
12445     ftprecv.localsize = 0L;
12446
12447     if (remfile) {                      /* See remcfm(), remtxt() */
12448         if (rempipe) {
12449             pipename = remdest;
12450         } else {
12451             local = remdest;
12452             if (remappd) lmode = "ab";
12453         }
12454     }
12455     out2screen = 0;
12456     if (!cmd) cmd = "";                 /* Core dump prevention */
12457     if (!remote) remote = "";
12458     if (!lmode) lmode = "";
12459
12460     if (pipename) {                     /* No recovery for pipes. */
12461         recover = 0;
12462         if (!local)
12463           local = pipename;
12464     } else {
12465         if (!local)                     /* Output to screen? */
12466           local = "-";
12467         out2screen = !strcmp(local,"-");
12468     }
12469     debug(F101,"ftp recvrequest out2screen","",out2screen);
12470
12471 #ifdef OS2
12472     if ( ftp_xla && out2screen && !k95stdout && !inserver )
12473         fcs = FC_UCS2;
12474 #endif /* OS2 */
12475
12476     if (out2screen)                     /* No recovery to screen */
12477       recover = 0;
12478     if (!ftp_typ)                       /* No recovery in text mode */
12479       recover = 0;
12480     ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
12481
12482     if (!ftprecv.is_retr)               /* No recovery except for RETRieve */
12483       recover = 0;
12484
12485 #ifdef COMMENT
12486     if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
12487         if (recursive && ckstrchr(local,'/')) {
12488             
12489         }
12490     }
12491 #endif /* COMMENT */
12492
12493     ftprecv.localsize = 0L;             /* Local file size */
12494     rs_len = 0L;                        /* Recovery point */
12495
12496     debug(F101,"ftp recvrequest recover","",recover);
12497     if (recover) {                      /* Recovering... */
12498         if (stat(local, &stbuf) < 0) {  /* Can't stat local file */
12499             debug(F101,"ftp recvrequest recover stat failed","",errno);
12500             recover = 0;                /* So cancel recovery */
12501         } else {                        /* Have local file info */
12502             ftprecv.localsize = stbuf.st_size;  /* Get size */
12503             /* Remote file smaller than local */
12504             if (fsize < ftprecv.localsize) {
12505                 debug(F101,"ftp recvrequest recover remote smaller","",fsize);
12506                 recover = 0;            /* Recovery can't work */
12507             } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
12508                 debug(F111,"ftp recvrequest recover equal size",
12509                       remote,ftprecv.localsize);
12510                 return(1);
12511             }
12512 #ifdef COMMENT
12513 /*
12514   The problem here is that the original partial file never got its date
12515   set, either because FTP DATES was OFF, or because the partial file was
12516   downloaded by some other program that doesn't set local file dates, or
12517   because Kermit only sets the file's date when the download was complete
12518   and successful.  In all these cases, the local file has a later time
12519   than the remote.
12520 */
12521             if (recover) {              /* Remote is bigger */
12522                 x = chkmodtime(local,remote,0); /* Check file dates */
12523                 debug(F111,"ftp recvrequest chkmodtime",remote,x);
12524                 if (x != 1)             /* Dates must be equal! */
12525                   recover = 0;          /* If not, get whole file */
12526             }
12527 #endif /* COMMENT */
12528         }
12529         debug(F111,"ftp recvrequest recover",remote,recover);
12530     }
12531
12532 #ifdef FTP_PROXY
12533     if (proxy && ftprecv.is_retr)
12534       return(proxtrans(cmd, local ? local : remote, remote));
12535 #endif /* FTP_PROXY */
12536
12537     ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
12538
12539     ftprecv.reply = 0;
12540     ftprecv.fcs = fcs;
12541     ftprecv.rcs = rcs;
12542     ftprecv.recover = recover;
12543     ftprecv.xlate = xlate;
12544     ftprecv.cmd = cmd;
12545     ftprecv.local = local;
12546     ftprecv.remote = remote;
12547     ftprecv.lmode = lmode;
12548     ftprecv.pipename = pipename;
12549     ftprecv.oldintp = NULL;
12550     ftpcode = 0;
12551
12552     havesigint = 0;
12553     ftprecv.oldintr = signal(SIGINT, cancelrecv);
12554     if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
12555       return -1;
12556     if (ftprecvret < 0)
12557       return -1;
12558
12559     if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
12560       return -1;
12561     return ftprecvret;
12562 }
12563
12564 /*
12565  * Need to start a listen on the data channel before we send the command,
12566  * otherwise the server's connect may fail.
12567  */
12568 static int
12569 initconn() {
12570     register char *p, *a;
12571     int result, tmpno = 0;
12572     int on = 1;
12573     GSOCKNAME_T len;
12574
12575 #ifndef NO_PASSIVE_MODE
12576     int a1,a2,a3,a4,p1,p2;
12577
12578     if (passivemode) {
12579         data = socket(AF_INET, SOCK_STREAM, 0);
12580         globaldin = data;
12581         if (data < 0) {
12582             perror("ftp: socket");
12583             return(-1);
12584         }
12585         if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
12586             printf("Passive mode refused\n");
12587             passivemode = 0;
12588             return(initconn());
12589         }
12590 /*
12591   Now we have a string of comma-separated one-byte unsigned integer values,
12592   The first four are the an IP address.  The fifth is the MSB of the port
12593   number, the sixth is the LSB.  From that we can make a sockaddr_in.
12594 */
12595         if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
12596             printf("Passive mode address scan failure\n");
12597             return(-1);
12598         };
12599 #ifndef NOHTTP
12600         if (tcp_http_proxy) {
12601 #ifdef OS2
12602             char * agent = "Kermit 95"; /* Default user agent */
12603 #else
12604             char * agent = "C-Kermit";
12605 #endif /* OS2 */
12606             register struct hostent *hp = 0;
12607             struct servent *destsp;
12608             char host[512], *p, *q;
12609 #ifdef IP_TOS
12610 #ifdef IPTOS_THROUGHPUT
12611             int tos;
12612 #endif /* IPTOS_THROUGHPUT */
12613 #endif /* IP_TOS */
12614             int s;
12615 #ifdef DEBUG
12616             extern int debtim;
12617             int xdebtim;
12618             xdebtim = debtim;
12619             debtim = 1;
12620 #endif /* DEBUG */
12621
12622             ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
12623                       ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
12624                       NULL,NULL,NULL
12625                       );
12626             memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
12627             for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
12628               *q = *p;
12629             *q = '\0';
12630
12631             hisctladdr.sin_addr.s_addr = inet_addr(host);
12632             if (hisctladdr.sin_addr.s_addr != -1) {
12633                 debug(F110,"initconn A",host,0);
12634                 hisctladdr.sin_family = AF_INET;
12635             } else {
12636                 debug(F110,"initconn B",host,0);
12637                 hp = gethostbyname(host);
12638 #ifdef HADDRLIST
12639                 hp = ck_copyhostent(hp); /* make safe copy that won't change */
12640 #endif /* HADDRLIST */
12641                 if (hp == NULL) {
12642                     fprintf(stderr, "ftp: %s: Unknown host\n", host);
12643                     ftpcode = -1;
12644 #ifdef DEBUG
12645                     debtim = xdebtim;
12646 #endif /* DEBUG */
12647                     return(0);
12648                 }
12649                 hisctladdr.sin_family = hp->h_addrtype;
12650 #ifdef HADDRLIST
12651                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
12652                        sizeof(hisctladdr.sin_addr));
12653 #else /* HADDRLIST */
12654                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
12655                        sizeof(hisctladdr.sin_addr));
12656 #endif /* HADDRLIST */
12657             }
12658             data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
12659             debug(F101,"initconn socket","",data);
12660             if (data < 0) {
12661                 perror("ftp: socket");
12662                 ftpcode = -1;
12663 #ifdef DEBUG
12664                 debtim = xdebtim;
12665 #endif /* DEBUG */
12666                 return(0);
12667             }
12668             if (*p == ':')
12669               p++;
12670             else
12671               p = "http";
12672
12673             destsp = getservbyname(p,"tcp");
12674             if (destsp)
12675               hisctladdr.sin_port = destsp->s_port;
12676             else if (p)
12677               hisctladdr.sin_port = htons(atoi(p));
12678             else
12679               hisctladdr.sin_port = htons(80);
12680             errno = 0;
12681 #ifdef HADDRLIST
12682             debug(F100,"initconn HADDRLIST","",0);
12683             while
12684 #else
12685             debug(F100,"initconn no HADDRLIST","",0);
12686             if
12687 #endif /* HADDRLIST */
12688               (connect(data, (struct sockaddr *)&hisctladdr,
12689                        sizeof (hisctladdr)) < 0) {
12690                   debug(F101,"initconn connect failed","",errno);
12691 #ifdef HADDRLIST
12692                   if (hp && hp->h_addr_list[1]) {
12693                       int oerrno = errno;
12694
12695                       fprintf(stderr,
12696                               "ftp: connect to address %s: ",
12697                               inet_ntoa(hisctladdr.sin_addr)
12698                               );
12699                       errno = oerrno;
12700                       perror((char *)0);
12701                       hp->h_addr_list++;
12702                       memcpy((char *)&hisctladdr.sin_addr,
12703                              hp->h_addr_list[0],
12704                              sizeof(hisctladdr.sin_addr));
12705                       fprintf(stdout, "Trying %s...\n",
12706                               inet_ntoa(hisctladdr.sin_addr));
12707 #ifdef TCPIPLIB
12708                       socket_close(data);
12709 #else /* TCPIPLIB */
12710                       close(data);
12711 #endif /* TCPIPLIB */
12712                       data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
12713                       if (data < 0) {
12714                           perror("ftp: socket");
12715                           ftpcode = -1;
12716 #ifdef DEBUG
12717                           debtim = xdebtim;
12718 #endif /* DEBUG */
12719                           return(0);
12720                       }
12721                       continue;
12722                   }
12723 #endif /* HADDRLIST */
12724                   perror("ftp: connect");
12725                   ftpcode = -1;
12726                   goto bad;
12727               }
12728             if (http_connect(data,
12729                              tcp_http_proxy_agent ?
12730                                tcp_http_proxy_agent :
12731                                  agent,
12732                              NULL,
12733                              tcp_http_proxy_user,
12734                              tcp_http_proxy_pwd,
12735                              0,
12736                              proxyhost
12737                              ) < 0) {
12738 #ifdef TCPIPLIB
12739                 socket_close(data);
12740 #else /* TCPIPLIB */
12741                 close(data);
12742 #endif /* TCPIPLIB */
12743                 perror("ftp: connect");
12744                 ftpcode = -1;
12745                 goto bad;
12746             }
12747         } else
12748 #endif /* NOHTTP */
12749         {
12750             data_addr.sin_family = AF_INET;
12751             data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
12752             data_addr.sin_port = htons((p1<<8)|p2);
12753
12754             if (connect(data,
12755                         (struct sockaddr *)&data_addr,
12756                         sizeof(data_addr)) < 0
12757                 ) {
12758                 perror("ftp: connect");
12759                 return(-1);
12760             }
12761         }
12762         debug(F100,"initconn connect ok","",0);
12763 #ifdef IP_TOS
12764 #ifdef IPTOS_THROUGHPUT
12765         on = IPTOS_THROUGHPUT;
12766         if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
12767           perror("ftp: setsockopt TOS (ignored)");
12768 #endif /* IPTOS_THROUGHPUT */
12769 #endif /* IP_TOS */
12770         memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
12771         return(0);
12772     }
12773 #endif /* NO_PASSIVE_MODE */
12774
12775   noport:
12776     memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
12777     if (sendport)
12778       data_addr.sin_port = 0;   /* let system pick one */
12779     if (data != -1) {
12780 #ifdef TCPIPLIB
12781         socket_close(data);
12782 #else /* TCPIPLIB */
12783 #ifdef USE_SHUTDOWN
12784         shutdown(data, 1+1);
12785 #endif /* USE_SHUTDOWN */
12786         close(data);
12787 #endif /* TCPIPLIB */
12788     }
12789     data = socket(AF_INET, SOCK_STREAM, 0);
12790     globaldin = data;
12791     if (data < 0) {
12792         perror("ftp: socket");
12793         if (tmpno)
12794           sendport = 1;
12795         return(-1);
12796     }
12797     if (!sendport) {
12798         if (setsockopt(data,
12799                        SOL_SOCKET,
12800                        SO_REUSEADDR,
12801                        (char *)&on,
12802                        sizeof (on)
12803                        ) < 0
12804             ) {
12805             perror("ftp: setsockopt (reuse address)");
12806             goto bad;
12807         }
12808     }
12809     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
12810         perror("ftp: bind");
12811         goto bad;
12812     }
12813     len = sizeof (data_addr);
12814     if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
12815         perror("ftp: getsockname");
12816         goto bad;
12817     }
12818     if (listen(data, 1) < 0) {
12819         perror("ftp: listen");
12820         goto bad;
12821     }
12822     if (sendport) {
12823         a = (char *)&data_addr.sin_addr;
12824         p = (char *)&data_addr.sin_port;
12825         ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
12826                   UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
12827                   UC(p[0]),",", UC(p[1]));
12828         result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
12829         if (result == REPLY_ERROR && sendport) {
12830             sendport = 0;
12831             tmpno = 1;
12832             goto noport;
12833         }
12834         return(result != REPLY_COMPLETE);
12835     }
12836     if (tmpno)
12837       sendport = 1;
12838 #ifdef IP_TOS
12839 #ifdef IPTOS_THROUGHPUT
12840     on = IPTOS_THROUGHPUT;
12841     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
12842       perror("ftp: setsockopt TOS (ignored)");
12843 #endif
12844 #endif
12845     return(0);
12846   bad:
12847 #ifdef TCPIPLIB
12848     socket_close(data);
12849 #else /* TCPIPLIB */
12850 #ifdef USE_SHUTDOWN
12851     shutdown(data, 1+1);
12852 #endif /* USE_SHUTDOWN */
12853     close(data);
12854 #endif /* TCPIPLIB */
12855     data = -1;
12856     globaldin = data;
12857     if (tmpno)
12858       sendport = 1;
12859     return(-1);
12860 }
12861
12862 #ifdef CK_SSL
12863 static int
12864 ssl_dataconn() {
12865     if (ssl_ftp_data_con!=NULL) {       /* Do SSL */
12866         SSL_free(ssl_ftp_data_con);
12867         ssl_ftp_data_con=NULL;
12868     }
12869     ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
12870
12871     SSL_set_fd(ssl_ftp_data_con,data);
12872     SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
12873
12874     SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
12875
12876     if (ssl_debug_flag) {
12877         fprintf(stderr,"=>START SSL connect on DATA\n");
12878         fflush(stderr);
12879     }
12880     if (SSL_connect(ssl_ftp_data_con) <= 0) {
12881         static char errbuf[1024];
12882         ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
12883                   ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
12884         fprintf(stderr,"%s\n", errbuf);
12885         fflush(stderr);
12886 #ifdef TCPIPLIB
12887         socket_close(data);
12888 #else /* TCPIPLIB */
12889 #ifdef USE_SHUTDOWN
12890         shutdown(data, 1+1);
12891 #endif /* USE_SHUTDOWN */
12892         close(data);
12893 #endif /* TCPIPLIB */
12894         data = -1;
12895         globaldin = data;
12896         return(-1);
12897     } else {
12898         ssl_ftp_data_active_flag=1;
12899
12900         if (!ssl_certsok_flag && !tls_is_krb5(2)) {
12901             char *subject = ssl_get_subject_name(ssl_ftp_data_con);
12902
12903             if (!subject) {
12904                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
12905                     debug(F110,"dataconn","[SSL _- FAILED]",0);
12906
12907                     ssl_ftp_data_active_flag = 0;
12908 #ifdef TCPIPLIB
12909                     socket_close(data);
12910 #else /* TCPIPLIB */
12911 #ifdef USE_SHUTDOWN
12912                     shutdown(data, 1+1);
12913 #endif /* USE_SHUTDOWN */
12914                     close(data);
12915 #endif /* TCPIPLIB */
12916                     data = -1;
12917                     globaldin = data;
12918                     return(-1);
12919                 } else {
12920                     if (!out2screen && displa && fdispla) {
12921                         ftscreen(SCR_TC,0,0L,"Display canceled");
12922                         /* fdispla = XYFD_B; */
12923                     }
12924
12925                     if (uq_ok(
12926           "Warning: Server didn't provide a certificate on data connection\n",
12927                                "Continue with file transfer? (Y/N)",
12928                               3,NULL,0) <= 0) {
12929                         debug(F110, "dataconn","[SSL - FAILED]",0);
12930                         ssl_ftp_data_active_flag = 0;
12931 #ifdef TCPIPLIB
12932                         socket_close(data);
12933 #else /* TCPIPLIB */
12934 #ifdef USE_SHUTDOWN
12935                         shutdown(data, 1+1);
12936 #endif /* USE_SHUTDOWN */
12937                         close(data);
12938 #endif /* TCPIPLIB */
12939                         data = -1;
12940                         globaldin = data;
12941                         return(-1);
12942                     }
12943                 }
12944             } else {
12945                 if (!out2screen && displa && fdispla == XYFD_C) {
12946                     ftscreen(SCR_TC,0,0L,"Display canceled");
12947                     /* fdispla = XYFD_B; */
12948                 }
12949
12950                 if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
12951                     debug(F110,"dataconn","[SSL - FAILED]",0);
12952                     ssl_ftp_data_active_flag = 0;
12953 #ifdef TCPIPLIB
12954                     socket_close(data);
12955 #else /* TCPIPLIB */
12956 #ifdef USE_SHUTDOWN
12957                     shutdown(data, 1+1);
12958 #endif /* USE_SHUTDOWN */
12959                     close(data);
12960 #endif /* TCPIPLIB */
12961                     data = -1;
12962                     globaldin = data;
12963                     return(-1);
12964                 }
12965             }
12966         }
12967         debug(F110,"dataconn","[SSL - OK]",0);
12968 #ifdef COMMENT
12969         /* This messes up the full screen file transfer display */
12970         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
12971 #endif /* COMMENT */
12972     }
12973     if (ssl_debug_flag) {
12974         fprintf(stderr,"=>DONE SSL connect on DATA\n");
12975         fflush(stderr);
12976     }
12977     return(data);
12978 }
12979 #endif /* CK_SSL */
12980
12981 static int
12982 dataconn(lmode) char *lmode; {
12983     int s;
12984 #ifdef IP_TOS
12985     int tos;
12986 #endif /* IP_TOS */
12987 #ifdef UCX50
12988     static u_int fromlen;
12989 #else
12990     static SOCKOPT_T fromlen;
12991 #endif /* UCX50 */
12992
12993     fromlen = sizeof(hisdataaddr);
12994
12995 #ifndef NO_PASSIVE_MODE
12996     if (passivemode) {
12997 #ifdef CK_SSL
12998         ssl_ftp_data_active_flag=0;
12999         if (ssl_ftp_active_flag &&
13000             (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13001           return(ssl_dataconn());
13002 #endif /* CK_SSL */
13003         return(data);
13004     }
13005 #endif /* NO_PASSIVE_MODE */
13006
13007     s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
13008     if (s < 0) {
13009         perror("ftp: accept");
13010 #ifdef TCPIPLIB
13011         socket_close(data);
13012 #else /* TCPIPLIB */
13013 #ifdef USE_SHUTDOWN
13014         shutdown(data, 1+1);
13015 #endif /* USE_SHUTDOWN */
13016         close(data);
13017 #endif /* TCPIPLIB */
13018         data = -1;
13019         globaldin = data;
13020         return(-1);
13021     }
13022 #ifdef TCPIPLIB
13023     socket_close(data);
13024 #else /* TCPIPLIB */
13025 #ifdef USE_SHUTDOWN
13026     shutdown(data, 1+1);
13027 #endif /* USE_SHUTDOWN */
13028     close(data);
13029 #endif /* TCPIPLIB */
13030     data = s;
13031     globaldin = data;
13032 #ifdef IP_TOS
13033 #ifdef IPTOS_THROUGHPUT
13034     tos = IPTOS_THROUGHPUT;
13035     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
13036       perror("ftp: setsockopt TOS (ignored)");
13037 #endif /* IPTOS_THROUGHPUT */
13038 #endif /* IP_TOS */
13039
13040 #ifdef CK_SSL
13041     ssl_ftp_data_active_flag=0;
13042     if (ssl_ftp_active_flag &&
13043         (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13044       return(ssl_dataconn());
13045 #endif /* CK_SSL */
13046     return(data);
13047 }
13048
13049 #ifdef FTP_PROXY
13050 static sigtype
13051 pscancel(sig) int sig; {
13052     cancelfile++;
13053 }
13054
13055 static VOID
13056 pswitch(flag) int flag; {
13057     extern int proxy;
13058     sig_t oldintr;
13059     static struct comvars {
13060         int connect;
13061         char name[MAXHOSTNAMELEN];
13062         struct sockaddr_in mctl;
13063         struct sockaddr_in hctl;
13064         FILE *in;
13065         FILE *out;
13066         int tpe;
13067         int curtpe;
13068         int cpnd;
13069         int sunqe;
13070         int runqe;
13071         int mcse;
13072         int ntflg;
13073         char nti[17];
13074         char nto[17];
13075         int mapflg;
13076         char mi[CKMAXPATH];
13077         char mo[CKMAXPATH];
13078         char *authtype;
13079         int clvl;
13080         int dlvl;
13081 #ifdef FTP_KRB4
13082         des_cblock session;
13083         des_key_schedule ftp_sched;
13084 #endif /* FTP_KRB4 */
13085 #ifdef FTP_GSSAPI
13086         gss_ctx_id_t gcontext;
13087 #endif /* GSSAPI */
13088     } proxstruct, tmpstruct;
13089     struct comvars *ip, *op;
13090
13091     cancelfile = 0;
13092     oldintr = signal(SIGINT, pscancel);
13093     if (flag) {
13094         if (proxy)
13095           return;
13096         ip = &tmpstruct;
13097         op = &proxstruct;
13098         proxy++;
13099     } else {
13100         if (!proxy)
13101           return;
13102         ip = &proxstruct;
13103         op = &tmpstruct;
13104         proxy = 0;
13105     }
13106     ip->connect = connected;
13107     connected = op->connect;
13108     if (ftp_host) {
13109         strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
13110         ip->name[MAXHOSTNAMELEN - 1] = '\0';
13111         ip->name[strlen(ip->name)] = '\0';
13112     } else
13113       ip->name[0] = 0;
13114     ftp_host = op->name;
13115     ip->hctl = hisctladdr;
13116     hisctladdr = op->hctl;
13117     ip->mctl = myctladdr;
13118     myctladdr = op->mctl;
13119     ip->in = csocket;
13120     csocket = op->in;
13121     ip->out = csocket;
13122     csocket = op->out;
13123     ip->tpe = ftp_typ;
13124     ftp_typ = op->tpe;
13125     ip->curtpe = curtype;
13126     curtype = op->curtpe;
13127     ip->cpnd = cpend;
13128     cpend = op->cpnd;
13129     ip->sunqe = ftp_usn;
13130     ftp_usn = op->sunqe;
13131     ip->mcse = mcase;
13132     mcase = op->mcse;
13133     ip->ntflg = ntflag;
13134     ntflag = op->ntflg;
13135     strncpy(ip->nti, ntin, 16);
13136     (ip->nti)[strlen(ip->nti)] = '\0';
13137     strcpy(ntin, op->nti);
13138     strncpy(ip->nto, ntout, 16);
13139     (ip->nto)[strlen(ip->nto)] = '\0';
13140     strcpy(ntout, op->nto);
13141     ip->mapflg = mapflag;
13142     mapflag = op->mapflg;
13143     strncpy(ip->mi, mapin, CKMAXPATH - 1);
13144     (ip->mi)[strlen(ip->mi)] = '\0';
13145     strcpy(mapin, op->mi);
13146     strncpy(ip->mo, mapout, CKMAXPATH - 1);
13147     (ip->mo)[strlen(ip->mo)] = '\0';
13148     strcpy(mapout, op->mo);
13149     ip->authtype = auth_type;
13150     auth_type = op->authtype;
13151     ip->clvl = ftp_cpl;
13152     ftp_cpl = op->clvl;
13153     ip->dlvl = ftp_dpl;
13154     ftp_dpl = op->dlvl;
13155     if (!ftp_cpl)
13156       ftp_cpl = FPL_CLR;
13157     if (!ftp_dpl)
13158       ftp_dpl = FPL_CLR;
13159 #ifdef FTP_KRB4
13160     memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
13161     memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
13162     memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
13163     memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
13164 #endif /* FTP_KRB4 */
13165 #ifdef FTP_GSSAPI
13166     ip->gcontext = gcontext;
13167     gcontext = op->gcontext;
13168 #endif /* GSSAPI */
13169     signal(SIGINT, oldintr);
13170     if (cancelfile) {
13171         cancelfile = 0;
13172         debug(F101,"pswitch cancelfile B","",cancelfile);
13173         (*oldintr)(SIGINT);
13174     }
13175 }
13176
13177 static sigtype
13178 cancelpt(sig) int sig; {
13179     printf("\n");
13180     fflush(stdout);
13181     ptabflg++;
13182     cancelfile = 0;
13183 #ifndef OS2
13184     longjmp(ptcancel, 1);
13185 #else
13186     PostCtrlCSem();
13187 #endif /* OS2 */
13188 }
13189
13190 void
13191 proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
13192     sig_t oldintr;
13193     int secndflag = 0, prox_type, nfnd;
13194     char *cmd2;
13195 #ifdef BSDSELECT
13196     fd_set mask;
13197 #endif /* BSDSELECT */
13198     sigtype cancelpt();
13199
13200     if (strcmp(cmd, "RETR"))
13201       cmd2 = "RETR";
13202     else
13203       cmd2 = unique ? "STOU" : "STOR";
13204     if ((prox_type = type) == 0) {
13205         if (servertype == SYS_UNIX && unix_proxy)
13206           prox_type = FTT_BIN;
13207         else
13208           prox_type = FTT_ASC;
13209     }
13210     if (curtype != prox_type)
13211       changetype(prox_type, 1);
13212     if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13213         printf("Proxy server does not support third party transfers.\n");
13214         return;
13215     }
13216     pswitch(0);
13217     if (!connected) {
13218         printf("No primary connection\n");
13219         pswitch(1);
13220         ftpcode = -1;
13221         return;
13222     }
13223     if (curtype != prox_type)
13224       changetype(prox_type, 1);
13225
13226     if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
13227         pswitch(1);
13228         return;
13229     }
13230
13231     /* Replace with calls to cc_execute() */
13232     if (setjmp(ptcancel))
13233       goto cancel;
13234     oldintr = signal(SIGINT, cancelpt);
13235     if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
13236         signal(SIGINT, oldintr);
13237         pswitch(1);
13238         return;
13239     }
13240     sleep(2000);
13241     pswitch(1);
13242     secndflag++;
13243     if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
13244       goto cancel;
13245     ptflag++;
13246     getreply(0,-1,-1,ftp_vbm,0);
13247     pswitch(0);
13248     getreply(0,-1,-1,ftp_vbm,0);
13249     signal(SIGINT, oldintr);
13250     pswitch(1);
13251     ptflag = 0;
13252     return;
13253
13254   cancel:
13255     signal(SIGINT, SIG_IGN);
13256     ptflag = 0;
13257     if (strcmp(cmd, "RETR") && !proxy)
13258       pswitch(1);
13259     else if (!strcmp(cmd, "RETR") && proxy)
13260       pswitch(0);
13261     if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
13262         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13263             pswitch(0);
13264             if (cpend)
13265               cancel_remote(0);
13266         }
13267         pswitch(1);
13268         if (ptabflg)
13269           ftpcode = -1;
13270         signal(SIGINT, oldintr);
13271         return;
13272     }
13273     if (cpend)
13274       cancel_remote(0);
13275     pswitch(!proxy);
13276     if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
13277         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13278             pswitch(0);
13279             if (cpend)
13280               cancel_remote(0);
13281             pswitch(1);
13282             if (ptabflg)
13283               ftpcode = -1;
13284             signal(SIGINT, oldintr);
13285             return;
13286         }
13287     }
13288     if (cpend)
13289       cancel_remote(0);
13290     pswitch(!proxy);
13291     if (cpend) {
13292 #ifdef BSDSELECT
13293         FD_ZERO(&mask);
13294         FD_SET(csocket, &mask);
13295         if ((nfnd = empty(&mask, 10)) <= 0) {
13296             if (nfnd < 0) {
13297                 perror("cancel");
13298             }
13299             if (ptabflg)
13300               ftpcode = -1;
13301             lostpeer();
13302         }
13303 #else /* BSDSELECT */
13304 #ifdef IBMSELECT
13305         if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
13306             if (nfnd < 0) {
13307                 perror("cancel");
13308             }
13309             if (ptabflg)
13310               ftpcode = -1;
13311             lostpeer();
13312         }
13313 #endif /* IBMSELECT */
13314 #endif /* BSDSELECT */
13315         getreply(0,-1,-1,ftp_vbm,0);
13316         getreply(0,-1,-1,ftp_vbm,0);
13317     }
13318     if (proxy)
13319       pswitch(0);
13320     pswitch(1);
13321     if (ptabflg)
13322       ftpcode = -1;
13323     signal(SIGINT, oldintr);
13324 }
13325 #endif /* FTP_PROXY */
13326
13327 #ifdef FTP_SECURITY
13328 #ifdef FTP_GSSAPI
13329
13330 struct {
13331     CONST gss_OID_desc * CONST * mech_type;
13332     char *service_name;
13333 } gss_trials[] = {
13334     { &gss_mech_krb5, "ftp" },
13335     { &gss_mech_krb5, "host" },
13336 };
13337
13338 int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
13339 #endif /* FTP_GSSAPI */
13340
13341 static int
13342 ftp_auth() {
13343     extern int setsafe();
13344     int j = 0, n;
13345 #ifdef FTP_KRB4
13346     char *service, inst[INST_SZ];
13347     ULONG cksum;
13348     ULONG checksum = (ULONG) getpid();
13349     CHAR out_buf[FTP_BUFSIZ];
13350     int i;
13351 #else /* FTP_KRB4 */
13352 #ifdef FTP_GSSAPI
13353     CHAR out_buf[FTP_BUFSIZ];
13354     int i;
13355 #endif /* FTP_GSSAPI */
13356 #endif /* FTP_KRB4 */
13357
13358     if (ssl_ftp_proxy)                  /* Do not allow AUTH over SSL proxy */
13359         return(0);
13360
13361     if (auth_type)
13362       return(1);                        /* auth already succeeded */
13363
13364     /* Try each auth type as specified by the end user */
13365     for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
13366 #ifdef FTP_GSSAPI
13367         if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
13368             n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
13369             if (n == REPLY_CONTINUE) {
13370                 OM_uint32 maj_stat, min_stat;
13371                 gss_name_t target_name;
13372                 gss_buffer_desc send_tok, recv_tok, *token_ptr;
13373                 char stbuf[FTP_BUFSIZ];
13374                 int comcode, trial;
13375                 struct gss_channel_bindings_struct chan;
13376                 char * realm = NULL;
13377                 char tgt[256];
13378
13379                 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
13380                 chan.initiator_address.length = 4;
13381                 chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
13382                 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
13383                 chan.acceptor_address.length = 4;
13384                 chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
13385                 chan.application_data.length = 0;
13386                 chan.application_data.value = 0;
13387
13388                 if (!quiet)
13389                   printf("GSSAPI accepted as authentication type\n");
13390
13391                 realm = ck_krb5_realmofhost(ftp_user_host);
13392                 if (realm) {
13393                     ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
13394                     debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
13395                     if ( krb5_autoget &&
13396                          !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
13397                             (ck_krb5_is_tgt_valid() > 0)) )
13398                         ck_krb5_autoget_TGT(realm);
13399                 }
13400
13401                 /* Blob from gss-client */
13402                 for (trial = 0; trial < n_gss_trials; trial++) {
13403                     /* ftp@hostname first, the host@hostname */
13404                     /* the V5 GSSAPI binding canonicalizes this for us... */
13405                     ckmakmsg(stbuf,FTP_BUFSIZ,
13406                              gss_trials[trial].service_name,
13407                              "@",
13408                              ftp_user_host,
13409                              NULL
13410                              );
13411                     if (ftp_deb)
13412                       fprintf(stderr,
13413                               "Authenticating to <%s>...\n", stbuf);
13414                     send_tok.value = stbuf;
13415                     send_tok.length = strlen(stbuf);
13416                     maj_stat = gss_import_name(&min_stat, &send_tok,
13417                                                gss_nt_service_name,
13418                                                &target_name
13419                                                );
13420                     if (maj_stat != GSS_S_COMPLETE) {
13421                         user_gss_error(maj_stat, min_stat, "parsing name");
13422                         secure_error("name parsed <%s>\n", stbuf);
13423                         continue;
13424                     }
13425                     token_ptr = GSS_C_NO_BUFFER;
13426                     gcontext = GSS_C_NO_CONTEXT; /* structure copy */
13427
13428                     do {
13429                         if (ftp_deb)
13430                           fprintf(stderr, "calling gss_init_sec_context\n");
13431                         maj_stat =
13432                           gss_init_sec_context(&min_stat,
13433                                                GSS_C_NO_CREDENTIAL,
13434                                                &gcontext,
13435                                                target_name,
13436                                                (gss_OID) *
13437                                                  gss_trials[trial].mech_type,
13438                                                GSS_C_MUTUAL_FLAG |
13439                                                GSS_C_REPLAY_FLAG |
13440                                                (ftp_cfw ?
13441                                                 GSS_C_DELEG_FLAG : 0),
13442                                                0,
13443                                                 /* channel bindings */
13444                                                 (krb5_d_no_addresses ?
13445                                                   GSS_C_NO_CHANNEL_BINDINGS :
13446                                                   &chan),
13447                                                 token_ptr,
13448                                                NULL,    /* ignore mech type */
13449                                                &send_tok,
13450                                                NULL,    /* ignore ret_flags */
13451                                                NULL
13452                                                );       /* ignore time_rec */
13453
13454                         if (maj_stat != GSS_S_COMPLETE &&
13455                             maj_stat != GSS_S_CONTINUE_NEEDED) {
13456                             if (trial == n_gss_trials-1)
13457                               user_gss_error(maj_stat,
13458                                              min_stat,
13459                                              "initializing context"
13460                                              );
13461                             gss_release_name(&min_stat, &target_name);
13462                             /* maybe we missed on the service name */
13463                             goto outer_loop;
13464                         }
13465                         if (send_tok.length != 0) {
13466                             int len;
13467                             reply_parse = "ADAT="; /* for ftpcmd() later */
13468                             len = FTP_BUFSIZ;
13469                             kerror =
13470                               radix_encode(send_tok.value,
13471                                            out_buf,
13472                                            send_tok.length,
13473                                            &len,
13474                                            RADIX_ENCODE
13475                                            );
13476                             if (kerror)  {
13477                                 fprintf(stderr,
13478                                         "Base 64 encoding failed: %s\n",
13479                                         radix_error(kerror)
13480                                         );
13481                                 goto gss_complete_loop;
13482                             }
13483                             comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
13484                             if (comcode != REPLY_COMPLETE
13485                                 && comcode != REPLY_CONTINUE /* (335) */
13486                                 ) {
13487                                 if (trial == n_gss_trials-1) {
13488                                     fprintf(stderr, "GSSAPI ADAT failed\n");
13489                                     /* force out of loop */
13490                                     maj_stat = GSS_S_FAILURE;
13491                                 }
13492                                 /*
13493                                   Backoff to the v1 gssapi is still possible.
13494                                   Send a new AUTH command.  If that fails,
13495                                   terminate the loop.
13496                                 */
13497                                 if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
13498                                     != REPLY_CONTINUE) {
13499                                     fprintf(stderr,
13500                                 "GSSAPI ADAT failed, AUTH restart failed\n");
13501                                     /* force out of loop */
13502                                     maj_stat = GSS_S_FAILURE;
13503                                 }
13504                                 goto outer_loop;
13505                             }
13506                             if (!reply_parse) {
13507                                 fprintf(stderr,
13508                               "No authentication data received from server\n");
13509                                 if (maj_stat == GSS_S_COMPLETE) {
13510                                     fprintf(stderr,
13511                                             "...but no more was needed\n");
13512                                     goto gss_complete_loop;
13513                                 } else {
13514                                     user_gss_error(maj_stat,
13515                                                    min_stat,
13516                                                    "no reply, huh?"
13517                                                    );
13518                                     goto gss_complete_loop;
13519                                 }
13520                             }
13521                             len = FTP_BUFSIZ;
13522                             kerror = radix_encode(reply_parse,out_buf,i,&len,
13523                                                   RADIX_DECODE);
13524                             if (kerror) {
13525                                 fprintf(stderr,
13526                                         "Base 64 decoding failed: %s\n",
13527                                         radix_error(kerror));
13528                                 goto gss_complete_loop;
13529                             }
13530
13531                             /* everything worked */
13532                             token_ptr = &recv_tok;
13533                             recv_tok.value = out_buf;
13534                             recv_tok.length = len;
13535                             continue;
13536
13537                             /* get out of loop clean */
13538                           gss_complete_loop:
13539                             trial = n_gss_trials-1;
13540                             gss_release_buffer(&min_stat, &send_tok);
13541                             gss_release_name(&min_stat, &target_name);
13542                             goto outer_loop;
13543                         }
13544                     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
13545
13546                   outer_loop:
13547                     if (maj_stat == GSS_S_COMPLETE)
13548                       break;
13549                 }
13550                 if (maj_stat == GSS_S_COMPLETE) {
13551                     printf("GSSAPI authentication succeeded\n");
13552                     reply_parse = NULL;
13553                     auth_type = "GSSAPI";
13554                     return(1);
13555                 } else {
13556                     fprintf(stderr, "GSSAPI authentication failed\n");
13557                     reply_parse = NULL;
13558                 }
13559             } else {
13560                 if (ftp_deb)
13561                 fprintf(stderr, "GSSAPI rejected as an authentication type\n");
13562                 if (ftpcode == 500 || ftpcode == 502)
13563                     return(0);
13564             }
13565         }
13566 #endif /* FTP_GSSAPI */
13567 #ifdef FTP_SRP
13568         if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
13569             if (srp_ftp_auth(ftp_user_host,NULL,NULL))
13570               return(1);
13571             else if (ftpcode == 500 || ftpcode == 502)
13572               return(0);
13573         }
13574 #endif /* FTP_SRP */
13575 #ifdef FTP_KRB4
13576         if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
13577             n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
13578             if (n == REPLY_CONTINUE) {
13579                 char tgt[4*REALM_SZ+1];
13580                 int rc;
13581
13582                 if (!quiet)
13583                   printf("KERBEROS_V4 accepted as authentication type\n");
13584                 ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
13585                 ckstrncpy(ftp_realm,
13586                           (char *)ck_krb4_realmofhost(ftp_user_host),
13587                           REALM_SZ
13588                           );
13589
13590                 ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
13591                 rc = ck_krb4_tkt_isvalid(tgt);
13592
13593                 if (rc <= 0 && krb4_autoget)
13594                   ck_krb4_autoget_TGT(ftp_realm);
13595
13596                 service = "ftp";
13597                 kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
13598                 if (kerror == KDC_PR_UNKNOWN) {
13599                     service = "rcmd";
13600                     kerror = krb_mk_req(&ftp_tkt,
13601                                         service,
13602                                         inst,
13603                                         ftp_realm,
13604                                         checksum
13605                                         );
13606                 }
13607                 if (kerror)
13608                   fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
13609                           krb_get_err_text(kerror));
13610                 if (!kerror) {
13611                     kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
13612                     if (kerror)
13613                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
13614                               krb_get_err_text(kerror));
13615                 }
13616                 if (!kerror) {
13617                     int rc;
13618                     rc = des_key_sched(ftp_cred.session, ftp_sched);
13619                     if (rc == -1) {
13620                        printf("?Invalid DES key specified in credentials\r\n");
13621                        debug(F110,"ftp_auth",
13622                              "invalid DES Key specified in credentials",0);
13623                     } else if ( rc == -2 ) {
13624                         printf("?Weak DES key specified in credentials\r\n");
13625                         debug(F110,"ftp_auth",
13626                               "weak DES Key specified in credentials",0);
13627                     } else if ( rc != 0 ) {
13628                         printf("?DES Key Schedule not set by credentials\r\n");
13629                         debug(F110,"ftp_auth",
13630                               "DES Key Schedule not set by credentials",0);
13631                     }
13632                     reply_parse = "ADAT=";
13633                     i = FTP_BUFSIZ;
13634                     kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
13635                                           &i, RADIX_ENCODE);
13636                     if (kerror) {
13637                         fprintf(stderr, "Base 64 encoding failed: %s\n",
13638                                 radix_error(kerror));
13639                         goto krb4_err;
13640                     }
13641                     if (i > FTP_BUFSIZ - 6)
13642                       printf("?ADAT data too long\n");
13643                     if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
13644                         REPLY_COMPLETE) {
13645                         fprintf(stderr, "Kerberos V4 authentication failed\n");
13646                         goto krb4_err;
13647                     }
13648                     if (!reply_parse) {
13649                         fprintf(stderr,
13650                              "No authentication data received from server\n");
13651                         goto krb4_err;
13652                     }
13653                     i = sizeof(out_buf);
13654                     kerror =
13655                       radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
13656                     if (kerror) {
13657                         fprintf(stderr, "Base 64 decoding failed: %s\n",
13658                                 radix_error(kerror));
13659                         goto krb4_err;
13660                     }
13661                     kerror = krb_rd_safe(out_buf, i,
13662 #ifdef KRB524
13663                                          ftp_cred.session,
13664 #else /* KRB524 */
13665                                          &ftp_cred.session,
13666 #endif /* KRB524 */
13667                                          &hisctladdr,
13668                                          &myctladdr,
13669                                          &ftp_msg_data
13670                                          );
13671                     if (kerror) {
13672                         fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
13673                                 krb_get_err_text(kerror));
13674                         goto krb4_err;
13675                     }
13676
13677                     /* fetch the (modified) checksum */
13678                     memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
13679                     if (ntohl(cksum) == checksum + 1) {
13680                         if (ftp_vbm)
13681                           printf("Kerberos V4 authentication succeeded\n");
13682                         reply_parse = NULL;
13683                         auth_type = "KERBEROS_V4";
13684                         return(1);
13685                     } else
13686                       fprintf(stderr,
13687                               "Kerberos V4 mutual authentication failed\n");
13688                   krb4_err:
13689                     reply_parse = NULL;
13690                 }
13691             } else {
13692                 if (ftp_deb)
13693                   fprintf(stderr,
13694                       "KERBEROS_V4 rejected as an authentication type\n");
13695                 if (ftpcode == 500 || ftpcode == 502)
13696                     return(0);
13697             }
13698         }
13699 #endif /* FTP_KRB4 */
13700 #ifdef CK_SSL
13701         if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
13702 #ifdef FTPHOST
13703             if (!hostcmd) {
13704                 ftpcmd("HOST",ftp_user_host,0,0,0);
13705                 hostcmd = 1;
13706             }
13707 #endif /* FTPHOST */
13708             n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
13709             if (n != REPLY_COMPLETE)
13710               n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
13711             if (n == REPLY_COMPLETE) {
13712                 if (!quiet)
13713                   printf("TLS accepted as authentication type\n");
13714
13715                 auth_type = "TLS";
13716                 ssl_auth();
13717                 if (ssl_ftp_active_flag ) {
13718                     ftp_dpl = FPL_CLR;
13719                     ftp_cpl = FPL_PRV;
13720                     return(1);
13721                 } else {
13722                     fprintf(stderr,"TLS authentication failed\n");
13723                     auth_type = NULL;
13724 #ifdef TCPIPLIB
13725                     socket_close(csocket);
13726 #else /* TCPIPLIB */
13727 #ifdef USE_SHUTDOWN
13728                     shutdown(csocket, 1+1);
13729 #endif /* USE_SHUTDOWN */
13730                     close(csocket);
13731 #endif /* TCPIPLIB */
13732                     csocket = -1;
13733                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
13734                       return(0);
13735                 }
13736             } else {
13737                 if (ftp_deb)
13738                   fprintf(stderr,"TLS rejected as an authentication type\n");
13739                 if (ftpcode == 500 || ftpcode == 502)
13740                     return(0);
13741             }
13742         }
13743         if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
13744 #ifdef FTPHOST
13745             if (!hostcmd) {
13746                 ftpcmd("HOST",ftp_user_host,0,0,0);
13747                 hostcmd = 1;
13748             }
13749 #endif /* FTPHOST */
13750             n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
13751             if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
13752                 if (!quiet)
13753                   printf("SSL accepted as authentication type\n");
13754                 auth_type = "SSL";
13755                 ssl_auth();
13756                 if (ssl_ftp_active_flag) {
13757                     ftp_dpl = FPL_PRV;
13758                     ftp_cpl = FPL_PRV;
13759                     setprotbuf(1<<20);
13760                     return(1);
13761                 } else {
13762                     fprintf(stderr,"SSL authentication failed\n");
13763                     auth_type = NULL;
13764 #ifdef TCPIPLIB
13765                     socket_close(csocket);
13766 #else /* TCPIPLIB */
13767 #ifdef USE_SHUTDOWN
13768                     shutdown(csocket, 1+1);
13769 #endif /* USE_SHUTDOWN */
13770                     close(csocket);
13771 #endif /* TCPIPLIB */
13772                     csocket = -1;
13773                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
13774                       return(0);
13775                 }
13776             } else {
13777                 if (ftp_deb)
13778                   fprintf(stderr, "SSL rejected as an authentication type\n");
13779                 if (ftpcode == 500 || ftpcode == 502)
13780                   return(0);
13781             }
13782         }
13783 #endif /* CK_SSL */
13784         /* Other auth types go here ... */
13785     } /* for (j;;) */
13786     return(0);
13787 }
13788 #endif /* FTP_SECURITY */
13789
13790 static int
13791 #ifdef CK_ANSIC
13792 setprotbuf(unsigned int size)
13793 #else
13794 setprotbuf(size) unsigned int size;
13795 #endif /* CK_ANSIC */
13796 /* setprotbuf */ {
13797     if (ucbuf)
13798       free(ucbuf);
13799     ucbuf = NULL;
13800     ucbufsiz = 0;
13801     actualbuf = size;
13802     while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
13803         if (actualbuf)
13804           actualbuf /= 2;
13805         else
13806           return(0);
13807     }
13808     ucbufsiz = actualbuf - FUDGE_FACTOR;
13809     debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
13810     if (ucbufsiz < 128) {
13811         printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
13812     } else if (ucbufsiz < 0) {
13813         printf("ERROR: ucbuf allocation failure\n");
13814         return(-1);
13815     }
13816     maxbuf = actualbuf;
13817     return(1);
13818 }
13819
13820 static int
13821 #ifdef CK_ANSIC
13822 setpbsz(unsigned int size)
13823 #else
13824 setpbsz(size) unsigned int size;
13825 #endif /* CK_ANSIC */
13826 /* setpbsz */ {
13827     if (!setprotbuf(size)) {
13828         perror("?Error while trying to malloc PROT buffer:");
13829 #ifdef FTP_SRP
13830         srp_reset();
13831 #endif /* FTP_SRP */
13832         ftpclose();
13833         return(-1);
13834     }
13835     reply_parse = "PBSZ=";
13836     ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
13837 #ifdef CK_SSL
13838              ssl_ftp_active_flag ? "0" :
13839 #endif /* CK_SSL */
13840              ckuitoa(actualbuf),NULL,NULL);
13841     if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
13842         if (connected) {
13843             printf("?Unable to negotiate PROT buffer size with FTP server\n");
13844             ftpclose();
13845         }
13846         return(-1);
13847     }
13848     if (reply_parse) {
13849         if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
13850           maxbuf = actualbuf;
13851     } else
13852       maxbuf = actualbuf;
13853     ucbufsiz = maxbuf - FUDGE_FACTOR;
13854     debug(F101,"setpbsz ucbufsiz","",ucbufsiz);    
13855     reply_parse = NULL;
13856     return(0);
13857 }
13858
13859 static VOID
13860 cancel_remote(din) int din; {
13861     CHAR buf[FTP_BUFSIZ];
13862     int x, nfnd;
13863 #ifdef BSDSELECT
13864     fd_set mask;
13865 #endif /* BSDSELECT */
13866 #ifdef IBMSELECT
13867     int fds[2], fdcnt = 0;
13868 #endif /* IBMSELECT */
13869 #ifdef DEBUG
13870     extern int debtim;
13871     int xdebtim;
13872     xdebtim = debtim;
13873     debtim = 1;
13874 #endif /* DEBUG */
13875     debug(F100,"ftp cancel_remote entry","",0);
13876 #ifdef CK_SSL
13877     if (ssl_ftp_active_flag) {
13878         /*
13879          * Send Telnet IP, Telnet DM but do so inline and within the
13880          * TLS channel
13881          */
13882         int count, error;
13883
13884         buf[0] = IAC;
13885         buf[1] = TN_IP;
13886         buf[2] = IAC;
13887         buf[3] = TN_DM;
13888         buf[4] = NUL;
13889
13890         count = SSL_write(ssl_ftp_con, buf, 4);
13891         debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
13892         error = SSL_get_error(ssl_ftp_con,count);
13893         debug(F111,"ftp cancel_remote","SSL_get_error()",error);
13894         switch (error) {
13895           case SSL_ERROR_NONE:
13896             break;
13897           case SSL_ERROR_WANT_WRITE:
13898           case SSL_ERROR_WANT_READ:
13899           case SSL_ERROR_SYSCALL:
13900 #ifdef NT
13901             {
13902                 int gle = GetLastError();
13903             }
13904 #endif /* NT */
13905           case SSL_ERROR_WANT_X509_LOOKUP:
13906           case SSL_ERROR_SSL:
13907           case SSL_ERROR_ZERO_RETURN:
13908           default:
13909             lostpeer();
13910             return;
13911         }
13912     } else
13913 #endif /* CK_SSL */
13914     {
13915         /*
13916          * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
13917          * after urgent byte rather than before as is protocol now.
13918          */
13919         buf[0] = IAC;
13920         buf[1] = TN_IP;
13921         buf[2] = IAC;
13922         buf[3] = NUL;
13923         if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
13924           perror("cancel");
13925         debug(F101,"ftp cancel_remote send 1","",x);
13926         buf[0] = TN_DM;
13927         x = send(csocket,(SENDARG2TYPE)buf,1,0);
13928         debug(F101,"ftp cancel_remote send 2","",x);
13929     }
13930     x = scommand("ABOR");
13931     debug(F101,"ftp cancel_remote scommand","",x);
13932 #ifdef BSDSELECT
13933     FD_ZERO(&mask);
13934     FD_SET(csocket, &mask);
13935     if (din) {
13936         FD_SET(din, &mask);
13937     }
13938     nfnd = empty(&mask, 10);
13939     debug(F101,"ftp cancel_remote empty","",nfnd);
13940     if ((nfnd) <= 0) {
13941         if (nfnd < 0) {
13942             perror("cancel");
13943         }
13944 #ifdef FTP_PROXY
13945         if (ptabflg)
13946           ftpcode = -1;
13947 #endif /* FTP_PROXY */
13948         lostpeer();
13949     }
13950     debug(F110,"ftp cancel_remote","D",0);
13951     if (din && FD_ISSET(din, &mask)) {
13952         /* Security: No threat associated with this read. */
13953         /* But you can't simply read the TLS data stream  */
13954 #ifdef CK_SSL
13955         if (ssl_ftp_data_active_flag) {
13956             int count, error;
13957             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
13958                     /* LOOP */ ;
13959         } else
13960 #endif /* CK_SSL */
13961         {
13962             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
13963                 /* LOOP */ ;
13964         }
13965     }
13966     debug(F110,"ftp cancel_remote","E",0);
13967 #else /* BSDSELECT */
13968 #ifdef IBMSELECT
13969     fds[0] = csocket;
13970     fdcnt++;
13971     if (din) {
13972         fds[1] = din;
13973         fdcnt++;
13974     }
13975     nfnd = empty(fds, fdcnt, 10);
13976     debug(F101,"ftp cancel_remote empty","",nfnd);
13977     if ((nfnd) <= 0) {
13978         if (nfnd < 0) {
13979             perror("cancel");
13980         }
13981 #ifdef FTP_PROXY
13982         if (ptabflg)
13983           ftpcode = -1;
13984 #endif /* FTP_PROXY */
13985         lostpeer();
13986     }
13987     debug(F110,"ftp cancel_remote","D",0);
13988     if (din && select(&din, 1,0,0,1) ) {
13989 #ifdef CK_SSL
13990         if (ssl_ftp_data_active_flag) {
13991             int count, error;
13992             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
13993                     /* LOOP */ ;
13994         } else
13995 #endif /* CK_SSL */
13996         {
13997             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
13998                 /* LOOP */ ;
13999         }
14000     }
14001     debug(F110,"ftp cancel_remote","E",0);
14002 #else /* IBMSELECT */
14003     Some form of select is required.
14004 #endif /* IBMSELECT */
14005 #endif /* BSDSELECT */
14006     if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
14007         debug(F110,"ftp cancel_remote","F",0);
14008         /* 552 needed for NIC style cancel */
14009         getreply(0,-1,-1,ftp_vbm,0);
14010         debug(F110,"ftp cancel_remote","G",0);
14011     }
14012     debug(F110,"ftp cancel_remote","H",0);
14013     getreply(0,-1,-1,ftp_vbm,0);
14014     debug(F110,"ftp cancel_remote","I",0);
14015 #ifdef DEBUG
14016     debtim = xdebtim;
14017 #endif /* DEBUG */
14018 }
14019
14020 static int
14021 fts_dpl(x) int x; {
14022     if (!auth_type
14023 #ifdef OS2
14024          || !ck_crypt_is_installed()
14025 #endif /* OS2 */
14026          ) {
14027         switch ( x ) {
14028           case FPL_PRV:
14029             printf("?Cannot set protection level to PRIVATE\n");
14030             return(0);
14031           case FPL_SAF:
14032             printf("?Cannot set protection level to SAFE\n");
14033             return(0);
14034         }
14035         ftp_dpl = x;
14036         return(1);
14037     }
14038
14039 #ifdef CK_SSL
14040     if (x == FPL_SAF &&
14041         (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
14042         printf("Cannot set protection level to safe\n");
14043         return(0);
14044     }
14045 #endif /* CK_SSL */
14046     /* Start with a PBSZ of 1 meg */
14047     if (x != FPL_CLR) {
14048         if (setpbsz(DEFAULT_PBSZ) < 0)
14049           return(0);
14050     }
14051     y = ftpcmd(x == FPL_CLR ? "PROT C" :
14052                (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
14053     if (y == REPLY_COMPLETE) {
14054         ftp_dpl = x;
14055         return(1);
14056     }
14057     return(0);
14058 }
14059
14060 static int
14061 fts_cpl(x) int x; {
14062     if (!auth_type 
14063 #ifdef OS2
14064          || !ck_crypt_is_installed()
14065 #endif /* OS2 */
14066          ) {
14067         switch ( x ) {
14068           case FPL_PRV:
14069             printf("?Cannot set protection level to PRIVATE\n");
14070             return(0);
14071           case FPL_SAF:
14072             printf("?Cannot set protection level to SAFE\n");
14073             return(0);
14074         }
14075         ftp_cpl = x;
14076         return(1);
14077     }
14078     if (x == FPL_CLR) {
14079         y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
14080         if (y == REPLY_COMPLETE) {
14081             ftp_cpl = x;
14082             return(1);
14083         }
14084         return(0);
14085     }
14086     ftp_cpl = x;
14087     return(1);
14088 }
14089
14090 #ifdef FTP_GSSAPI
14091 static VOID
14092 user_gss_error(maj_stat, min_stat, s)
14093     OM_uint32 maj_stat, min_stat;
14094     char *s;
14095 {
14096     /* a lot of work just to report the error */
14097     OM_uint32 gmaj_stat, gmin_stat, msg_ctx;
14098     gss_buffer_desc msg;
14099     msg_ctx = 0;
14100     while (!msg_ctx) {
14101         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
14102                                        GSS_C_GSS_CODE,
14103                                        GSS_C_NULL_OID,
14104                                        &msg_ctx,
14105                                        &msg
14106                                        );
14107         if ((gmaj_stat == GSS_S_COMPLETE)||
14108             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14109             fprintf(stderr, "GSSAPI error major: %s\n",
14110                     (char*)msg.value);
14111             gss_release_buffer(&gmin_stat, &msg);
14112         }
14113         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14114           break;
14115     }
14116     msg_ctx = 0;
14117     while (!msg_ctx) {
14118         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
14119                                        GSS_C_MECH_CODE,
14120                                        GSS_C_NULL_OID,
14121                                        &msg_ctx,
14122                                        &msg
14123                                        );
14124         if ((gmaj_stat == GSS_S_COMPLETE)||
14125             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14126             fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value);
14127             gss_release_buffer(&gmin_stat, &msg);
14128         }
14129         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14130           break;
14131     }
14132     fprintf(stderr, "GSSAPI error: %s\n", s);
14133 }
14134 #endif /* FTP_GSSAPI */
14135
14136 #ifndef NOMHHOST
14137 #ifdef datageneral
14138 #define NOMHHOST
14139 #else
14140 #ifdef HPUX5WINTCP
14141 #define NOMHHOST
14142 #endif /* HPUX5WINTCP */
14143 #endif /* datageneral */
14144 #endif /* NOMHHOST */
14145
14146 #ifdef INADDRX
14147 static struct in_addr inaddrx;
14148 #endif /* INADDRX */
14149
14150 static char *
14151 ftp_hookup(host, port, tls) char * host; int port; int tls; {
14152     register struct hostent *hp = 0;
14153 #ifdef IP_TOS
14154 #ifdef IPTOS_THROUGHPUT
14155     int tos;
14156 #endif /* IPTOS_THROUGHPUT */
14157 #endif /* IP_TOS */
14158     int s;
14159     GSOCKNAME_T len;
14160     static char hostnamebuf[MAXHOSTNAMELEN];
14161     char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
14162     int  cport;
14163 #ifdef DEBUG
14164     extern int debtim;
14165     int xdebtim;
14166     xdebtim = debtim;
14167     debtim = 1;
14168 #endif /* DEBUG */
14169
14170 #ifndef NOHTTP
14171     if (tcp_http_proxy) {
14172         struct servent *destsp;
14173         char *p, *q;
14174
14175         ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
14176         for (p = tcp_http_proxy, q = hostname;
14177              *p != '\0' && *p != ':';
14178              p++, q++
14179              )
14180           *q = *p;
14181         *q = '\0';
14182
14183         if (*p == ':')
14184           p++;
14185         else
14186           p = "http";
14187
14188         destsp = getservbyname(p,"tcp");
14189         if (destsp)
14190           cport = ntohs(destsp->s_port);
14191         else if (p) {
14192           cport = atoi(p);
14193         } else
14194           cport = 80;
14195     } else
14196 #endif /* NOHTTP */
14197     {
14198         ckstrncpy(hostname,host,MAXHOSTNAMELEN);
14199         cport = port;
14200     }
14201     memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
14202     hisctladdr.sin_addr.s_addr = inet_addr(host);
14203     if (hisctladdr.sin_addr.s_addr != -1) {
14204         debug(F110,"ftp hookup A",hostname,0);
14205         hisctladdr.sin_family = AF_INET;
14206         ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
14207     } else {
14208         debug(F110,"ftp hookup B",hostname,0);
14209         hp = gethostbyname(hostname);
14210 #ifdef HADDRLIST
14211         hp = ck_copyhostent(hp);        /* make safe copy that won't change */
14212 #endif /* HADDRLIST */
14213         if (hp == NULL) {
14214             fprintf(stderr, "ftp: %s: Unknown host\n", host);
14215             ftpcode = -1;
14216 #ifdef DEBUG
14217             debtim = xdebtim;
14218 #endif /* DEBUG */
14219             return((char *) 0);
14220         }
14221         hisctladdr.sin_family = hp->h_addrtype;
14222 #ifdef HADDRLIST
14223         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
14224                sizeof(hisctladdr.sin_addr));
14225 #else /* HADDRLIST */
14226         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
14227                sizeof(hisctladdr.sin_addr));
14228 #endif /* HADDRLIST */
14229         ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
14230     }
14231     debug(F110,"ftp hookup C",hostnamebuf,0);
14232     ftp_host = hostnamebuf;
14233     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14234     debug(F101,"ftp hookup socket","",s);
14235     if (s < 0) {
14236         perror("ftp: socket");
14237         ftpcode = -1;
14238 #ifdef DEBUG
14239         debtim = xdebtim;
14240 #endif /* DEBUG */
14241         return(0);
14242     }
14243     hisctladdr.sin_port = htons(cport);
14244     errno = 0;
14245 #ifdef HADDRLIST
14246     debug(F100,"ftp hookup HADDRLIST","",0);
14247     while
14248 #else
14249     debug(F100,"ftp hookup no HADDRLIST","",0);
14250     if
14251 #endif /* HADDRLIST */
14252       (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
14253           debug(F101,"ftp hookup connect failed","",errno);
14254 #ifdef HADDRLIST
14255           if (hp && hp->h_addr_list[1]) {
14256               int oerrno = errno;
14257
14258               fprintf(stderr, "ftp: connect to address %s: ",
14259                       inet_ntoa(hisctladdr.sin_addr));
14260               errno = oerrno;
14261               perror((char *) 0);
14262               hp->h_addr_list++;
14263               memcpy((char *)&hisctladdr.sin_addr,
14264                      hp->h_addr_list[0],
14265                      sizeof(hisctladdr.sin_addr));
14266               fprintf(stdout, "Trying %s...\n",
14267                       inet_ntoa(hisctladdr.sin_addr));
14268 #ifdef TCPIPLIB
14269               socket_close(s);
14270 #else /* TCPIPLIB */
14271               close(s);
14272 #endif /* TCPIPLIB */
14273               s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14274               if (s < 0) {
14275                   perror("ftp: socket");
14276                   ftpcode = -1;
14277 #ifdef DEBUG
14278                   debtim = xdebtim;
14279 #endif /* DEBUG */
14280                   return(0);
14281               }
14282               continue;
14283           }
14284 #endif /* HADDRLIST */
14285           perror("ftp: connect");
14286           ftpcode = -1;
14287           goto bad;
14288       }
14289     debug(F100,"ftp hookup connect ok","",0);
14290
14291     len = sizeof (myctladdr);
14292     errno = 0;
14293     if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
14294         debug(F101,"ftp hookup getsockname failed","",errno);
14295         perror("ftp: getsockname");
14296         ftpcode = -1;
14297         goto bad;
14298     }
14299     debug(F100,"ftp hookup getsockname ok","",0);
14300
14301 #ifndef NOHTTP
14302     if (tcp_http_proxy) {
14303 #ifdef OS2
14304         char * agent = "Kermit 95";     /* Default user agent */
14305 #else
14306         char * agent = "C-Kermit";
14307 #endif /* OS2 */
14308
14309         if (http_connect(s,agent,NULL,
14310                          tcp_http_proxy_user,
14311                          tcp_http_proxy_pwd,
14312                          0,
14313                          proxyhost
14314                          ) < 0) {
14315             char * foo = NULL;
14316 #ifdef TCPIPLIB
14317             socket_close(s);
14318 #else /* TCPIPLIB */
14319             close(s);
14320 #endif /* TCPIPLIB */
14321
14322             while (foo == NULL && tcp_http_proxy != NULL ) {
14323
14324                 if (tcp_http_proxy_errno == 401 ||
14325                      tcp_http_proxy_errno == 407 ) {
14326                     char uid[UIDBUFLEN];
14327                     char pwd[PWDSIZ];
14328                     struct txtbox tb[2];
14329                     int ok;
14330
14331                     tb[0].t_buf = uid;
14332                     tb[0].t_len = UIDBUFLEN;
14333                     tb[0].t_lbl = "Proxy Userid: ";
14334                     tb[0].t_dflt = NULL;
14335                     tb[0].t_echo = 1;
14336                     tb[1].t_buf = pwd;
14337                     tb[1].t_len = 256;
14338                     tb[1].t_lbl = "Proxy Passphrase: ";
14339                     tb[1].t_dflt = NULL;
14340                     tb[1].t_echo = 2;
14341
14342                     ok = uq_mtxt("Proxy Server Authentication Required\n",
14343                                   NULL, 2, tb);
14344
14345                     if (ok && uid[0]) {
14346                         char * proxy_user, * proxy_pwd;
14347
14348                         proxy_user = tcp_http_proxy_user;
14349                         proxy_pwd  = tcp_http_proxy_pwd;
14350
14351                         tcp_http_proxy_user = uid;
14352                         tcp_http_proxy_pwd = pwd;
14353
14354                         foo = ftp_hookup(host, port, 0);
14355
14356                         debug(F110,"ftp_hookup()",foo,0);
14357                         memset(pwd,0,PWDSIZ);
14358                         tcp_http_proxy_user = proxy_user;
14359                         tcp_http_proxy_pwd = proxy_pwd;
14360                     } else
14361                         break;
14362                 } else
14363                     break;
14364             }
14365             if (foo != NULL)
14366               return(foo);
14367             perror("ftp: connect");
14368             ftpcode = -1;
14369             goto bad;
14370         }
14371         ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
14372     }
14373 #endif /* NOHTTP */
14374
14375     csocket = s;
14376
14377 #ifdef CK_SSL
14378     if (tls) {
14379         /* FTP over SSL
14380          * If the connection is over an SSL proxy then the
14381          * auth_type will be NULL.  However, I'm not sure
14382          * whether we should protect the data channel in
14383          * that case or not.
14384          */
14385
14386         debug(F100,"ftp hookup use_tls","",0);
14387         if (!ssl_auth()) {
14388             debug(F100,"ftp hookup ssl_auth failed","",0);
14389             auth_type = NULL;
14390             ftpcode = -1;
14391             csocket = -1;
14392             goto bad;
14393         }
14394         ssl_ftp_proxy = 1;
14395     }
14396 #endif /* CK_SSL */
14397
14398 #ifdef IP_TOS
14399 #ifdef IPTOS_LOWDELAY
14400     tos = IPTOS_LOWDELAY;
14401     if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
14402       perror("ftp: setsockopt TOS (ignored)");
14403 #endif
14404 #endif
14405     if (!quiet)
14406       printf("Connected to %s.\n", host);
14407
14408     /* Read greeting from server */
14409     if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
14410         debug(F100,"ftp hookup bad reply","",0);
14411 #ifdef TCPIPLIB
14412         socket_close(csocket);
14413 #else /* TCPIPLIB */
14414         close(csocket);
14415 #endif /* TCPIPLIB */
14416         ftpcode = -1;
14417         goto bad;
14418     }
14419 #ifdef SO_OOBINLINE
14420     {
14421         int on = 1;
14422         errno = 0;
14423         if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
14424                        sizeof(on)) < 0) {
14425             perror("ftp: setsockopt");
14426             debug(F101,"ftp hookup setsockopt failed","",errno);
14427         }
14428 #ifdef DEBUG
14429         else
14430           debug(F100,"ftp hookup setsockopt ok","",0);
14431 #endif /* DEBUG */
14432     }
14433 #endif /* SO_OOBINLINE */
14434
14435 #ifdef DEBUG
14436     debtim = xdebtim;
14437 #endif /* DEBUG */
14438     return(ftp_host);
14439
14440   bad:
14441     debug(F100,"ftp hookup bad","",0);
14442 #ifdef TCPIPLIB
14443     socket_close(s);
14444 #else /* TCPIPLIB */
14445     close(s);
14446 #endif /* TCPIPLIB */
14447 #ifdef DEBUG
14448     debtim = xdebtim;
14449 #endif /* DEBUG */
14450     csocket = -1;
14451     return((char *)0);
14452 }
14453
14454 static VOID
14455 ftp_init() {
14456     int i, n;
14457
14458     /* The purpose of the initial REST 0 is not clear, but other FTP */
14459     /* clients do it.  In any case, failure of this command is not a */
14460     /* reliable indication that the server does not support Restart. */
14461
14462     okrestart = 0;
14463     if (!noinit) {
14464         n = ftpcmd("REST 0",NULL,0,0,0);
14465         if (n == REPLY_COMPLETE)
14466           okrestart = 1;
14467 #ifdef COMMENT
14468         else if (ftp_deb)
14469           printf("WARNING: Unable to restore file pointer.\n");
14470 #endif /* COMMENT */
14471     }
14472     n = ftpcmd("SYST",NULL,0,0,0);      /* Get server system type */
14473     if (n == REPLY_COMPLETE) {
14474         register char *cp, c = NUL;
14475         cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
14476         if (cp == NULL)
14477           cp = ckstrchr(ftp_reply_str+4,'\r');
14478         if (cp) {
14479             if (cp[-1] == '.')
14480               cp--;
14481             c = *cp;                    /* Save this char */
14482             *cp = '\0';                 /* Replace it with NUL */
14483         }
14484         if (!quiet)
14485           printf("Remote system type is %s.\n",ftp_reply_str+4);
14486         ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
14487         if (cp)                         /* Put back saved char */
14488           *cp = c;
14489     }
14490     alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
14491
14492     if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
14493     else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
14494     else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
14495     else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
14496     else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
14497     else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
14498     else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
14499
14500 #ifdef FTP_PROXY
14501     unix_proxy = 0;
14502     if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
14503 #endif /* FTP_PROXY */
14504
14505     if (ftp_cmdlin && xfermode == XMODE_M)
14506       ftp_typ = binary;                 /* Type given on command line */
14507     else                                /* Otherwise set it automatically */
14508       ftp_typ = alike ? FTT_BIN : FTT_ASC;
14509     changetype(ftp_typ,0);              /* Change to this type */
14510     g_ftp_typ = ftp_typ;                /* Make it the global type */
14511     if (!quiet)
14512       printf("Default transfer mode is %s\n",
14513              ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
14514              );
14515     for (i = 0; i < 16; i++)            /* Init server FEATure table */
14516       sfttab[i] = 0;
14517     if (!noinit) {
14518         n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
14519 #ifdef COMMENT
14520         if (n != REPLY_COMPLETE)
14521           printf("WARNING: Server does not accept MODE S(TREAM)\n");
14522 #endif /* COMMENT */
14523         n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
14524 #ifdef COMMENT
14525         if (n != REPLY_COMPLETE)
14526           printf("WARNING: Server does not accept STRU F(ILE)\n");
14527 #endif /* COMMENT */
14528         if (featok) {
14529             n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
14530             if (n == REPLY_COMPLETE) {
14531                 debug(F101,"ftp_init FEAT","",sfttab[0]);
14532                 if (deblog || ftp_deb) {
14533                     int i;
14534                     for (i = 1; i < 16 && i < nfeattab; i++) {
14535                         debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
14536                         if (ftp_deb)
14537                           printf("  Server %s %s\n",
14538                                  sfttab[i] ? "supports" : "does not support",
14539                                  feattab[i].kwd
14540                                  );
14541                     }
14542                     /* Deal with disabled MLST opts here if necessary */
14543                     /* But why would it be? */
14544                 }
14545             }
14546         }
14547     }
14548 }
14549
14550 static int
14551 ftp_login(host) char * host; {          /* (also called from ckuusy.c) */
14552     static char ftppass[PASSBUFSIZ]="";
14553     char tmp[PASSBUFSIZ];
14554     char *user = NULL, *pass = NULL, *acct = NULL;
14555     int n, aflag = 0;
14556     extern char uidbuf[];
14557     extern char pwbuf[];
14558     extern int  pwflg, pwcrypt;
14559
14560     debug(F111,"ftp_login",ftp_logname,ftp_log);
14561
14562     if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
14563       anonymous = 1;
14564     if (!ckstrcmp(ftp_logname,"ftp",-1,0))
14565       anonymous = 1;
14566
14567 #ifdef FTP_SRP
14568     if (auth_type && !strcmp(auth_type, "SRP")) {
14569         user = srp_user;
14570         pass = srp_pass;
14571         acct = srp_acct;
14572     } else
14573 #endif /* FTP_SRP */
14574       if (anonymous) {
14575           user = "anonymous";
14576           if (ftp_tmp) {                /* They gave a password */
14577               pass = ftp_tmp;
14578           } else if (ftp_apw) {         /* SET FTP ANONYMOUS-PASSWORD */
14579               pass = ftp_apw;
14580           } else {                      /* Supply user@host */
14581               ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
14582               pass = tmp;
14583           }
14584           debug(F110,"ftp anonymous",pass,0);
14585       } else {
14586 #ifdef USE_RUSERPASS
14587           if (ruserpass(host, &user, &pass, &acct) < 0) {
14588               ftpcode = -1;
14589               return(0);
14590           }
14591 #endif /* USE_RUSERPASS */
14592           if (ftp_logname) {
14593               user = ftp_logname;
14594               pass = ftp_tmp;
14595           } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
14596               user = uidbuf;
14597               if (ftp_tmp) {
14598                   pass = ftp_tmp;
14599               } else if (pwbuf[0] && pwflg) {
14600                   ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
14601 #ifdef OS2
14602                   if ( pwcrypt )
14603                       ck_encrypt((char *)ftppass);
14604 #endif /* OS2 */
14605                   pass = ftppass;
14606               }
14607           }
14608           acct = ftp_acc;
14609           while (user == NULL) {
14610               char *myname, prompt[PROMPTSIZ];
14611               int ok;
14612
14613               myname = whoami();
14614               if (myname)
14615                 ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
14616                           NULL,NULL,NULL,NULL,NULL,NULL,NULL);
14617               else
14618                 ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
14619               tmp[0] = '\0';
14620               
14621               ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
14622                           DEFAULT_UQ_TIMEOUT);
14623               if (!ok || *tmp == '\0')
14624                 user = myname;
14625               else
14626                 user = brstrip(tmp);
14627           }
14628       }
14629     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
14630     if (n == REPLY_COMPLETE) {
14631         /* determine if we need to send a dummy password */
14632         if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
14633           ftpcmd("PASS dummy",NULL,0,0,1);
14634     } else if (n == REPLY_CONTINUE) {
14635 #ifdef CK_ENCRYPTION
14636         int oldftp_cpl;
14637 #endif /* CK_ENCRYPTION */
14638
14639         if (pass == NULL) {
14640             int ok;
14641             setint();
14642             ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
14643                         DEFAULT_UQ_TIMEOUT);
14644             if (ok)
14645                 pass = brstrip(ftppass);
14646         }
14647
14648 #ifdef CK_ENCRYPTION
14649         oldftp_cpl = ftp_cpl;
14650         ftp_cpl = FPL_PRV;
14651 #endif /* CK_ENCRYPTION */
14652         n = ftpcmd("PASS",pass,-1,-1,1);
14653         if (!anonymous && pass) {
14654             char * p = pass;
14655             while (*p++) *(p-1) = NUL;
14656             makestr(&ftp_tmp,NULL);
14657         }
14658 #ifdef CK_ENCRYPTION
14659         /* level may have changed */
14660         if (ftp_cpl == FPL_PRV)
14661           ftp_cpl = oldftp_cpl;
14662 #endif /* CK_ENCRYPTION */
14663     }
14664     if (n == REPLY_CONTINUE) {
14665         aflag++;
14666         if (acct == NULL) {
14667             static char ftpacct[80];
14668             int ok;
14669             setint();
14670             ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
14671                         DEFAULT_UQ_TIMEOUT);
14672             if (ok)
14673                 acct = brstrip(ftpacct);
14674         }
14675         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
14676     }
14677     if (n != REPLY_COMPLETE) {
14678         fprintf(stderr, "FTP login failed.\n");
14679         if (haveurl)
14680           doexit(BAD_EXIT,-1);
14681         return(0);
14682     }
14683     if (!aflag && acct != NULL) {
14684         ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
14685     }
14686     makestr(&ftp_logname,user);
14687     loggedin = 1;
14688 #ifdef LOCUS
14689     /* Unprefixed file management commands go to server */
14690     if (autolocus && !ftp_cmdlin) {
14691         setlocus(0,1);
14692     }
14693 #endif /* LOCUS */
14694     ftp_init();
14695
14696     if (anonymous && !quiet) {
14697         printf(" Logged in as anonymous (%s)\n",pass);
14698         memset(pass, 0, strlen(pass));
14699     }
14700     if (ftp_rdir) {
14701         if (doftpcwd(ftp_rdir,-1) < 1)
14702           doexit(BAD_EXIT,-1);
14703     }
14704
14705 #ifdef FTP_PROXY
14706     if (proxy)
14707       return(1);
14708 #endif /* FTP_PROXY */
14709     return(1);
14710 }
14711
14712 static int
14713 ftp_reset() {
14714     int rc;
14715 #ifdef BSDSELECT
14716     int nfnd = 1;
14717     fd_set mask;
14718     FD_ZERO(&mask);
14719     while (nfnd > 0) {
14720         FD_SET(csocket, &mask);
14721         if ((nfnd = empty(&mask,0)) < 0) {
14722             perror("reset");
14723             ftpcode = -1;
14724             lostpeer();
14725             return(0);
14726         } else if (nfnd) {
14727             getreply(0,-1,-1,ftp_vbm,0);
14728         }
14729     }
14730 #else /* BSDSELECT */
14731 #ifdef IBMSELECT
14732     int nfnd = 1;
14733     while (nfnd > 0) {
14734         if ((nfnd = empty(&csocket,1,0)) < 0) {
14735             perror("reset");
14736             ftpcode = -1;
14737             lostpeer();
14738             return(0);
14739         } else if (nfnd) {
14740             getreply(0,-1,-1,ftp_vbm,0);
14741         }
14742     }
14743 #endif /* IBMSELECT */
14744 #endif /* BSDSELECT */
14745     rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
14746     if (rc > 0)
14747       loggedin = 0;
14748     return(rc);
14749 }
14750
14751 static int
14752 ftp_rename(from, to) char * from, * to; {
14753     int lcs = -1, rcs = -1;
14754 #ifndef NOCSETS
14755     if (ftp_xla) {
14756         lcs = ftp_csl;
14757         if (lcs < 0) lcs = fcharset;
14758         rcs = ftp_csx;
14759         if (rcs < 0) rcs = ftp_csr;
14760     }
14761 #endif /* NOCSETS */
14762     if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
14763         return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
14764     }
14765     return(0);                          /* Failure */
14766 }
14767
14768 static int
14769 ftp_umask(mask) char * mask; {
14770     int rc;
14771     rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
14772     return(rc);
14773 }
14774
14775 static int
14776 ftp_user(user,pass,acct) char * user, * pass, * acct; {
14777     int n = 0, aflag = 0;
14778     char pwd[PWDSIZ];
14779
14780     if (!auth_type && ftp_aut) {
14781 #ifdef FTP_SRP
14782         if (ck_srp_is_installed()) {
14783             if (srp_ftp_auth( NULL, user, pass)) {
14784                 makestr(&pass,srp_pass);
14785             }
14786         }
14787 #endif /* FTP_SRP */
14788     }
14789     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
14790     if (n == REPLY_COMPLETE)
14791       n = ftpcmd("PASS dummy",NULL,0,0,1);
14792     else if (n == REPLY_CONTINUE) {
14793 #ifdef CK_ENCRYPTION
14794         int oldftp_cpl;
14795 #endif /* CK_ENCRYPTION */
14796         if (pass == NULL || !pass[0]) {
14797             int ok;
14798             pwd[0] = '\0';
14799             setint();
14800             ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
14801                         DEFAULT_UQ_TIMEOUT);
14802             if (ok)
14803                 pass = brstrip(pwd);
14804         }
14805
14806 #ifdef CK_ENCRYPTION
14807         if ((oldftp_cpl = ftp_cpl) == PROT_S)
14808           ftp_cpl = PROT_P;
14809 #endif /* CK_ENCRYPTION */
14810         n = ftpcmd("PASS",pass,-1,-1,1);
14811         memset(pass, 0, strlen(pass));
14812 #ifdef CK_ENCRYPTION
14813         /* level may have changed */
14814         if (ftp_cpl == PROT_P)
14815           ftp_cpl = oldftp_cpl;
14816 #endif /* CK_ENCRYPTION */
14817     }
14818     if (n == REPLY_CONTINUE) {
14819         if (acct == NULL || !acct[0]) {
14820             int ok;
14821             pwd[0] = '\0';
14822             setint();
14823             ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
14824                         DEFAULT_UQ_TIMEOUT);
14825             if (ok)
14826                 acct = pwd;
14827         }
14828         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
14829         aflag++;
14830     }
14831     if (n != REPLY_COMPLETE) {
14832         printf("Login failed.\n");
14833         return(0);
14834     }
14835     if (!aflag && acct != NULL && acct[0]) {
14836         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
14837     }
14838     if (n == REPLY_COMPLETE) {
14839         makestr(&ftp_logname,user);
14840         loggedin = 1;
14841         ftp_init();
14842         return(1);
14843     }
14844     return(0);
14845 }
14846
14847 char *
14848 ftp_authtype() {
14849     if (!connected)
14850       return("NULL");
14851     return(auth_type ? auth_type : "NULL");
14852 }
14853
14854 char *
14855 ftp_cpl_mode() {
14856     switch (ftp_cpl) {
14857       case FPL_CLR:
14858         return("clear");
14859       case FPL_SAF:
14860         return("safe");
14861       case FPL_PRV:
14862         return("private");
14863       case FPL_CON:
14864         return("confidential");
14865       default:
14866         return("(error)");
14867     }
14868 }
14869
14870 char *
14871 ftp_dpl_mode() {
14872     switch (ftp_dpl) {
14873       case FPL_CLR:
14874         return("clear");
14875       case FPL_SAF:
14876         return("safe");
14877       case FPL_PRV:
14878         return("private");
14879       case FPL_CON:
14880         return("confidential");
14881       default:
14882         return("(error)");
14883     }
14884 }
14885
14886
14887 /* remote_files() */
14888 /*
14889    Returns next remote filename on success;
14890    NULL on error or no more files with global rfrc set to:
14891      -1: Bad argument
14892      -2: Server error response to NLST, e.g. file not found
14893      -3: No more files
14894      -9: Internal error
14895 */
14896 #define FTPNAMBUFLEN CKMAXPATH+1024
14897
14898 /* Check: ckmaxfiles CKMAXOPEN */
14899
14900 #define MLSDEPTH 128                    /* Stack of open temp files */
14901 static int mlsdepth = 0;                /* Temp file stack depth */
14902 static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
14903 static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
14904
14905 static VOID
14906 mlsreset() {                            /* Reset MGET temp-file stack */
14907     int i;
14908     for (i = 0; i <= mlsdepth; i++) {
14909         if (tmpfilptr[i]) {
14910             fclose(tmpfilptr[i]);
14911             tmpfilptr[i] = NULL;
14912             if (tmpfilnam[i]) {
14913 #ifdef OS2
14914                 unlink(tmpfilnam[i]);
14915 #endif /* OS2 */
14916                 free(tmpfilnam[i]);
14917             }
14918         }
14919     }
14920     mlsdepth = 0;
14921 }
14922
14923 static CHAR *
14924 #ifdef CK_ANSIC
14925 remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
14926 #else /* CK_ANSIC */
14927 remote_files(new_query, arg, pattern, proxy_switch)
14928     int new_query;
14929     CHAR * arg;                         /* That we send to the server */
14930     CHAR * pattern;                     /* That we use locally */
14931     int proxy_switch;
14932 #endif /* CK_ANSIC */
14933 /* remote_files */ {
14934     static CHAR buf[FTPNAMBUFLEN];
14935     CHAR *cp, *whicharg;
14936     char * cdto = NULL;
14937     char * p;
14938     int i, x, forced = 0;
14939     int lcs = 0, rcs = 0, xlate = 0;
14940
14941     debug(F101,"ftp remote_files new_query","",new_query);
14942     debug(F110,"ftp remote_files arg",arg,0);
14943     debug(F110,"ftp remote_files pattern",pattern,0);
14944
14945     rfrc = -1;
14946     if (pattern)                        /* Treat empty pattern same as NULL */
14947       if (!*pattern)
14948         pattern = NULL;
14949     if (arg)                            /* Ditto for arg */
14950       if (!*arg)
14951         arg = NULL;
14952
14953   again:
14954
14955     if (new_query) {
14956         if (tmpfilptr[mlsdepth]) {
14957             fclose(tmpfilptr[mlsdepth]);
14958             tmpfilptr[mlsdepth] = NULL;
14959 #ifdef OS2
14960             if (!ftp_deb && !deblog)
14961               unlink(tmpfilnam[mlsdepth]);
14962 #endif /* OS2 */
14963         }
14964     }
14965     if (tmpfilptr[mlsdepth] == NULL) {
14966         extern char * tempdir;
14967         char * p;
14968         debug(F110,"ftp remote_files tempdir",tempdir,0);
14969         if (tempdir) {
14970             p = tempdir;
14971         } else {
14972 #ifdef OS2
14973 #ifdef NT
14974             p = getenv("K95TMP");
14975 #else
14976             p = getenv("K2TMP");
14977 #endif /* NT */
14978             if (!p)
14979 #endif /* OS2 */
14980               p = getenv("CK_TMP");
14981             if (!p)
14982               p = getenv("TMPDIR");
14983             if (!p) p = getenv("TEMP");
14984             if (!p) p = getenv("TMP");
14985 #ifdef OS2ORUNIX
14986             if (p) {
14987                 int len = strlen(p);
14988                 if (p[len-1] != '/'
14989 #ifdef OS2
14990                     && p[len-1] != '\\'
14991 #endif /* OS2 */
14992                      ) {
14993                     static char foo[CKMAXPATH];
14994                     ckstrncpy(foo,p,CKMAXPATH);
14995                     ckstrncat(foo,"/",CKMAXPATH);
14996                     p = foo;
14997                 }
14998             } else
14999 #else /* OS2ORUNIX */
15000             if (!p)
15001 #endif /* OS2ORUNIX */
15002 #ifdef UNIX                             /* Systems that have a standard */
15003                 p = "/tmp/";            /* temporary directory... */
15004 #else
15005 #ifdef datageneral
15006             p = ":TMP:";
15007 #else
15008             p = "";
15009 #endif /* datageneral */
15010 #endif /* UNIX */
15011         }
15012         debug(F110,"ftp remote_files p",p,0);
15013
15014         /* Get temp file */
15015
15016         if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
15017             ckmakmsg((char *)tmpfilnam[mlsdepth],
15018                      CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
15019         } else {
15020             printf("?Malloc failure: remote_files()\n");
15021             return(NULL);
15022         }
15023
15024 #ifdef NT
15025         {
15026             char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
15027             if ( tmpfil )
15028                 ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
15029         }
15030 #else /* NT */
15031 #ifdef MKTEMP
15032 #ifdef MKSTEMP
15033         x = mkstemp((char *)tmpfilnam[mlsdepth]);
15034         if (x > -1) close(x);           /* We just want the name. */
15035 #else
15036         mktemp((char *)tmpfilnam[mlsdepth]);
15037 #endif /* MKSTEMP */
15038         /* if no mktmpnam() the name will just be "ckXXXXXX"... */
15039 #endif /* MKTEMP */
15040 #endif /* NT */
15041
15042         debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
15043               tmpfilnam[mlsdepth],mlsdepth);
15044
15045 #ifdef FTP_PROXY
15046         if (proxy_switch) {
15047             pswitch(!proxy);
15048         }
15049 #endif /* FTP_PROXY */
15050
15051         debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
15052         debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
15053         debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
15054
15055 #ifndef NOCSETS
15056         xlate = ftp_xla;                /* SET FTP CHARACTER-SET-TRANSLATION */
15057         if (xlate) {                    /* ON? */
15058             lcs = ftp_csl;              /* Local charset */
15059             if (lcs < 0) lcs = fcharset;
15060             if (lcs < 0) xlate = 0;
15061         }
15062         if (xlate) {                    /* Still ON? */
15063             rcs = ftp_csx;              /* Remote (Server) charset */
15064             if (rcs < 0) rcs = ftp_csr;
15065             if (rcs < 0) xlate = 0;
15066         }
15067 #endif /* NOCSETS */
15068
15069         forced = mgetforced;            /* MGET method forced? */
15070         if (!forced || !mgetmethod)     /* Not forced... */
15071           mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
15072               SND_MLS :
15073               SND_NLS; 
15074 /*                                           
15075   User's Command:                 Result:
15076     mget /nlst                     NLST (NULL)
15077     mget /nlst foo                 NLST foo
15078     mget /nlst *.txt               NLST *.txt 
15079     mget /nlst /match:*.txt        NLST (NULL)
15080     mget /nlst /match:*.txt  foo   NLST foo   
15081     mget /mlsd                     MLSD (NULL)
15082     mget /mlsd foo                 MLSD foo
15083     mget /mlsd *.txt               MLSD (NULL)
15084     mget /mlsd /match:*.txt        MLSD (NULL)
15085     mget /mlsd /match:*.txt  foo   MLSD foo
15086 */
15087         x = -1;
15088         while (x < 0) {
15089             if (pattern) {              /* Don't simplify this! */
15090                 whicharg = arg;
15091             } else if (mgetmethod == SND_MLS) {
15092                 if (arg)
15093                   whicharg = iswild((char *)arg) ? NULL : arg;
15094                 else
15095                   whicharg = NULL;
15096             } else {
15097                 whicharg = arg;
15098             }
15099             debug(F110,"ftp remote_files mgetmethod",
15100                   mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
15101             debug(F110,"ftp remote_files whicharg",whicharg,0);
15102
15103             x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
15104                             (char *)tmpfilnam[mlsdepth],
15105                             (char *)whicharg,
15106                             "wb",
15107                             0,
15108                             0,
15109                             NULL,
15110                             xlate,
15111                             lcs,
15112                             rcs
15113                             );
15114             if (x < 0) {                /* Chosen method wasn't accepted */
15115                 if (forced) {
15116                     if (ftpcode > 500 && ftpcode < 505 && !quiet)
15117                       printf("?%s: Not supported by server\n",
15118                              mgetmethod == SND_MLS ? "MLSD" : "NLST"
15119                              );
15120                     rfrc = -2;          /* Fail */
15121                     return(NULL);
15122                 }
15123                 /* Not forced - if MLSD failed, try NLST */
15124                 if (mgetmethod == SND_MLS) {  /* Server lied about MLST */
15125                     sfttab[SFT_MLST] = 0;     /* So disable it */
15126                     mlstok = 0;               /* and */
15127                     mgetmethod = SND_NLS;     /* try NLST */
15128                     continue;
15129                 }
15130                 rfrc = -2;
15131                 return(NULL);
15132             }
15133         }
15134 #ifdef FTP_PROXY
15135         if (proxy_switch) {
15136             pswitch(!proxy);
15137         }
15138 #endif /* FTP_PROXY */
15139         tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
15140 #ifndef OS2
15141         if (tmpfilptr[mlsdepth]) {
15142             if (!ftp_deb && !deblog)
15143               unlink(tmpfilnam[mlsdepth]);
15144         }
15145 #endif /* OS2 */
15146       notemp:
15147         if (!tmpfilptr[mlsdepth]) {
15148             debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
15149             if ((!dpyactive || ftp_deb))
15150               printf("?Can't find list of remote files, oops\n");
15151             rfrc = -9;
15152             return(NULL);
15153         }
15154         if (ftp_deb)
15155           printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
15156     }
15157     buf[0] = NUL;
15158     buf[FTPNAMBUFLEN-1] = NUL;
15159     buf[FTPNAMBUFLEN-2] = NUL;
15160
15161     /* We have to redo all this because the first time was only for */
15162     /* for getting the file list, now it's for getting each file */
15163
15164     if (arg && mgetmethod == SND_MLS) { /* MLSD */
15165         if (!pattern && iswild((char *)arg)) {
15166             pattern = arg;              /* Wild arg is really a pattern */
15167             if (pattern)
15168               if (!*pattern)
15169                 pattern = NULL;
15170             arg = NULL;                 /* and not an arg */
15171         }
15172         if (new_query) {                /* Initial query? */
15173             cdto = (char *)arg;         /* (nonwild) arg given? */
15174             if (cdto)
15175               if (!*cdto)
15176                 cdto = NULL;
15177             if (cdto)                   /* If so, then CD to it */
15178               doftpcwd(cdto,0);
15179         }
15180     }
15181     new_query = 0;
15182
15183     if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
15184         fclose(tmpfilptr[mlsdepth]);
15185         tmpfilptr[mlsdepth] = NULL;
15186
15187 #ifdef OS2
15188         if (!ftp_deb && !deblog)
15189           unlink(tmpfilnam[mlsdepth]);
15190 #endif /* OS2 */
15191         if (ftp_deb && !deblog) {
15192             printf("(Temporary file %s NOT deleted)\n",
15193                    (char *)tmpfilnam[mlsdepth]);
15194         }
15195         if (mlsdepth <= 0) {            /* EOF at depth 0 */
15196             rfrc = -3;                  /* means we're done */
15197             return(NULL);
15198         }
15199         printf("POPPING(%d)...\n",mlsdepth-1); 
15200         if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
15201         mlsdepth--;
15202         doftpcdup();
15203         zchdir("..");                   /* <-- Not portable */
15204         goto again;
15205     }
15206     if (buf[FTPNAMBUFLEN-1]) {
15207         printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
15208                FTPNAMBUFLEN
15209                );
15210         debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
15211         return(NULL);
15212     }
15213     /* debug(F110,"ftp remote_files buf 1",buf,0); */
15214     if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
15215       *cp = '\0';
15216     if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
15217       *cp = '\0';
15218     debug(F110,"ftp remote_files buf",buf,0);
15219     rfrc = 0;
15220
15221     if (ftp_deb)
15222       printf("[%s]\n",(char *)buf);
15223
15224     havesize = -1L;                     /* Initialize file facts... */
15225     havetype = -0;
15226     makestr(&havemdtm,NULL);
15227     p = (char *)buf;
15228
15229     if (mgetmethod == SND_NLS) {        /* NLST... */
15230         if (pattern) {
15231             if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15232               goto again;
15233         }
15234     } else {                            /* MLSD... */
15235         p = parsefacts((char *)buf);
15236         switch (havetype) {
15237           case FTYP_FILE:               /* File: Get it if it matches */
15238             if (pattern) {
15239                 if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15240                   goto again;
15241             }
15242             break;
15243           case FTYP_CDIR:               /* Current directory */
15244           case FTYP_PDIR:               /* Parent directory */
15245             goto again;                 /* Skip */
15246           case FTYP_DIR:                /* (Sub)Directory */
15247             if (!recursive)             /* If not /RECURSIVE */
15248               goto again;               /* Skip */
15249             if (mlsdepth < MLSDEPTH) {
15250                 char * p2 = NULL;
15251                 mlsdepth++;
15252                 printf("RECURSING [%s](%d)...\n",p,mlsdepth); 
15253                 if (doftpcwd(p,0) > 0) {
15254                     int x;
15255                     if (!ckstrchr(p,'/')) {
15256                         /* zmkdir() needs dirsep */
15257                         if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
15258                             strcpy(p2,p);       /* SAFE */
15259                             strcat(p2,"/");     /* SAFE */
15260                             p = p2;
15261                         }
15262                     }
15263 #ifdef NOMKDIR
15264                     x = -1;
15265 #else
15266                     x = zmkdir(p);
15267 #endif /* NOMKDIR */
15268                     if (x > -1) {
15269                         zchdir(p);
15270                         p = (char *)remote_files(1,arg,pattern,0);
15271                         if (p2) free(p2);
15272                     } else {
15273                         printf("?mkdir failed: [%s] Depth=%d\n",
15274                                p,
15275                                mlsdepth
15276                                );
15277                         mlsreset();
15278                         if (p2) free(p2);
15279                         return(NULL);
15280                     }
15281                 } else {
15282                     printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
15283                     mlsreset();
15284                     return(NULL);
15285                 }
15286             } else {
15287                 printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
15288                        mlsdepth
15289                        );
15290                 mlsreset();
15291                 return(NULL);
15292             }
15293         }
15294     }
15295
15296 #ifdef DEBUG
15297     if (deblog) {
15298         debug(F101,"remote_files havesize","",havesize);
15299         debug(F101,"remote_files havetype","",havetype);
15300         debug(F110,"remote_files havemdtm",havemdtm,0); 
15301         debug(F110,"remote_files name",p,0);    
15302     }
15303 #endif /* DEBUG */
15304     return((CHAR *)p);
15305 }
15306
15307 /* N O T  P O R T A B L E !!! */
15308
15309 #if (SIZEOF_SHORT == 4)
15310 typedef unsigned short ftp_uint32;
15311 typedef short ftp_int32;
15312 #else
15313 #if (SIZEOF_INT == 4)
15314 typedef unsigned int ftp_uint32;
15315 typedef int ftp_int32;
15316 #else
15317 #if (SIZEOF_LONG == 4)
15318 typedef ULONG ftp_uint32;
15319 typedef long ftp_int32;
15320 #endif
15321 #endif
15322 #endif
15323
15324 /* Perhaps use these in general, certainly use them for GSSAPI */
15325
15326 #ifndef looping_write
15327 #define ftp_int32 int
15328 #define ftp_uint32 unsigned int
15329 static int
15330 looping_write(fd, buf, len)
15331     int fd;
15332     register CONST char *buf;
15333     int len;
15334 {
15335     int cc;
15336     register int wrlen = len;
15337     do {
15338         cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
15339         if (cc < 0) {
15340             if (errno == EINTR)
15341               continue;
15342             return(cc);
15343         } else {
15344             buf += cc;
15345             wrlen -= cc;
15346         }
15347     } while (wrlen > 0);
15348     return(len);
15349 }
15350 #endif
15351 #ifndef looping_read
15352 static int
15353 looping_read(fd, buf, len)
15354     int fd;
15355     register char *buf;
15356     register int len;
15357 {
15358     int cc, len2 = 0;
15359
15360     do {
15361         cc = recv(fd, (char *)buf, len,0);
15362         if (cc < 0) {
15363             if (errno == EINTR)
15364               continue;
15365             return(cc);                 /* errno is already set */
15366         } else if (cc == 0) {
15367             return(len2);
15368         } else {
15369             buf += cc;
15370             len2 += cc;
15371             len -= cc;
15372         }
15373     } while (len > 0);
15374     return(len2);
15375 }
15376 #endif /* looping_read */
15377
15378 #define ERR -2
15379
15380 #ifdef COMMENT
15381 static
15382 secure_putbyte(fd, c) int fd; CHAR c; {
15383     int ret;
15384
15385     ucbuf[nout++] = c;
15386     if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
15387         nout = 0;
15388         if (!ftpissecure())
15389           ret = send(fd, (SENDARG2TYPE)ucbuf,
15390                      (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
15391         else
15392           ret = secure_putbuf(fd,
15393                               ucbuf,
15394                               (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
15395                               );
15396         return(ret?ret:c);
15397     }
15398     return(c);
15399 }
15400 #endif /* COMMENT */
15401
15402 /* returns:
15403  *       0  on success
15404  *      -1  on error (errno set)
15405  *      -2  on security error
15406  */
15407 static int
15408 secure_flush(fd) int fd; {
15409     int rc = 0;
15410     int len = 0;
15411
15412     if (nout > 0) {
15413         len = nout;
15414         if (!ftpissecure()) {
15415             rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
15416             nout = 0;
15417             goto xflush;
15418         } else {
15419             rc = secure_putbuf(fd, ucbuf, nout);
15420             if (rc)
15421               goto xflush;
15422         }
15423     }
15424     rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
15425
15426   xflush:
15427     if (rc > -1 && len > 0 && fdispla != XYFD_B) {
15428         spackets++;
15429         spktl = len;
15430         ftscreen(SCR_PT,'D',spackets,NULL);
15431     }
15432     return(rc);
15433 }
15434
15435 #ifdef COMMENT                          /* (not used) */
15436 /* returns:
15437  *      c>=0  on success
15438  *      -1    on error
15439  *      -2    on security error
15440  */
15441 static int
15442 #ifdef CK_ANSIC
15443 secure_putc(char c, int fd)
15444 #else
15445 secure_putc(c, fd) char c; int fd;
15446 #endif /* CK_ANSIC */
15447 /* secure_putc */ {
15448     return(secure_putbyte(fd, (CHAR) c));
15449 }
15450 #endif /* COMMENT */
15451
15452 /* returns:
15453  *      nbyte on success
15454  *      -1  on error (errno set)
15455  *      -2  on security error
15456  */
15457 static int
15458 #ifdef CK_ANSIC
15459 secure_write(int fd, CHAR * buf, unsigned int nbyte)
15460 #else
15461 secure_write(fd, buf, nbyte)
15462     int fd;
15463     CHAR * buf;
15464     unsigned int nbyte;
15465 #endif /* CK_ANSIC */
15466 {
15467     int ret;
15468
15469     if (!ftpissecure()) {
15470         if (nout > 0) {
15471             if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
15472               return(ret);
15473             nout = 0;
15474         }
15475         return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
15476     } else {
15477         int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
15478         int bsent = 0;
15479
15480         while (bsent < nbyte) {
15481             int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
15482                         (ucbuflen - nout) : (nbyte - bsent));
15483 #ifdef DEBUG
15484             if (deblog) {
15485                 debug(F101,"secure_write ucbuflen","",ucbuflen);
15486                 debug(F101,"secure_write ucbufsiz","",ucbufsiz);
15487                 debug(F101,"secure_write bsent","",bsent);
15488                 debug(F101,"secure_write b2cp","",b2cp);
15489             }
15490 #endif /* DEBUG */
15491             memcpy(&ucbuf[nout],&buf[bsent],b2cp);
15492             nout += b2cp;
15493             bsent += b2cp;
15494
15495             if (nout == ucbuflen) {
15496                 nout = 0;
15497                 ret = secure_putbuf(fd, ucbuf, ucbuflen);
15498                 if (ret < 0)
15499                   return(ret);
15500             }
15501         }
15502         return(bsent);
15503     }
15504 }
15505
15506 /* returns:
15507  *       0  on success
15508  *      -1  on error (errno set)
15509  *      -2  on security error
15510  */
15511 static int
15512 #ifdef CK_ANSIC
15513 secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
15514 #else
15515 secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
15516 #endif /* CK_ANSIC */
15517 {
15518     static char *outbuf = NULL;         /* output ciphertext */
15519 #ifdef FTP_SECURITY
15520     static unsigned int bufsize = 0;    /* size of outbuf */
15521 #endif /* FTP_SECURITY */
15522     ftp_int32 length   = 0;
15523     ftp_uint32 net_len = 0;
15524
15525     /* Other auth types go here ... */
15526 #ifdef CK_SSL
15527     if (ssl_ftp_data_active_flag) {
15528         int count, error;
15529
15530         /* there is no need to send an empty buffer when using SSL/TLS */
15531         if ( nbyte == 0 )
15532           return(0);
15533
15534         count = SSL_write(ssl_ftp_data_con, buf, nbyte);
15535         error = SSL_get_error(ssl_ftp_data_con,count);
15536         switch (error) {
15537           case SSL_ERROR_NONE:
15538             return(0);
15539           case SSL_ERROR_WANT_WRITE:
15540           case SSL_ERROR_WANT_READ:
15541           case SSL_ERROR_SYSCALL:
15542 #ifdef NT
15543             {
15544                 int gle = GetLastError();
15545                 if (gle == 0)
15546                   return(0);
15547                 debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
15548             }
15549 #endif /* NT */
15550           case SSL_ERROR_WANT_X509_LOOKUP:
15551           case SSL_ERROR_SSL:
15552           case SSL_ERROR_ZERO_RETURN:
15553           default:
15554             SSL_shutdown(ssl_ftp_data_con);
15555             SSL_free(ssl_ftp_data_con);
15556             ssl_ftp_data_active_flag = 0;
15557             ssl_ftp_data_con = NULL;
15558 #ifdef TCPIPLIB
15559             socket_close(data);
15560 #else /* TCPIPLIB */
15561 #ifdef USE_SHUTDOWN
15562             shutdown(data, 1+1);
15563 #endif /* USE_SHUTDOWN */
15564             close(data);
15565 #endif /* TCPIPLIB */
15566             data = -1;
15567             globaldin = data;
15568             return(-1);
15569         }
15570         return(-1);
15571     }
15572 #endif /* CK_SSL */
15573
15574 #ifdef FTP_SRP
15575     if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
15576         if (bufsize < nbyte + FUDGE_FACTOR) {
15577             if (outbuf?
15578                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
15579                 (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
15580                 bufsize = nbyte + FUDGE_FACTOR;
15581             } else {
15582                 bufsize = 0;
15583                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
15584                 return(ERR);
15585             }
15586         }
15587         if ((length =
15588              srp_encode(ftp_dpl == FPL_PRV,
15589                         (CHAR *) buf,
15590                         (CHAR *) outbuf,
15591                         nbyte
15592                         )
15593              ) < 0) {
15594             secure_error ("srp_encode failed");
15595             return ERR;
15596         }
15597     }
15598 #endif /* FTP_SRP */
15599 #ifdef FTP_KRB4
15600     if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
15601         struct sockaddr_in myaddr, hisaddr;
15602         GSOCKNAME_T len;
15603         len = sizeof(myaddr);
15604         if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
15605             secure_error("secure_putbuf: getsockname failed");
15606             return(ERR);
15607         }
15608         len = sizeof(hisaddr);
15609         if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
15610             secure_error("secure_putbuf: getpeername failed");
15611             return(ERR);
15612         }
15613         if (bufsize < nbyte + FUDGE_FACTOR) {
15614             if (outbuf ?
15615                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
15616                  (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
15617                 bufsize = nbyte + FUDGE_FACTOR;
15618             } else {
15619                 bufsize = 0;
15620                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
15621                 return(ERR);
15622             }
15623         }
15624         if (ftp_dpl == FPL_PRV) {
15625             length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
15626                                  ftp_sched,
15627 #ifdef KRB524
15628                                  ftp_cred.session,
15629 #else /* KRB524 */
15630                                  &ftp_cred.session,
15631 #endif /* KRB524 */
15632                                  &myaddr,
15633                                  &hisaddr
15634                                  );
15635         } else {
15636             length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
15637 #ifdef KRB524
15638                                  ftp_cred.session,
15639 #else /* KRB524 */
15640                                  &ftp_cred.session,
15641 #endif /* KRB524 */
15642                                  &myaddr,
15643                                  &hisaddr
15644                                  );
15645         }
15646         if (length == -1) {
15647             secure_error("krb_mk_%s failed for KERBEROS_V4",
15648                          ftp_dpl == FPL_PRV ? "priv" : "safe");
15649             return(ERR);
15650         }
15651     }
15652 #endif /* FTP_KRB4 */
15653 #ifdef FTP_GSSAPI
15654     if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
15655         gss_buffer_desc in_buf, out_buf;
15656         OM_uint32 maj_stat, min_stat;
15657         int conf_state;
15658
15659         in_buf.value = buf;
15660         in_buf.length = nbyte;
15661         maj_stat = gss_seal(&min_stat, gcontext,
15662                             (ftp_dpl == FPL_PRV), /* confidential */
15663                             GSS_C_QOP_DEFAULT,
15664                             &in_buf,
15665                             &conf_state,
15666                             &out_buf
15667                             );
15668         if (maj_stat != GSS_S_COMPLETE) {
15669             /* generally need to deal */
15670             /* ie. should loop, but for now just fail */
15671             user_gss_error(maj_stat, min_stat,
15672                            ftp_dpl == FPL_PRV?
15673                            "GSSAPI seal failed":
15674                            "GSSAPI sign failed");
15675             return(ERR);
15676         }
15677         if (bufsize < out_buf.length) {
15678             if (outbuf ?
15679                 (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
15680                 (outbuf = malloc((unsigned) out_buf.length))) {
15681                 bufsize = out_buf.length;
15682             } else {
15683                 bufsize = 0;
15684                 secure_error("%s (in malloc of PROT buffer)",
15685                              ck_errstr());
15686                 return(ERR);
15687             }
15688         }
15689         memcpy(outbuf, out_buf.value, length=out_buf.length);
15690         gss_release_buffer(&min_stat, &out_buf);
15691     }
15692 #endif /* FTP_GSSAPI */
15693     net_len = htonl((ULONG) length);
15694     if (looping_write(fd, (char *)&net_len, 4) == -1)
15695       return(-1);
15696     if (looping_write(fd, outbuf, length) != length)
15697       return(-1);
15698     return(0);
15699 }
15700
15701 /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
15702
15703 static int
15704 secure_getbyte(fd,fc) int fd,fc; {
15705     /* number of chars in ucbuf, pointer into ucbuf */
15706     static unsigned int nin = 0, bufp = 0;
15707     int kerror;
15708     ftp_uint32 length;
15709
15710     if (fc) {
15711         nin = bufp = 0;
15712         ucbuf[0] = NUL;
15713         return(0);
15714     }
15715     if (nin == 0) {
15716         if (iscanceled())
15717           return(-9);
15718 #ifdef CK_SSL
15719         if (ssl_ftp_data_active_flag) {
15720             int count, error;
15721             count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
15722             error = SSL_get_error(ssl_ftp_data_con,count);
15723             switch (error) {
15724               case SSL_ERROR_NONE:
15725                 nin = bufp = count;
15726                 rpackets++;
15727                 pktnum++;
15728                 if (fdispla != XYFD_B) {
15729                     rpktl = count;
15730                     ftscreen(SCR_PT,'D',rpackets,NULL);
15731                 }
15732                 break;
15733               case SSL_ERROR_WANT_WRITE:
15734               case SSL_ERROR_WANT_READ:
15735               case SSL_ERROR_SYSCALL:
15736 #ifdef NT
15737                 {
15738                     int gle = GetLastError();
15739                 }
15740 #endif /* NT */
15741               case SSL_ERROR_WANT_X509_LOOKUP:
15742               case SSL_ERROR_SSL:
15743               case SSL_ERROR_ZERO_RETURN:
15744               default:
15745                 nin = bufp = count = 0;
15746                 SSL_shutdown(ssl_ftp_data_con);
15747                 SSL_free(ssl_ftp_data_con);
15748                 ssl_ftp_data_active_flag = 0;
15749                 ssl_ftp_data_con = NULL;
15750 #ifdef TCPIPLIB
15751                 socket_close(data);
15752 #else /* TCPIPLIB */
15753 #ifdef USE_SHUTDOWN
15754                 shutdown(data, 1+1);
15755 #endif /* USE_SHUTDOWN */
15756                 close(data);
15757 #endif /* TCPIPLIB */
15758                 data = -1;
15759                 globaldin = data;
15760                 break;
15761             }
15762         } else
15763 #endif /* CK_SSL */
15764           {
15765               kerror = looping_read(fd, (char *)&length, sizeof(length));
15766               if (kerror != sizeof(length)) {
15767                   secure_error("Couldn't read PROT buffer length: %d/%s",
15768                                kerror,
15769                                kerror == -1 ? ck_errstr()
15770                                : "premature EOF"
15771                                );
15772                   return(ERR);
15773               }
15774               debug(F101,"secure_getbyte length","",length);
15775               debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
15776
15777               length = (ULONG) ntohl(length);
15778               if (length > maxbuf) {
15779                   secure_error("Length (%d) of PROT buffer > PBSZ=%u",
15780                                length,
15781                                maxbuf
15782                                );
15783                   return(ERR);
15784               }
15785               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
15786                   secure_error("Couldn't read %u byte PROT buffer: %s",
15787                                length,
15788                                kerror == -1 ? ck_errstr() : "premature EOF"
15789                                );
15790                   return(ERR);
15791               }
15792
15793               /* Other auth types go here ... */
15794 #ifdef FTP_SRP
15795               if (strcmp(auth_type, "SRP") == 0) {
15796                   if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
15797                                                 (CHAR *) ucbuf,
15798                                                 ucbuf,
15799                                                 length
15800                                                 )
15801                        ) == -1) {
15802                       secure_error ("srp_encode failed" );
15803                       return ERR;
15804                   }
15805               }
15806 #endif /* FTP_SRP */
15807 #ifdef FTP_KRB4
15808               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
15809                   struct sockaddr_in myaddr, hisaddr;
15810                   GSOCKNAME_T len;
15811                   len = sizeof(myaddr);
15812                   if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
15813                       secure_error("secure_putbuf: getsockname failed");
15814                       return(ERR);
15815                   }
15816                   len = sizeof(hisaddr);
15817                   if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
15818                       secure_error("secure_putbuf: getpeername failed");
15819                       return(ERR);
15820                   }
15821                   if (ftp_dpl) {
15822                       kerror = krb_rd_priv(ucbuf, length, ftp_sched,
15823 #ifdef KRB524
15824                                            ftp_cred.session,
15825 #else /* KRB524 */
15826                                            &ftp_cred.session,
15827 #endif /* KRB524 */
15828                                            &hisaddr, &myaddr, &ftp_msg_data);
15829                   } else {
15830                       kerror = krb_rd_safe(ucbuf, length,
15831 #ifdef KRB524
15832                                            ftp_cred.session,
15833 #else /* KRB524 */
15834                                            &ftp_cred.session,
15835 #endif /* KRB524 */
15836                                            &hisaddr, &myaddr, &ftp_msg_data);
15837                   }
15838                   if (kerror) {
15839                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
15840                                    ftp_dpl == FPL_PRV ? "priv" : "safe",
15841                                    krb_get_err_text(kerror));
15842                       return(ERR);
15843                   }
15844                   memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
15845                   nin = bufp = ftp_msg_data.app_length;
15846               }
15847 #endif /* FTP_KRB4 */
15848 #ifdef FTP_GSSAPI
15849               if (strcmp(auth_type, "GSSAPI") == 0) {
15850                   gss_buffer_desc xmit_buf, msg_buf;
15851                   OM_uint32 maj_stat, min_stat;
15852                   int conf_state;
15853
15854                   xmit_buf.value = ucbuf;
15855                   xmit_buf.length = length;
15856                   conf_state = (ftp_dpl == FPL_PRV);
15857                   /* decrypt/verify the message */
15858                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
15859                                         &msg_buf, &conf_state, NULL);
15860                   if (maj_stat != GSS_S_COMPLETE) {
15861                       user_gss_error(maj_stat, min_stat,
15862                                      (ftp_dpl == FPL_PRV)?
15863                                      "failed unsealing ENC message":
15864                                      "failed unsealing MIC message");
15865                       return ERR;
15866                   }
15867                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
15868                   gss_release_buffer(&min_stat, &msg_buf);
15869               }
15870 #endif /* FTP_GSSAPI */
15871               /* Other auth types go here ... */
15872
15873               /* Update file transfer display */
15874               rpackets++;
15875               pktnum++;
15876               if (fdispla != XYFD_B) {
15877                   rpktl = nin;
15878                   ftscreen(SCR_PT,'D',rpackets,NULL);
15879               }
15880           }
15881     }
15882     if (nin == 0)
15883       return(EOF);
15884     else
15885       return(ucbuf[bufp - nin--]);
15886 }
15887
15888 /* secure_getc(fd,fc)
15889  * Call with:
15890  *   fd = file descriptor for connection.
15891  *   fc = 0 to get a character, fc != 0 to initialize buffer pointers.
15892  * Returns:
15893  *   c>=0 on success (character value)
15894  *   -1   on EOF
15895  *   -2   on security error
15896  */
15897 static int
15898 secure_getc(fd,fc) int fd,fc; {         /* file descriptor, function code */
15899     if (!ftpissecure()) {
15900         static unsigned int nin = 0, bufp = 0;
15901         if (fc) {
15902             nin = bufp = 0;
15903             ucbuf[0] = NUL;
15904             return(0);
15905         }
15906         if (nin == 0) {
15907             if (iscanceled())
15908               return(-9);
15909             nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
15910             if (nin <= 0) {
15911                 debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
15912                 debug(F101,"secure_getc returns EOF","",EOF);
15913                 nin = bufp = 0;
15914                 return(EOF);
15915             }
15916             debug(F101,"ftp secure_getc recv","",nin);
15917             hexdump("ftp secure_getc recv",ucbuf,16);
15918             rpackets++;
15919             pktnum++;
15920             if (fdispla != XYFD_B) {
15921                 rpktl = nin;
15922                 ftscreen(SCR_PT,'D',rpackets,NULL);
15923             }
15924         }
15925         return(ucbuf[bufp - nin--]);
15926     } else
15927       return(secure_getbyte(fd,fc));
15928 }
15929
15930 /* returns:
15931  *     n>0  on success (n == # of bytes read)
15932  *       0  on EOF
15933  *      -1  on error (errno set), only for FPL_CLR
15934  *      -2  on security error
15935  */
15936 static int
15937 secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
15938     static int c = 0;
15939     int i;
15940
15941     debug(F101,"secure_read bytes requested","",nbyte);
15942     if (c == EOF)
15943       return(c = 0);
15944     for (i = 0; nbyte > 0; nbyte--) {
15945         c = secure_getc(fd,0);
15946         switch (c) {
15947           case -9:                      /* Canceled from keyboard */
15948             debug(F101,"ftp secure_read interrupted","",c);
15949             return(0);
15950           case ERR:
15951             debug(F101,"ftp secure_read error","",c);
15952             return(c);
15953           case EOF:
15954             debug(F101,"ftp secure_read EOF","",c);
15955             if (!i)
15956               c = 0;
15957             return(i);
15958           default:
15959             buf[i++] = c;
15960         }
15961     }
15962     return(i);
15963 }
15964
15965 #ifdef USE_RUSERPASS
15966 /* BEGIN_RUSERPASS
15967  *
15968  * Copyright (c) 1985 Regents of the University of California.
15969  * All rights reserved.
15970  *
15971  * Redistribution and use in source and binary forms, with or without
15972  * modification, are permitted provided that the following conditions
15973  * are met:
15974  * 1. Redistributions of source code must retain the above copyright
15975  *    notice, this list of conditions and the following disclaimer.
15976  * 2. Redistributions in binary form must reproduce the above copyright
15977  *    notice, this list of conditions and the following disclaimer in the
15978  *    documentation and/or other materials provided with the distribution.
15979  * 3. All advertising materials mentioning features or use of this software
15980  *    must display the following acknowledgement:
15981  *      This product includes software developed by the University of
15982  *      California, Berkeley and its contributors.
15983  * 4. Neither the name of the University nor the names of its contributors
15984  *    may be used to endorse or promote products derived from this software
15985  *    without specific prior written permission.
15986  *
15987  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15988  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15989  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15990  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
15991  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15992  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
15993  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15994  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
15995  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
15996  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
15997  * SUCH DAMAGE.
15998  */
15999
16000 #ifndef lint
16001 static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
16002 #endif /* not lint */
16003
16004 #ifndef MAXHOSTNAMELEN
16005 #define MAXHOSTNAMELEN 64
16006 #endif
16007
16008 char * renvlook();
16009 static FILE * cfile;
16010
16011 #define DEFAULT 1
16012 #define LOGIN   2
16013 #define PASSWD  3
16014 #define ACCOUNT 4
16015 #define MACDEF  5
16016 #define ID      10
16017 #define MACH    11
16018
16019 static char tokval[100];
16020
16021 static struct toktab {
16022     char *tokstr;
16023     int tval;
16024 } toktab[]= {
16025     "default",  DEFAULT,
16026     "login",    LOGIN,
16027     "password", PASSWD,
16028     "passwd",   PASSWD,
16029     "account",  ACCOUNT,
16030     "machine",  MACH,
16031     "macdef",   MACDEF,
16032     0,          0
16033 };
16034
16035 static int
16036 token() {
16037     char *cp;
16038     int c;
16039     struct toktab *t;
16040
16041     if (feof(cfile))
16042       return(0);
16043     while ((c = getc(cfile)) != EOF &&
16044            (c == '\n' || c == '\t' || c == ' ' || c == ','))
16045       continue;
16046     if (c == EOF)
16047       return(0);
16048     cp = tokval;
16049     if (c == '"') {
16050         while ((c = getc(cfile)) != EOF && c != '"') {
16051             if (c == '\\')
16052               c = getc(cfile);
16053             *cp++ = c;
16054         }
16055     } else {
16056         *cp++ = c;
16057         while ((c = getc(cfile)) != EOF
16058                && c != '\n' && c != '\t' && c != ' ' && c != ',') {
16059             if (c == '\\')
16060               c = getc(cfile);
16061             *cp++ = c;
16062         }
16063     }
16064     *cp = 0;
16065     if (tokval[0] == 0)
16066       return(0);
16067     for (t = toktab; t->tokstr; t++)
16068       if (!strcmp(t->tokstr, tokval))
16069         return(t->tval);
16070     return(ID);
16071 }
16072
16073 ruserpass(host, aname, apass, aacct)
16074     char *host, **aname, **apass, **aacct;
16075 {
16076     char *hdir, buf[FTP_BUFSIZ], *tmp;
16077     char myname[MAXHOSTNAMELEN], *mydomain;
16078     int t, i, c, usedefault = 0;
16079 #ifdef NT
16080     struct _stat stb;
16081 #else /* NT */
16082     struct stat stb;
16083 #endif /* NT */
16084
16085     hdir = getenv("HOME");
16086     if (hdir == NULL)
16087         hdir = ".";
16088     ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
16089     cfile = fopen(buf, "r");
16090     if (cfile == NULL) {
16091         if (errno != ENOENT)
16092           perror(buf);
16093         return(0);
16094     }
16095     if (gethostname(myname, MAXHOSTNAMELEN) < 0)
16096       myname[0] = '\0';
16097     if ((mydomain = ckstrchr(myname, '.')) == NULL)
16098       mydomain = "";
16099
16100   next:
16101     while ((t = token())) switch(t) {
16102
16103       case DEFAULT:
16104         usedefault = 1;
16105         /* FALL THROUGH */
16106
16107       case MACH:
16108         if (!usedefault) {
16109             if (token() != ID)
16110               continue;
16111             /*
16112              * Allow match either for user's input host name
16113              * or official hostname.  Also allow match of
16114              * incompletely-specified host in local domain.
16115              */
16116             if (ckstrcmp(host, tokval,-1,1) == 0)
16117               goto match;
16118             if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
16119               goto match;
16120             if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
16121                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16122                 ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
16123                 tokval[tmp - ftp_host] == '\0')
16124               goto match;
16125             if ((tmp = ckstrchr(host, '.')) != NULL &&
16126                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16127                 ckstrcmp(host, tokval, tmp - host, 0) == 0 &&
16128                 tokval[tmp - host] == '\0')
16129               goto match;
16130             continue;
16131         }
16132
16133       match:
16134         while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
16135
16136           case LOGIN:
16137             if (token())
16138               if (*aname == 0) {
16139                   *aname = malloc((unsigned) strlen(tokval) + 1);
16140                   strcpy(*aname, tokval);      /* safe */
16141               } else {
16142                   if (strcmp(*aname, tokval))
16143                     goto next;
16144               }
16145             break;
16146           case PASSWD:
16147             if (strcmp(*aname, "anonymous") &&
16148                 fstat(fileno(cfile), &stb) >= 0 &&
16149                 (stb.st_mode & 077) != 0) {
16150                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16151                 fprintf(stderr, "Remove password or correct mode.\n");
16152                 goto bad;
16153             }
16154             if (token() && *apass == 0) {
16155                 *apass = malloc((unsigned) strlen(tokval) + 1);
16156                 strcpy(*apass, tokval);          /* safe */
16157             }
16158             break;
16159           case ACCOUNT:
16160             if (fstat(fileno(cfile), &stb) >= 0
16161                 && (stb.st_mode & 077) != 0) {
16162                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16163                 fprintf(stderr, "Remove account or correct mode.\n");
16164                 goto bad;
16165             }
16166             if (token() && *aacct == 0) {
16167                 *aacct = malloc((unsigned) strlen(tokval) + 1);
16168                 strcpy(*aacct, tokval);          /* safe */
16169             }
16170             break;
16171
16172           default:
16173             fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
16174             break;
16175         }
16176         goto done;
16177     }
16178
16179   done:
16180     fclose(cfile);
16181     return(0);
16182
16183   bad:
16184     fclose(cfile);
16185     return(-1);
16186 }
16187 #endif /* USE_RUSERPASS */
16188
16189 static char *radixN =
16190   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16191
16192 static char pad = '=';
16193
16194 static int
16195 radix_encode(inbuf, outbuf, inlen, outlen, decode)
16196     CHAR inbuf[], outbuf[];
16197     int inlen, *outlen, decode;
16198 {
16199     int i, j, D = 0;
16200     char *p;
16201     CHAR c = NUL;
16202
16203     if (decode) {
16204         for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
16205             if ((p = ckstrchr(radixN, inbuf[i])) == NULL)
16206               return(1);
16207             D = p - radixN;
16208             switch (i&3) {
16209               case 0:
16210                 outbuf[j] = D<<2;
16211                 break;
16212               case 1:
16213                 outbuf[j++] |= D>>4;
16214                 outbuf[j] = (D&15)<<4;
16215                 break;
16216               case 2:
16217                 outbuf[j++] |= D>>2;
16218                 outbuf[j] = (D&3)<<6;
16219                 break;
16220               case 3:
16221                 outbuf[j++] |= D;
16222             }
16223             if (j == *outlen)
16224               return(4);
16225         }
16226         switch (i&3) {
16227           case 1: return(3);
16228           case 2: if (D&15) return(3);
16229             if (strcmp((char *)&inbuf[i], "==")) return(2);
16230             break;
16231           case 3: if (D&3) return(3);
16232             if (strcmp((char *)&inbuf[i], "="))  return(2);
16233         }
16234         *outlen = j;
16235     } else {
16236         for (i = 0, j = 0; i < inlen; i++) {
16237             switch (i%3) {
16238               case 0:
16239                 outbuf[j++] = radixN[inbuf[i]>>2];
16240                 c = (inbuf[i]&3)<<4;
16241                 break;
16242               case 1:
16243                 outbuf[j++] = radixN[c|inbuf[i]>>4];
16244                 c = (inbuf[i]&15)<<2;
16245                 break;
16246               case 2:
16247                 outbuf[j++] = radixN[c|inbuf[i]>>6];
16248                 outbuf[j++] = radixN[inbuf[i]&63];
16249                 c = 0;
16250             }
16251             if (j == *outlen)
16252               return(4);
16253         }
16254         if (i%3) outbuf[j++] = radixN[c];
16255         switch (i%3) {
16256           case 1: outbuf[j++] = pad;
16257           case 2: outbuf[j++] = pad;
16258         }
16259         outbuf[*outlen = j] = '\0';
16260     }
16261     return(0);
16262 }
16263
16264 static char *
16265 radix_error(e) int e;
16266 {
16267     switch (e) {
16268       case 0:  return("Success");
16269       case 1:  return("Bad character in encoding");
16270       case 2:  return("Encoding not properly padded");
16271       case 3:  return("Decoded # of bits not a multiple of 8");
16272       case 4:  return("Output buffer too small");
16273       default: return("Unknown error");
16274     }
16275 }
16276 /* END_RUSERPASS */
16277
16278 #ifdef FTP_SRP
16279 /*---------------------------------------------------------------------------+
16280  |                                                                           |
16281  |   Package: srpftp                                                         |
16282  |   Author: Eugene Jhong                                                    |
16283  |                                                                           |
16284  +---------------------------------------------------------------------------*/
16285
16286 /*
16287  * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
16288  * All Rights Reserved.
16289  *
16290  * Permission is hereby granted, free of charge, to any person obtaining
16291  * a copy of this software and associated documentation files (the
16292  * "Software"), to deal in the Software without restriction, including
16293  * without limitation the rights to use, copy, modify, merge, publish,
16294  * distribute, sublicense, and/or sell copies of the Software, and to
16295  * permit persons to whom the Software is furnished to do so, subject to
16296  * the following conditions:
16297  *
16298  * The above copyright notice and this permission notice shall be
16299  * included in all copies or substantial portions of the Software.
16300  *
16301  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16302  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16303  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16304  *
16305  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
16306  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
16307  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
16308  * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
16309  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16310  *
16311  * In addition, the following conditions apply:
16312  *
16313  * 1. Any software that incorporates the SRP authentication technology
16314  *    must display the following acknowlegment:
16315  *    "This product uses the 'Secure Remote Password' cryptographic
16316  *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
16317  *
16318  * 2. Any software that incorporates all or part of the SRP distribution
16319  *    itself must also display the following acknowledgment:
16320  *    "This product includes software developed by Tom Wu and Eugene
16321  *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
16322  *
16323  * 3. Redistributions in source or binary form must retain an intact copy
16324  *    of this copyright notice and list of conditions.
16325  */
16326
16327 #define SRP_PROT_VERSION        1
16328
16329 #ifdef CK_ENCRYPTION
16330 #define SRP_DEFAULT_CIPHER      CIPHER_ID_CAST5_CBC
16331 #else
16332 #define SRP_DEFAULT_CIPHER      CIPHER_ID_NONE
16333 #endif /* CK_ENCRYPTION */
16334
16335 #define SRP_DEFAULT_HASH        HASH_ID_SHA
16336
16337 CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
16338 CHAR srp_pref_hash = HASH_ID_SHA;
16339
16340 static struct t_client *tc = NULL;
16341 static CHAR *skey = NULL;
16342 static krypto_context *incrypt = NULL;
16343 static krypto_context *outcrypt = NULL;
16344
16345 typedef unsigned int srp_uint32;
16346
16347 /*--------------------------------------------------------------+
16348  | srp_selcipher: select cipher                                 |
16349  +--------------------------------------------------------------*/
16350 static int
16351 srp_selcipher (cname) char *cname; {
16352     cipher_desc *cd;
16353
16354     if (!(cd = cipher_getdescbyname (cname))) {
16355         int i;
16356         CHAR *list = cipher_getlist ();
16357
16358         fprintf (stderr, "ftp: supported ciphers:\n\n");
16359         for (i = 0; i < strlen (list); i++)
16360           fprintf (stderr, "    %s\n", (cipher_getdescbyid(list[i]))->name);
16361         fprintf (stderr, "\n");
16362         return -1;
16363     }
16364     srp_pref_cipher = cd->id;
16365     return 0;
16366 }
16367
16368 /*--------------------------------------------------------------+
16369  | srp_selhash: select hash                                     |
16370  +--------------------------------------------------------------*/
16371 static int
16372 srp_selhash (hname) char *hname; {
16373     hash_desc *hd;
16374
16375     if (!(hd = hash_getdescbyname (hname))) {
16376         int i;
16377         CHAR *list = hash_getlist ();
16378
16379         fprintf (stderr, "ftp: supported hash functions:\n\n");
16380         for (i = 0; i < strlen (list); i++)
16381           fprintf (stderr, "    %s\n", (hash_getdescbyid(list[i]))->name);
16382         fprintf (stderr, "\n");
16383         return -1;
16384     }
16385     srp_pref_hash = hd->id;
16386     return 0;
16387 }
16388
16389 /*--------------------------------------------------------------+
16390  | srp_userpass: get username and password                      |
16391  +--------------------------------------------------------------*/
16392 static int
16393 srp_userpass (host) char *host; {
16394     char tmp[BUFSIZ], prompt[PROMPTSIZ];
16395     char *user;
16396
16397     user = NULL;
16398 #ifdef USE_RUSERPASS
16399     ruserpass (host, &user, &srp_pass, &srp_acct);
16400 #endif /* USE_RUSERPASS */
16401
16402     while (user == NULL)     {
16403         char *myname;
16404         int ok;
16405
16406         myname = whoami();
16407         if (!myname) myname = "";
16408         if (myname[0])
16409           ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
16410                     NULL,NULL,NULL,NULL,NULL,NULL,NULL);
16411         else
16412           ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
16413         tmp[0] = '\0';
16414         ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
16415                     DEFAULT_UQ_TIMEOUT);
16416         if (!ok || *tmp == '\0')
16417           user = myname;
16418         else
16419           user = brstrip(tmp);
16420     }
16421     ckstrncpy (srp_user, user,BUFSIZ);
16422     return(0);
16423 }
16424
16425 /*--------------------------------------------------------------+
16426  | srp_reset: reset srp information                             |
16427  +--------------------------------------------------------------*/
16428 static int
16429 srp_reset () {
16430     if (tc) { t_clientclose (tc); tc = NULL; }
16431     if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
16432     if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
16433     return(0);
16434 }
16435
16436 /*--------------------------------------------------------------+
16437  | srp_ftp_auth: perform srp authentication                         |
16438  +--------------------------------------------------------------*/
16439 static int
16440 srp_ftp_auth(host, user, pass)
16441     char *host;
16442     char *user;
16443     char *pass;
16444 {
16445     struct t_num *wp;
16446     struct t_num N;
16447     struct t_num g;
16448     struct t_num s;
16449     struct t_num yp;
16450     CHAR buf[FTP_BUFSIZ];
16451     CHAR tmp[FTP_BUFSIZ];
16452     CHAR *bp, *cp;
16453     int n, e, clen, blen, len, i;
16454     CHAR cid = 0;
16455     CHAR hid = 0;
16456
16457     srp_pass = srp_acct = 0;
16458
16459     n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
16460     if (n != REPLY_CONTINUE) {
16461         if (ftp_deb)
16462             fprintf(stderr, "SRP rejected as an authentication type\n");
16463         return(0);
16464     } else {                            /* Send protocol version */
16465         CHAR vers[4];
16466         memset (vers, 0, 4);
16467         vers[3] = SRP_PROT_VERSION;
16468         if (!quiet)
16469           printf ("SRP accepted as authentication type.\n");
16470         bp = tmp; blen = 0;
16471         srp_put (vers, &bp, 4, &blen);
16472         len = FTP_BUFSIZ;
16473         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16474           goto encode_error;
16475         reply_parse = "ADAT=";
16476         n = ftpcmd("ADAT",buf,-1,-1,0);
16477     }
16478     if (n == REPLY_CONTINUE) {          /* Get protocol version */
16479         bp = buf;
16480         if (!reply_parse)
16481           goto data_error;
16482         blen = FTP_BUFSIZ;
16483         if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
16484           goto decode_error;
16485         if (srp_get (&bp, &cp, &blen, &clen) != 4)
16486           goto data_error;
16487
16488         if (host) {                     /* Get username/password if needed */
16489             srp_userpass (host);
16490         } else {
16491             ckstrncpy (srp_user, user, BUFSIZ);
16492             srp_pass = pass;
16493         }
16494         bp = tmp; blen = 0;             /* Send username */
16495         srp_put (srp_user, &bp, strlen (srp_user), &blen);
16496         len = FTP_BUFSIZ;
16497         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16498           goto encode_error;
16499         reply_parse = "ADAT=";
16500         n = ftpcmd("ADAT",buf,-1,-1,0);
16501     }
16502     if (n == REPLY_CONTINUE) {          /* Get N, g and s */
16503         bp = buf;
16504         if (!reply_parse)
16505           goto data_error;
16506         blen = FTP_BUFSIZ;
16507         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
16508           goto decode_error;
16509         if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
16510           goto data_error;
16511         if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
16512           goto data_error;
16513         if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
16514           goto data_error;
16515         if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
16516             fprintf (stderr, "Unable to open SRP client structure.\n");
16517             goto bad;
16518         }
16519         wp = t_clientgenexp (tc);       /* Send wp */
16520         bp = tmp; blen = 0;
16521         srp_put (wp->data, &bp, wp->len, &blen);
16522         len = FTP_BUFSIZ;
16523         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16524           goto encode_error;
16525         reply_parse = "ADAT=";
16526         n = ftpcmd("ADAT",buf,-1,-1,0);
16527     }
16528     if (n == REPLY_CONTINUE) {          /* Get yp */
16529         bp = buf;
16530         if (!reply_parse)
16531           goto data_error;
16532         blen = FTP_BUFSIZ;
16533         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
16534           goto decode_error;
16535         if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
16536           goto data_error;
16537         if (!srp_pass) {
16538             static char ftppass[PASSBUFSIZ];
16539             int ok;
16540             setint();
16541             ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
16542                         DEFAULT_UQ_TIMEOUT);
16543             if (ok)
16544               srp_pass = brstrip(ftppass);
16545         }
16546         t_clientpasswd (tc, srp_pass);
16547         memset (srp_pass, 0, strlen (srp_pass));
16548         skey = t_clientgetkey (tc, &yp); /* Send response */
16549         bp = tmp; blen = 0;
16550         srp_put (t_clientresponse (tc), &bp, 20, &blen);
16551         len = FTP_BUFSIZ;
16552         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16553           goto encode_error;
16554         reply_parse = "ADAT=";
16555         n = ftpcmd("ADAT",buf,-1,-1,0);
16556     }
16557     if (n == REPLY_CONTINUE) {          /* Get response */
16558         bp = buf;
16559         if (!reply_parse)
16560           goto data_error;
16561         blen = FTP_BUFSIZ;
16562         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
16563           goto encode_error;
16564         if (srp_get (&bp, &cp, &blen, &clen) != 20)
16565           goto data_error;
16566         if (t_clientverify (tc, cp)) {
16567             fprintf (stderr, "WARNING: bad response to client challenge.\n");
16568             goto bad;
16569         }
16570         bp = tmp; blen = 0;             /* Send nothing */
16571         srp_put ("\0", &bp, 1, &blen);
16572         len = FTP_BUFSIZ;
16573         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16574           goto encode_error;
16575         reply_parse = "ADAT=";
16576         n = ftpcmd("ADAT",buf,-1,-1,0);
16577     }
16578     if (n == REPLY_CONTINUE) {          /* Get cipher & hash lists, seqnum */
16579         CHAR seqnum[4];
16580         CHAR *clist;
16581         CHAR *hlist;
16582         CHAR *p1;
16583         int clist_len, hlist_len;
16584         bp = buf;
16585         if (!reply_parse)
16586           goto data_error;
16587         blen = FTP_BUFSIZ;
16588         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
16589           goto encode_error;
16590         if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
16591           goto data_error;
16592         if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
16593           goto data_error;
16594         if (srp_get (&bp, &cp, &blen, &clen) != 4)
16595           goto data_error;
16596         memcpy (seqnum, cp, 4);
16597         if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
16598           cid = srp_pref_cipher;
16599         if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
16600           cid = SRP_DEFAULT_CIPHER;
16601         if (!cid) {
16602             CHAR *loclist = cipher_getlist ();
16603             for (i = 0; i < strlen (loclist); i++)
16604               if (cipher_supported (clist, loclist[i])) {
16605                   cid = loclist[i];
16606                   break;
16607               }
16608         }
16609         if (!cid) {
16610             fprintf (stderr, "Unable to agree on cipher.\n");
16611             goto bad;
16612         }
16613         /* Choose hash */
16614
16615         if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
16616           hid = srp_pref_hash;
16617
16618         if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
16619           hid = SRP_DEFAULT_HASH;
16620
16621         if (!hid) {
16622             CHAR *loclist = hash_getlist ();
16623             for (i = 0; i < strlen (loclist); i++)
16624               if (hash_supported (hlist, loclist[i])) {
16625                   hid = loclist[i];
16626                   break;
16627               }
16628         }
16629         if (!hid) {
16630             fprintf (stderr, "Unable to agree on hash.\n");
16631             goto bad;
16632         }
16633         /* Set incrypt */
16634
16635         if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
16636                                     KRYPTO_DECODE)))
16637           goto bad;
16638
16639         /* Generate random number for outkey and outseqnum */
16640
16641         t_random (seqnum, 4);
16642
16643         /* Send cid, hid, outkey, outseqnum */
16644
16645         bp = tmp; blen = 0;
16646         srp_put (&cid, &bp, 1, &blen);
16647         srp_put (&hid, &bp, 1, &blen);
16648         srp_put (seqnum, &bp, 4, &blen);
16649         len = FTP_BUFSIZ;
16650         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16651           goto encode_error;
16652         reply_parse = "ADAT=";
16653         n = ftpcmd("ADAT",buf,-1,-1,0);
16654
16655         /* Set outcrypt */
16656
16657         if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
16658                                      KRYPTO_ENCODE)))
16659           goto bad;
16660
16661         t_clientclose (tc);
16662         tc = NULL;
16663     }
16664     if (n != REPLY_COMPLETE)
16665       goto bad;
16666
16667     if (ftp_vbm) {
16668         if (ftp_deb)
16669           printf("\n");
16670         printf ("SRP authentication succeeded.\n");
16671         printf ("Using cipher %s and hash function %s.\n",
16672                 (cipher_getdescbyid(cid))->name,
16673                 (hash_getdescbyid(hid))->name
16674                 );
16675     }
16676     reply_parse = NULL;
16677     auth_type = "SRP";
16678     return(1);
16679
16680   encode_error:
16681     fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
16682     goto bad;
16683
16684   decode_error:
16685     fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
16686     goto bad;
16687
16688   data_error:
16689     fprintf (stderr, "Unable to unmarshal authentication data.\n");
16690     goto bad;
16691
16692   bad:
16693     fprintf (stderr, "SRP authentication failed, trying regular login.\n");
16694     reply_parse = NULL;
16695     return(0);
16696 }
16697
16698 /*--------------------------------------------------------------+
16699  | srp_put: put item to send buffer                             |
16700  +--------------------------------------------------------------*/
16701 static int
16702 srp_put (in, out, inlen, outlen)
16703     CHAR *in;
16704     CHAR **out;
16705     int inlen;
16706     int *outlen;
16707 {
16708     srp_uint32 net_len;
16709
16710     net_len = htonl (inlen);
16711     memcpy (*out, &net_len, 4);
16712
16713     *out += 4; *outlen += 4;
16714
16715     memcpy (*out, in, inlen);
16716
16717     *out += inlen; *outlen += inlen;
16718     return(0);
16719 }
16720
16721 /*--------------------------------------------------------------+
16722  | srp_get: get item from receive buffer                        |
16723  +--------------------------------------------------------------*/
16724 static int
16725 srp_get (in, out, inlen, outlen)
16726     CHAR **in;
16727     CHAR **out;
16728     int *inlen;
16729     int *outlen;
16730 {
16731     srp_uint32 net_len;
16732
16733     if (*inlen < 4) return -1;
16734
16735     memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
16736     *outlen = ntohl (net_len);
16737
16738     if (*inlen < *outlen) return -1;
16739
16740     *out = *in; *inlen -= *outlen; *in += *outlen;
16741
16742     return *outlen;
16743 }
16744
16745 /*--------------------------------------------------------------+
16746  | srp_encode: encode control message                           |
16747  +--------------------------------------------------------------*/
16748 static int
16749 srp_encode (private, in, out, len)
16750     int private;
16751     CHAR *in;
16752     CHAR *out;
16753     unsigned len;
16754 {
16755     if (private)
16756       return krypto_msg_priv (outcrypt, in, out, len);
16757     else
16758       return krypto_msg_safe (outcrypt, in, out, len);
16759 }
16760
16761 /*--------------------------------------------------------------+
16762  | srp_decode: decode control message                           |
16763  +--------------------------------------------------------------*/
16764 static int
16765 srp_decode (private, in, out, len)
16766     int private;
16767     CHAR *in;
16768     CHAR *out;
16769     unsigned len;
16770 {
16771     if (private)
16772       return krypto_msg_priv (incrypt, in, out, len);
16773     else
16774       return krypto_msg_safe (incrypt, in, out, len);
16775 }
16776
16777 #endif /* FTP_SRP */
16778
16779
16780
16781 #ifdef NOT_USED
16782 /*
16783   The following code is from the Unix FTP client.  Be sure to
16784   make sure that the functionality is not lost.  Especially
16785   the Proxy stuff even though we have not yet implemented it.
16786 */
16787
16788 /* Send multiple files  */
16789
16790 static int
16791 ftp_mput(argc, argv) int argc; char **argv; {
16792     register int i;
16793     sig_t oldintr;
16794     int ointer;
16795     char *tp;
16796     sigtype mcancel();
16797
16798     if (argc < 2 && !another(&argc, &argv, "local-files")) {
16799         printf("usage: %s local-files\n", argv[0]);
16800         ftpcode = -1;
16801         return;
16802     }
16803     mname = argv[0];
16804     mflag = 1;
16805     oldintr = signal(SIGINT, mcancel);
16806
16807     /* Replace with calls to cc_execute() */
16808     setjmp(jcancel);
16809 #ifdef FTP_PROXY
16810     if (proxy) {
16811         char *cp, *tp2, tmpbuf[CKMAXPATH];
16812
16813         while ((cp = remglob(argv,0)) != NULL) {
16814             if (*cp == 0) {
16815                 mflag = 0;
16816                 continue;
16817             }
16818             if (mflag && confirm(argv[0], cp)) {
16819                 tp = cp;
16820                 if (mcase) {
16821                     while (*tp && !islower(*tp)) {
16822                         tp++;
16823                     }
16824                     if (!*tp) {
16825                         tp = cp;
16826                         tp2 = tmpbuf;
16827                         while ((*tp2 = *tp) != 0) {
16828                             if (isupper(*tp2)) {
16829                                 *tp2 = 'a' + *tp2 - 'A';
16830                             }
16831                             tp++;
16832                             tp2++;
16833                         }
16834                     }
16835                     tp = tmpbuf;
16836                 }
16837                 if (ntflag) {
16838                     tp = dotrans(tp);
16839                 }
16840                 if (mapflag) {
16841                     tp = domap(tp);
16842                 }
16843                 sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0);
16844                 if (!mflag && fromatty) {
16845                     ointer = interactive;
16846                     interactive = 1;
16847                     if (confirm("Continue with","mput")) {
16848                         mflag++;
16849                     }
16850                     interactive = ointer;
16851                 }
16852             }
16853         }
16854         signal(SIGINT, oldintr);
16855         mflag = 0;
16856         return;
16857     }
16858 #endif /* FTP_PROXY */
16859     for (i = 1; i < argc; i++) {
16860         register char **cpp, **gargs;
16861
16862         if (mflag && confirm(argv[0], argv[i])) {
16863             tp = argv[i];
16864             sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
16865             if (!mflag && fromatty) {
16866                 ointer = interactive;
16867                 interactive = 1;
16868                 if (confirm("Continue with","mput")) {
16869                     mflag++;
16870                 }
16871                 interactive = ointer;
16872             }
16873         }
16874         continue;
16875
16876         gargs = ftpglob(argv[i]);
16877         if (globerr != NULL) {
16878             printf("%s\n", globerr);
16879             if (gargs) {
16880                 blkfree(gargs);
16881                 free((char *)gargs);
16882             }
16883             continue;
16884         }
16885         for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
16886             if (mflag && confirm(argv[0], *cpp)) {
16887                 tp = *cpp;
16888                 sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
16889                 if (!mflag && fromatty) {
16890                     ointer = interactive;
16891                     interactive = 1;
16892                     if (confirm("Continue with","mput")) {
16893                         mflag++;
16894                     }
16895                     interactive = ointer;
16896                 }
16897             }
16898         }
16899         if (gargs != NULL) {
16900             blkfree(gargs);
16901             free((char *)gargs);
16902         }
16903     }
16904     signal(SIGINT, oldintr);
16905     mflag = 0;
16906 }
16907
16908 /* Get multiple files */
16909
16910 static int
16911 ftp_mget(argc, argv) int argc; char **argv; {
16912     int rc = -1;
16913     sig_t oldintr;
16914     int ointer;
16915     char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
16916     sigtype mcancel();
16917
16918     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
16919         printf("usage: %s remote-files\n", argv[0]);
16920         ftpcode = -1;
16921         return(-1);
16922     }
16923     mname = argv[0];
16924     mflag = 1;
16925     oldintr = signal(SIGINT,mcancel);
16926     /* Replace with calls to cc_execute() */
16927     setjmp(jcancel);
16928     while ((cp = remglob(argv,proxy)) != NULL) {
16929         if (*cp == '\0') {
16930             mflag = 0;
16931             continue;
16932         }
16933         if (mflag && confirm(argv[0], cp)) {
16934             tp = cp;
16935             if (mcase) {
16936                 while (*tp && !islower(*tp)) {
16937                     tp++;
16938                 }
16939                 if (!*tp) {
16940                     tp = cp;
16941                     tp2 = tmpbuf;
16942                     while ((*tp2 = *tp) != 0) {
16943                         if (isupper(*tp2)) {
16944                             *tp2 = 'a' + *tp2 - 'A';
16945                         }
16946                         tp++;
16947                         tp2++;
16948                     }
16949                 }
16950                 tp = tmpbuf;
16951             }
16952             rc = (recvrequest("RETR", tp, cp, "wb",
16953                                tp != cp || !interactive) == 0,0,NULL,0,0,0);
16954             if (!mflag && fromatty) {
16955                 ointer = interactive;
16956                 interactive = 1;
16957                 if (confirm("Continue with","mget")) {
16958                     mflag++;
16959                 }
16960                 interactive = ointer;
16961             }
16962         }
16963     }
16964     signal(SIGINT,oldintr);
16965     mflag = 0;
16966     return(rc);
16967 }
16968
16969 /* Delete multiple files */
16970
16971 static int
16972 mdelete(argc, argv) int argc; char **argv; {
16973     sig_t oldintr;
16974     int ointer;
16975     char *cp;
16976     sigtype mcancel();
16977
16978     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
16979         printf("usage: %s remote-files\n", argv[0]);
16980         ftpcode = -1;
16981         return(-1);
16982     }
16983     mname = argv[0];
16984     mflag = 1;
16985     oldintr = signal(SIGINT, mcancel);
16986     /* Replace with calls to cc_execute() */
16987     setjmp(jcancel);
16988     while ((cp = remglob(argv,0)) != NULL) {
16989         if (*cp == '\0') {
16990             mflag = 0;
16991             continue;
16992         }
16993         if (mflag && confirm(argv[0], cp)) {
16994             rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
16995             if (!mflag && fromatty) {
16996                 ointer = interactive;
16997                 interactive = 1;
16998                 if (confirm("Continue with", "mdelete")) {
16999                     mflag++;
17000                 }
17001                 interactive = ointer;
17002             }
17003         }
17004     }
17005     signal(SIGINT, oldintr);
17006     mflag = 0;
17007     return(rc);
17008 }
17009
17010 /* Get a directory listing of multiple remote files */
17011
17012 static int
17013 mls(argc, argv) int argc; char **argv; {
17014     sig_t oldintr;
17015     int ointer, i;
17016     char *cmd, mode[1], *dest;
17017     sigtype mcancel();
17018     int rc = -1;
17019
17020     if (argc < 2 && !another(&argc, &argv, "remote-files"))
17021       goto usage;
17022     if (argc < 3 && !another(&argc, &argv, "local-file")) {
17023       usage:
17024         printf("usage: %s remote-files local-file\n", argv[0]);
17025         ftpcode = -1;
17026         return(-1);
17027     }
17028     dest = argv[argc - 1];
17029     argv[argc - 1] = NULL;
17030     if (strcmp(dest, "-") && *dest != '|')
17031       if (!globulize(&dest) ||
17032           !confirm("output to local-file:", dest)) {
17033           ftpcode = -1;
17034           return(-1);
17035       }
17036     cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
17037     mname = argv[0];
17038     mflag = 1;
17039     oldintr = signal(SIGINT, mcancel);
17040     /* Replace with calls to cc_execute() */
17041     setjmp(jcancel);
17042     for (i = 1; mflag && i < argc-1; ++i) {
17043         *mode = (i == 1) ? 'w' : 'a';
17044         rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
17045         if (!mflag && fromatty) {
17046             ointer = interactive;
17047             interactive = 1;
17048             if (confirm("Continue with", argv[0])) {
17049                 mflag ++;
17050             }
17051             interactive = ointer;
17052         }
17053     }
17054     signal(SIGINT, oldintr);
17055     mflag = 0;
17056     return(rc);
17057 }
17058
17059 static char *
17060 remglob(argv,doswitch) char *argv[]; int doswitch; {
17061     char temp[16];
17062     static char buf[CKMAXPATH];
17063     static FILE *ftemp = NULL;
17064     static char **args;
17065     int oldhash;
17066     char *cp, *mode;
17067
17068     if (!mflag) {
17069         if (!doglob) {
17070             args = NULL;
17071         } else {
17072             if (ftemp) {
17073                 (void) fclose(ftemp);
17074                 ftemp = NULL;
17075             }
17076         }
17077         return(NULL);
17078     }
17079     if (!doglob) {
17080         if (args == NULL)
17081           args = argv;
17082         if ((cp = *++args) == NULL)
17083           args = NULL;
17084         return(cp);
17085     }
17086     if (ftemp == NULL) {
17087         (void) strcpy(temp, _PATH_TMP);
17088 #ifdef MKTEMP
17089 #ifndef MKSTEMP
17090         (void) mktemp(temp);
17091 #endif /* MKSTEMP */
17092 #endif /* MKTEMP */
17093         verbose = 0;
17094         oldhash = hash, hash = 0;
17095 #ifdef FTP_PROXY
17096         if (doswitch) {
17097             pswitch(!proxy);
17098         }
17099 #endif /* FTP_PROXY */
17100         for (mode = "wb"; *++argv != NULL; mode = "ab")
17101           recvrequest ("NLST", temp, *argv, mode, 0);
17102 #ifdef FTP_PROXY
17103         if (doswitch) {
17104             pswitch(!proxy);
17105         }
17106 #endif /* FTP_PROXY */
17107         hash = oldhash;
17108         ftemp = fopen(temp, "r");
17109         unlink(temp);
17110         if (ftemp == NULL && (!dpyactive || ftp_deb)) {
17111             printf("Can't find list of remote files, oops\n");
17112             return(NULL);
17113         }
17114     }
17115     if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
17116         fclose(ftemp), ftemp = NULL;
17117         return(NULL);
17118     }
17119     if ((cp = ckstrchr(buf,'\n')) != NULL)
17120       *cp = '\0';
17121     return(buf);
17122 }
17123 #endif /* NOT_USED */
17124 #endif /* TCPSOCKET (top of file) */
17125 #endif /* SYSFTP (top of file) */
17126 #endif /* NOFTP (top of file) */