dh_installchangelogs: adjust to use ckc301.txt. changelog: explain -O1 usage
[ckermit.git] / ckcftp.c
1 #define FTP_TIMEOUT
2
3 /*  C K C F T P  --  FTP Client for C-Kermit  */
4
5 char *ckftpv = "FTP Client, 9.0.259, 15 Jun 2011";
6
7 /*
8   Authors:
9     Jeffrey E Altman <jaltman@secure-endpoints.com>
10       Secure Endpoints Inc., New York City
11     Frank da Cruz <fdc@columbia.edu>,
12       The Kermit Project, Columbia University.
13
14   Copyright (C) 2000, 2011,
15     Trustees of Columbia University in the City of New York.
16     All rights reserved.  See the C-Kermit COPYING.TXT file or the
17     copyright text in the ckcmai.c module for disclaimer and permissions.
18
19   Portions of conditionally included code Copyright Regents of the
20     University of California and The Stanford SRP Authentication Project;
21     see notices below.
22 */
23
24 /*
25   Pending...
26
27   . Implement recursive NLST downloads by trying to CD to each filename.
28     If it works, it's a directory; if not, it's a file -- GET it.  But
29     that won't work with servers like wu-ftpd that don't send directory 
30     names.  Recursion with MLSD is done.
31
32   . Make syslog entries for session?  Files?
33
34   . Messages are printed to stdout and stderr in random fashion.  We should
35     either print everything to stdout, or else be systematic about when
36     to use stderr.
37
38   . Implement mail (MAIL, MLFL, MSOM, etc) if any servers support it.
39
40   . Adapt to VMS.  Big job because of its record-oriented file system.
41     RMS programmer required.  There are probably also some VMS TCP/IP
42     product-specific wrinkles, e.g. attribute preservation in VMS-to-VMS
43     transfers using special options for Multinet or other FTP servers
44     (find out about STRU VMS).
45 */
46
47 /*
48   Quick FTP command reference:
49
50   RFC765 (1980) and earlier:
51     MODE  S(tream), B(lock), C(ompressed)
52     STRU  F(ILE), R(ECORD), P(AGE)
53     TYPE  A(SCII) <format>,  E(BCDIC) <format>, I(MAGE), L(OCAL) <bytesize>
54     PORT  - Port
55     PASV  - Passive mode
56     USER  - User
57     PASS  - Password
58     ACCT  - Account
59     CWD   - Change Working Directory
60     REIN  - Logout but not disconnect
61     QUIT  - Bye
62     RETR  - Retreive
63     STOR  - Store
64     APPE  - Append
65     ALLO  - Allocate
66     REST  - Restart
67     RNFR  - Rename from
68     RNTO  - Rename to
69     ABOR  - Cancel
70     DELE  - Delete
71     LIST  - Directory
72     NLST  - Name List
73     SITE  - Site parameters or commands
74     STAT  - Status
75     HELP  - Help
76     NOOP  - Noop
77
78   RFC959 (1985):
79     CDUP  - Change to Parent Directory
80     SMNT  - Structure Mount
81     STOU  - Store Unique
82     RMD   - Remove Directory
83     MKD   - Make Directory
84     PWD   - Print Directory
85     SYST  - System
86
87   RFC2389 (1998):
88     FEAT  - List Features (done)
89     OPTS  - Send options (done)
90
91   RFC2640 (1999):
92     LANG  - Specify language for messages (not done)
93
94   Pending (Internet Drafts):
95     SIZE  - File size (done)
96     MDTM  - File modification date-time (done)
97     MLST  - File name and attribute list (single file) (not done)
98     MLSD  - File list with attributes (multiple files) (done)
99     MAIL, MLFL, MSOM - mail delivery (not done)
100
101   Alphabetical syntax list:
102     ABOR <CRLF>
103     ACCT <SP> <account-information> <CRLF>
104     ALLO <SP> <decimal-integer> [<SP> R <SP> <decimal-integer>] <CRLF>
105     APPE <SP> <pathname> <CRLF>
106     CDUP <CRLF>
107     CWD  <SP> <pathname> <CRLF>
108     DELE <SP> <pathname> <CRLF>
109     FEAT <CRLF>
110     HELP [<SP> <string>] <CRLF>
111     LANG [<SP> <language-tag> ] <CRLF>
112     LIST [<SP> <pathname>] <CRLF>
113     MKD  <SP> <pathname> <CRLF>
114     MLSD [<SP> <pathname>] <CRLF>
115     MLST [<SP> <pathname>] <CRLF>
116     MODE <SP> <mode-code> <CRLF>
117     NLST [<SP> <pathname-or-wildcard>] <CRLF>
118     NOOP <CRLF>
119     OPTS <SP> <commandname> [ <SP> <command-options> ] <CRLF>
120     PASS <SP> <password> <CRLF>
121     PASV <CRLF>
122     PORT <SP> <host-port> <CRLF>
123     PWD  <CRLF>
124     QUIT <CRLF>
125     REIN <CRLF>
126     REST <SP> <marker> <CRLF>
127     RETR <SP> <pathname> <CRLF>
128     RMD  <SP> <pathname> <CRLF>
129     RNFR <SP> <pathname> <CRLF>
130     RNTO <SP> <pathname> <CRLF>
131     SITE <SP> <string> <CRLF>
132     SIZE <SP> <pathname> <CRLF>
133     SMNT <SP> <pathname> <CRLF>
134     STAT [<SP> <pathname>] <CRLF>
135     STOR <SP> <pathname> <CRLF>
136     STOU <CRLF>
137     STRU <SP> <structure-code> <CRLF>
138     SYST <CRLF>
139     TYPE <SP> <type-code> <CRLF>
140     USER <SP> <username> <CRLF>
141 */
142 #include "ckcsym.h"                     /* Standard includes */
143 #include "ckcdeb.h"
144
145 #ifndef NOFTP                           /* NOFTP  = no FTP */
146 #ifndef SYSFTP                          /* SYSFTP = use external ftp client */
147 #ifdef TCPSOCKET                        /* Build only if TCP/IP included */
148 #define CKCFTP_C
149
150 /* Note: much of the following duplicates what was done in ckcdeb.h */
151 /* but let's not mess with it unless it causes trouble. */
152
153 #ifdef CK_ANSIC
154 #include <stdarg.h>
155 #else /* CK_ANSIC */
156 #include <varargs.h>
157 #endif /* CK_ANSIC */
158 #include <signal.h>
159 #ifdef OS2
160 #ifdef OS2ONLY
161 #include <os2.h>
162 #endif /* OS2ONLY */
163 #include "ckowin.h"
164 #include "ckocon.h"
165 #endif /* OS2 */
166 #ifndef ZILOG
167 #ifdef NT
168 #include <setjmpex.h>
169 #ifdef NTSIG
170 extern int TlsIndex;
171 #endif /* NTSIG */
172 #else /* NT */
173 #include <setjmp.h>
174 #endif /* NT */
175 #else
176 #include <setret.h>
177 #endif /* ZILOG */
178 #include "ckcsig.h"
179 #ifdef VMS
180 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
181 #include <stat.h>
182 #else /* def VMS */
183 #include <sys/stat.h>
184 #endif /* def VMS [else] */
185 #include <ctype.h>
186 #include <errno.h>
187 #ifndef NOTIMEH
188 #include <time.h>
189 #endif /* NOTIMEH */
190 #ifndef EPIPE
191 #define EPIPE 32                        /* Broken pipe error */
192 #endif /* EPIPE */
193
194 /* Kermit includes */
195
196 #include "ckcasc.h"
197 #include "ckcker.h"
198 #include "ckucmd.h"
199 #include "ckuusr.h"
200 #include "ckcnet.h"                     /* Includes ckctel.h */
201 #include "ckctel.h"                     /* (then why include it again?) */
202 #include "ckcxla.h"
203
204 #ifdef CK_SSL
205 #include "ckuath.h"                     /* SMS 2007/02/15 */
206 #endif /* def CK_SSL */
207
208 /*
209   How to get the struct timeval definition so we can call select().  The
210   xxTIMEH symbols are defined in ckcdeb.h, overridden in various makefile
211   targets.  The problem is: maybe we have already included some header file
212   that defined struct timeval, and maybe we didn't.  If we did, we don't want
213   to include another header file that defines it again or the compilation will
214   fail.  If we didn't, we have to include the header file where it's defined.
215   But in some cases even that won't work because of strict POSIX constraints
216   or somesuch, or because this introduces other conflicts (e.g. struct tm
217   multiply defined), in which case we have to define it ourselves, but this
218   can work only if we didn't already encounter a definition.
219 */
220 #ifndef DCLTIMEVAL
221 #ifdef SV68R3V6
222 #define DCLTIMEVAL
223 #else
224 #ifdef SCO234
225 #define DCLTIMEVAL
226 #endif /* SCO234 */
227 #endif /* SV68R3V6 */
228 #endif /* DCLTIMEVAL */
229
230 #ifdef DCLTIMEVAL
231 /* Also maybe in some places the elements must be unsigned... */
232 struct timeval {
233     long tv_sec;
234     long tv_usec;
235 };
236 #ifdef COMMENT
237 /* Currently we don't use this... */
238 struct timezone {
239     int tz_minuteswest;
240     int tz_dsttime;
241 };
242 #endif /* COMMENT */
243 #else  /* !DCLTIMEVAL */
244 #ifndef NOSYSTIMEH
245 #ifdef SYSTIMEH
246 #include <sys/time.h>
247 #endif /* SYSTIMEH */
248 #endif /* NOSYSTIMEH */
249 #ifndef NOSYSTIMEBH
250 #ifdef SYSTIMEBH
251 #include <sys/timeb.h>
252 #endif /* SYSTIMEBH */
253 #endif /* NOSYSTIMEBH */
254 #endif /* DCLTIMEVAL */
255
256 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
257 #ifdef VMS
258 #include <types.h>
259 #else /* def VMS */
260 #include <sys/types.h>
261 #endif /* def VMS [else] */
262 #include <stdio.h>
263 #include <string.h>
264 #ifdef HAVE_STDLIB_H
265 #include <stdlib.h>
266 #endif /* HAVE_STDLIB_H */
267
268 #ifndef NOSETTIME
269 #ifdef COMMENT
270 /* This section moved to ckcdeb.h */
271 #ifdef POSIX
272 #define UTIMEH
273 #else
274 #ifdef HPUX9
275 #define UTIMEH
276 #else
277 #ifdef OS2
278 #define SYSUTIMEH
279 #endif /* OS2 */
280 #endif /* HPUX9 */
281 #endif /* POSIX */
282 #endif /* COMMENT */
283
284 #ifdef VMS                              /* SMS 2007/02/15 */
285 #include "ckvrtl.h"                     /* for utime() */
286 #else  /* def VMS */
287 #ifdef SYSUTIMEH
288 #include <sys/utime.h>
289 #else
290 #ifdef UTIMEH
291 #include <utime.h>
292 #define SYSUTIMEH
293 #endif /* UTIMEH */
294 #endif /* SYSUTIMEH */
295 #endif /* def VMS */
296 #endif /* NOSETTIME */
297
298 #ifndef SCO_OSR504
299 #ifdef SELECT_H
300 #include <sys/select.h>
301 #endif /* SELECT_H */
302 #endif /* SCO_OSR504 */
303
304 #ifndef INADDR_NONE                     /* 2010-03-29 */
305 #define INADDR_NONE -1
306 #endif  /* INADDR_NONE */
307
308 /* select() dialects... */
309
310 #ifdef UNIX
311 #define BSDSELECT                       /* BSD select() syntax/semantics */
312 #ifndef FD_SETSIZE
313 #define FD_SETSIZE 128
314 #endif  /* FD_SETSIZE */
315 #ifdef HPUX6                            /* For HP-UX 6.5 circa 1989 */
316 typedef long fd_mask;
317 #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
318 #ifndef howmany
319 #define howmany(x, y)   (((x)+((y)-1))/(y))
320 #endif  /* howmany */
321 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
322 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
323 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
324 #define FD_COPY(f, t)   bcopy(f, t, sizeof(*(f)))
325 #define FD_ZERO(p)      bzero(p, sizeof(*(p)))
326 #endif  /* HPUX6 */
327
328 #else
329 #ifdef OS2                              /* OS/2 or Win32 */
330 #ifdef NT
331 #define BSDSELECT
332 #else /* NT */
333 #define IBMSELECT
334 #endif /* NT */
335 #endif /* OS2 */
336 #endif /* UNIX */
337
338 #ifdef VMS
339 #define BSDSELECT                       /* SMS 2007/02/15 */
340 #endif /* def VMS */
341
342 /* Other select() peculiarities */
343
344 #ifdef HPUX
345 #ifndef HPUX10                          /* HP-UX 9.xx and earlier */
346 #ifndef HPUX1100
347 /* The three interior args to select() are (int *) rather than (fd_set *) */
348 #ifndef INTSELECT
349 #define INTSELECT
350 #endif /* INTSELECT */
351 #endif /* HPUX1100 */
352 #endif /* HPUX10 */
353 #endif /* HPUX */
354
355 #ifdef CK_SOCKS                         /* SOCKS Internet relay package */
356 #ifdef CK_SOCKS5                        /* SOCKS 5 */
357 #define accept  SOCKSaccept
358 #define bind    SOCKSbind
359 #define connect SOCKSconnect
360 #define getsockname SOCKSgetsockname
361 #define listen SOCKSlisten
362 #else  /* Not SOCKS 5 */
363 #define accept  Raccept
364 #define bind    Rbind
365 #define connect Rconnect
366 #define getsockname Rgetsockname
367 #define listen Rlisten
368 #endif /* CK_SOCKS5 */
369 #endif /* CK_SOCKS */
370
371 #ifndef NOHTTP
372 extern char * tcp_http_proxy;           /* Name[:port] of http proxy server */
373 extern int    tcp_http_proxy_errno;
374 extern char * tcp_http_proxy_user;
375 extern char * tcp_http_proxy_pwd;
376 extern char * tcp_http_proxy_agent;
377 #define HTTPCPYL 1024
378 static char proxyhost[HTTPCPYL];
379 #endif /* NOHTTP */
380 int ssl_ftp_proxy = 0;                  /* FTP over SSL/TLS Proxy Server */
381
382 /* Feature selection */
383
384 #ifndef USE_SHUTDOWN
385 /*
386   We don't use shutdown() because (a) we always call it just before close()
387   so it's redundant and unnecessary, and (b) it introduces a long pause on
388   some platforms like SV/68 R3.
389 */
390 /* #define USE_SHUTDOWN */
391 #endif /* USE_SHUTDOWN */
392
393 #ifndef NORESEND
394 #ifndef NORESTART                       /* Restart / recover */
395 #ifndef FTP_RESTART
396 #define FTP_RESTART
397 #endif /* FTP_RESTART */
398 #endif /* NORESTART */
399 #endif /* NORESEND */
400
401 #ifndef NOUPDATE                        /* Update mode */
402 #ifndef DOUPDATE
403 #define DOUPDATE
404 #endif /* DOUPDATE */
405 #endif /* NOUPDATE */
406
407 #ifndef UNICODE                         /* Unicode required */
408 #ifndef NOCSETS                         /* for charset translation */
409 #define NOCSETS
410 #endif /* NOCSETS */
411 #endif /* UNICODE */
412
413 #ifndef OS2
414 #ifndef HAVE_MSECS                      /* Millisecond timer */
415 #ifdef UNIX
416 #ifdef GFTIMER
417 #define HAVE_MSECS
418 #endif /* GFTIMER */
419 #endif /* UNIX */
420 #endif /* HAVE_MSECS */
421 #endif /* OS2 */
422
423 #ifdef PIPESEND                         /* PUT from pipe */
424 #ifndef PUTPIPE
425 #define PUTPIPE
426 #endif /* PUTPIPE */
427 #endif /* PIPESEND */
428
429 #ifndef NOSPL                           /* PUT from array */
430 #ifndef PUTARRAY
431 #define PUTARRAY
432 #endif /* PUTARRAY */
433 #endif /* NOSPL */
434
435 /* Security... */
436
437 #ifdef CK_SRP
438 #define FTP_SRP
439 #endif /* CK_SRP */
440
441 #ifdef CK_KERBEROS
442 #ifdef KRB4
443 /*
444   There is a conflict between the Key Schedule formats used internally
445   within the standalone MIT KRB4 library and that used by Eric Young
446   in OpenSSL and his standalone DES library.  Therefore, KRB4 FTP AUTH
447   cannot be supported when either of those two packages are used.
448 */
449 #ifdef KRB524
450 #define FTP_KRB4
451 #else /* KRB524 */
452 #ifndef CK_SSL
453 #ifndef LIBDES
454 #define FTP_KRB4
455 #endif /* LIBDES */
456 #endif /* CK_SSL */
457 #endif /* KRB524 */
458 #endif /* KRB4 */
459 #ifdef KRB5
460 #ifndef HEIMDAL
461 #ifndef NOFTP_GSSAPI                    /* 299 */
462 #define FTP_GSSAPI
463 #endif  /* NOFTP_GSSAPI */
464 #endif /* HEIMDAL */
465 #endif /* KRB5 */
466 #endif /* CK_KERBEROS */
467
468 /* FTP_SECURITY is defined if any of the above is selected */
469 #ifndef FTP_SECURITY
470 #ifdef FTP_GSSAPI
471 #define FTP_SECURITY
472 #else
473 #ifdef FTP_KRB4
474 #define FTP_SECURITY
475 #else
476 #ifdef FTP_SRP
477 #define FTP_SECURITY
478 #else
479 #ifdef CK_SSL
480 #define FTP_SECURITY
481 #endif /* CK_SSL */
482 #endif /* FTP_SRP */
483 #endif /* FTP_KRB4 */
484 #endif /* FTP_GSSAPI */
485 #endif /* FTP_SECURITY */
486
487 #ifdef CK_DES
488 #ifdef CK_SSL
489 #ifndef LIBDES
490 #define LIBDES
491 #endif /* LIBDES */
492 #endif /* CK_SSL */
493 #endif /* CK_DES */
494
495 #ifdef CRYPT_DLL
496 #ifndef LIBDES
497 #define LIBDES
498 #endif /* LIBDES */
499 #endif /* CRYPT_DLL */
500
501 #ifdef FTP_KRB4
502 #define des_cblock Block
503 #define des_key_schedule Schedule
504 #ifdef KRB524
505 #ifdef NT
506 #define _WINDOWS
507 #endif /* NT */
508 #include "kerberosIV/krb.h"
509 #else /* KRB524 */
510 #ifdef SOLARIS
511 #ifndef sun
512 /* For some reason lost in history the Makefile Solaris targets have -Usun */
513 #define sun
514 #endif /* sun */
515 #endif /* SOLARIS */
516 #include "krb.h"
517 #define krb_get_err_text_entry krb_get_err_text
518 #endif /* KRB524 */
519 #endif /* FTP_KRB4 */
520
521 #ifdef CK_SSL
522 #ifdef FTP_KRB4
523 #ifndef HEADER_DES_H
524 #define HEADER_DES_H
525 #endif /* HEADER_DES_H */
526 #endif /* FTP_KRB4 */
527 #include "ck_ssl.h"
528 #endif /* CK_SSL */
529
530 #ifdef FTP_SRP
531 #ifdef HAVE_PWD_H
532 #include "pwd.h"
533 #endif /* HAVE_PWD_H */
534 #include "t_pwd.h"
535 #include "t_client.h"
536 #include "krypto.h"
537 #endif /* FTP_SRP */
538
539 #ifdef FTP_GSSAPI
540 #include <gssapi/gssapi.h>
541 /*
542   Need to include the krb5 file, because we're doing manual fallback
543   from the v2 mech to the v1 mech.  Once there's real negotiation,
544   we can be generic again.
545 */
546 #include <gssapi/gssapi_generic.h>
547 #include <gssapi/gssapi_krb5.h>
548 static gss_ctx_id_t gcontext;
549
550 #ifdef MACOSX
551 /** exported constants defined in gssapi_krb5{,_nx}.h **/
552
553 /* these are bogus, but will compile */
554
555 /*
556  * The OID of the draft krb5 mechanism, assigned by IETF, is:
557  *      iso(1) org(3) dod(5) internet(1) security(5)
558  *      kerberosv5(2) = 1.3.5.1.5.2
559  * The OID of the krb5_name type is:
560  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
561  *      krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1
562  * The OID of the krb5_principal type is:
563  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
564  *      krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2
565  * The OID of the proposed standard krb5 mechanism is:
566  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
567  *      krb5(2) = 1.2.840.113554.1.2.2
568  * The OID of the proposed standard krb5 v2 mechanism is:
569  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
570  *      krb5v2(3) = 1.2.840.113554.1.2.3
571  *
572  */
573
574 /*
575  * Encoding rules: The first two values are encoded in one byte as 40
576  * * value1 + value2.  Subsequent values are encoded base 128, most
577  * significant digit first, with the high bit (\200) set on all octets
578  * except the last in each value's encoding.
579  */
580
581 static CONST gss_OID_desc
582 ck_krb5_gss_oid_array[] = {
583    /* this is the official, rfc-specified OID */
584    {9, "\052\206\110\206\367\022\001\002\002"},
585    /* this is the unofficial, wrong OID */
586    {5, "\053\005\001\005\002"},
587    /* this is the v2 assigned OID */
588    {9, "\052\206\110\206\367\022\001\002\003"},
589    /* these two are name type OID's */
590    {10, "\052\206\110\206\367\022\001\002\002\001"},
591    {10, "\052\206\110\206\367\022\001\002\002\002"},
592    { 0, 0 }
593 };
594
595 static
596 CONST gss_OID_desc * CONST gss_mech_krb5_v2 = ck_krb5_gss_oid_array+2;
597
598 #ifdef MACOSX103
599 static
600 CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0;
601 #endif /* MACOSX103 */
602
603 #ifndef MACOSX
604 static
605 CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0;
606 static
607 CONST gss_OID_desc * CONST gss_mech_krb5_old = ck_krb5_gss_oid_array+1;
608 static
609 CONST gss_OID_desc * CONST gss_nt_krb5_name = ck_krb5_gss_oid_array+3;
610 static
611 CONST gss_OID_desc * CONST gss_nt_krb5_principal = ck_krb5_gss_oid_array+4;
612 #endif  /* MACOSX */
613
614 /*
615  * See krb5/gssapi_krb5.c for a description of the algorithm for
616  * encoding an object identifier.
617  */
618
619 /*
620  * The OID of user_name is:
621  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
622  *      generic(1) user_name(1) = 1.2.840.113554.1.2.1.1
623  * machine_uid_name:
624  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
625  *      generic(1) machine_uid_name(2) = 1.2.840.113554.1.2.1.2
626  * string_uid_name:
627  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
628  *      generic(1) string_uid_name(3) = 1.2.840.113554.1.2.1.3
629  * service_name:
630  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
631  *      generic(1) service_name(4) = 1.2.840.113554.1.2.1.4
632  * exported_name:
633  *      1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes),
634  *          4(gss-api-exported-name)
635  * host_based_service_name (v2):
636  *      iso (1) org (3), dod (6), internet (1), security (5), nametypes(6),
637  *      gss-host-based-services(2)
638  */
639
640 static gss_OID_desc ck_oids[] = {
641    {10, "\052\206\110\206\367\022\001\002\001\001"},
642    {10, "\052\206\110\206\367\022\001\002\001\002"},
643    {10, "\052\206\110\206\367\022\001\002\001\003"},
644    {10, "\052\206\110\206\367\022\001\002\001\004"},
645    { 6, "\053\006\001\005\006\004"},
646    { 6, "\053\006\001\005\006\002"},
647 };
648
649 static gss_OID ck_gss_nt_user_name = ck_oids+0;
650 static gss_OID ck_gss_nt_machine_uid_name = ck_oids+1;
651 static gss_OID ck_gss_nt_string_uid_name = ck_oids+2;
652 static gss_OID ck_gss_nt_service_name = ck_oids+3;
653 static gss_OID ck_gss_nt_exported_name = ck_oids+4;
654 static gss_OID ck_gss_nt_service_name_v2 = ck_oids+5;
655 #endif /* MACOSX */
656 #endif /* FTP_GSSAPI */
657
658 #ifdef OS2
659 #ifdef FTP_SRP
660 #define MAP_KRYPTO
661 #ifdef SRPDLL
662 #define MAP_SRP
663 #endif /* SRPDLL */
664 #endif /* FTP_SRP */
665 #ifdef FTP_KRB4
666 #define MAP_KRB4
667 #ifdef CK_ENCRYPTION
668 #define MAP_DES
669 #endif /* CK_ENCRYPTION */
670 #endif /* FTP_KRB4 */
671 #ifdef FTP_GSSAPI
672 #define MAP_GSSAPI
673 #define GSS_OIDS
674 #endif /* FTP_GSSAPI */
675 #include "ckoath.h"
676
677 extern int k95stdout, wherex[], wherey[];
678 extern unsigned char colorcmd;
679 #endif /* OS2 */
680
681 #ifdef FTP_KRB4
682 static char ftp_realm[REALM_SZ + 1];
683 static KTEXT_ST ftp_tkt;
684 #ifdef OS2
685 static LEASH_CREDENTIALS ftp_cred;
686 #else /* OS2 */
687 static CREDENTIALS ftp_cred;
688 #endif /* OS2 */
689 static MSG_DAT ftp_msg_data;
690 static des_key_schedule ftp_sched;
691 static int foo[4] = {99,99,99,99};
692 #endif /* FTP_KRB4 */
693
694 /* getreply() function codes */
695
696 #define GRF_AUTH 1                      /* Reply to AUTH command */
697 #define GRF_FEAT 2                      /* Reply to FEAT command */
698
699 /* Operational definitions */
700
701 #define DEF_VBM 0                       /* Default verbose mode */
702 /* #define SETVBM */                    /* (see getreply) */
703
704 #define URL_ONEFILE                     /* GET, not MGET, for FTP URL */
705
706 #define FTP_BUFSIZ 10240                /* Max size for FTP cmds & replies */
707 #define SRVNAMLEN 32                    /* Max length for server type name */
708 #define PWDSIZ 256
709 #define PASSBUFSIZ 256
710 #define PROMPTSIZ 256
711
712 #ifndef MGETMAX                         /* Max operands for MGET command */
713 #define MGETMAX 1000
714 #endif /* MGETMAX */
715
716 #ifdef FTP_SRP
717 #define FUDGE_FACTOR 100
718 #endif /* FTP_SRP */
719
720 /*
721   Amount of growth from cleartext to ciphertext.  krb_mk_priv adds this
722   number bytes.  Must be defined for each auth type.
723   GSSAPI appears to add 52 bytes, but I'm not sure it is a constant--hartmans
724   3DES requires 56 bytes.  Lets use 96 just to be sure.
725 */
726 #ifdef FTP_GSSAPI
727 #ifndef FUDGE_FACTOR
728 #define FUDGE_FACTOR 96
729 #endif /* FUDGE_FACTOR */
730 #endif /* FTP_GSSAPI */
731
732 #ifdef FTP_KRB4
733 #ifndef FUDGE_FACTOR
734 #define FUDGE_FACTOR 32
735 #endif /* FUDGE_FACTOR */
736 #endif /* FTP_KRB4 */
737
738 #ifndef FUDGE_FACTOR                    /* In case no auth types define it */
739 #define FUDGE_FACTOR 0
740 #endif /* FUDGE_FACTOR */
741
742 #ifndef MAXHOSTNAMELEN
743 #define MAXHOSTNAMELEN 64
744 #endif /* MAXHOSTNAMELEN */
745 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
746
747 /* Fascist compiler toadying */
748
749 #ifndef SENDARG2TYPE
750 #ifdef COMMENT                          /* Might be needed here and there */
751 #define SENDARG2TYPE const char *
752 #else
753 #define SENDARG2TYPE char *
754 #endif /* COMMENT */
755 #endif /* SENDARG2TYPE */
756
757 /* Common text messages */
758
759 static char *nocx = "?No FTP control connection\n";
760
761 static char *fncnam[] = {
762   "rename", "overwrite", "backup", "append", "discard", "ask", "update",
763   "dates-differ", ""
764 };
765
766 /* Macro definitions */
767
768 /* Used to speed up text-mode PUTs */
769 #define zzout(fd,c) \
770 ((fd<0)?(-1):((nout>=ucbufsiz)?(zzsend(fd,c)):(ucbuf[nout++]=c)))
771
772 #define CHECKCONN() if(!connected){printf(nocx);return(-9);}
773
774 /* Externals */
775
776 #ifdef CK_URL
777 extern struct urldata g_url;
778 #endif /* CK_URL */
779
780 #ifdef DYNAMIC
781 extern char *zinbuffer, *zoutbuffer;    /* Regular Kermit file i/o */
782 #else
783 extern char zinbuffer[], zoutbuffer[];
784 #endif /* DYNAMIC */
785 extern char *zinptr, *zoutptr;
786 extern int zincnt, zoutcnt, zobufsize, fncact;
787
788 #ifdef CK_TMPDIR
789 extern int f_tmpdir;                    /* Directory changed temporarily */
790 extern char savdir[];                   /* For saving current directory */
791 extern char * dldir;
792 #endif /* CK_TMPDIR */
793
794 extern char * rfspec, * sfspec, * srfspec, * rrfspec; /* For WHERE command */
795
796 extern xx_strp xxstring;
797 extern struct keytab onoff[], txtbin[], rpathtab[];
798 extern int nrpathtab, xfiletype, patterns, gnferror, moving, what, pktnum;
799 extern int success, nfils, sndsrc, quiet, nopush, recursive, inserver, binary;
800 extern int filepeek, nscanfile, fsecs, xferstat, xfermode, lastxfer, tsecs;
801 extern int backgrd, spackets, rpackets, spktl, rpktl, xaskmore, cmd_rows;
802 extern int nolinks, msgflg, keep;
803 extern CK_OFF_T fsize, ffc, tfc, sendstart, sndsmaller, sndlarger, rs_len;
804 extern long filcnt, xfsecs, tfcps, cps, oldcps;
805
806 #ifdef FTP_TIMEOUT
807 int ftp_timed_out = 0;
808 long ftp_timeout = 0;
809 #endif  /* FTP_TIMEOUT */
810
811 #ifdef GFTIMER
812 extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
813 #else
814 extern long xfsecs;
815 #endif /* GFTIMER */
816
817 extern char filnam[], * filefile, myhost[];
818 extern char * snd_move, * rcv_move, * snd_rename, * rcv_rename;
819 extern int g_skipbup, skipbup, sendmode;
820 extern int g_displa, fdispla, displa;
821
822 #ifdef LOCUS
823 extern int locus, autolocus;
824 #endif /* LOCUS */
825
826 #ifndef NOCSETS
827 extern int nfilc, dcset7, dcset8, fileorder;
828 extern struct csinfo fcsinfo[];
829 extern struct keytab fcstab[];
830 extern int fcharset;
831 #endif /* NOCSETS */
832
833 extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */
834 extern char sndnbefore[], sndnafter[], *rcvexcept[];
835 extern CHAR feol;
836
837 extern char * remdest;
838 extern int remfile, remappd, rempipe;
839
840 #ifndef NOSPL
841 extern int cmd_quoting;
842 #ifdef PUTARRAY
843 extern int sndxlo, sndxhi, sndxin;
844 extern char sndxnam[];
845 extern char **a_ptr[];                  /* Array pointers */
846 extern int a_dim[];                     /* Array dimensions */
847 #endif /* PUTARRAY */
848 #endif /* NOSPL */
849
850 #ifndef NOMSEND                         /* MPUT and ADD SEND-LIST lists */
851 extern char *msfiles[];
852 extern int filesinlist;
853 extern struct filelist * filehead;
854 extern struct filelist * filetail;
855 extern struct filelist * filenext;
856 extern int addlist;
857 extern char fspec[];                    /* Most recent filespec */
858 extern int fspeclen;                    /* Length of fspec[] buffer */
859 #endif /* NOMSEND */
860
861 extern int pipesend;
862 #ifdef PIPESEND
863 extern char * sndfilter, * rcvfilter;
864 #endif /* PIPESEND */
865
866 #ifdef CKROOT
867 extern int ckrooterr;
868 #endif /* CKROOT */
869
870 #ifdef KRB4
871 extern int krb4_autoget;
872 _PROTOTYP(char * ck_krb4_realmofhost,(char *));
873 #endif /* KRB4 */
874
875 #ifdef KRB5
876 extern int krb5_autoget;
877 extern int krb5_d_no_addresses;
878 _PROTOTYP(char * ck_krb5_realmofhost,(char *));
879 #endif /* KRB5 */
880
881 #ifdef DCMDBUF
882 extern char *atmbuf;                    /* Atom buffer (malloc'd) */
883 extern char *cmdbuf;                    /* Command buffer (malloc'd) */
884 extern char *line;                      /* Big string buffer #1 */
885 extern char *tmpbuf;                    /* Big string buffer #2 */
886 #else
887 extern char atmbuf[];                   /* The same, but static */
888 extern char cmdbuf[];
889 extern char line[];
890 extern char tmpbuf[];
891 #endif /* DCMDBUF */
892
893 extern char * cmarg, * cmarg2, ** cmlist; /* For setting up file lists */
894
895 /* Public variables declared here */
896
897 #ifdef NOXFER
898 int ftpget  =  1;                       /* GET/PUT/REMOTE orientation FTP */
899 #else
900 int ftpget  =  2;                       /* GET/PUT/REMOTE orientation AUTO */
901 #endif /* NOXFER */
902 int ftpcode = -1;                       /* Last FTP response code */
903 int ftp_cmdlin = 0;                     /* FTP invoked from command line */
904 int ftp_fai = 0;                        /* FTP failure count */
905 int ftp_deb = 0;                        /* FTP debugging */
906 int ftp_dis = -1;                       /* FTP display style */
907 int ftp_log = 1;                        /* FTP Auto-login */
908 int sav_log = -1;
909 int ftp_action = 0;                     /* FTP action from command line */
910 int ftp_dates = 1;                      /* Set file dates from server */
911 int ftp_xfermode = XMODE_A;             /* FTP-specific transfer mode */
912
913 char ftp_reply_str[FTP_BUFSIZ] = "";    /* Last line of previous reply */
914 char ftp_srvtyp[SRVNAMLEN] = { NUL, NUL }; /* Server's system type */
915 char ftp_user_host[MAX_DNS_NAMELEN]= ""; /* FTP hostname specified by user */
916 char * ftp_host = NULL;                 /* FTP hostname */
917 char * ftp_logname = NULL;              /* FTP username */
918 char * ftp_rdir = NULL;                 /* Remote directory from cmdline */
919 char * ftp_apw = NULL;                  /* Anonymous password */
920
921 /* Definitions and typedefs needed for prototypes */
922
923 #define sig_t my_sig_t
924 #define sigtype SIGTYP
925 typedef sigtype (*sig_t)();
926
927 /* Static global variables */
928
929 static char ftpsndbuf[FTP_BUFSIZ+64];
930
931 static char * fts_sto = NULL;
932
933 static int ftpsndret = 0;
934 static struct _ftpsnd {
935     sig_t oldintr, oldintp;
936     int            reply;
937     int            incs,
938                    outcs;
939     char *         cmd, * local, * remote;
940     int            bytes;
941     int            restart;
942     int            xlate;
943     char *         lmode;
944 } ftpsnd;
945
946 /*
947   This is just a first stab -- these strings should match how the
948   corresponding FTP servers identify themselves.
949 */
950 #ifdef UNIX
951 static char * myostype = "UNIX";
952 #else
953 #ifdef VMS
954 /* not yet... */
955 static char * myostype = "VMS";
956 #else
957 #ifdef OS2
958 #ifdef NT
959 static char * myostype = "WIN32";
960 #else
961 static char * myostype = "OS/2";
962 #endif /* NT */
963 #else
964 static char * myostype = "UNSUPPORTED";
965 #endif /* OS2  */
966 #endif /* VMS */
967 #endif /* UNIX */
968
969 static int noinit = 0;                  /* Don't send REST, STRU, MODE */
970 static int alike = 0;                   /* Client/server like platforms */
971 static int local = 1;                   /* Shadows Kermit global 'local' */
972 static int dout = -1;                   /* Data connection file descriptor */
973 static int dpyactive = 0;               /* Data transfer is active */
974 static int globaldin = -1;              /* Data connection f.d. */
975 static int out2screen = 0;              /* GET output is to screen */
976 static int forcetype = 0;               /* Force text or binary mode */
977 static int cancelfile = 0;              /* File canceled */
978 static int cancelgroup = 0;             /* Group canceled */
979 static int anonymous = 0;               /* Logging in as anonymous */
980 static int loggedin = 0;                /* Logged in (or not) */
981 static int puterror = 0;                /* What to do on PUT error */
982 static int geterror = 0;                /* What to do on GET error */
983 static int rfrc = 0;                    /* remote_files() return code */
984 static int okrestart = 0;               /* Server understands REST */
985 static int printlines = 0;              /* getreply()should print data lines */
986 static int haveurl = 0;                 /* Invoked by command-line FTP URL */
987 static int mdtmok = 1;                  /* Server supports MDTM */
988 static int sizeok = 1;
989 static int featok = 1;
990 static int mlstok = 1;
991 static int stouarg = 1;
992 static int typesent = 0;
993 static int havesigint = 0;
994 static long havetype =  0;
995 static CK_OFF_T havesize = (CK_OFF_T)-1;
996 static char * havemdtm = NULL;
997 static int mgetmethod = 0;              /* NLST or MLSD */
998 static int mgetforced = 0;
999
1000 static int i, /* j, k, */ x, y, z;      /* Volatile temporaries */
1001 static int c0, c1;                      /* Temp variables for characters */
1002
1003 static char putpath[CKMAXPATH+1] = { NUL, NUL };
1004 static char asnambuf[CKMAXPATH+1] = { NUL, NUL };
1005
1006 #define RFNBUFSIZ 4096                  /* Remote filename buffer size */
1007
1008 static unsigned int maxbuf = 0, actualbuf = 0;
1009 static CHAR *ucbuf = NULL;
1010 static int ucbufsiz = 0;
1011 static unsigned int nout = 0;           /* Number of chars in ucbuf */
1012
1013 static jmp_buf recvcancel;
1014 static jmp_buf sendcancel;
1015 static jmp_buf ptcancel;
1016 static jmp_buf jcancel;
1017 static int ptabflg = 0;
1018
1019 /* Protection level symbols */
1020
1021 #define FPL_CLR 1                       /* Clear */
1022 #define FPL_SAF 2                       /* Safe */
1023 #define FPL_PRV 3                       /* Private */
1024 #define FPL_CON 4                       /* Confidential */
1025
1026 /* Symbols for file types returned by MLST/MLSD */
1027
1028 #define FTYP_FILE 1                     /* Regular file */
1029 #define FTYP_DIR  2                     /* Directory */
1030 #define FTYP_CDIR 3                     /* Current directory */
1031 #define FTYP_PDIR 4                     /* Parent directory */
1032
1033 /* File type symbols keyed to the file-type symbols from ckcker.h */
1034
1035 #define FTT_ASC XYFT_T                  /* ASCII (text) */
1036 #define FTT_BIN XYFT_B                  /* Binary (image) */
1037 #define FTT_TEN XYFT_X                  /* TENEX (TOPS-20) */
1038
1039 /* Server feature table - sfttab[0] > 0 means server supports FEAT and OPTS */
1040
1041 static int sfttab[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1042
1043 #define SFT_AUTH  1                     /* FTP server feature codes */
1044 #define SFT_LANG  2
1045 #define SFT_MDTM  3
1046 #define SFT_MLST  4
1047 #define SFT_PBSZ  5
1048 #define SFT_PROT  6
1049 #define SFT_REST  7
1050 #define SFT_SIZE  8
1051 #define SFT_TVFS  9
1052 #define SFT_UTF8 10
1053
1054 #define CNV_AUTO  2                     /* FTP filename conversion */
1055 #define CNV_CNV   1
1056 #define CNV_LIT   0
1057
1058 /* SET FTP values */
1059
1060 static int                              /* SET FTP values... */
1061   ftp_aut = 1,                          /* Auto-authentication */
1062 #ifdef FTP_SECURITY
1063   ftp_cry = 1,                          /* Auto-encryption */
1064   ftp_cfw = 0,                          /* Credential forwarding */
1065 #endif /* FTP_SECURITY */
1066   ftp_cpl = FPL_CLR,                    /* Command protection level */
1067   ftp_dpl = FPL_CLR,                    /* Data protection level */
1068 #ifdef FTP_PROXY
1069   ftp_prx = 0,                          /* Use proxy */
1070 #endif /* FTP_PROXY */
1071   sav_psv = -1,                         /* For saving passive mode */
1072   ftp_psv = 1,                          /* Passive mode */
1073   ftp_spc = 1,                          /* Send port commands */
1074   ftp_typ = FTT_ASC,                    /* Type */
1075   get_auto = 1,                         /* Automatic type switching for GET */
1076   tenex = 0,                            /* Type is Tenex */
1077   ftp_usn = 0,                          /* Unique server names */
1078   ftp_prm = 0,                          /* Permissions */
1079   ftp_cnv = CNV_AUTO,                   /* Filename conversion (2 = auto) */
1080   ftp_vbm = DEF_VBM,                    /* Verbose mode */
1081   ftp_vbx = DEF_VBM,                    /* Sticky version of same */
1082   ftp_err = 0,                          /* Error action */
1083   ftp_fnc = -1;                         /* Filename collision action */
1084
1085 #ifdef CK_SSL
1086 static int ftp_bug_use_ssl_v2 = 0;      /* use SSLv2 for AUTH SSL */
1087 #endif /* CK_SSL */
1088
1089 static int
1090 #ifdef NOCSETS
1091   ftp_csr = -1,                         /* Remote (server) character set */
1092 #else
1093   ftp_csr = FC_UTF8,
1094 #endif /* NOCSETS */
1095   ftp_xla = 0;                          /* Character-set translation on/off */
1096 int
1097   ftp_csx = -1,                         /* Remote charset currently in use */
1098   ftp_csl = -1;                         /* Local charset currently in use */
1099
1100 static int g_ftp_typ = FTT_ASC;         /* For saving and restoring ftp_typ */
1101
1102 char * ftp_nml = NULL;                  /* /NAMELIST */
1103 char * ftp_tmp = NULL;                  /* Temporary string */
1104 static char * ftp_acc = NULL;           /* Account string */
1105 static char * auth_type = NULL;         /* Authentication type */
1106 static char * srv_renam = NULL;         /* Server-rename string */
1107 FILE * fp_nml = NULL;                   /* Namelist file pointer */
1108
1109 static int csocket = -1;                /* Control socket */
1110 static int connected = 0;               /* Connected to FTP server */
1111 /* static unsigned short ftp_port = 0; */ /* FTP port */ 
1112 /* static int ftp_port = 0; */          /* SMS 2007/02/15 */
1113 static int ftp_port = 0;                /* fdc 2007/08/30 */
1114 #ifdef FTPHOST
1115 static int hostcmd = 0;                 /* Has HOST command been sent */
1116 #endif /* FTPHOST */
1117 static int form, mode, stru, bytesize, curtype = FTT_ASC;
1118 static char bytename[8];
1119
1120 /* For parsing replies to FTP server command */
1121 static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
1122
1123 #ifdef FTP_PROXY
1124 static int proxy, unix_proxy
1125 #endif /* FTP_PROXY */
1126
1127 static char pasv[64];                   /* Passive-mode port */
1128 static int passivemode = 0;
1129 static int sendport = 0;
1130 static int servertype = 0;              /* FTP server's OS type */
1131
1132 static int testing = 0;
1133 static char ftpcmdbuf[FTP_BUFSIZ];
1134
1135 /* Macro definitions */
1136
1137 #define UC(b) ckitoa(((int)b)&0xff)
1138 #define nz(x) ((x) == 0 ? 1 : (x))
1139
1140 /* Command tables and definitions */
1141
1142 #define FTP_ACC  1                      /* FTP command keyword codes */
1143 #define FTP_APP  2
1144 #define FTP_CWD  3
1145 #define FTP_CHM  4
1146 #define FTP_CLS  5
1147 #define FTP_DEL  6
1148 #define FTP_DIR  7
1149 #define FTP_GET  8
1150 #define FTP_IDL  9
1151 #define FTP_MDE 10
1152 #define FTP_MDI 11
1153 #define FTP_MGE 12
1154 #define FTP_MKD 13
1155 #define FTP_MOD 14
1156 #define FTP_MPU 15
1157 #define FTP_OPN 16
1158 #define FTP_PUT 17
1159 #define FTP_PWD 18
1160 #define FTP_RGE 19
1161 #define FTP_REN 20
1162 #define FTP_RES 21
1163 #define FTP_HLP 22
1164 #define FTP_RMD 23
1165 #define FTP_STA 24
1166 #define FTP_SIT 25
1167 #define FTP_SIZ 26
1168 #define FTP_SYS 27
1169 #define FTP_UMA 28
1170 #define FTP_GUP 29
1171 #define FTP_USR 30
1172 #define FTP_QUO 31
1173 #define FTP_TYP 32
1174 #define FTP_FEA 33
1175 #define FTP_OPT 34
1176 #define FTP_CHK 35
1177 #define FTP_VDI 36
1178 #define FTP_ENA 37
1179 #define FTP_DIS 38
1180 #define FTP_REP 39
1181
1182 struct keytab gprtab[] = {              /* GET-PUT-REMOTE keywords */
1183     { "auto",    2, 0 },
1184     { "ftp",     1, 0 },
1185     { "kermit",  0, 0  }
1186 };
1187
1188 static struct keytab qorp[] = {         /* QUIT or PROCEED keywords */
1189     { "proceed", 0, 0 },                /* 0 = proceed */
1190     { "quit",    1, 0 }                 /* 1 = quit */
1191 };
1192
1193 static struct keytab ftpcmdtab[] = {    /* FTP command table */
1194     { "account",   FTP_ACC, 0 },
1195     { "append",    FTP_APP, 0 },
1196     { "bye",       FTP_CLS, 0 },
1197     { "cd",        FTP_CWD, 0 },
1198     { "cdup",      FTP_GUP, 0 },
1199     { "check",     FTP_CHK, 0 },
1200     { "chmod",     FTP_CHM, 0 },
1201     { "close",     FTP_CLS, 0 },
1202     { "cwd",       FTP_CWD, CM_INV },
1203     { "delete",    FTP_MDE, 0 },
1204     { "directory", FTP_DIR, 0 },
1205     { "disable",   FTP_DIS, 0 },
1206     { "enable",    FTP_ENA, 0 },
1207     { "features",  FTP_FEA, 0 },
1208     { "get",       FTP_GET, 0 },
1209     { "help",      FTP_HLP, 0 },
1210     { "idle",      FTP_IDL, 0 },
1211     { "login",     FTP_USR, CM_INV },
1212     { "mdelete",   FTP_MDE, CM_INV },
1213     { "mget",      FTP_MGE, 0 },
1214     { "mkdir",     FTP_MKD, 0 },
1215     { "modtime",   FTP_MOD, 0 },
1216     { "mput",      FTP_MPU, 0 },
1217     { "open",      FTP_OPN, 0 },
1218     { "opt",       FTP_OPT, CM_INV|CM_ABR },
1219     { "opts",      FTP_OPT, CM_INV },
1220     { "options",   FTP_OPT, 0 },
1221     { "put",       FTP_PUT, 0 },
1222     { "pwd",       FTP_PWD, 0 },
1223     { "quit",      FTP_CLS, CM_INV },
1224     { "quote",     FTP_QUO, 0 },
1225     { "reget",     FTP_RGE, 0 },
1226     { "rename",    FTP_REN, 0 },
1227     { "reput",     FTP_REP, 0 },
1228     { "resend",    FTP_REP, CM_INV },
1229     { "reset",     FTP_RES, 0 },
1230     { "rmdir",     FTP_RMD, 0 },
1231     { "send",      FTP_PUT, CM_INV },
1232     { "site",      FTP_SIT, 0 },
1233     { "size",      FTP_SIZ, 0 },
1234     { "status",    FTP_STA, 0 },
1235     { "system",    FTP_SYS, 0 },
1236     { "type",      FTP_TYP, 0 },
1237     { "umask",     FTP_UMA, 0 },
1238     { "up",        FTP_GUP, CM_INV },
1239     { "user",      FTP_USR, 0 },
1240     { "vdirectory",FTP_VDI, 0 },
1241     { "", 0, 0 }
1242 };
1243 static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1;
1244
1245 #define OPN_ANO 1                       /* FTP OPEN switch codes */
1246 #define OPN_PSW 2
1247 #define OPN_USR 3
1248 #define OPN_ACC 4
1249 #define OPN_ACT 5
1250 #define OPN_PSV 6
1251 #define OPN_TLS 7
1252 #define OPN_NIN 8
1253 #define OPN_NOL 9
1254
1255 #ifdef FTP_SECURITY
1256 #ifdef CK_SSL
1257 #define USETLSTAB
1258 static struct keytab tlstab[] = {       /* FTP SSL/TLS switches */
1259     { "/ssl",       OPN_TLS, 0    },
1260     { "/tls",       OPN_TLS, 0    },
1261     { "", 0, 0 }
1262 };
1263 static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1;
1264 #endif /* CK_SSL */
1265 #endif /* FTP_SECURITY */
1266
1267 static struct keytab ftpswitab[] = {    /* FTP command switches */
1268     { "/account",   OPN_ACC, CM_ARG },
1269     { "/active",    OPN_ACT, 0      },
1270     { "/anonymous", OPN_ANO, 0      },
1271     { "/noinit",    OPN_NIN, 0      },
1272     { "/nologin",   OPN_NOL, 0      },
1273     { "/passive",   OPN_PSV, 0      },
1274     { "/password",  OPN_PSW, CM_ARG },
1275     { "/user",      OPN_USR, CM_ARG },
1276     { "", 0, 0 }
1277 };
1278 static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1;
1279
1280 /* FTP { ENABLE, DISABLE } items */
1281
1282 #define ENA_FEAT 1
1283 #define ENA_MDTM 2
1284 #define ENA_MLST 3
1285 #define ENA_SIZE 4
1286 #define ENA_AUTH 5
1287
1288 static struct keytab ftpenatab[] = {
1289     { "AUTH",  ENA_AUTH, 0 },
1290     { "FEAT",  ENA_FEAT, 0 },
1291     { "MDTM",  ENA_MDTM, 0 },
1292     { "ML",    ENA_MLST, CM_INV|CM_ABR },
1293     { "MLS",   ENA_MLST, CM_INV|CM_ABR },
1294     { "MLSD",  ENA_MLST, CM_INV },
1295     { "MLST",  ENA_MLST, 0 },
1296     { "SIZE",  ENA_SIZE, 0 },
1297     { "", 0, 0 }
1298 };
1299 static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1;
1300
1301 /* SET FTP command keyword indices */
1302
1303 #define FTS_AUT  1                      /* Autoauthentication */
1304 #define FTS_CRY  2                      /* Encryption */
1305 #define FTS_LOG  3                      /* Autologin */
1306 #define FTS_CPL  4                      /* Command protection level */
1307 #define FTS_CFW  5                      /* Credentials forwarding */
1308 #define FTS_DPL  6                      /* Data protection level */
1309 #define FTS_DBG  7                      /* Debugging */
1310 #define FTS_PSV  8                      /* Passive mode */
1311 #define FTS_SPC  9                      /* Send port commands */
1312 #define FTS_TYP 10                      /* (file) Type */
1313 #define FTS_USN 11                      /* Unique server names (for files) */
1314 #define FTS_VBM 12                      /* Verbose mode */
1315 #define FTS_ATP 13                      /* Authentication type */
1316 #define FTS_CNV 14                      /* Filename conversion */
1317 #define FTS_TST 15                      /* Test (progress) messages */
1318 #define FTS_PRM 16                      /* (file) Permissions */
1319 #define FTS_XLA 17                      /* Charset translation */
1320 #define FTS_CSR 18                      /* Server charset */
1321 #define FTS_ERR 19                      /* Error action */
1322 #define FTS_FNC 20                      /* Collision */
1323 #define FTS_SRP 21                      /* SRP options */
1324 #define FTS_GFT 22                      /* GET automatic file-type switching */
1325 #define FTS_DAT 23                      /* Set file dates */
1326 #define FTS_STO 24                      /* Server time offset */
1327 #define FTS_APW 25                      /* Anonymous password */
1328 #define FTS_DIS 26                      /* File-transfer display style */
1329 #define FTS_BUG 27                      /* Bug(s) */
1330 #define FTS_TMO 28                      /* Timeout */
1331
1332 /* FTP BUGS */
1333
1334 #define FTB_SV2  1                      /* use SSLv2 */
1335
1336 static struct keytab ftpbugtab[] = {
1337     { "use-ssl-v2",     FTB_SV2,        0 }
1338 };
1339 static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab));
1340
1341 /* FTP PUT options (mutually exclusive, not a bitmask) */
1342
1343 #define PUT_UPD 1                       /* Update */
1344 #define PUT_RES 2                       /* Restart */
1345 #define PUT_SIM 4                       /* Simulation */
1346 #define PUT_DIF 8                       /* Dates Differ */
1347
1348 static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */
1349 #ifndef MAC
1350     { "append",    XYFX_A, 0 },         /* append to old file */
1351 #endif /* MAC */
1352 #ifdef COMMENT
1353     { "ask",       XYFX_Q, 0 },         /* ask what to do (not implemented) */
1354 #endif
1355     { "backup",    XYFX_B, 0 },         /* rename old file */
1356 #ifndef MAC
1357     { "dates-differ", XYFX_M, 0 },      /* accept if dates differ */
1358     { "discard",   XYFX_D, 0 },         /* don't accept new file */
1359     { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
1360 #endif /* MAC */
1361     { "overwrite", XYFX_X, 0 },         /* overwrite the old file */
1362     { "rename",    XYFX_R, 0 },         /* rename the incoming file */
1363 #ifndef MAC                             /* This crashes Mac Kermit. */
1364     { "update",    XYFX_U, 0 },         /* replace if newer */
1365 #endif /* MAC */
1366     { "", 0, 0 }
1367 };
1368 static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1;
1369
1370
1371 #ifdef FTP_SECURITY
1372 /* FTP authentication options */
1373
1374 #define FTA_AUTO 0                      /* Auto */
1375 #define FTA_SRP  1                      /* SRP */
1376 #define FTA_GK5  2                      /* Kerberos 5 */
1377 #define FTA_K4   3                      /* Kerberos 4 */
1378 #define FTA_SSL  4                      /* SSL */
1379 #define FTA_TLS  5                      /* TLS */
1380
1381 /* FTP authentication types */
1382
1383 #define FTPATYPS 8
1384 static int ftp_auth_type[FTPATYPS] = {
1385 #ifdef FTP_GSSAPI
1386     FTA_GK5,                            /* GSSAPI Kerberos 5 */
1387 #endif /* FTP_GK5 */
1388 #ifdef FTP_SRP
1389     FTA_SRP,                            /* SRP */
1390 #endif /* FTP_SRP */
1391 #ifdef FTP_KRB4
1392     FTA_K4,                             /* Kerberos 4 */
1393 #endif /* FTP_KRB4 */
1394 #ifdef CK_SSL
1395     FTA_TLS,                            /* TLS */
1396     FTA_SSL,                            /* SSL */
1397 #endif /* CK_SSL */
1398     0
1399 };
1400
1401 static struct keytab ftpauth[] = {      /* SET FTP AUTHTYPE cmd table */
1402     { "automatic", FTA_AUTO,  CM_INV },
1403 #ifdef FTP_GSSAPI
1404     { "gssapi-krb5", FTA_GK5, 0 },
1405 #endif /* FTP_GSSAPI */
1406 #ifdef FTP_KRB4
1407     { "k4",       FTA_K4,     CM_INV },
1408 #endif /* FTP_KRB4 */
1409 #ifdef FTP_GSSAPI
1410     { "k5",        FTA_GK5,   CM_INV },
1411 #endif /* FTP_GSSAPI */
1412 #ifdef FTP_KRB4
1413     { "kerberos4", FTA_K4,    0 },
1414 #endif /* FTP_KRB4 */
1415 #ifdef FTP_GSSAPI
1416     { "kerberos5", FTA_GK5,   CM_INV },
1417 #endif /* FTP_GSSAPI */
1418 #ifdef FTP_KRB4
1419     { "kerberos_iv",FTA_K4,   CM_INV },
1420 #endif /* FTP_KRB4 */
1421 #ifdef FTP_GSSAPI
1422     { "kerberos_v", FTA_GK5,  CM_INV },
1423 #endif /* FTP_GSSAPI */
1424 #ifdef FTP_KRB4
1425     { "krb4",     FTA_K4,     CM_INV },
1426 #endif /* FTP_KRB4 */
1427 #ifdef FTP_GSSAPI
1428     { "krb5",     FTA_GK5,    CM_INV },
1429 #endif /* FTP_GSSAPI */
1430 #ifdef FTP_SRP
1431     { "srp",      FTA_SRP,     0 },
1432 #endif /* FTP_SRP */
1433 #ifdef CK_SSL
1434     { "ssl",      FTA_SSL,     0 },
1435     { "tls",      FTA_TLS,     0 },
1436 #endif /* CK_SSL */
1437     { "", 0, 0 }
1438 };
1439 static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1;
1440
1441 #ifdef FTP_SRP
1442 #define SRP_CIPHER 1
1443 #define SRP_HASH   2
1444 static struct keytab ftpsrp[] = {      /* SET FTP SRP command table */
1445     { "cipher",   SRP_CIPHER,     0 },
1446     { "hash",     SRP_HASH,       0 },
1447     { "", 0, 0 }
1448 };
1449 static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1;
1450 #endif /* FTP_SRP */
1451 #endif /* FTP_SECURITY */
1452
1453 static struct keytab ftpset[] = {       /* SET FTP commmand table */
1454     { "anonymous-password",       FTS_APW, 0 },
1455 #ifdef FTP_SECURITY
1456     { "authtype",                 FTS_ATP, 0 },
1457     { "autoauthentication",       FTS_AUT, 0 },
1458     { "autoencryption",           FTS_CRY, 0 },
1459 #endif /* FTP_SECURITY */
1460     { "autologin",                FTS_LOG, 0 },
1461     { "bug",                      FTS_BUG, 0 },
1462 #ifndef NOCSETS
1463     { "character-set-translation",FTS_XLA, 0 },
1464 #endif /* NOCSETS */
1465     { "collision",                FTS_FNC, 0 },
1466 #ifdef FTP_SECURITY
1467     { "command-protection-level", FTS_CPL, 0 },
1468     { "cpl",                      FTS_CPL, CM_INV },
1469     { "credential-forwarding",    FTS_CFW, 0 },
1470     { "da",                       FTS_DAT, CM_INV|CM_ABR },
1471     { "data-protection-level",    FTS_DPL, 0 },
1472 #endif /* FTP_SECURITY */
1473     { "dates",                    FTS_DAT, 0 },
1474     { "debug",                    FTS_DBG, 0 },
1475     { "display",                  FTS_DIS, 0 },
1476 #ifdef FTP_SECURITY
1477     { "dpl",                      FTS_DPL, CM_INV },
1478 #endif /* FTP_SECURITY */
1479     { "error-action",             FTS_ERR, 0 },
1480     { "filenames",                FTS_CNV, 0 },
1481     { "get-filetype-switching",   FTS_GFT, 0 },
1482     { "passive-mode",             FTS_PSV, 0 },
1483     { "pasv",                     FTS_PSV, CM_INV },
1484     { "permissions",              FTS_PRM, 0 },
1485     { "progress-messages",        FTS_TST, 0 },
1486     { "send-port-commands",       FTS_SPC, 0 },
1487 #ifndef NOCSETS
1488     { "server-character-set",     FTS_CSR, 0 },
1489 #endif /* NOCSETS */
1490     { "server-time-offset",       FTS_STO, 0 },
1491 #ifdef FTP_SRP
1492     { "srp",                      FTS_SRP, 0 },
1493 #else
1494     { "srp",                      FTS_SRP, CM_INV },
1495 #endif /* FTP_SRP */
1496 #ifdef FTP_TIMEOUT
1497     { "timeout",                  FTS_TMO, 0 },
1498 #endif  /* FTP_TIMEOUT */
1499     { "type",                     FTS_TYP, 0 },
1500     { "unique-server-names",      FTS_USN, 0 },
1501     { "verbose-mode",             FTS_VBM, 0 },
1502     { "", 0, 0 }
1503 };
1504 static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1;
1505
1506 /*
1507   GET and PUT switches are approximately the same as Kermit GET and SEND,
1508   and use the same SND_xxx definitions, but hijack a couple for FTP use.
1509   Don't just make up new ones, since the number of SND_xxx options must be
1510   known in advance for the switch-parsing arrays.
1511 */
1512 #define SND_USN SND_PRO                 /* /UNIQUE instead of /PROTOCOL */
1513 #define SND_PRM SND_PIP                 /* /PERMISSIONS instead of /PIPES */
1514 #define SND_TEN SND_CAL                 /* /TENEX instead of /CALIBRATE */
1515
1516 static struct keytab putswi[] = {       /* FTP PUT switch table */
1517     { "/after",                SND_AFT, CM_ARG },
1518 #ifdef PUTARRAY
1519     { "/array",                SND_ARR, CM_ARG },
1520 #endif /* PUTARRAY */
1521     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1522     { "/as-name",              SND_ASN, CM_ARG },
1523     { "/ascii",                SND_TXT, CM_INV },
1524     { "/b",                    SND_BIN, CM_INV|CM_ABR },
1525     { "/before",               SND_BEF, CM_ARG },
1526     { "/binary",               SND_BIN, 0 },
1527 #ifdef PUTPIPE
1528     { "/command",              SND_CMD, CM_PSH },
1529 #endif /* PUTPIPE */
1530 #ifdef COMMENT
1531 /* This works but it's dangerous */
1532 #ifdef DOUPDATE
1533     { "/dates-differ",         SND_DIF, CM_INV },
1534 #endif /* DOUPDATE */
1535 #endif /* COMMENT */
1536     { "/delete",               SND_DEL, 0 },
1537 #ifdef UNIXOROSK
1538     { "/dotfiles",             SND_DOT, 0 },
1539 #endif /* UNIXOROSK */
1540     { "/error-action",         SND_ERR, CM_ARG },
1541     { "/except",               SND_EXC, CM_ARG },
1542     { "/filenames",            SND_NAM, CM_ARG },
1543 #ifdef PIPESEND
1544 #ifndef NOSPL
1545     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1546 #endif /* NOSPL */
1547 #endif /* PIPESEND */
1548 #ifdef CKSYMLINK
1549     { "/followlinks",          SND_LNK, 0 },
1550 #endif /* CKSYMLINK */
1551 #ifdef VMS
1552     { "/image",                SND_IMG, 0 },
1553 #else
1554     { "/image",                SND_BIN, CM_INV },
1555 #endif /* VMS */
1556     { "/larger-than",          SND_LAR, CM_ARG },
1557     { "/listfile",             SND_FIL, CM_ARG },
1558 #ifndef NOCSETS
1559     { "/local-character-set",  SND_CSL, CM_ARG },
1560 #endif /* NOCSETS */
1561 #ifdef CK_TMPDIR
1562     { "/move-to",              SND_MOV, CM_ARG },
1563 #endif /* CK_TMPDIR */
1564     { "/nobackupfiles",        SND_NOB, 0 },
1565 #ifdef UNIXOROSK
1566     { "/nodotfiles",           SND_NOD, 0 },
1567 #endif /* UNIXOROSK */
1568 #ifdef CKSYMLINK
1569     { "/nofollowlinks",        SND_NLK, 0 },
1570 #endif /* CKSYMLINK */
1571
1572     { "/not-after",            SND_NAF, CM_ARG },
1573     { "/not-before",           SND_NBE, CM_ARG },
1574 #ifdef UNIX
1575     { "/permissions",          SND_PRM, CM_ARG },
1576 #else
1577     { "/permissions",          SND_PRM, CM_ARG|CM_INV },
1578 #endif /* UNIX */
1579     { "/quiet",                SND_SHH, 0 },
1580 #ifdef FTP_RESTART
1581     { "/recover",              SND_RES, 0 },
1582 #endif /* FTP_RESTART */
1583 #ifdef RECURSIVE
1584     { "/recursive",            SND_REC, 0 },
1585 #endif /* RECURSIVE */
1586     { "/rename-to",            SND_REN, CM_ARG },
1587 #ifdef FTP_RESTART
1588     { "/restart",              SND_RES, CM_INV },
1589 #endif /* FTP_RESTART */
1590 #ifndef NOCSETS
1591     { "/server-character-set", SND_CSR, CM_ARG },
1592 #endif /* NOCSETS */
1593     { "/server-rename-to",     SND_SRN, CM_ARG },
1594     { "/simulate",             SND_SIM, 0 },
1595     { "/since",                SND_AFT, CM_INV|CM_ARG },
1596     { "/smaller-than",         SND_SMA, CM_ARG },
1597 #ifdef COMMENT
1598     { "/starting-at",          SND_STA, CM_ARG },
1599 #endif /* COMMENT */
1600 #ifdef RECURSIVE
1601     { "/subdirectories",       SND_REC, CM_INV },
1602 #endif /* RECURSIVE */
1603     { "/tenex",                SND_TEN, 0 },
1604     { "/text",                 SND_TXT, 0 },
1605 #ifndef NOCSETS
1606     { "/transparent",          SND_XPA, 0 },
1607 #endif /* NOCSETS */
1608     { "/type",                 SND_TYP, CM_ARG },
1609 #ifdef DOUPDATE
1610     { "/update",               SND_UPD, 0 },
1611 #endif /* DOUPDATE */
1612     { "/unique-server-names",  SND_USN, 0 },
1613     { "", 0, 0 }
1614 };
1615 static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1;
1616
1617 static struct keytab getswi[] = {       /* FTP [M]GET switch table */
1618     { "/after",                SND_AFT, CM_INV },
1619     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1620     { "/as-name",              SND_ASN, CM_ARG },
1621     { "/ascii",                SND_TXT, CM_INV },
1622     { "/before",               SND_BEF, CM_INV },
1623     { "/binary",               SND_BIN, 0 },
1624     { "/collision",            SND_COL, CM_ARG },
1625 #ifdef PUTPIPE
1626     { "/command",              SND_CMD, CM_PSH },
1627 #endif /* PUTPIPE */
1628     { "/delete",               SND_DEL, 0 },
1629     { "/error-action",         SND_ERR, CM_ARG },
1630     { "/except",               SND_EXC, CM_ARG },
1631     { "/filenames",            SND_NAM, CM_ARG },
1632 #ifdef PIPESEND
1633 #ifndef NOSPL
1634     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1635 #endif /* NOSPL */
1636 #endif /* PIPESEND */
1637 #ifdef VMS
1638     { "/image",                SND_IMG, 0 },
1639 #else
1640     { "/image",                SND_BIN, CM_INV },
1641 #endif /* VMS */
1642     { "/larger-than",          SND_LAR, CM_ARG },
1643     { "/listfile",             SND_FIL, CM_ARG },
1644 #ifndef NOCSETS
1645     { "/local-character-set",  SND_CSL, CM_ARG },
1646 #endif /* NOCSETS */
1647     { "/match",                SND_PAT, CM_ARG },
1648     { "/ml",                   SND_MLS, CM_INV|CM_ABR },
1649     { "/mls",                  SND_MLS, CM_INV|CM_ABR },
1650     { "/mlsd",                 SND_MLS, 0 },
1651     { "/mlst",                 SND_MLS, CM_INV },
1652 #ifdef CK_TMPDIR
1653     { "/move-to",              SND_MOV, CM_ARG },
1654 #endif /* CK_TMPDIR */
1655     { "/namelist",             SND_NML, CM_ARG },
1656     { "/nlst",                 SND_NLS, 0 },
1657     { "/nobackupfiles",        SND_NOB, 0 },
1658     { "/nodotfiles",           SND_NOD, 0 },
1659 #ifdef DOUPDATE
1660     { "/dates-differ",         SND_DIF, CM_INV },
1661 #endif /* DOUPDATE */
1662     { "/not-after",            SND_NAF, CM_INV },
1663     { "/not-before",           SND_NBE, CM_INV },
1664     { "/permissions",          SND_PRM, CM_INV },
1665     { "/quiet",                SND_SHH, 0 },
1666 #ifdef FTP_RESTART
1667     { "/recover",              SND_RES, 0 },
1668 #endif /* FTP_RESTART */
1669 #ifdef RECURSIVE
1670     { "/recursive",            SND_REC, 0 },
1671 #endif /* RECURSIVE */
1672     { "/rename-to",            SND_REN, CM_ARG },
1673 #ifdef FTP_RESTART
1674     { "/restart",              SND_RES, CM_INV },
1675 #endif /* FTP_RESTART */
1676 #ifndef NOCSETS
1677     { "/server-character-set", SND_CSR, CM_ARG },
1678 #endif /* NOCSETS */
1679     { "/server-rename-to",     SND_SRN, CM_ARG },
1680     { "/smaller-than",         SND_SMA, CM_ARG },
1681 #ifdef RECURSIVE
1682     { "/subdirectories",       SND_REC, CM_INV },
1683 #endif /* RECURSIVE */
1684     { "/text",                 SND_TXT, 0 },
1685     { "/tenex",                SND_TEN, 0 },
1686 #ifndef NOCSETS
1687     { "/transparent",          SND_XPA, 0 },
1688 #endif /* NOCSETS */
1689     { "/to-screen",            SND_MAI, 0 },
1690 #ifdef DOUPDATE
1691     { "/update",               SND_UPD, CM_INV },
1692 #endif /* DOUPDATE */
1693     { "", 0, 0 }
1694 };
1695 static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1;
1696
1697 static struct keytab delswi[] = {       /* FTP [M]DELETE switch table */
1698     { "/error-action",         SND_ERR, CM_ARG },
1699     { "/except",               SND_EXC, CM_ARG },
1700     { "/filenames",            SND_NAM, CM_ARG },
1701     { "/larger-than",          SND_LAR, CM_ARG },
1702     { "/nobackupfiles",        SND_NOB, 0 },
1703 #ifdef UNIXOROSK
1704     { "/nodotfiles",           SND_NOD, 0 },
1705 #endif /* UNIXOROSK */
1706     { "/quiet",                SND_SHH, 0 },
1707 #ifdef RECURSIVE
1708     { "/recursive",            SND_REC, 0 },
1709 #endif /* RECURSIVE */
1710     { "/smaller-than",         SND_SMA, CM_ARG },
1711 #ifdef RECURSIVE
1712     { "/subdirectories",       SND_REC, CM_INV },
1713 #endif /* RECURSIVE */
1714     { "", 0, 0 }
1715 };
1716 static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1;
1717
1718 static struct keytab fntab[] = {        /* Filename conversion keyword table */
1719     { "automatic",    2, CNV_AUTO },
1720     { "converted",    1, CNV_CNV  },
1721     { "literal",      0, CNV_LIT  }
1722 };
1723 static int nfntab = (sizeof(fntab) / sizeof(struct keytab));
1724
1725 static struct keytab ftptyp[] = {       /* SET FTP TYPE table */
1726     { "ascii",        FTT_ASC, 0 },
1727     { "binary",       FTT_BIN, 0 },
1728     { "tenex",        FTT_TEN, 0 },
1729     { "text",         FTT_ASC, CM_INV },
1730     { "", 0, 0 }
1731 };
1732 static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1;
1733
1734 #ifdef FTP_SECURITY
1735 static struct keytab ftppro[] = {       /* SET FTP PROTECTION-LEVEL table */
1736     { "clear",        FPL_CLR, 0 },
1737     { "confidential", FPL_CON, 0 },
1738     { "private",      FPL_PRV, 0 },
1739     { "safe",         FPL_SAF, 0 },
1740     { "", 0, 0 }
1741 };
1742 static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1;
1743 #endif /* FTP_SECURITY */
1744
1745 /* Definitions for FTP from RFC765. */
1746
1747 /* Reply codes */
1748
1749 #define REPLY_PRELIM    1               /* Positive preliminary */
1750 #define REPLY_COMPLETE  2               /* Positive completion */
1751 #define REPLY_CONTINUE  3               /* Positive intermediate */
1752 #define REPLY_TRANSIENT 4               /* Transient negative completion */
1753 #define REPLY_ERROR     5               /* Permanent negative completion */
1754 #define REPLY_SECURE    6               /* Security encoded message */
1755
1756 /* Form codes and names */
1757
1758 #define FORM_N 1                        /* Non-print */
1759 #define FORM_T 2                        /* Telnet format effectors */
1760 #define FORM_C 3                        /* Carriage control (ASA) */
1761
1762 /* Structure codes and names */
1763
1764 #define STRU_F 1                        /* File (no record structure) */
1765 #define STRU_R 2                        /* Record structure */
1766 #define STRU_P 3                        /* Page structure */
1767
1768 /* Mode types and names */
1769
1770 #define MODE_S 1                        /* Stream */
1771 #define MODE_B 2                        /* Block */
1772 #define MODE_C 3                        /* Compressed */
1773
1774 /* Protection levels and names */
1775
1776 #define PROT_C 1                        /* Clear */
1777 #define PROT_S 2                        /* Safe */
1778 #define PROT_P 3                        /* Private */
1779 #define PROT_E 4                        /* Confidential */
1780
1781 #ifdef COMMENT                          /* Not used */
1782 #ifdef FTP_NAMES
1783 char *strunames[]  =  {"0", "File",     "Record", "Page" };
1784 char *formnames[]  =  {"0", "Nonprint", "Telnet", "Carriage-control" };
1785 char *modenames[]  =  {"0", "Stream",   "Block",  "Compressed" };
1786 char *levelnames[] =  {"0", "Clear",    "Safe",   "Private",  "Confidential" };
1787 #endif /* FTP_NAMES */
1788
1789 /* Record Tokens */
1790
1791 #define REC_ESC '\377'                  /* Record-mode Escape */
1792 #define REC_EOR '\001'                  /* Record-mode End-of-Record */
1793 #define REC_EOF '\002'                  /* Record-mode End-of-File */
1794
1795 /* Block Header */
1796
1797 #define BLK_EOR           0x80          /* Block is End-of-Record */
1798 #define BLK_EOF           0x40          /* Block is End-of-File */
1799 #define BLK_REPLY_ERRORS  0x20          /* Block might have errors */
1800 #define BLK_RESTART       0x10          /* Block is Restart Marker */
1801 #define BLK_BYTECOUNT 2                 /* Bytes in this block */
1802 #endif /* COMMENT */
1803
1804 #define RADIX_ENCODE 0                  /* radix_encode() function codes */
1805 #define RADIX_DECODE 1
1806
1807 /*
1808   The default setpbsz() value in the Unix FTP client is 1<<20 (1MB).  This
1809   results in a serious performance degradation due to the increased number
1810   of page faults and the inability to overlap encrypt/decrypt, file i/o, and
1811   network i/o.  So instead we set the value to 1<<13 (8K), about half the size
1812   of the typical TCP window.  Maybe we should add a command to allow the value
1813   to be changed.
1814 */
1815 #define DEFAULT_PBSZ 1<<13
1816
1817 /* Prototypes */
1818
1819 _PROTOTYP(int remtxt, (char **) );
1820 _PROTOTYP(char * gskreason, (int) );
1821 _PROTOTYP(static int ftpclose,(void));
1822 _PROTOTYP(static int zzsend, (int, CHAR));
1823 _PROTOTYP(static int getreply,(int,int,int,int,int));
1824 _PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int));
1825 _PROTOTYP(static int setpbsz,(unsigned int));
1826 _PROTOTYP(static int recvrequest,(char *,char *,char *,char *,
1827   int,int,char *,int,int,int));
1828 _PROTOTYP(static int ftpcmd,(char *,char *,int,int,int));
1829 _PROTOTYP(static int fts_cpl,(int));
1830 _PROTOTYP(static int fts_dpl,(int));
1831 #ifdef FTP_SECURITY
1832 _PROTOTYP(static int ftp_auth, (void));
1833 #endif /* FTP_SECURITY */
1834 _PROTOTYP(static int ftp_user, (char *, char *, char *));
1835 _PROTOTYP(static int ftp_login, (char *));
1836 _PROTOTYP(static int ftp_reset, (void));
1837 _PROTOTYP(static int ftp_rename, (char *, char *));
1838 _PROTOTYP(static int ftp_umask, (char *));
1839 _PROTOTYP(static int secure_flush, (int));
1840 #ifdef COMMENT
1841 _PROTOTYP(static int secure_putc, (char, int));
1842 #endif /* COMMENT */
1843 _PROTOTYP(static int secure_write, (int, CHAR *, unsigned int));
1844 _PROTOTYP(static int scommand, (char *));
1845 _PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int));
1846 _PROTOTYP(static int secure_getc, (int, int));
1847 _PROTOTYP(static int secure_getbyte, (int, int));
1848 _PROTOTYP(static int secure_read, (int, char *, int));
1849 _PROTOTYP(static int initconn, (void));
1850 _PROTOTYP(static int dataconn, (char *));
1851 _PROTOTYP(static int setprotbuf,(unsigned int));
1852 _PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int));
1853
1854 _PROTOTYP(static char * radix_error,(int));
1855 _PROTOTYP(static char * ftp_hookup,(char *, int, int));
1856 _PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int));
1857
1858 _PROTOTYP(static VOID mlsreset, (void));
1859 _PROTOTYP(static VOID secure_error, (char *fmt, ...));
1860 _PROTOTYP(static VOID lostpeer, (void));
1861 _PROTOTYP(static VOID cancel_remote, (int));
1862 _PROTOTYP(static VOID changetype, (int, int));
1863
1864 _PROTOTYP(static sigtype cmdcancel, (int));
1865
1866 #ifdef FTP_SRP
1867 _PROTOTYP(static int srp_reset, ());
1868 _PROTOTYP(static int srp_ftp_auth, (char *,char *,char *));
1869 _PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *));
1870 _PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *));
1871 _PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int));
1872 _PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int));
1873 _PROTOTYP(static int srp_selcipher, (char *));
1874 _PROTOTYP(static int srp_selhash, (char *));
1875 #endif /* FTP_SRP */
1876
1877 #ifdef FTP_GSSAPI
1878 _PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *));
1879 #endif /* FTP_GSSAPI */
1880
1881 /*  D O F T P A R G  --  Do an FTP command-line argument.  */
1882
1883 #ifdef FTP_SECURITY
1884 #ifndef NOICP
1885 #define FT_NOGSS   1
1886 #define FT_NOK4    2
1887 #define FT_NOSRP   3
1888 #define FT_NOSSL   4
1889 #define FT_NOTLS   5
1890 #define FT_CERTFI  6
1891 #define FT_OKCERT  7
1892 #define FT_DEBUG   8
1893 #define FT_KEY     9
1894 #define FT_SECURE 10
1895 #define FT_VERIFY 11
1896
1897 static struct keytab ftpztab[] = {
1898     { "!gss",    FT_NOGSS,  0 },
1899     { "!krb4",   FT_NOK4,   0 },
1900     { "!srp",    FT_NOSRP,  0 },
1901     { "!ssl",    FT_NOSSL,  0 },
1902     { "!tls",    FT_NOTLS,  0 },
1903     { "cert",    FT_CERTFI, CM_ARG },
1904     { "certsok", FT_OKCERT, 0 },
1905     { "debug",   FT_DEBUG,  0 },
1906     { "key",     FT_KEY,    CM_ARG },
1907     { "nogss",   FT_NOGSS,  0 },
1908     { "nokrb4",  FT_NOK4,   0 },
1909     { "nosrp",   FT_NOSRP,  0 },
1910     { "nossl",   FT_NOSSL,  0 },
1911     { "notls",   FT_NOTLS,  0 },
1912 #ifdef COMMENT
1913     { "secure",  FT_SECURE, 0 },
1914 #endif /* COMMENT */
1915     { "verify",  FT_VERIFY, CM_ARG },
1916     { "", 0, 0 }
1917 };
1918 static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1;
1919
1920 /*
1921   The following cipher and hash tables should be replaced with
1922   dynamicly created versions based upon the linked library.
1923 */
1924 #define SRP_BLOWFISH_ECB    1
1925 #define SRP_BLOWFISH_CBC    2
1926 #define SRP_BLOWFISH_CFB64  3
1927 #define SRP_BLOWFISH_OFB64  4
1928 #define SRP_CAST5_ECB       5
1929 #define SRP_CAST5_CBC       6
1930 #define SRP_CAST5_CFB64     7
1931 #define SRP_CAST5_OFB64     8
1932 #define SRP_DES_ECB         9
1933 #define SRP_DES_CBC        10
1934 #define SRP_DES_CFB64      11
1935 #define SRP_DES_OFB64      12
1936 #define SRP_DES3_ECB       13
1937 #define SRP_DES3_CBC       14
1938 #define SRP_DES3_CFB64     15
1939 #define SRP_DES3_OFB64     16
1940
1941 static struct keytab ciphertab[] = {
1942     { "blowfish_ecb",   SRP_BLOWFISH_ECB,   0 },
1943     { "blowfish_cbc",   SRP_BLOWFISH_CBC,   0 },
1944     { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 },
1945     { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 },
1946     { "cast5_ecb",      SRP_CAST5_ECB,      0 },
1947     { "cast5_cbc",      SRP_CAST5_CBC,      0 },
1948     { "cast5_cfb64",    SRP_CAST5_CFB64,    0 },
1949     { "cast5_ofb64",    SRP_CAST5_OFB64,    0 },
1950     { "des_ecb",        SRP_DES_ECB,        0 },
1951     { "des_cbc",        SRP_DES_CBC,        0 },
1952     { "des_cfb64",      SRP_DES_CFB64,      0 },
1953     { "des_ofb64",      SRP_DES_OFB64,      0 },
1954     { "des3_ecb",       SRP_DES3_ECB,       0 },
1955     { "des3_cbc",       SRP_DES3_CBC,       0 },
1956     { "des3_cfb64",     SRP_DES3_CFB64,     0 },
1957     { "des3_ofb64",     SRP_DES3_OFB64,     0 },
1958     { "none",           0, 0 },
1959     { "", 0, 0 }
1960 };
1961 static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1;
1962
1963 #define SRP_MD5  1
1964 #define SRP_SHA  2
1965 static struct keytab hashtab[] = {
1966     { "md5",              SRP_MD5,        0 },
1967     { "none",             0,              0 },
1968     { "sha",              SRP_SHA,        0 },
1969     { "", 0, 0 }
1970 };
1971 static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1;
1972 #endif /* NOICP */
1973 #endif /* FTP_SECURITY */
1974
1975 static char *
1976 strval(s1,s2) char * s1, * s2; {
1977     if (!s1) s1 = "";
1978     if (!s2) s2 = "";
1979     return(*s1 ? s1 : (*s2 ? s2 : "(none)"));
1980 }
1981
1982 #ifndef NOCSETS
1983 static char * rfnptr = NULL;
1984 static int rfnlen = 0;
1985 static char rfnbuf[RFNBUFSIZ];          /* Remote filename translate buffer */
1986 static char * xgnbp = NULL;
1987
1988 static int
1989 strgetc() {                             /* Helper function for xgnbyte() */
1990     int c;
1991     if (!xgnbp)
1992       return(-1);
1993     if (!*xgnbp)
1994       return(-1);
1995     c = (unsigned) *xgnbp++;
1996     return(((unsigned) c) & 0xff);
1997 }
1998
1999 static int                              /* Helper function for xpnbyte() */
2000 #ifdef CK_ANSIC
2001 strputc(char c)
2002 #else
2003 strputc(c) char c;
2004 #endif /* CK_ANSIC */
2005 {
2006     rfnlen = rfnptr - rfnbuf;
2007     if (rfnlen >= (RFNBUFSIZ - 1))
2008       return(-1);
2009     *rfnptr++ = c;
2010     *rfnptr = NUL;
2011     return(0);
2012 }
2013
2014 static int
2015 #ifdef CK_ANSIC
2016 xprintc(char c)
2017 #else
2018 xprintc(c) char c;
2019 #endif /* CK_ANSIC */
2020 {
2021     printf("%c",c);
2022     return(0);
2023 }
2024
2025 static VOID
2026 bytswap(c0,c1) int * c0, * c1; {
2027     int t;
2028     t = *c0;
2029     *c0 = *c1;
2030     *c1 = t;
2031 }
2032 #endif /* NOCSETS */
2033
2034 #ifdef CKLOGDIAL
2035 char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */
2036 int ftplogactive = 0;
2037 long ftplogprev = 0L;
2038
2039 VOID
2040 ftplogend() {
2041     extern int dialog;
2042     extern char diafil[];
2043     long d1, d2, t1, t2;
2044     char buf[32], * p;
2045
2046     debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive);
2047     debug(F110,"ftp cx log buf",ftplogbuf,0);
2048
2049     if (!ftplogactive || !ftplogbuf[0]) /* No active record */
2050       return;
2051
2052     ftplogactive = 0;                   /* Record is not active */
2053
2054     d1 = mjd((char *)ftplogbuf);        /* Get start date of this session */
2055     ckstrncpy(buf,ckdate(),31);         /* Get current date */
2056     d2 = mjd(buf);                      /* Convert them to mjds */
2057     p = ftplogbuf;                      /* Get start time */
2058     p[11] = NUL;
2059     p[14] = NUL;                        /* Convert to seconds */
2060     t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2061     p[11] = ':';
2062     p[14] = ':';
2063     p = buf;                            /* Get end time */
2064     p[11] = NUL;
2065     p[14] = NUL;
2066     t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2067     t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */
2068     if (t2 > -1L) {
2069         ftplogprev = t2;
2070         p = hhmmss(t2);
2071         ckstrncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */
2072         ckstrncat(ftplogbuf,p,CXLOGBUFL);
2073     } else
2074       ftplogprev = 0L;
2075     debug(F101,"ftp cx log dialog","",dialog);
2076     if (dialog) {                       /* If logging */
2077         int x;
2078         x = diaopn(diafil,1,1);         /* Open log in append mode */
2079         if (x > 0) {
2080             debug(F101,"ftp cx log open","",x);
2081             x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */
2082             debug(F101,"ftp cx log write","",x);
2083             x = zclose(ZDIFIL);         /* Close the log */
2084             debug(F101,"ftp cx log close","",x);
2085         }
2086     }
2087 }
2088
2089 VOID
2090 dologftp() {
2091     ftplogend();                        /* Previous session not closed out? */
2092     ftplogprev = 0L;
2093     ftplogactive = 1;                   /* Record is active */
2094
2095     ckmakxmsg(ftplogbuf,CXLOGBUFL,
2096               ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(),
2097               " T=FTP N=", strval(ftp_host,NULL)," H=",myhost,
2098               " P=", ckitoa(ftp_port)," "); /* SMS 2007/02/15 */
2099     debug(F110,"ftp cx log begin",ftplogbuf,0);
2100 }
2101 #endif /* CKLOGDIAL */
2102
2103 static char * dummy[2] = { NULL, NULL };
2104
2105 static struct keytab modetab[] = {
2106     { "active",  0, 0 },
2107     { "passive", 1, 0 }
2108 };
2109
2110 #ifndef NOCMDL
2111 int                                     /* Called from ckuusy.c */
2112 #ifdef CK_ANSIC
2113 doftparg(char c)
2114 #else
2115 doftparg(c) char c;
2116 #endif /* CK_ANSIC */
2117 /* doftparg */ {
2118     int x, z;
2119     char *xp;
2120     extern char **xargv, *xarg0;
2121     extern int xargc, stayflg, haveftpuid;
2122     extern char uidbuf[];
2123
2124     xp = *xargv+1;                      /* Pointer for bundled args */
2125     while (c) {
2126         if (ckstrchr("MuDPkcHzm",c)) {  /* Options that take arguments */
2127             if (*(xp+1)) {
2128                 fatal("?Invalid argument bundling");
2129             }
2130             xargv++, xargc--;
2131             if ((xargc < 1) || (**xargv == '-')) {
2132                 fatal("?Required argument missing");
2133             }
2134         }
2135         switch (c) {                    /* Big switch on arg */
2136           case 'h':                     /* help */
2137            printf("C-Kermit's FTP client command-line personality.  Usage:\n");
2138             printf("  %s [ options ] host [ port ] [-pg files ]\n\n",xarg0);
2139             printf("Options:\n");
2140             printf("  -h           = help (this message)\n");
2141             printf("  -m mode      = \"passive\" (default) or \"active\"\n");
2142             printf("  -u name      = username for autologin (or -M)\n");
2143             printf("  -P password  = password for autologin (RISKY)\n");
2144             printf("  -A           = autologin anonymously\n");
2145             printf("  -D directory = cd after autologin\n");
2146             printf("  -b           = force binary mode\n");
2147             printf("  -a           = force text (\"ascii\") mode (or -T)\n");
2148             printf("  -d           = debug (double to add timestamps)\n");
2149             printf("  -n           = no autologin\n");
2150             printf("  -v           = verbose (default)\n");
2151             printf("  -q           = quiet\n");
2152             printf("  -S           = Stay (issue command prompt when done)\n");
2153             printf("  -Y           = do not execute Kermit init file\n");
2154             printf("  -p files     = files to put after autologin (or -s)\n");
2155             printf("  -g files     = files to get after autologin\n");
2156             printf("  -R           = recursive (for use with -p)\n");
2157
2158 #ifdef FTP_SECURITY
2159             printf("\nSecurity options:\n");
2160             printf("  -k realm     = Kerberos 4 realm\n");
2161             printf("  -f           = Kerboros 5 credentials forwarding\n");
2162             printf("  -x           = autoencryption mode\n");
2163             printf("  -c cipher    = SRP cipher type\n");
2164             printf("  -H hash      = SRP encryption hash\n");
2165             printf("  -z option    = Security options\n");
2166 #endif /* FTP_SECURITY */
2167
2168             printf("\n-p or -g, if given, should be last.  Example:\n");
2169             printf("  ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n");
2170
2171             doexit(GOOD_EXIT,-1);
2172             break;
2173
2174           case 'R':                     /* Recursive */
2175             recursive = 1;
2176             break;
2177
2178           case 'd':                     /* Debug */
2179 #ifdef DEBUG
2180             if (deblog) {
2181                 extern int debtim;
2182                 debtim = 1;
2183             } else {
2184                 deblog = debopn("debug.log",0);
2185                 debok = 1;
2186             }
2187 #endif /* DEBUG */
2188             /* fall thru on purpose */
2189
2190           case 't':                     /* Trace */
2191             ftp_deb++;
2192             break;
2193
2194           case 'n':                     /* No autologin */
2195             ftp_log = 0;
2196             break;
2197
2198           case 'i':                     /* No prompt */
2199           case 'v':                     /* Verbose */
2200             break;                      /* (ignored) */
2201
2202           case 'q':                     /* Quiet */
2203             quiet = 1;
2204             break;
2205
2206           case 'S':                     /* Stay */
2207             stayflg = 1;
2208             break;
2209
2210           case 'M':
2211           case 'u':                     /* My User Name */
2212             if ((int)strlen(*xargv) > 63) {
2213                 fatal("username too long");
2214             }
2215             ckstrncpy(uidbuf,*xargv,UIDBUFLEN);
2216             haveftpuid = 1;
2217             break;
2218
2219           case 'A':
2220             ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2221             haveftpuid = 1;
2222             break;
2223
2224           case 'T':                     /* Text */
2225           case 'a':                     /* "ascii" */
2226           case 'b':                     /* Binary */
2227             binary = (c == 'b') ? FTT_BIN : FTT_ASC;
2228             ftp_xfermode = XMODE_M;
2229             filepeek = 0;
2230             patterns = 0;
2231             break;
2232
2233           case 'g':                     /* Get */
2234           case 'p':                     /* Put */
2235           case 's': {                   /* Send (= Put) */
2236               int havefiles, rc;
2237               if (ftp_action) {
2238                   fatal("Only one FTP action at a time please");
2239               }
2240               if (*(xp+1)) {
2241                   fatal("invalid argument bundling after -s");
2242               }
2243               nfils = 0;                /* Initialize file counter */
2244               havefiles = 0;            /* Assume nothing to send  */
2245               cmlist = xargv + 1;       /* Remember this pointer */
2246
2247               while (++xargv, --xargc > 0) { /* Traverse the list */
2248                   if (c == 'g') {
2249                       havefiles++;
2250                       nfils++;
2251                       continue;
2252                   }
2253 #ifdef RECURSIVE
2254                   if (!strcmp(*xargv,".")) {
2255                       havefiles = 1;
2256                       nfils++;
2257                       recursive = 1;
2258                   } else
2259 #endif /* RECURSIVE */
2260                     if ((rc = zchki(*xargv)) > -1 || (rc == -2)) {
2261                         if  (rc != -2)
2262                           havefiles = 1;
2263                         nfils++;
2264                     } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) {
2265                         havefiles = 1;
2266                         nfils++;
2267                     }
2268               }
2269               xargc++, xargv--;         /* Adjust argv/argc */
2270               if (!havefiles) {
2271                   if (c == 'g') {
2272                       fatal("No files to put");
2273                   } else {
2274                       fatal("No files to get");
2275                   }
2276               }
2277               ftp_action = c;
2278               break;
2279           }
2280           case 'D':                     /* Directory */
2281             makestr(&ftp_rdir,*xargv);
2282             break;
2283
2284           case 'm':                     /* Mode (Active/Passive */
2285             ftp_psv = lookup(modetab,*xargv,2,NULL);
2286             if (ftp_psv < 0) fatal("Invalid mode");
2287             break;
2288
2289           case 'P':
2290             makestr(&ftp_tmp,*xargv);   /* You-Know-What */
2291             break;
2292
2293           case 'Y':                     /* No initialization file */
2294             break;                      /* (already done in prescan) */
2295
2296 #ifdef CK_URL
2297           case 'U': {                   /* URL */
2298               /* These are set by urlparse() - any not set are NULL */
2299               if (g_url.hos) {
2300 /*
2301   Kermit has accepted host:port notation since many years before URLs were
2302   invented.  Unfortunately, URLs conflict with this notation.  Thus "ftp
2303   host:449" looks like a URL and results in service = host and host = 449.
2304   Here we try to catch this situation transparently to the user.
2305 */
2306                   if (ckstrcmp(g_url.svc,"ftp",-1,0)
2307 #ifdef CK_SSL
2308                        && ckstrcmp(g_url.svc,"ftps",-1,0)
2309 #endif /* CK_SSL */
2310                        ) {
2311                       if (!g_url.usr &&
2312                           !g_url.psw &&
2313                           !g_url.por &&
2314                           !g_url.pth) {
2315                           g_url.por = g_url.hos;
2316                           g_url.hos = g_url.svc;
2317                           g_url.svc = "ftp";
2318                       } else {
2319                           ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=",
2320                                    g_url.svc," host=",g_url.hos);
2321                           fatal(tmpbuf);
2322                       }
2323                   }
2324                   makestr(&ftp_host,g_url.hos);
2325                   if (g_url.usr) {
2326                       haveftpuid = 1;
2327                       ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
2328                       makestr(&ftp_logname,uidbuf);
2329                   }
2330                   if (g_url.psw) {
2331                       makestr(&ftp_tmp,g_url.psw);
2332                   }
2333                   if (g_url.pth) {
2334                       if (!g_url.usr) {
2335                           haveftpuid = 1;
2336                           ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2337                           makestr(&ftp_logname,uidbuf);
2338                       }
2339                       if (ftp_action) {
2340                           fatal("Only one FTP action at a time please");
2341                       }
2342                       if (!stayflg)
2343                         quiet = 1;
2344                       nfils = 1;
2345                       dummy[0] = g_url.pth;
2346                       cmlist = dummy;
2347                       ftp_action = 'g';
2348                   }
2349                   xp = NULL;
2350                   haveurl = 1;
2351               }
2352               break;
2353           }
2354 #endif /* CK_URL */
2355
2356 #ifdef FTP_SECURITY
2357           case 'k': {                   /* K4 Realm */
2358 #ifdef FTP_KRB4
2359               ckstrncpy(ftp_realm,*xargv, REALM_SZ);
2360 #endif /* FTP_KRB4 */
2361               if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv);
2362               break;
2363           }
2364           case 'f': {
2365 #ifdef FTP_GSSAPI
2366               ftp_cfw = 1;
2367               if (ftp_deb) printf("K5 Credentials Forwarding\n");
2368 #else /* FTP_GSSAPI */
2369               printf("K5 Credentials Forwarding not supported\n");
2370 #endif /* FTP_GSSAPI */
2371               break;
2372           }
2373           case 'x': {
2374               ftp_cry = 1;
2375               if (ftp_deb) printf("Autoencryption\n");
2376               break;
2377           }
2378           case 'c': {                   /* Cipher */
2379 #ifdef FTP_SRP
2380               if (!srp_selcipher(*xargv)) {
2381                   if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv);
2382               } else
2383                 printf("?Invalid SRP cipher type: \"%s\"\n",*xargv);
2384 #else /* FTP_SRP */
2385               printf("?SRP not supported\n");
2386 #endif /* FTP_SRP */
2387               break;
2388           }
2389           case 'H': {
2390 #ifdef FTP_SRP
2391               if (!srp_selhash(*xargv)) {
2392                   if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv);
2393               } else
2394                 printf("?Invalid SRP hash type: \"%s\"\n",*xargv);
2395 #else /* FTP_SRP */
2396               printf("?SRP not supported\n");
2397 #endif /* FTP_SRP */
2398               break;
2399           }
2400           case 'z': {
2401               /* *xargv contains a value of the form tag=value */
2402               /* we need to lookup the tag and save the value  */
2403               char * p = NULL, * q = NULL;
2404               makestr(&p,*xargv);
2405               y = ckindex("=",p,0,0,1);
2406               if (y > 0)
2407                 p[y-1] = '\0';
2408               x = lookup(ftpztab,p,nftpztab,&z);
2409               if (x < 0) {
2410                   printf("?Invalid security option: \"%s\"\n",p);
2411               } else {
2412                   if (ftp_deb)
2413                     printf("Security option: \"%s",p);
2414                   if (ftpztab[z].flgs & CM_ARG) {
2415                       if (y <= 0)
2416                         fatal("?Missing required value");
2417                       q = &p[y];
2418                       if (!*q)
2419                         fatal("?Missing required value");
2420                       if (ftp_deb)
2421                         printf("=%s\"",q);
2422                   }
2423                   switch (ftpztab[z].kwval) { /* -z options w/args */
2424                     case FT_NOGSS:
2425 #ifdef FTP_GSSAPI
2426                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2427                           if (ftp_auth_type[z] == FTA_GK5) {
2428                               for (y = z;
2429                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2430                                    y++
2431                                    )
2432                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2433                               ftp_auth_type[FTPATYPS-1] = 0;
2434                               break;
2435                           }
2436                       }
2437 #endif /* FTP_GSSAPI */
2438                       break;
2439                     case FT_NOK4:
2440 #ifdef FTP_KRB4
2441                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2442                           if (ftp_auth_type[z] == FTA_K4) {
2443                               for (y = z;
2444                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2445                                    y++
2446                                    )
2447                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2448                               ftp_auth_type[FTPATYPS-1] = 0;
2449                               break;
2450                           }
2451                       }
2452 #endif /* FTP_KRB4 */
2453                       break;
2454                     case FT_NOSRP:
2455 #ifdef FTP_SRP
2456                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2457                           if (ftp_auth_type[z] == FTA_SRP) {
2458                               for (y = z;
2459                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2460                                    y++
2461                                    )
2462                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2463                               ftp_auth_type[FTPATYPS-1] = 0;
2464                               break;
2465                           }
2466                       }
2467 #endif /* FTP_SRP */
2468                       break;
2469                     case FT_NOSSL:
2470 #ifdef CK_SSL
2471                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2472                           if (ftp_auth_type[z] == FTA_SSL) {
2473                               for (y = z;
2474                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2475                                    y++
2476                                    )
2477                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2478                               ftp_auth_type[FTPATYPS-1] = 0;
2479                               break;
2480                           }
2481                       }
2482 #endif /* CK_SSL */
2483                       break;
2484                     case FT_NOTLS:
2485 #ifdef CK_SSL
2486                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2487                           if (ftp_auth_type[z] == FTA_TLS) {
2488                               for (y = z;
2489                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2490                                    y++
2491                                    )
2492                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2493                               ftp_auth_type[FTPATYPS-1] = 0;
2494                               break;
2495                           }
2496                       }
2497 #endif /* CK_SSL */
2498                       break;
2499                     case FT_CERTFI:
2500 #ifdef CK_SSL
2501                       makestr(&ssl_rsa_cert_file,q);
2502 #endif /* CK_SSL */
2503                       break;
2504                     case FT_OKCERT:
2505 #ifdef CK_SSL
2506                       ssl_certsok_flag = 1;
2507 #endif /* CK_SSL */
2508                       break;
2509                     case FT_DEBUG:
2510 #ifdef DEBUG
2511                       if (deblog) {
2512                           extern int debtim;
2513                           debtim = 1;
2514                       } else {
2515                           deblog = debopn("debug.log",0);
2516                       }
2517 #endif /* DEBUG */
2518                       break;
2519                     case FT_KEY:
2520 #ifdef CK_SSL
2521                       makestr(&ssl_rsa_key_file,q);
2522 #endif /* CK_SSL */
2523                       break;
2524                     case FT_SECURE:
2525                       /* no equivalent */
2526                       break;
2527                     case FT_VERIFY:
2528 #ifdef CK_SSL
2529                       if (!rdigits(q))
2530                         printf("?Bad number: %s\n",q);
2531                       ssl_verify_flag = atoi(q);
2532 #endif /* CK_SSL */
2533                       break;
2534                   }
2535               }
2536               if (ftp_deb) printf("\"\n");
2537               free(p);
2538               break;
2539           }
2540 #endif /* FTP_SECURITY */
2541
2542           default:
2543             fatal2(*xargv,
2544                    "unknown command-line option, type \"ftp -h\" for help"
2545                    );
2546         }
2547         if (!xp) break;
2548         c = *++xp;                      /* See if options are bundled */
2549     }
2550     return(0);
2551 }
2552 #endif /* NOCMDL */
2553
2554 int
2555 ftpisconnected() {
2556     return(connected);
2557 }
2558
2559 int
2560 ftpisloggedin() {
2561     return(connected ? loggedin : 0);
2562 }
2563
2564 int
2565 ftpissecure() {
2566     return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1);
2567 }
2568
2569 static VOID
2570 ftscreen(n, c, z, s) int n; char c; CK_OFF_T z; char * s; {
2571     if (displa && fdispla && !backgrd && !quiet && !out2screen) {
2572         if (!dpyactive) {
2573             ckscreen(SCR_PT,'S',(CK_OFF_T)0,"");
2574             dpyactive = 1;
2575         }
2576         ckscreen(n,c,z,s);
2577     }
2578 }
2579
2580 #ifndef OS2
2581 /*  g m s t i m e r  --  Millisecond timer */
2582
2583 long
2584 gmstimer() {
2585 #ifdef HAVE_MSECS
2586     /* For those versions of ztime() that also set global ztmsec. */
2587     char *p = NULL;
2588     long z;
2589     ztime(&p);
2590     if (!p) return(0L);
2591     if (!*p) return(0L);
2592     z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
2593     return(z * 1000 + ztmsec);
2594 #else
2595     return((long)time(NULL) * 1000L);
2596 #endif /* HAVE_MSECS */
2597 }
2598 #endif /* OS2 */
2599
2600 /*  d o s e t f t p  --  The SET FTP command  */
2601
2602 int
2603 dosetftp() {
2604     int cx;
2605     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */
2606       return(cx);
2607     switch (cx) {
2608
2609       case FTS_FNC:                     /* Filename collision action */
2610         if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
2611           return(x);
2612         if ((y = cmcfm()) < 0)
2613           return(y);
2614         ftp_fnc = x;
2615         return(1);
2616
2617       case FTS_CNV:                     /* Filename conversion */
2618         if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
2619           return(x);
2620         if ((y = cmcfm()) < 0)
2621           return(y);
2622         ftp_cnv = x;
2623         return(1);
2624
2625       case FTS_DBG:                     /* Debug messages */
2626         return(seton(&ftp_deb));
2627
2628       case FTS_LOG:                     /* Auto-login */
2629         return(seton(&ftp_log));
2630
2631       case FTS_PSV:                     /* Passive mode */
2632         return(dosetftppsv());
2633
2634       case FTS_SPC:                     /* Send port commands */
2635         x = seton(&ftp_spc);
2636         if (x > 0) sendport = ftp_spc;
2637         return(x);
2638
2639       case FTS_TYP:                     /* Type */
2640         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
2641           return(x);
2642         if ((y = cmcfm()) < 0) return(y);
2643         ftp_typ = x;
2644         g_ftp_typ = x;
2645         tenex = (ftp_typ == FTT_TEN);
2646         return(1);
2647
2648       case FTS_USN:                     /* Unique server names */
2649         return(seton(&ftp_usn));
2650
2651       case FTS_VBM:                     /* Verbose mode */
2652         if ((x = seton(&ftp_vbm)) < 0)  /* Per-command copy */
2653           return(x);
2654         ftp_vbx = ftp_vbm;              /* Global sticky copy */
2655         return(x);
2656
2657       case FTS_TST:                     /* "if (testing)" messages */
2658         return(seton(&testing));
2659
2660       case FTS_PRM:                     /* Send permissions */
2661         return(setonaut(&ftp_prm));
2662
2663       case FTS_AUT:                     /* Auto-authentication */
2664         return(seton(&ftp_aut));
2665
2666       case FTS_ERR:                     /* Error action */
2667         if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
2668           return(x);
2669         if ((y = cmcfm()) < 0)
2670           return(y);
2671         ftp_err = x;
2672         return(success = 1);
2673
2674 #ifndef NOCSETS
2675       case FTS_XLA:                     /* Translation */
2676         return(seton(&ftp_xla));
2677
2678       case FTS_CSR:                     /* Server charset */
2679         if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0)
2680           return(x);
2681         if ((y = cmcfm()) < 0)
2682           return(y);
2683         ftp_csr = x;
2684         ftp_xla = 1;                    /* Also enable translation */
2685         return(success = 1);
2686 #endif /* NOCSETS */
2687
2688       case FTS_GFT:
2689         return(seton(&get_auto));       /* GET-filetype-switching */
2690
2691       case FTS_DAT:
2692         return(seton(&ftp_dates));      /* Set file dates */
2693
2694 #ifdef FTP_TIMEOUT
2695       case FTS_TMO:                     /* Timeout */
2696         if ((x = cmnum("Number of seconds","0",10,&z,xxstring)) < 0)
2697           return(x);
2698         if ((y = cmcfm()) < 0)
2699           return(y);
2700         ftp_timeout = z;
2701         return(success = 1);
2702 #endif  /* FTP_TIMEOUT */
2703
2704       case FTS_STO: {                   /* Server time offset */
2705           char * s, * p = NULL;
2706           long k;
2707           if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0)
2708             return(x);
2709           if (!strcmp(s,"+0")) {
2710               s = NULL;
2711           } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */
2712               printf("?Invalid time offset\n");
2713               return(-9);
2714           }
2715           makestr(&p,s);                /* Make a safe copy the string */
2716           if ((x = cmcfm()) < 0) {      /* Get confirmation */
2717               if (p)
2718                 makestr(&p,NULL);
2719               return(x);
2720           }
2721           fts_sto = p;                  /* Confirmed - set the string. */
2722           return(success = 1);
2723       }
2724       case FTS_APW: {
2725           char * s;
2726           if ((x = cmtxt("Text", "", &s, xxstring)) < 0)
2727             return(x);
2728           makestr(&ftp_apw, *s ? s : NULL);
2729           return(success = 1);
2730       }
2731
2732       case FTS_BUG: {
2733           if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0) 
2734             return(x);
2735           switch (x) {
2736 #ifdef CK_SSL
2737           case FTB_SV2:
2738             return seton(&ftp_bug_use_ssl_v2);
2739 #endif /* CK_SSL */
2740           default:
2741             return(-2);
2742           }
2743       }
2744
2745 #ifdef FTP_SECURITY
2746       case FTS_CRY:                     /* Auto-encryption */
2747         return(seton(&ftp_cry));
2748
2749       case FTS_CFW:                     /* Credential-forwarding */
2750         return(seton(&ftp_cfw));
2751
2752       case FTS_CPL:                     /* Command protection level */
2753         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2754         if ((y = cmcfm()) < 0) return(y);
2755         success = fts_cpl(x);
2756         return(success);
2757
2758       case FTS_DPL:                     /* Data protection level */
2759         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2760         if ((y = cmcfm()) < 0) return(y);
2761           success = fts_dpl(x);
2762           return(success);
2763
2764       case FTS_ATP: {                   /* FTP Auth Type */
2765           int i, j, atypes[8];
2766
2767           for (i = 0; i < 8; i++) {
2768               if ((y = cmkey(ftpauth,nftpauth,"",
2769                              (i == 0) ? "automatic" : "",
2770                              xxstring)) < 0) {
2771                   if (y == -3)
2772                     break;
2773                   return(y);
2774               }
2775               if (i > 0 && (y == FTA_AUTO)) {
2776                   printf("?Choice may only be used in first position.\r\n");
2777                   return(-9);
2778               }
2779               for (j = 0; j < i; j++) {
2780                   if (atypes[j] == y) {
2781                       printf("\r\n?Choice has already been used.\r\n");
2782                       return(-9);
2783                   }
2784               }
2785               atypes[i] = y;
2786               if (y == FTA_AUTO) {
2787                   i++;
2788                   break;
2789               }
2790           }
2791           if (i < 8)
2792             atypes[i] = 0;
2793           if ((z = cmcfm()) < 0)
2794             return(z);
2795           if (atypes[0] == FTA_AUTO) {
2796               i = 0;
2797 #ifdef FTP_GSSAPI
2798               ftp_auth_type[i++] = FTA_GK5;
2799 #endif /* FTP_GSSAPI */
2800 #ifdef FTP_SRP
2801               ftp_auth_type[i++] = FTA_SRP;
2802 #endif /* FTP_SRP */
2803 #ifdef FTP_KRB4
2804               ftp_auth_type[i++] = FTA_K4;
2805 #endif /* FTP_KRB4 */
2806 #ifdef CK_SSL
2807               ftp_auth_type[i++] = FTA_TLS;
2808               ftp_auth_type[i++] = FTA_SSL;
2809 #endif /* CK_SSL */
2810               ftp_auth_type[i] = 0;
2811           } else {
2812               for (i = 0; i < 8; i++)
2813                 ftp_auth_type[i] = atypes[i];
2814           }
2815           return(success = 1);
2816       }
2817
2818       case FTS_SRP:
2819 #ifdef FTP_SRP
2820         if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0)
2821           return(x);
2822         switch (x) {
2823           case SRP_CIPHER:
2824             if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0)
2825               return(x);
2826             if ((z = cmcfm()) < 0)
2827               return(z);
2828             success = !srp_selcipher(ciphertab[x].kwd);
2829             return(success);
2830           case SRP_HASH:
2831             if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0)
2832               return(x);
2833             if ((z = cmcfm()) < 0)
2834               return(z);
2835             success = !srp_selhash(hashtab[x].kwd);
2836             return(success = 1);
2837           default:
2838             if ((z = cmcfm()) < 0)
2839               return(z);
2840             return(-2);
2841         }
2842 #else /* FTP_SRP */
2843         if ((z = cmcfm()) < 0)
2844           return(z);
2845         return(-2);
2846 #endif /* FTP_SRP */
2847 #endif /* FTP_SECURITY */
2848
2849       case FTS_DIS:
2850         doxdis(2);                      /* 2 == ftp */
2851         return(success = 1);
2852
2853       default:
2854         return(-2);
2855     }
2856 }
2857
2858 int
2859 ftpbye() {
2860     int x;
2861     if (!connected)
2862       return(1);
2863     if (testing)
2864       printf(" ftp closing %s...\n",ftp_host);
2865     x = ftpclose();
2866     return((x > -1) ? 1 : 0);
2867 }
2868
2869 /*  o p e n f t p  --  Parse FTP hostname & port and open */
2870
2871 static int
2872 openftp(s,opn_tls) char * s; int opn_tls; {
2873     char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL;
2874     int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0;
2875     int haveuser = 0;
2876     struct FDB sw, fl, cm;
2877     extern int nnetdir;                 /* Network services directory */
2878     extern int nhcount;                 /* Lookup result */
2879     extern char *nh_p[];                /* Network directory entry pointers */
2880     extern char *nh_p2[];               /* Network directory entry nettype */
2881
2882     if (!s) return(-2);
2883     if (!*s) return(-2);
2884
2885     makestr(&hostname,s);
2886     hostsave = hostname;
2887     makestr(&ftp_logname,NULL);
2888     anonymous = 0;
2889     noinit = 0;
2890
2891     debug(F110,"ftp open",hostname,0);
2892
2893     if (sav_psv > -1) {                 /* Restore prevailing active/passive */
2894         ftp_psv = sav_psv;              /* selection in case it was */
2895         sav_psv = -1;                   /* temporarily overriden by a switch */
2896     }
2897     if (sav_log > -1) {                 /* Ditto for autologin */
2898         ftp_log = sav_log;
2899         sav_log = -1;
2900     }
2901     cmfdbi(&sw,                         /* Switches */
2902            _CMKEY,
2903            "Service name or port;\n or switch",
2904            "",                          /* default */
2905            "",                          /* addtl string data */
2906            nftpswi,                     /* addtl numeric data 1: tbl size */
2907            4,                           /* addtl numeric data 2: none */
2908            xxstring,                    /* Processing function */
2909            ftpswitab,                   /* Keyword table */
2910            &fl                          /* Pointer to next FDB */
2911            );
2912     cmfdbi(&fl,                         /* A host name or address */
2913            _CMFLD,                      /* fcode */
2914            "",                          /* help */
2915            "xYzBoo",                    /* default */
2916            "",                          /* addtl string data */
2917            0,                           /* addtl numeric data 1 */
2918            0,                           /* addtl numeric data 2 */
2919            xxstring,
2920            NULL,
2921            &cm
2922            );
2923     cmfdbi(&cm,                         /* Command confirmation */
2924            _CMCFM,
2925            "",
2926            "",
2927            "",
2928            0,
2929            0,
2930            NULL,
2931            NULL,
2932            NULL
2933            );
2934
2935     for (n = 0;; n++) {
2936         rc = cmfdb(&sw);                /* Parse a service name or a switch */
2937         if (rc < 0)
2938           goto xopenftp;
2939
2940         if (cmresult.fcode == _CMCFM) { /* Done? */
2941             break;
2942         } else if (cmresult.fcode == _CMFLD) {  /* Port */
2943             if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1))
2944               makestr(&service,cmresult.sresult);
2945             else
2946               makestr(&service,opn_tls?"ftps":"ftp");
2947         } else if (cmresult.fcode == _CMKEY) { /* Have a switch */
2948             c = cmgbrk();               /* get break character */
2949             getval = (c == ':' || c == '=');
2950             rc = -9;
2951             if (getval && !(cmresult.kflags & CM_ARG)) {
2952                 printf("?This switch does not take arguments\n");
2953                 goto xopenftp;
2954             }
2955             if (!getval && (cmresult.kflags & CM_ARG)) {
2956                 printf("?This switch requires an argument\n");
2957                 goto xopenftp;
2958             }
2959             switch (cmresult.nresult) { /* Switch */
2960               case OPN_ANO:             /* /ANONYMOUS */
2961                 anonymous++;
2962                 nologin = 0;
2963                 break;
2964               case OPN_NIN:             /* /NOINIT */
2965                 noinit++;
2966                 break;
2967               case OPN_NOL:             /* /NOLOGIN */
2968                 nologin++;
2969                 anonymous = 0;
2970                 makestr(&ftp_logname,NULL);
2971                 break;
2972               case OPN_PSW:             /* /PASSWORD */
2973                 if (!anonymous)         /* Don't log real passwords */
2974                   debok = 0;
2975                 rc = cmfld("Password for FTP server","",&p,xxstring);
2976                 if (rc == -3) {
2977                     makestr(&ftp_tmp,NULL);
2978                 } else if (rc < 0) {
2979                     goto xopenftp;
2980                 } else {
2981                     makestr(&ftp_tmp,brstrip(p));
2982                     nologin = 0;
2983                 }
2984                 break;
2985               case OPN_USR:             /* /USER */
2986                 rc = cmfld("Username for FTP server","",&p,xxstring);
2987                 if (rc == -3) {
2988                     makestr(&ftp_logname,NULL);
2989                 } else if (rc < 0) {
2990                     goto xopenftp;
2991                 } else {
2992                     nologin = 0;
2993                     anonymous = 0;
2994                     haveuser = 1;
2995                     makestr(&ftp_logname,brstrip(p));
2996                 }
2997                 break;
2998               case OPN_ACC:
2999                 rc = cmfld("Account for FTP server","",&p,xxstring);
3000                 if (rc == -3) {
3001                     makestr(&ftp_acc,NULL);
3002                 } else if (rc < 0) {
3003                     goto xopenftp;
3004                 } else {
3005                     makestr(&ftp_acc,brstrip(p));
3006                 }
3007                 break;
3008               case OPN_ACT:
3009                 opn_psv = 0;
3010                 break;
3011               case OPN_PSV:
3012                 opn_psv = 1;
3013                 break;
3014               case OPN_TLS:
3015                 opn_tls = 1;
3016                 break;
3017               default:
3018                 break;
3019             }
3020         }
3021         if (n == 0) {                   /* After first time through */
3022             cmfdbi(&sw,                 /* accept only switches */
3023                    _CMKEY,
3024                    "\nCarriage return to confirm to command, or switch",
3025                    "",
3026                    "",
3027                    nftpswi,
3028                    4,
3029                    xxstring,
3030                    ftpswitab,
3031                    &cm
3032                    );
3033         }
3034     }
3035 #ifdef COMMENT
3036     debug(F100,"ftp openftp while exit","",0);
3037     rc = cmcfm();
3038     debug(F101,"ftp openftp cmcfm rc","",rc);
3039     if (rc < 0)
3040       goto xopenftp;
3041 #endif /* COMMENT */
3042
3043     if (opn_psv > -1) {                 /* /PASSIVE or /ACTIVE switch given */
3044         sav_psv = ftp_psv;
3045         ftp_psv = opn_psv;
3046     }
3047     if (nologin || haveuser) {          /* /NOLOGIN or /USER switch given */
3048         sav_log = ftp_log;
3049         ftp_log = haveuser ? 1 : 0;
3050     }
3051     if (*hostname == '=') {             /* Bypass directory lookup */
3052         hostname++;                     /* if hostname starts with '=' */
3053         havehost++;
3054     } else if (isdigit(*hostname)) {    /* or if it starts with a digit */
3055         havehost++;
3056     }
3057     if (!service)
3058       makestr(&service,opn_tls?"ftps":"ftp");
3059
3060 #ifndef NODIAL
3061     if (!havehost && nnetdir > 0) {     /* If there is a networks directory */
3062         lunet(hostname);                /* Look up the name */
3063         debug(F111,"ftp openftp lunet",hostname,nhcount);
3064         if (nhcount == 0) {
3065             if (testing)
3066               printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3067             success = ftpopen(hostname,service,opn_tls);
3068             debug(F101,"ftp openftp A ftpopen success","",success);
3069             rc = success;
3070         } else {
3071             int found = 0;
3072             for (i = 0; i < nhcount; i++) {
3073                 if (nh_p2[i])           /* If network type specified */
3074                   if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0))
3075                     continue;
3076                 found++;
3077                 makestr(&hostname,nh_p[i]);
3078                 debug(F111,"ftpopen lunet substitution",hostname,i);
3079                 if (testing)
3080                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3081                 success = ftpopen(hostname,service,opn_tls);
3082                 debug(F101,"ftp openftp B ftpopen success","",success);
3083                 rc = success;
3084                 if (success)
3085                   break;
3086             }
3087             if (!found) {               /* E.g. if no network types match */
3088                 if (testing)
3089                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3090                 success = ftpopen(hostname,service,opn_tls);
3091                 debug(F101,"ftp openftp C ftpopen success","",success);
3092                 rc = success;
3093             }
3094         }
3095     } else {
3096 #endif /* NODIAL */
3097         if (testing)
3098           printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3099         success = ftpopen(hostname,service,opn_tls);
3100         debug(F111,"ftp openftp D ftpopen success",hostname,success);
3101         debug(F111,"ftp openftp D ftpopen connected",hostname,connected);
3102         rc = success;
3103 #ifndef NODIAL
3104     }
3105 #endif /* NODIAL */
3106
3107   xopenftp:
3108     debug(F101,"ftp openftp xopenftp rc","",rc);
3109     if (hostsave) free(hostsave);
3110     if (service) free(service);
3111     if (rc < 0 && ftp_logname) {
3112         free(ftp_logname);
3113         ftp_logname = NULL;
3114     }
3115     if (ftp_tmp) {
3116         free(ftp_tmp);
3117         ftp_tmp = NULL;
3118     }
3119     return(rc);
3120 }
3121
3122 VOID                                    /* 12 Aug 2007 */
3123 doftpglobaltype(x) int x; {
3124     ftp_xfermode = XMODE_M;             /* Set manual FTP transfer mode */
3125     ftp_typ = x;                        /* Used by top-level BINARY and */
3126     g_ftp_typ = x;                      /* ASCII commands. */
3127     get_auto = 0;
3128     forcetype = 1;
3129 }
3130
3131 int
3132 doftpacct() {
3133     int x;
3134     char * s;
3135     if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
3136       return(x);
3137     CHECKCONN();
3138     makestr(&ftp_acc,brstrip(s));
3139     if (testing)
3140       printf(" ftp account: \"%s\"\n",ftp_acc);
3141     success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
3142     return(success);
3143 }
3144
3145 int
3146 doftpusr() {                            /* Log in as USER */
3147     extern char uidbuf[];
3148     extern char pwbuf[];
3149     extern int  pwflg, pwcrypt;
3150     int x;
3151     char *s, * acct = "";
3152
3153     debok = 0;                          /* Don't log */
3154
3155     if ((x = cmfld("Remote username or ID",uidbuf,&s,xxstring)) < 0)
3156       return(x);
3157     ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */
3158     if ((x = cmfld("Remote password","",&s,xxstring)) < 0) {
3159         if (x == -3) { /* no input */
3160             if ( pwbuf[0] && pwflg ) {
3161                 ckstrncpy(tmpbuf,(char *)pwbuf,TMPBUFSIZ);
3162 #ifdef OS2
3163                 if ( pwcrypt )
3164                     ck_encrypt((char *)tmpbuf);
3165 #endif /* OS2 */
3166             }
3167         } else {
3168             return(x);
3169         }
3170     } else {
3171         ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
3172     }
3173     if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command",
3174                    "", &s, xxstring)) < 0)
3175       return(x);
3176     CHECKCONN();
3177     if (*s) {
3178         x = strlen(tmpbuf);
3179         if (x > 0) {
3180             acct = &tmpbuf[x+2];
3181             ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2);
3182         }
3183     }
3184     if (testing)
3185       printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf);
3186     success = ftp_user(line,tmpbuf,acct);
3187 #ifdef CKLOGDIAL
3188     dologftp();
3189 #endif /* CKLOGDIAL */
3190     return(success);
3191 }
3192
3193 /* DO (various FTP commands)... */
3194
3195 int
3196 doftptyp(type) int type; {              /* TYPE */
3197     CHECKCONN();
3198     ftp_typ = type;
3199     changetype(ftp_typ,ftp_vbm);
3200     debug(F101,"doftptyp changed type","",type);
3201     return(1);
3202 }
3203
3204 static int
3205 doftpxmkd(s,vbm) char * s; int vbm; {   /* MKDIR action */
3206     int lcs = -1, rcs = -1;
3207 #ifndef NOCSETS
3208     if (ftp_xla) {
3209         lcs = ftp_csl;
3210         if (lcs < 0) lcs = fcharset;
3211         rcs = ftp_csx;
3212         if (rcs < 0) rcs = ftp_csr;
3213     }
3214 #endif /* NOCSETS */
3215     debug(F110,"ftp doftpmkd",s,0);
3216     if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3217       return(success = 1);
3218     if (ftpcode == 500 || ftpcode == 502) {
3219         if (!quiet)
3220           printf("MKD command not recognized, trying XMKD\n");
3221         if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3222           return(success = 1);
3223     }
3224     return(success = 0);
3225 }
3226
3227 static int
3228 doftpmkd() {                            /* MKDIR parse */
3229     int x;
3230     char * s;
3231     if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0)
3232       return(x);
3233     CHECKCONN();
3234     ckstrncpy(line,s,LINBUFSIZ);
3235     if (testing)
3236       printf(" ftp mkdir \"%s\"...\n",line);
3237     return(success = doftpxmkd(line,-1));
3238 }
3239
3240 static int
3241 doftprmd() {                            /* RMDIR */
3242     int x, lcs = -1, rcs = -1;
3243     char * s;
3244     if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
3245       return(x);
3246     CHECKCONN();
3247     ckstrncpy(line,s,LINBUFSIZ);
3248     if (testing)
3249       printf(" ftp rmdir \"%s\"...\n",line);
3250 #ifndef NOCSETS
3251     if (ftp_xla) {
3252         lcs = ftp_csl;
3253         if (lcs < 0) lcs = fcharset;
3254         rcs = ftp_csx;
3255         if (rcs < 0) rcs = ftp_csr;
3256     }
3257 #endif /* NOCSETS */
3258     if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE)
3259       return(success = 1);
3260     if (ftpcode == 500 || ftpcode == 502) {
3261         if (!quiet)
3262           printf("RMD command not recognized, trying XMKD\n");
3263         success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
3264     } else
3265       success = 0;
3266     return(success);
3267 }
3268
3269 static int
3270 doftpren() {                            /* RENAME */
3271     int x;
3272     char * s;
3273     if ((x = cmfld("Remote filename","",&s,xxstring)) < 0)
3274       return(x);
3275     ckstrncpy(line,s,LINBUFSIZ);
3276     if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0)
3277       return(x);
3278     ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3279     if ((x = cmcfm()) < 0)
3280       return(x);
3281     CHECKCONN();
3282     if (testing)
3283       printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf);
3284     success = ftp_rename(line,tmpbuf);
3285     return(success);
3286 }
3287
3288 int
3289 doftpres() {                            /* RESET (log out without close) */
3290     int x;
3291     if ((x = cmcfm()) < 0)
3292       return(x);
3293     CHECKCONN();
3294     if (testing)
3295       printf(" ftp reset...\n");
3296     return(success = ftp_reset());
3297 }
3298
3299 static int
3300 doftpxhlp() {                           /* HELP */
3301     int x;
3302     char * s;
3303     if ((x = cmtxt("Command name", "", &s, xxstring)) < 0)
3304       return(x);
3305     CHECKCONN();
3306     ckstrncpy(line,s,LINBUFSIZ);
3307     if (testing)
3308       printf(" ftp help \"%s\"...\n",line);
3309     /* No need to translate -- all FTP commands are ASCII */
3310     return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE));
3311 }
3312
3313 static int
3314 doftpdir(cx) int cx; {                  /* [V]DIRECTORY */
3315     int x, lcs = 0, rcs = 0, xlate = 0;
3316     char * p, * s, * m = "";
3317     if (cx == FTP_VDI) {
3318         switch (servertype) {
3319           case SYS_VMS:
3320           case SYS_DOS:
3321           case SYS_TOPS10:
3322           case SYS_TOPS20:
3323             m = "*.*";
3324             break;
3325           default:
3326             m = "*";
3327         }
3328     }
3329     if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0)
3330       return(x);
3331     if ((x = remtxt(&s)) < 0)
3332       return(x);
3333 #ifdef NOCSETS
3334     xlate = 0;
3335 #else
3336     xlate = ftp_xla;
3337 #endif /* NOCSETS */
3338     line[0] = NUL;
3339     ckstrncpy(line,s,LINBUFSIZ);
3340     s = line;
3341     CHECKCONN();
3342
3343 #ifndef NOCSETS
3344     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
3345         lcs = ftp_csl;                  /* Local charset */
3346         if (lcs < 0) lcs = fcharset;
3347         if (lcs < 0) xlate = 0;
3348     }
3349     if (xlate) {                        /* Still ON? */
3350         rcs = ftp_csx;                  /* Remote (Server) charset */
3351         if (rcs < 0) rcs = ftp_csr;
3352         if (rcs < 0) xlate = 0;
3353     }
3354 #endif /* NOCSETS */
3355
3356     if (testing) {
3357         p = s;
3358         if (!p) p = "";
3359         if (*p)
3360           printf("Directory of files %s at %s:\n", line, ftp_host);
3361         else
3362           printf("Directory of files at %s:\n", ftp_host);
3363     }
3364     debug(F111,"doftpdir",s,cx);
3365
3366     if (cx == FTP_DIR) {
3367         /* Translation of line[] is done inside recvrequest() */
3368         /* when it calls ftpcmd(). */
3369         return(success =
3370           (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0));
3371     }
3372     success = 1;                        /* VDIR - one file at a time... */
3373     p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */
3374     cancelgroup = 0;
3375     if (!ftp_vbm && !quiet)
3376       printlines = 1;
3377     while (p && !cancelfile && !cancelgroup) { /* STAT one file */
3378         if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) {
3379             success = 0;
3380             break;
3381         }
3382         p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */
3383         debug(F110,"ftp vdir file",s,0);
3384     }
3385     return(success);
3386 }
3387
3388 static int
3389 doftppwd() {                            /* PWD */
3390     int x, lcs = -1, rcs = -1;
3391 #ifndef NOCSETS
3392     if (ftp_xla) {
3393         lcs = ftp_csl;
3394         if (lcs < 0) lcs = fcharset;
3395         rcs = ftp_csx;
3396         if (rcs < 0) rcs = ftp_csr;
3397     }
3398 #endif /* NOCSETS */
3399     if ((x = cmcfm()) < 0)
3400       return(x);
3401     CHECKCONN();
3402     if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) {
3403         success = 1;
3404     } else if (ftpcode == 500 || ftpcode == 502) {
3405         if (ftp_deb)
3406           printf("PWD command not recognized, trying XPWD\n");
3407         success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE);
3408     }
3409     return(success);
3410 }
3411
3412 static int
3413 doftpcwd(s,vbm) char * s; int vbm; {    /* CD (CWD) */
3414     int lcs = -1, rcs = -1;
3415 #ifndef NOCSETS
3416     if (ftp_xla) {
3417         lcs = ftp_csl;
3418         if (lcs < 0) lcs = fcharset;
3419         rcs = ftp_csx;
3420         if (rcs < 0) rcs = ftp_csr;
3421     }
3422 #endif /* NOCSETS */
3423
3424     debug(F110,"ftp doftpcwd",s,0);
3425     if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3426       return(success = 1);
3427     if (ftpcode == 500 || ftpcode == 502) {
3428         if (!quiet)
3429           printf("CWD command not recognized, trying XCWD\n");
3430         if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3431           return(success = 1);
3432     }
3433     return(success = 0);
3434 }
3435
3436 static int
3437 doftpcdup() {                           /* CDUP */
3438     debug(F100,"ftp doftpcdup","",0);
3439     if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE)
3440       return(success = 1);
3441     if (ftpcode == 500 || ftpcode == 502) {
3442         if (!quiet)
3443           printf("CDUP command not recognized, trying XCUP\n");
3444         if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE)
3445           return(success = 1);
3446     }
3447     return(success = 0);
3448 }
3449
3450 /* s y n c d i r  --  Synchronizes client & server directories */
3451
3452 /*
3453   Call with:
3454     local = pointer to pathname of local file to be sent.
3455     sim   = 1 for simulation, 0 for real uploading.
3456   Returns 0 on failure, 1 on success.
3457
3458   The 'local' argument is relative to the initial directory of the MPUT,
3459   i.e. the root of the tree being uploaded.  If the directory of the
3460   argument file is different from the directory of the previous file
3461   (which is stored in global putpath[]), this routine does the appropriate
3462   CWDs, CDUPs, and/or MKDIRs to position the FTP server in the same place.
3463 */
3464 static int cdlevel = 0, cdsimlvl = 0;   /* Tree-level trackers */
3465
3466 static int
3467 syncdir(local,sim) char * local; int sim; {
3468     char buf[CKMAXPATH+1];
3469     char tmp[CKMAXPATH+1];
3470     char msgbuf[CKMAXPATH+64];
3471     char c, * p = local, * s = buf, * q = buf, * psep, * ssep;
3472     int i, k = 0, done = 0, itsadir = 0, saveq;
3473
3474     debug(F110,"ftp syncdir local (new)",local,0);
3475     debug(F110,"ftp syncdir putpath (old)",putpath,0);
3476
3477     itsadir = isdir(local);             /* Is the local file a directory? */
3478     saveq = quiet;
3479
3480     while ((*s = *p)) {                 /* Copy the argument filename */
3481         if (++k == CKMAXPATH)           /* so we can poke it. */
3482           return(-1);
3483         if (*s == '/')                  /* Pointer to rightmost dirsep */
3484           q = s;
3485         s++;
3486         p++;
3487     }
3488     if (!itsadir)                       /* If it's a regular file */
3489       *q = NUL;                         /* keep just the path part */
3490
3491     debug(F110,"ftp syncdir buf",buf,0);
3492     if (!strcmp(buf,putpath)) {         /* Same path as previous file? */
3493         if (itsadir) {                  /* This file is a directory? */
3494             if (doftpcwd(local,0)) {    /* Try to CD to it */
3495                 doftpcdup();            /* Worked - CD back up */
3496             } else if (sim) {           /* Simulating... */
3497                 if (fdispla == XYFD_B) {
3498                     printf("WOULD CREATE DIRECTORY %s\n",local);
3499                 } else if (fdispla) {
3500                     ckmakmsg(msgbuf,CKMAXPATH,
3501                              "WOULD CREATE DIRECTORY",local,NULL,NULL);
3502                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3503                 }
3504                 /* See note above */
3505                 return(0);
3506             } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */
3507                 return(0);
3508             } else {                    /* Remote directory created OK */
3509                 if (fdispla == XYFD_B) {
3510                     printf("CREATED DIRECTORY %s\n",local);
3511                 } else if (fdispla) {
3512                     ckmakmsg(msgbuf,CKMAXPATH+64,
3513                              "CREATED DIRECTORY ",local,NULL,NULL);
3514                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3515                 }
3516             }
3517         }
3518         debug(F110,"ftp syncdir no change",buf,0);
3519         return(1);                      /* Yes, done. */
3520     }
3521     ckstrncpy(tmp,buf,CKMAXPATH+1);     /* Make a safe (pre-poked) copy */
3522     debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */
3523
3524     p = buf;                            /* New */
3525     s = putpath;                        /* Old */
3526
3527     debug(F110,"ftp syncdir A (old) s",s,0); /* Previous */
3528     debug(F110,"ftp syncdir A (new) p",p,0); /* New */
3529
3530     psep = buf;
3531     ssep = putpath;
3532     while (*p != NUL && *s != NUL && *p == *s) {
3533         if (*p == '/') { psep = p+1; ssep = s+1; }
3534         p++,s++;
3535     }
3536     /*
3537       psep and ssep point to the first path segment that differs.
3538       We have to do as many CDUPs as there are path segments in ssep.
3539       then we have to do as many MKDs and CWDs as there are segments in psep.
3540     */
3541     s = ssep;
3542     p = psep;
3543
3544     debug(F110,"ftp syncdir B (old) s",s,0); /* Previous */
3545     debug(F110,"ftp syncdir B (new) p",p,0); /* New */
3546
3547     /* p and s now point to the leftmost spot where the paths differ */
3548
3549     if (*s) {                           /* We have to back up */
3550         k = 1;                          /* How many levels counting this one */
3551         while ((c = *s++)) {            /* Count dirseps remaining in prev */
3552             if (c == '/' && *s)
3553               k++;
3554         }
3555         debug(F101,"ftp syncdir levels up","",k);
3556
3557         for (i = 1; i <= k; i++) {       /* Do that many CDUPs */
3558             debug(F111,"ftp syncdir CDUP A",p,i);
3559             if (fdispla == XYFD_B)
3560               printf(" CDUP\n");
3561             if (sim && cdsimlvl) {
3562                 cdsimlvl--;
3563             } else {
3564                 if (!doftpcdup()) {
3565                     quiet = saveq;
3566                     return(0);
3567                 }
3568             }
3569             cdlevel--;
3570         }
3571         if (!*p)                        /* If we don't have to go down */
3572           goto xcwd;                    /* we're done. */
3573     }
3574 #ifdef COMMENT
3575     while (p > buf && *p && *p != '/')  /* If in middle of segment */
3576       p--;                              /* back up to beginning */
3577     if (*p == '/')                      /* and terminate there */
3578       p++;
3579 #endif  /* COMMENT */
3580
3581     debug(F110,"ftp syncdir NEW PATH",p,0);
3582
3583     s = p;                              /* Point to start of new down path. */
3584     while (1) {                         /* Loop through characters. */
3585         if (*s == '/' || !*s) {         /* Have a segment. */
3586             if (!*s)                    /* If end of string, */
3587               done++;                   /* after this segment we're done. */
3588             else
3589               *s = NUL;                 /* NUL out the separator. */
3590             if (*p) {                   /* If segment is not empty */
3591                 debug(F110,"ftp syncdir down segment",p,0);
3592                 if (!doftpcwd(p,0)) {   /* Try to CD to it */
3593                     if (sim) {
3594                         if (fdispla == XYFD_B) {
3595                             printf(" WOULD CREATE DIRECTORY %s\n",local);
3596                         } else if (fdispla) {
3597                             ckmakmsg(msgbuf,CKMAXPATH,
3598                                      "WOULD CREATE DIRECTORY",
3599                                      local,NULL,NULL);
3600                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3601                         }
3602                         cdsimlvl++;
3603                     } else {
3604                         if (!doftpxmkd(p,0)) { /* Can't CD - try to create */
3605                             debug(F110,"ftp syncdir mkdir failed",p,0); 
3606 /*
3607   Suppose we are executing SEND /RECURSIVE.  Locally we have a directory
3608   FOO but the remote has a regular file with the same name.  We can't CD
3609   to it, can't MKDIR it either.  There's no way out but to fail and let
3610   the user handle the problem.
3611 */
3612                             quiet = saveq;
3613                             return(0);
3614                         }
3615                         debug(F110,"ftp syncdir mkdir OK",p,0); 
3616                         if (fdispla == XYFD_B) {
3617                             printf(" CREATED DIRECTORY %s\n",p);
3618                         } else if (fdispla) {
3619                             ckmakmsg(msgbuf,CKMAXPATH,
3620                                      "CREATED DIRECTORY ",p,NULL,NULL);
3621                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3622                         }
3623                         if (!doftpcwd(p,0)) { /* Try again to CD */
3624                             debug(F110,"ftp syncdir CD failed",p,0); 
3625                             quiet = saveq;
3626                             return(0);
3627                         }
3628                         if (fdispla == XYFD_B) printf(" CWD %s\n",p);
3629                         debug(F110,"ftp syncdir CD OK",p,0); 
3630                     }
3631                 }
3632                 cdlevel++;
3633             }
3634             if (done)                   /* Quit if no next segment */
3635               break;
3636             p = s+1;                    /* Point to next segment */
3637         }
3638         s++;                            /* Point to next source char */
3639     }
3640
3641   xcwd:
3642     ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */
3643     quiet = saveq;
3644     return(1);
3645 }
3646
3647 #ifdef DOUPDATE
3648 #ifdef DEBUG
3649 static VOID
3650 dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */
3651     if (deblog) {
3652         debug(F111,"ftp year ",s,xx->tm_year);
3653         debug(F111,"ftp month",s,xx->tm_mon);
3654         debug(F111,"ftp day  ",s,xx->tm_mday);
3655         debug(F111,"ftp hour ",s,xx->tm_hour);
3656         debug(F111,"ftp min  ",s,xx->tm_min);
3657         debug(F111,"ftp sec  ",s,xx->tm_sec);
3658     }
3659 }
3660 #endif /* DEBUG */
3661
3662 /*  t m c o m p a r e  --  Compare two struct tm's */
3663
3664 /*  Like strcmp() but for struct tm's  */
3665 /*  Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */
3666
3667 static int
3668 tmcompare(xx,yy) struct tm * xx, * yy; {
3669
3670     if (xx->tm_year < yy->tm_year)      /* First year less than second */
3671       return(-1);
3672     if (xx->tm_year > yy->tm_year)      /* First year greater than second */
3673       return(1);
3674
3675     /* Years are equal so compare months */
3676
3677     if (xx->tm_mon  < yy->tm_mon)       /* And so on... */
3678       return(-1);
3679     if (xx->tm_mon  > yy->tm_mon)
3680       return(1);
3681
3682     if (xx->tm_mday < yy->tm_mday)
3683       return(-1);
3684     if (xx->tm_mday > yy->tm_mday)
3685       return(1);
3686
3687     if (xx->tm_hour < yy->tm_hour)
3688       return(-1);
3689     if (xx->tm_hour > yy->tm_hour)
3690       return(1);
3691
3692     if (xx->tm_min  < yy->tm_min)
3693       return(-1);
3694     if (xx->tm_min  > yy->tm_min)
3695       return(1);
3696
3697     if (xx->tm_sec  < yy->tm_sec)
3698       return(-1);
3699     if (xx->tm_sec  > yy->tm_sec)
3700       return(1);
3701
3702     return(0);
3703 }
3704 #endif /* DOUPDATE */
3705
3706 #ifndef HAVE_TIMEGM             /* For platforms that do not have timegm() */
3707 static CONST int MONTHDAYS[] = { /* Number of days in each month. */
3708     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3709 };
3710
3711 /* Macro for whether a given year is a leap year. */
3712 #define ISLEAP(year) \
3713 (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
3714 #endif /* HAVE_TIMEGM */
3715
3716 /*  m k u t i m e  --  Like mktime() but argument is already UTC */
3717
3718 static time_t
3719 #ifdef CK_ANSIC
3720 mkutime(struct tm * tm)
3721 #else
3722 mkutime(tm) struct tm * tm;
3723 #endif /* CK_ANSIC */
3724 /* mkutime */ {
3725 #ifdef HAVE_TIMEGM
3726     return(timegm(tm));                 /* Have system service, use it. */
3727 #else
3728 /*
3729   Contributed by Russ Allbery (rra@stanford.edu), used by permission.
3730   Given a struct tm representing a calendar time in UTC, convert it to
3731   seconds since epoch.  Returns (time_t) -1 if the time is not
3732   convertable.  Note that this function does not canonicalize the provided
3733   struct tm, nor does it allow out-of-range values or years before 1970.
3734   Result should be identical with timegm().
3735 */
3736     time_t result = 0;
3737     int i;
3738     /*
3739       We do allow some ill-formed dates, but we don't do anything special
3740       with them and our callers really shouldn't pass them to us.  Do
3741       explicitly disallow the ones that would cause invalid array accesses
3742       or other algorithm problems.
3743     */
3744 #ifdef DEBUG
3745     if (deblog) {
3746         debug(F101,"mkutime tm_mon","",tm->tm_mon);
3747         debug(F101,"mkutime tm_year","",tm->tm_year);
3748     }
3749 #endif /* DEBUG */
3750     if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70)
3751       return((time_t) -1);
3752
3753     /* Convert to time_t. */
3754     for (i = 1970; i < tm->tm_year + 1900; i++)
3755       result += 365 + ISLEAP(i);
3756     for (i = 0; i < tm->tm_mon; i++)
3757       result += MONTHDAYS[i];
3758     if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900))
3759       result++;
3760     result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
3761     result = 60 * result + tm->tm_min;
3762     result = 60 * result + tm->tm_sec;
3763     debug(F101,"mkutime result","",result);
3764     return(result);
3765 #endif /* HAVE_TIMEGM */
3766 }
3767
3768
3769 /*
3770   s e t m o d t i m e  --  Set file modification time.
3771
3772   f = char * filename;
3773   t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local)
3774
3775   UNIX-specific; isolates mainline code from hideous #ifdefs.
3776   Returns:
3777     0 on success,
3778    -1 on error.
3779
3780 */
3781 static int
3782 #ifdef CK_ANSIC
3783 setmodtime(char * f, time_t t)
3784 #else
3785 setmodtime(f,t) char * f; time_t t;
3786 #endif /* CK_ANSIC */
3787 /* setmodtime */ {
3788 #ifdef NT
3789     struct _stat sb;
3790 #else /* NT */
3791     struct stat sb;
3792 #endif /* NT */
3793     int x, rc = 0;
3794 #ifdef BSD44
3795     struct timeval tp[2];
3796 #else  /* def BSD44 */
3797 #ifdef V7
3798     struct utimbuf {
3799         time_t timep[2];
3800     } tp;
3801 #else  /* def V7 */
3802 #ifdef SYSUTIMEH
3803 #ifdef NT
3804     struct _utimbuf tp;
3805 #else /* NT */
3806     struct utimbuf tp;
3807 #endif /* NT */
3808 #else /* def SYSUTIMEH */
3809 #ifdef VMS
3810     struct utimbuf tp;
3811 #define SYSUTIMEH               /* Our utimbuf matches this one. */
3812 #else /* def VMS */
3813     struct utimbuf {
3814         time_t atime;
3815         time_t mtime;
3816     } tp;
3817 #endif /* def VMS [else] */
3818 #endif /* def SYSUTIMEH [else] */
3819 #endif /* def V7 [else] */
3820 #endif /* def BSD44 [else] */
3821
3822     if (stat(f,&sb) < 0) {
3823         debug(F111,"setmodtime stat failure",f,errno);
3824         return(-1);
3825     }
3826 #ifdef BSD44
3827     tp[0].tv_sec = sb.st_atime;         /* Access time first */
3828     tp[1].tv_sec = t;                   /* Update time second */
3829     debug(F111,"setmodtime BSD44",f,t);
3830 #else
3831 #ifdef V7
3832     tp.timep[0] = t;                    /* Set modif. time to creation date */
3833     tp.timep[1] = sb.st_atime;          /* Don't change the access time */
3834     debug(F111,"setmodtime V7",f,t);
3835 #else
3836 #ifdef SYSUTIMEH
3837     tp.modtime = t;                     /* Set modif. time to creation date */
3838     tp.actime = sb.st_atime;            /* Don't change the access time */
3839     debug(F111,"setmodtime SYSUTIMEH",f,t);
3840 #else
3841     tp.mtime = t;                       /* Set modif. time to creation date */
3842     tp.atime = sb.st_atime;             /* Don't change the access time */
3843     debug(F111,"setmodtime (other)",f,t);
3844 #endif /* SYSUTIMEH */
3845 #endif /* V7 */
3846 #endif /* BSD44 */
3847
3848     /* Try to set the file date */
3849
3850 #ifdef BSD44
3851     x = utimes(f,tp);
3852     debug(F111,"setmodtime utimes()","BSD44",x);
3853 #else
3854 #ifdef IRIX65
3855     {
3856       /*
3857         The following produces the nonsensical warning:
3858         Argument  of type "const struct utimbuf *" is incompatible with
3859         parameter of type "const struct utimbuf *".  If you can make it
3860         go away, be my guest.
3861       */
3862         const struct utimbuf * t2 = &tp;
3863         x = utime(f,t2);
3864     }
3865 #else
3866     x = utime(f,&tp);
3867     debug(F111,"setmodtime utime()","other",x);
3868 #endif /* IRIX65 */
3869 #endif /* BSD44 */
3870     if (x)
3871       rc = -1;
3872
3873     debug(F101,"setmodtime result","",rc);
3874     return(rc);
3875 }
3876
3877
3878 /*
3879   c h k m o d t i m e  --  Check/Set file modification time.
3880
3881   fc = function code:
3882     0 = Check; returns:
3883       -1 on error,
3884        0 if local older than remote,
3885        1 if modtimes are equal,
3886        2 if local newer than remote.
3887     1 = Set (local file's modtime from remote's); returns:
3888       -1 on error,
3889        0 on success.
3890 */
3891 static int
3892 chkmodtime(local,remote,fc) char * local, * remote; int fc; {
3893 #ifdef NT
3894     struct _stat statbuf;
3895 #else /* NT */
3896     struct stat statbuf;
3897 #endif /* NT */
3898     struct tm * tmlocal = NULL;
3899     struct tm tmremote;
3900     int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0;
3901     char * s, timebuf[64];
3902
3903     debug(F111,"chkmodtime",local,mdtmok);
3904     if (!mdtmok)                        /* Server supports MDTM? */
3905       return(-1);                       /* No don't bother. */
3906
3907 #ifndef NOCSETS
3908     if (ftp_xla) {
3909         lcs = ftp_csl;
3910         if (lcs < 0) lcs = fcharset;
3911         rcs = ftp_csx;
3912         if (rcs < 0) rcs = ftp_csr;
3913     }
3914 #endif /* NOCSETS */
3915
3916     if (fc == 0) {
3917         rc = stat(local,&statbuf);
3918         if (rc == 0) {                  /* Get local file's mod time */
3919             /* Convert to struct tm */
3920             tmlocal = gmtime((time_t *)&statbuf.st_mtime);
3921 #ifdef DEBUG
3922             if (tmlocal) {
3923                 dbtime(local,tmlocal);
3924             }
3925 #endif /* DEBUG */
3926         }
3927     }
3928     /* Get remote file's mod time as yyyymmddhhmmss */
3929
3930     if (havemdtm) {                     /* Already got it from MLSD? */
3931         s = havemdtm;
3932         flag++;
3933     } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) {
3934         char c;
3935         bzero((char *)&tmremote, sizeof(struct tm));
3936         s = ftp_reply_str;
3937         while ((c = *s++)) {            /* Skip past response code */
3938             if (c == SP) {
3939                 flag++;
3940                 break;
3941             }
3942         }
3943     }
3944     if (flag) {
3945         debug(F111,"ftp chkmodtime string",s,flag);
3946         if (fts_sto) {                  /* User gave server time offset? */
3947             char * p;
3948             debug(F110,"ftp chkmodtime offset",fts_sto,0);
3949             ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */
3950             if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */
3951                 ckstrncpy(timebuf,p,64);      /* Convert to MDTM format */
3952                 timebuf[8]  = timebuf[9];  /* h */
3953                 timebuf[9]  = timebuf[10]; /* h */
3954                 timebuf[10] = timebuf[12]; /* m */
3955                 timebuf[11] = timebuf[13]; /* m */
3956                 timebuf[12] = timebuf[12]; /* s */
3957                 timebuf[13] = timebuf[13]; /* s */
3958                 timebuf[14] = NUL;
3959                 s = timebuf;
3960                 debug(F110,"ftp chkmodtime adjust",s,0);
3961             }
3962         }
3963         if (flag) {                     /* Convert to struct tm */
3964             char * pat;
3965             int y2kbug = 0;             /* Seen in Kerberos 4 FTP servers */
3966             if (!ckstrcmp(s,"191",3,0)) {
3967                 pat = "%05d%02d%02d%02d%02d%02d";
3968                 y2kbug++;
3969                 debug(F110,"ftp chkmodtime Y2K BUG detected",s,0);
3970             } else {
3971                 pat = "%04d%02d%02d%02d%02d%02d";
3972             }
3973             if (sscanf(s,               /* Parse into struct tm */
3974                        pat,
3975                        &(tmremote.tm_year),
3976                        &(tmremote.tm_mon),
3977                        &(tmremote.tm_mday),
3978                        &(tmremote.tm_hour),
3979                        &(tmremote.tm_min),
3980                        &(tmremote.tm_sec)
3981                        ) == 6) {
3982                 tmremote.tm_year -= (y2kbug ? 19000 : 1900);
3983                 debug(F101,"ftp chkmodtime year","",tmremote.tm_year);
3984                 tmremote.tm_mon--;
3985
3986 #ifdef DEBUG
3987                 debug(F100,"SERVER TIME FOLLOWS:","",0);
3988                 dbtime(remote,&tmremote);
3989 #endif /* DEBUG */
3990
3991                 if (havedate > -1)
3992                   havedate = 1;
3993             }
3994         }
3995     } else {                            /* Failed */
3996         debug(F101,"ftp chkmodtime ftpcode","",ftpcode);
3997         if (ftpcode == 500 ||           /* Command unrecognized */
3998             ftpcode == 502 ||           /* Command not implemented */
3999             ftpcode == 202)             /* Command superfluous */
4000           mdtmok = 0;                   /* Don't ask this server again */
4001         return(-1);
4002     }
4003     if (fc == 0) {                      /* Compare */
4004         if (havedate == 1) {            /* Only if we have both file dates */
4005             /*
4006               Compare with local file's time.  We don't use
4007               clock time (time_t) here in case of signed/unsigned
4008               confusion, etc.
4009             */
4010             int xx;
4011 #ifdef COMMENT
4012 #ifdef DEBUG        
4013             if (deblog) {
4014                 dbtime("LOCAL",tmlocal);
4015                 dbtime("REMOT",&tmremote);
4016             }
4017 #endif /* DEBUG */
4018 #endif /* COMMENT */
4019             xx = tmcompare(tmlocal,&tmremote);
4020             debug(F101,"chkmodtime tmcompare","",xx);
4021             return(xx + 1);
4022         }
4023     } else if (ftp_dates) {             /* Set */
4024         /*
4025           Here we must convert struct tm to time_t
4026           without applying timezone conversion, for which
4027           there is no portable API.  The method is hidden
4028           in mkutime(), defined above.
4029         */
4030         time_t utc;
4031         utc = mkutime(&tmremote);
4032         debug(F111,"ftp chkmodtime mkutime",remote,utc);
4033         if (utc != (time_t)-1)
4034           return(setmodtime(local,utc));
4035     }
4036     return(-1);
4037 }
4038
4039 /* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */
4040
4041 static int
4042 getfile(remote,local,recover,append,pipename,xlate,fcs,rcs)
4043     char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs;
4044 /* getfile */ {
4045     int rc = -1;
4046     ULONG t0, t1;
4047
4048 #ifdef GFTIMER
4049     CKFLOAT sec;
4050 #else
4051     int sec = 0;
4052 #endif /* GFTIMER */
4053     char fullname[CKMAXPATH+1];
4054
4055     debug(F110,"ftp getfile remote A",remote,0);
4056     debug(F110,"ftp getfile local A",local,0);
4057     debug(F110,"ftp getfile pipename",pipename,0);
4058     if (!remote) remote = "";
4059
4060 #ifdef PATTERNS
4061     /* Automatic type switching? */
4062     if (ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype) {
4063         int x;
4064         x = matchname(remote,0,servertype);
4065         debug(F111,"ftp getfile matchname",remote,x);
4066         switch (x) {
4067           case 0: ftp_typ = FTT_ASC; break;
4068           case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break;
4069           default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ;
4070         }
4071         changetype(ftp_typ,ftp_vbm);
4072         binary = ftp_typ;               /* For file-transfer display */
4073     }
4074 #endif /* PATTERNS */
4075
4076 #ifndef NOCSETS
4077     ftp_csx = -1;                       /* For file-transfer display */
4078     ftp_csl = -1;                       /* ... */
4079
4080     if (rcs > -1)                       /* -1 means no translation */
4081       if (ftp_typ == FTT_ASC)           /* File type is "ascii"? */
4082         if (fcs < 0)                    /* File charset not forced? */
4083           fcs = fcharset;               /* use prevailing FILE CHARACTER-SET */
4084     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4085         debug(F110,"ftp getfile","initxlate",0);
4086         initxlate(rcs,fcs);             /* NB: opposite order of PUT */
4087         ftp_csx = rcs;
4088         ftp_csl = fcs;
4089     } else
4090       xlate = 0;
4091 #endif /* NOCSETS */
4092
4093     if (!local) local = "";
4094     if (!pipename && !*local)
4095       local = remote;
4096
4097     out2screen = !strcmp(local,"-");
4098
4099     fullname[0] = NUL;
4100     if (pipename) {
4101         ckstrncpy(fullname,pipename,CKMAXPATH+1);
4102     } else {
4103         zfnqfp(local,CKMAXPATH,fullname);
4104         if (!fullname[0])
4105           ckstrncpy(fullname,local,CKMAXPATH+1);
4106     }
4107     if (!out2screen && displa && fdispla) { /* Screen */
4108         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,remote);
4109         ftscreen(SCR_AN,0,(CK_OFF_T)0,fullname);
4110         ftscreen(SCR_FS,0,fsize,"");
4111     }
4112     tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0);
4113     tlog(F110," as",fullname,0);
4114     debug(F111,"ftp getfile size",remote,fsize);
4115     debug(F111,"ftp getfile local",local,out2screen);
4116
4117     ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH);
4118
4119     t0 = gmstimer();                    /* Start time */
4120     debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */
4121     rc = recvrequest("RETR",
4122                      local,
4123                      remote,
4124                      append ? "ab" : "wb",
4125                      0,
4126                      recover,
4127                      pipename,
4128                      xlate,
4129                      fcs,
4130                      rcs
4131                      );
4132     t1 = gmstimer();                    /* End time */
4133     debug(F111,"ftp getfile t1",remote,t1);
4134     debug(F111,"ftp getfile sec",remote,(t1-t0)/1000);
4135 #ifdef GFTIMER
4136     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4137     fpxfsecs = sec;                     /* (for doxlog()) */
4138 #else
4139     sec = (t1 - t0) / 1000;
4140     xfsecs = (int)sec;
4141 #endif /* GFTIMER */
4142
4143 #ifdef FTP_TIMEOUT
4144     if (ftp_timed_out)
4145       rc = -4;
4146 #endif  /* FTP_TIMEOUT */
4147
4148     debug(F111,"ftp recvrequest rc",remote,rc);
4149     if (cancelfile || cancelgroup) {
4150         debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup);
4151         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4152     } else if (rc > 0) {
4153         debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup);
4154         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,cmarg);
4155     } else if (rc < 0) {
4156         switch (ftpcode) {
4157           case -4:                      /* Network error */
4158           case -2:                      /* File error */
4159             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,ck_errstr());
4160             break;
4161           case -3:
4162             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,
4163                      "Failure to make data connection");
4164             break;
4165           case -1:                      /* (should be covered above) */
4166             ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4167             break;
4168           default:
4169             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4170         }
4171     } else {                            /* Tudo bem */
4172         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4173         if (rc == 0) {
4174             ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,""); /* For screen */
4175             makestr(&rrfspec,remote);     /* For WHERE command */
4176             makestr(&rfspec,fullname);
4177         }
4178     }
4179     if (rc > -1) {
4180         if (ftp_dates)                  /* If FTP DATES ON... */
4181           if (!pipename && !out2screen) /* and it's a real file */
4182             if (rc < 1 && rc != -3)     /* and it wasn't skipped */
4183               if (connected)            /* and we still have a connection */
4184                 if (zchki(local) > -1) { /* and the file wasn't discarded */
4185                     chkmodtime(local,remote,1); /* set local file date */
4186                     debug(F110,"ftp get set date",local,0);
4187                 }
4188         filcnt++;                       /* Used by \v(filenum) */
4189     }
4190 #ifdef TLOG
4191     if (tralog) {
4192         if (rc > 0) {
4193             tlog(F100," recovery skipped","",0);
4194         } else if (rc == 0) {
4195             tlog(F101," complete, size", "", fsize);
4196         } else if (cancelfile) {
4197             tlog(F100," canceled by user","",0);
4198 #ifdef FTP_TIMEOUT
4199         } else if (ftp_timed_out) {
4200             tlog(F100," timed out","",0);
4201 #endif  /* FTP_TIMEOUT */
4202         } else {
4203             tlog(F110," failed:",ftp_reply_str,0);
4204         }
4205         if (!tlogfmt)
4206           doxlog(what,local,fsize,ftp_typ,rc,"");
4207     }
4208 #endif /* TLOG */
4209     return(rc);
4210 }
4211
4212 /* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */
4213 /* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */
4214
4215 static int
4216 putfile(cx,
4217     local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg)
4218     char * local, * remote, * mvto, *rnto, *srvrn;
4219     int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg, prm;
4220
4221 /* putfile */ {
4222
4223     char asname[CKMAXPATH+1];
4224     char fullname[CKMAXPATH+1];
4225     int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0;
4226     int xlate = 0, restart = 0, mt = -1;
4227     char * s = NULL, * cmd = NULL;
4228     ULONG t0 = 0, t1 = 0;               /* Times for stats */
4229     int ofcs = 0, orcs = 0;
4230
4231 #ifdef GFTIMER
4232     CKFLOAT sec = 0.0;
4233 #else
4234     int sec = 0;
4235 #endif /* GFTIMER */
4236     debug(F111,"ftp putfile flg",local,flg);
4237     debug(F110,"ftp putfile srv_renam",srvrn,0);
4238     debug(F101,"ftp putfile fcs","",fcs);
4239     debug(F101,"ftp putfile rcs","",rcs);
4240
4241     ofcs = fcs;                         /* Save charset args */
4242     orcs = rcs;
4243
4244     sendstart = (CK_OFF_T)0;
4245     restart = flg & PUT_RES;
4246     if (!remote)
4247       remote = "";
4248
4249     /* FTP protocol command to send to server */
4250     cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR");
4251
4252     if (x_cnv == SET_AUTO) {            /* Name conversion is auto */
4253         if (alike) {                    /* If server & client are alike */
4254             nc = 0;                     /* no conversion */
4255         } else {                        /* If they are different */
4256             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
4257               nc = -1;                  /* only minimal conversions needed */
4258             else                        /* otherwise */
4259               nc = 1;                   /* full conversion */
4260         }
4261     } else                              /* Not auto - do what user said */
4262       nc = x_cnv;
4263
4264     /* If Transfer Mode is Automatic, determine file type */
4265     if (ftp_xfermode == XMODE_A && filepeek && !pipesend) {
4266         if (isdir(local)) {             /* If it's a directory */
4267             k = FT_BIN;                 /* skip the file scan */
4268         } else {
4269             debug(F110,"FTP PUT calling scanfile",local,0);
4270             k = scanfile(local,&o,nscanfile); /* Scan the file */
4271         }
4272         debug(F111,"FTP PUT scanfile",local,k);
4273         if (k > -1 && !forcetype) {
4274             ftp_typ = (k == FT_BIN) ? 1 : 0;
4275             if (xft > -1 && ftp_typ != xft) {
4276                 if (flg & PUT_SIM)
4277                   tlog(F110,"ftp put SKIP (Type):", local, 0);
4278                 return(SKP_TYP);
4279             }
4280             if (ftp_typ == 1 && tenex)  /* User said TENEX? */
4281               ftp_typ = FTT_TEN;
4282         }
4283     }
4284 #ifndef NOCSETS
4285     ftp_csx = -1;                       /* For file-transfer display */
4286     ftp_csl = -1;                       /* ... */
4287
4288     if (rcs > -1) {                     /* -1 means no translation */
4289         if (ftp_typ == 0) {             /* File type is "ascii"? */
4290             if (fcs < 0) {              /* File charset not forced? */
4291                 if (k < 0) {            /* If we didn't scan */
4292                     fcs = fcharset;     /* use prevailing FILE CHARACTER-SET */
4293                 } else {                /* If we did scan, use scan result */
4294                     switch (k) {
4295                       case FT_TEXT:     /* Unknown text */
4296                         fcs = fcharset;
4297                         break;
4298                       case FT_7BIT:     /* 7-bit text */
4299                         fcs = dcset7;
4300                         break;
4301                       case FT_8BIT:     /* 8-bit text */
4302                         fcs = dcset8;
4303                         break;
4304                       case FT_UTF8:     /* UTF-8 */
4305                         fcs = FC_UTF8;
4306                         break;
4307                       case FT_UCS2:     /* UCS-2 */
4308                         fcs = FC_UCS2;
4309                         if (o > -1)     /* Input file byte order */
4310                           fileorder = o;
4311                         break;
4312                       default:
4313                         rcs = -1;
4314                     }
4315                 }
4316             }
4317         }
4318     }
4319     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4320         debug(F110,"ftp putfile","initxlate",0);
4321         initxlate(fcs,rcs);
4322         debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs);
4323         xlate = 1;
4324         ftp_csx = rcs;
4325         ftp_csl = fcs;
4326     }
4327 #endif /* NOCSETS */
4328
4329     binary = ftp_typ;                   /* For file-transfer display */
4330     asname[0] = NUL;
4331
4332     if (recursive) {                    /* If sending recursively, */
4333         if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */
4334           return(-1);                   /* Don't PUT if it fails. */
4335         else if (isdir(local))          /* It's a directory */
4336           return(0);                    /* Don't send it! */
4337     }
4338     if (*remote) {                      /* If an as-name template was given */
4339 #ifndef NOSPL
4340         if (cmd_quoting) {              /* and COMMAND QUOTING is ON */
4341             y = CKMAXPATH;              /* evaluate it for this file */
4342             s = asname;
4343             zzstring(remote,&s,&y);
4344         } else
4345 #endif /* NOSPL */
4346           ckstrncpy(asname,remote,CKMAXPATH);   /* (or take it literally) */
4347     } else {                                    /* No as-name */
4348         nzltor(local,asname,nc,0,CKMAXPATH);    /* use local name strip path */
4349         debug(F110,"FTP PUT nzltor",asname,0);
4350     }
4351     /* Preliminary messages and log entries */
4352
4353     fullname[0] = NUL;
4354     zfnqfp(local,CKMAXPATH,fullname);
4355     if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1);
4356     fullname[CKMAXPATH] = NUL;
4357
4358     if (displa && fdispla) {            /* Screen */
4359         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,local);
4360         ftscreen(SCR_AN,0,(CK_OFF_T)0,asname);
4361         ftscreen(SCR_FS,0,fsize,"");
4362     }
4363 #ifdef DOUPDATE
4364     if (flg & (PUT_UPD|PUT_DIF)) {      /* Date-checking modes... */
4365         mt = chkmodtime(fullname,asname,0);
4366         debug(F111,"ftp putfile chkmodtime",asname,mt);
4367         if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */
4368             tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0);
4369             /* Skip this one */
4370             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_DAT,fullname);
4371             filcnt++;
4372             return(SKP_DAT);
4373         } else if (mt == 1) {           /* Times are equal */
4374             tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0);
4375             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_EQU,fullname); /* Skip it */
4376             filcnt++;
4377             return(SKP_DAT);
4378         }
4379         /* Local file is newer */
4380         tlog(F110,ftp_typ ? "ftp put /update BINARY:" :
4381              "ftp put /update TEXT:", fullname, 0);
4382     } else if (flg & PUT_RES) {
4383         tlog(F110,ftp_typ ? "ftp put /recover BINARY:" :
4384              "ftp put /recover TEXT:", fullname, 0);
4385     } else {
4386         tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4387     }
4388 #else
4389     tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4390 #endif /* DOUPDATE */
4391     tlog(F110," as",asname,0);
4392
4393 #ifndef NOCSETS
4394     if (xlate) {
4395         debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs);
4396         tlog(F110," file character set:",fcsinfo[fcs].keyword,0);
4397         tlog(F110," server character set:",fcsinfo[rcs].keyword,0);
4398     } else if (!ftp_typ) {
4399         tlog(F110," character sets:","no conversion",0);
4400         fcs = ofcs;                     /* Binary file but we still must */
4401         rcs = orcs;                     /* translate its name */
4402     }
4403 #endif /* NOCSETS */
4404
4405     /* PUT THE FILE */
4406
4407     t0 = gmstimer();                    /* Start time */
4408     if (flg & PUT_SIM) {                /* rc > 0 is a skip reason code */
4409         if (flg & (PUT_UPD|PUT_DIF)) {  /* (see SKP_xxx in ckcker.h) */
4410             rc = (mt < 0) ?             /* Update mode... */
4411               SKP_XNX :                 /* Remote file doesn't exist */
4412                 SKP_XUP;                /* Remote file is older */
4413         } else {
4414             rc = SKP_SIM;               /* "Would be sent", period. */
4415         }
4416     } else {
4417         rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart);
4418     }
4419     t1 = gmstimer();                    /* End time */
4420     filcnt++;                           /* File number */
4421
4422 #ifdef GFTIMER
4423     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4424     fpxfsecs = sec;                     /* (for doxlog()) */
4425 #else
4426     sec = (t1 - t0) / 1000;
4427     xfsecs = (int)sec;
4428 #endif /* GFTIMER */
4429
4430     debug(F111,"ftp sendrequest rc",local,rc);
4431
4432     if (cancelfile || cancelgroup) {
4433         debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup);
4434         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4435     } else if (rc > 0) {
4436         debug(F101,"ftp put skipped",local,rc);
4437         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)rc,fullname);
4438     } else if (rc < 0) {
4439         debug(F111,"ftp put error",local,ftpcode);
4440         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4441     } else {
4442         debug(F111,"ftp put not canceled",ckitoa(displa),fdispla);
4443         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4444         debug(F111,"ftp put ST_OK",local,rc);
4445         ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,"");
4446         debug(F110,"ftp put old sfspec",sfspec,0);
4447         makestr(&sfspec,fullname);      /* For WHERE command */
4448         debug(F110,"ftp put new sfspec",sfspec,0);
4449         debug(F110,"ftp put old srfspec",srfspec,0);
4450         makestr(&srfspec,asname);
4451         debug(F110,"ftp put new srfspec",srfspec,0);
4452     }
4453
4454     /* Final log entries */
4455
4456 #ifdef TLOG
4457     if (tralog) {
4458         if (rc > 0) {
4459             if (rc == SKP_XNX)
4460               tlog(F100," /simulate: WOULD BE SENT:","no remote file",0);
4461             else if (rc == SKP_XUP)
4462               tlog(F100," /simulate: WOULD BE SENT:","remote file older",0);
4463             else if (rc == SKP_SIM)
4464               tlog(F100," /simulate: WOULD BE SENT","",0);
4465             else
4466               tlog(F110," skipped:",gskreason(rc),0);
4467         } else if (rc == 0) {
4468             tlog(F101," complete, size", "", fsize);
4469         } else if (cancelfile) {
4470             tlog(F100," canceled by user","",0);
4471         } else {
4472             tlog(F110," failed:",ftp_reply_str,0);
4473         }
4474         if (!tlogfmt)
4475           doxlog(what,local,fsize,ftp_typ,rc,"");
4476     }
4477 #endif /* TLOG */
4478
4479     if (rc < 0)                         /* PUT did not succeed */
4480       return(-1);                       /* so done. */
4481
4482     if (flg & PUT_SIM)                  /* Simulating, skip the rest. */
4483       return(SKP_SIM);
4484
4485 #ifdef UNIX
4486     /* Set permissions too? */
4487
4488     if (prm) {                          /* Change permissions? */
4489         s = zgperm(local);              /* Get perms of local file */
4490         if (!s) s = "";
4491         x = strlen(s);
4492         if (x > 3) s += (x - 3);
4493         if (rdigits(s)) {
4494             ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL);
4495             x =
4496               ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE;
4497             tlog(F110, x ? " chmod" : " chmod failed",
4498                  s,
4499                  0
4500                  );
4501             if (!x)
4502               return(-1);
4503         }
4504     }
4505 #endif /* UNIX */
4506
4507     /* Disposition of source file */
4508
4509     if (moving) {
4510         x = zdelet(local);
4511         tlog(F110, (x > -1) ?
4512              " deleted" : " failed to delete",
4513              local,
4514              0
4515              );
4516         if (x < 0)
4517           return(-1);
4518     } else if (mvto) {
4519         x = zrename(local,mvto);
4520         tlog(F110, (x > -1) ?
4521              " moved source to" : " failed to move source to",
4522              mvto,
4523              0
4524              );
4525         if (x < 0)
4526           return(-1);
4527         /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,mvto); */
4528
4529     } else if (rnto) {
4530         char * s = rnto;
4531 #ifndef NOSPL
4532         int y;                          /* Pass it thru the evaluator */
4533         extern int cmd_quoting;         /* for \v(filename) */
4534         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4535             y = CKMAXPATH;
4536             s = (char *)asname;
4537             zzstring(rnto,&s,&y);
4538             s = (char *)asname;
4539         }
4540 #endif /* NOSPL */
4541         if (s) if (*s) {
4542             int x;
4543             x = zrename(local,s);
4544             tlog(F110, (x > -1) ?
4545                  " renamed source file to" :
4546                  " failed to rename source file to",
4547                  s,
4548                  0
4549                  );
4550             if (x < 0)
4551               return(-1);
4552             /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,s); */
4553         }
4554     }
4555
4556     /* Disposition of destination file */
4557
4558     if (srvrn) {                        /* /SERVER-RENAME: */
4559         char * s = srvrn;
4560 #ifndef NOSPL
4561         int y;                          /* Pass it thru the evaluator */
4562         extern int cmd_quoting; /* for \v(filename) */
4563         debug(F111,"ftp putfile srvrn",s,1);
4564
4565         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4566             y = CKMAXPATH;
4567             s = (char *)fullname;       /* We can recycle this buffer now */
4568             zzstring(srvrn,&s,&y);
4569             s = (char *)fullname;
4570         }
4571 #endif /* NOSPL */
4572         debug(F111,"ftp putfile srvrn",s,2);
4573         if (s) if (*s) {
4574             int x;
4575             x = ftp_rename(asname,s);
4576             debug(F111,"ftp putfile ftp_rename",asname,x);
4577             tlog(F110, (x > 0) ?
4578                  " renamed destination file to" :
4579                  " failed to rename destination file to",
4580                  s,
4581                  0
4582                  );
4583             if (x < 1)
4584               return(-1);
4585         }
4586     }
4587     return(0);
4588 }
4589
4590 /* xxout must only be used for ASCII transfers */
4591 static int
4592 #ifdef CK_ANSIC
4593 xxout(char c)
4594 #else
4595 xxout(c) char c;
4596 #endif /* CK_ANSIC */
4597 {
4598 #ifndef OS2
4599 #ifndef VMS
4600 #ifndef MAC
4601 #ifndef OSK
4602     /* For Unix, DG, Stratus, Amiga, Gemdos, other */
4603     if (c == '\012') {
4604         if (zzout(dout,(CHAR)'\015') < 0)
4605           return(-1);
4606         ftpsnd.bytes++;
4607     }
4608 #else /* OSK */
4609     if (c == '\015') {
4610         c = '\012';
4611         if (zzout(dout,(CHAR)'\015') < 0)
4612           return(-1);
4613         ftpsnd.bytes++;
4614     }
4615 #endif /* OSK */
4616 #else /* MAC */
4617     if (c == '\015') {
4618         c = '\012';
4619         if (zzout(dout,(CHAR)'\015') < 0)
4620           return(-1);
4621         ftpsnd.bytes++;
4622     }
4623 #endif /* MAC */
4624 #endif /* VMS */
4625 #endif /* OS2 */
4626     if (zzout(dout,(CHAR)c) < 0)
4627       return(-1);
4628     ftpsnd.bytes++;
4629     return(0);
4630 }
4631
4632 static int
4633 #ifdef CK_ANSIC
4634 scrnout(char c)
4635 #else
4636 scrnout(c) char c;
4637 #endif /* CK_ANSIC */
4638 {
4639     return(putchar(c));
4640 }
4641
4642 static int
4643 #ifdef CK_ANSIC
4644 pipeout(char c)
4645 #else
4646 pipeout(c) char c;
4647 #endif /* CK_ANSIC */
4648 {
4649     return(zmchout(c));
4650 }
4651
4652 static int
4653 ispathsep(c) int c; {
4654     switch (servertype) {
4655       case SYS_VMS:
4656       case SYS_TOPS10:
4657       case SYS_TOPS20:
4658         return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0);
4659       case SYS_OS2:
4660       case SYS_WIN32:
4661       case SYS_DOS:
4662         return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0);
4663       case SYS_VOS:
4664         return((c == '>') ? 1 : 0);
4665       default:
4666         return((c == '/') ? 1 : 0);
4667     }
4668 }
4669
4670 static int
4671 iscanceled() {
4672 #ifdef CK_CURSES
4673     extern int ck_repaint();
4674 #endif /* CK_CURSES */
4675     int x, rc = 0;
4676     char c = 0;
4677     if (cancelfile)
4678       return(1);
4679     x = conchk();                       /* Any chars waiting at console? */
4680     if (x-- > 0) {                      /* Yes...  */
4681         c = coninc(5);                  /* Get one */
4682         switch (c) {
4683           case 032:                     /* Ctrl-X or X */
4684           case 'z':
4685           case 'Z': cancelgroup++;      /* fall thru on purpose */
4686           case 030:                     /* Ctrl-Z or Z */
4687           case 'x':
4688           case 'X': cancelfile++; rc++; break;
4689 #ifdef CK_CURSES
4690           case 'L':
4691           case 'l':
4692           case 014:                     /* Ctrl-L or L or Ctrl-W */
4693           case 027:
4694             ck_repaint();               /* Refresh screen */
4695 #endif /* CK_CURSES */
4696         }
4697     }
4698     while (x-- > 0)                     /* Soak up any rest */
4699       c = coninc(1);
4700     return(rc);
4701 }
4702
4703 #ifdef FTP_TIMEOUT
4704 /* fc = 0 for read; 1 for write */
4705 static int
4706 check_data_connection(fd,fc) int fd, fc; {
4707     int x;
4708     struct timeval tv;
4709     fd_set in, out, err;
4710
4711     if (ftp_timeout < 1L)
4712       return(0);
4713
4714     FD_ZERO(&in);
4715     FD_ZERO(&out);
4716     FD_ZERO(&err);
4717     FD_SET(fd,fc ? &out : &in);
4718     tv.tv_sec = ftp_timeout;            /* Time limit */
4719     tv.tv_usec = 0L;
4720
4721 #ifdef INTSELECT
4722     x = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err,&tv);
4723 #else
4724     x = select(FD_SETSIZE,&in,&out,&err,&tv);
4725 #endif /* INTSELECT */
4726
4727     if (x == 0) {
4728 #ifdef EWOULDBLOCK
4729         errno = EWOULDBLOCK;
4730 #else
4731 #ifdef EAGAIN
4732         errno = EAGAIN;
4733 #else
4734         errno = 11;
4735 #endif  /* EAGAIN */
4736 #endif  /* EWOULDBLOCK */
4737         debug(F100,"ftp check_data_connection TIMOUT","",0);
4738         return(-3);
4739     }
4740     return(0);
4741 }
4742 #endif  /* FTP_TIMEOUT */
4743
4744 /* zzsend - used by buffered output macros. */
4745
4746 static int
4747 #ifdef CK_ANSIC
4748 zzsend(int fd, CHAR c)
4749 #else
4750 zzsend(fd,c) int fd; CHAR c;
4751 #endif /* CK_ANSIC */
4752 {
4753     int rc;
4754
4755     debug(F101,"zzsend ucbufsiz","",ucbufsiz);
4756     debug(F101,"zzsend nout","",nout);
4757     debug(F111,"zzsend","secure?",ftpissecure());
4758
4759     if (iscanceled())                   /* Check for cancellation */
4760       return(-9);
4761
4762 #ifdef FTP_TIMEOUT    
4763     ftp_timed_out = 0;
4764     if (check_data_connection(fd,1) < 0) {
4765         ftp_timed_out = 1;
4766         return(-3);
4767     }
4768 #endif  /* FTP_TIMEOUT */
4769
4770     rc = (!ftpissecure()) ?
4771       send(fd, (SENDARG2TYPE)ucbuf, nout, 0) :
4772         secure_putbuf(fd, ucbuf, nout);
4773     ucbuf[nout] = NUL;
4774     nout = 0;
4775     ucbuf[nout++] = c;
4776     spackets++;
4777     pktnum++;
4778     if (rc > -1 && fdispla != XYFD_B) {
4779         spktl = nout;
4780         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
4781     }
4782     return(rc);
4783 }
4784
4785 /* c m d l i n p u t  --  Command-line PUT */
4786
4787 int
4788 cmdlinput(stay) int stay; {
4789     int x, rc = 0, done = 0, good = 0, status = 0;
4790     ULONG t0, t1;                       /* Times for stats */
4791 #ifdef GFTIMER
4792     CKFLOAT sec;
4793 #else
4794     int sec = 0;
4795 #endif /* GFTIMER */
4796
4797     if (quiet) {                        /* -q really means quiet */
4798         displa = 0;
4799         fdispla = 0;
4800     } else {
4801         displa = 1;
4802         fdispla = XYFD_B;
4803     }
4804     testing = 0;
4805     out2screen = 0;
4806     dpyactive = 0;
4807     what = W_FTP|W_SEND;
4808
4809 #ifndef NOSPL
4810     cmd_quoting = 0;
4811 #endif /* NOSPL */
4812     sndsrc = nfils;
4813
4814     t0 = gmstimer();                    /* Record starting time */
4815
4816     while (!done && !cancelgroup) {     /* Loop for all files */
4817
4818         cancelfile = 0;
4819         x = gnfile();                   /* Get next file from list(s) */
4820         if (x == 0)                     /* (see gnfile() comments...) */
4821           x = gnferror;
4822
4823         switch (x) {
4824           case 1:                       /* File to send */
4825             rc = putfile(FTP_PUT,       /* Function (PUT, APPEND) */
4826                          filnam,        /* Local file to send */
4827                          filnam,        /* Remote name for file */
4828                          forcetype,     /* Text/binary mode forced */
4829                          0,             /* Not moving */
4830                          NULL,          /* No move-to */
4831                          NULL,          /* No rename-to */
4832                          NULL,          /* No server-rename */
4833                          ftp_cnv,       /* Filename conversion */
4834                          0,             /* Unique-server-names */
4835                          -1,            /* All file types */
4836                          0,             /* No permissions */
4837                          -1,            /* No character sets */
4838                          -1,            /* No character sets */
4839                          0              /* No update or restart */
4840                          );
4841             if (rc > -1) {
4842                 good++;
4843                 status = 1;
4844             }
4845             if (cancelfile) {
4846                 continue;               /* Or break? */
4847             }
4848             if (rc < 0) {
4849                 ftp_fai++;
4850             }
4851             continue;                   /* Or break? */
4852
4853           case 0:                       /* No more files, done */
4854             done++;
4855             continue;
4856
4857           case -2:
4858           case -1:
4859             printf("?%s: file not found - \"%s\"\n",
4860                    puterror ? "Fatal" : "Warning",
4861                    filnam
4862                    );
4863             continue;                   /* or break? */
4864           case -3:
4865             printf("?Warning access denied - \"%s\"\n", filnam);
4866             continue;                   /* or break? */
4867           case -5:
4868             printf("?Too many files match\n");
4869             done++;
4870             break;
4871           case -6:
4872             if (good < 1)
4873               printf("?No files selected\n");
4874             done++;
4875             break;
4876           default:
4877             printf("?getnextfile() - unknown failure\n");
4878             done++;
4879         }
4880     }
4881     if (status > 0) {
4882         if (cancelgroup)
4883           status = 0;
4884         else if (cancelfile && good < 1)
4885           status = 0;
4886     }
4887     success = status;
4888     x = success;
4889     if (x > -1) {
4890         lastxfer = W_FTP|W_SEND;
4891         xferstat = success;
4892     }
4893     t1 = gmstimer();                    /* End time */
4894 #ifdef GFTIMER
4895     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4896     if (!sec) sec = 0.001;
4897     fptsecs = sec;
4898 #else
4899     sec = (t1 - t0) / 1000;
4900     if (!sec) sec = 1;
4901 #endif /* GFTIMER */
4902     tfcps = (long) (tfc / sec);
4903     tsecs = (int)sec;
4904     lastxfer = W_FTP|W_SEND;
4905     xferstat = success;
4906     if (dpyactive)
4907       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
4908     if (!stay)
4909       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
4910     return(success);
4911 }
4912
4913
4914 /*  d o f t p p u t  --  Parse and execute PUT, MPUT, and APPEND  */
4915
4916 int
4917 #ifdef CK_ANSIC
4918 doftpput(int cx, int who)               /* who == 1 for ftp, 0 for kermit */
4919 #else
4920 doftpput(cx,who) int cx, who;
4921 #endif /* CK_ANSIC */
4922 {
4923     struct FDB sf, fl, sw, cm;
4924     int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0;
4925     int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0;
4926     char * s, * s2;
4927
4928     int x_csl, x_csr = -1;              /* Local and remote charsets */
4929     int x_xla = 0;
4930     int x_recurse = 0;
4931     char c, * p;                        /* Workers */
4932 #ifdef PUTARRAY
4933     int range[2];                       /* Array range */
4934     char ** ap = NULL;                  /* Array pointer */
4935     int arrayx = -1;                    /* Array index */
4936 #endif /* PUTARRAY */
4937     ULONG t0 = 0L, t1 = 0L;             /* Times for stats */
4938 #ifdef GFTIMER
4939     CKFLOAT sec;
4940 #else
4941     int sec = 0;
4942 #endif /* GFTIMER */
4943
4944     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
4945     success = 0;                        /* Assume failure */
4946     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
4947     out2screen = 0;                     /* Not outputting file to screen */
4948     putflags = 0;                       /* PUT options */
4949     x_cnv = ftp_cnv;                    /* Filename conversion */
4950     x_usn = ftp_usn;                    /* Unique server names */
4951     x_prm = ftp_prm;                    /* Permissions */
4952     if (x_prm == SET_AUTO)              /* Permissions AUTO */
4953       x_prm = alike;
4954
4955 #ifndef NOCSETS
4956     x_csr = ftp_csr;                    /* Inherit global server charset */
4957     x_csl = ftp_csl;
4958     if (x_csl < 0)
4959       x_csl = fcharset;
4960     x_xla = ftp_xla;
4961 #endif /* NOCSETS */
4962
4963     makestr(&filefile,NULL);            /* No filename list file yet. */
4964     makestr(&srv_renam,NULL);           /* Clear /SERVER-RENAME: */
4965     makestr(&snd_rename,NULL);          /*  PUT /RENAME */
4966     makestr(&snd_move,NULL);            /*  PUT /MOVE */
4967     putpath[0] = NUL;                   /* Initialize for syncdir(). */
4968     puterror = ftp_err;                 /* Inherit global error action. */
4969     what = W_SEND|W_FTP;                /* What we're doing (sending w/FTP) */
4970     asnambuf[0] = NUL;                  /* Clear as-name buffer */
4971
4972     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
4973         ftp_typ = g_ftp_typ;
4974         /* g_ftp_typ = -1; */
4975     }
4976     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
4977         pv[i].sval = NULL;              /* to null pointers */
4978         pv[i].ival = -1;                /* and -1 int values */
4979         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
4980     }
4981     if (who == 0) {                     /* Called with unprefixed command */
4982         switch (cx) {
4983           case XXRSEN:  pv[SND_RES].ival = 1; break;
4984           case XXCSEN:  pv[SND_CMD].ival = 1; break;
4985           case XXMOVE:  pv[SND_DEL].ival = 1; break;
4986           case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */
4987           case XXMSE:   mput++; break;
4988         }
4989     } else {
4990         if (cx == FTP_REP)
4991           pv[SND_RES].ival = 1;
4992         if (cx == FTP_MPU)
4993           mput++;
4994     }
4995     cmfdbi(&sw,                         /* First FDB - command switches */
4996            _CMKEY,                      /* fcode */
4997            "Filename, or switch",       /* hlpmsg */
4998            "",                          /* default */
4999            "",                          /* addtl string data */
5000            nputswi,                     /* addtl numeric data 1: tbl size */
5001            4,                           /* addtl numeric data 2: 4 = cmswi */
5002            xxstring,                    /* Processing function */
5003            putswi,                      /* Keyword table */
5004            &sf                          /* Pointer to next FDB */
5005            );
5006     cmfdbi(&fl,                         /* 3rd FDB - local filespec */
5007            _CMFLD,                      /* fcode */
5008            "",                          /* hlpmsg */
5009            "",                          /* default */
5010            "",                          /* addtl string data */
5011            0,                           /* addtl numeric data 1 */
5012            0,                           /* addtl numeric data 2 */
5013            xxstring,
5014            NULL,
5015            &cm
5016            );
5017     cmfdbi(&cm,                         /* 4th FDB - Confirmation */
5018            _CMCFM,                      /* fcode */
5019            "",                          /* hlpmsg */
5020            "",                          /* default */
5021            "",                          /* addtl string data */
5022            0,                           /* addtl numeric data 1 */
5023            0,                           /* addtl numeric data 2 */
5024            NULL,
5025            NULL,
5026            NULL
5027            );
5028
5029   again:
5030     cmfdbi(&sf,                         /* 2nd FDB - file to send */
5031            _CMIFI,                      /* fcode */
5032            "",                          /* hlpmsg */
5033            "",                          /* default */
5034            "",                          /* addtl string data */
5035            /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */
5036            nolinks | x_recurse,         /* addtl numeric data 1 */
5037            0,                           /* dirflg 0 means "not dirs only" */
5038            xxstring,
5039            NULL,
5040 #ifdef COMMENT
5041            mput ? &cm : &fl
5042 #else
5043            &fl
5044 #endif /* COMMENT */
5045            );
5046
5047     while (1) {                         /* Parse zero or more switches */
5048         x = cmfdb(&sw);                 /* Parse something */
5049         debug(F101,"ftp put cmfdb A","",x);
5050         debug(F101,"ftp put fcode A","",cmresult.fcode);
5051         if (x < 0)                      /* Error */
5052           goto xputx;                   /* or reparse needed */
5053         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
5054           break;
5055         c = cmgbrk();                   /* Get break character */
5056         getval = (c == ':' || c == '='); /* to see how they ended the switch */
5057         if (getval && !(cmresult.kflags & CM_ARG)) {
5058             printf("?This switch does not take arguments\n");
5059             x = -9;
5060             goto xputx;
5061         }
5062         if (!getval && (cmgkwflgs() & CM_ARG)) {
5063             printf("?This switch requires an argument\n");
5064             x = -9;
5065             goto xputx;
5066         }
5067         n = cmresult.nresult;           /* Numeric result = switch value */
5068         debug(F101,"ftp put switch","",n);
5069
5070         switch (n) {                    /* Process the switch */
5071           case SND_AFT:                 /* Send /AFTER:date-time */
5072           case SND_BEF:                 /* Send /BEFORE:date-time */
5073           case SND_NAF:                 /* Send /NOT-AFTER:date-time */
5074           case SND_NBE:                 /* Send /NOT-BEFORE:date-time */
5075             if (!getval) break;
5076             if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) {
5077                 if (x == -3) {
5078                     printf("?Date-time required\n");
5079                     x = -9;
5080                 }
5081                 goto xputx;
5082             }
5083             pv[n].ival = 1;
5084             makestr(&(pv[n].sval),s);
5085             break;
5086
5087           case SND_ASN:                 /* /AS-NAME: */
5088             debug(F101,"ftp put /as-name getval","",getval);
5089             if (!getval) break;
5090             if ((x = cmfld("Name to send under","",&s,NULL)) < 0) {
5091                 if (x == -3) {
5092                     printf("?name required\n");
5093                     x = -9;
5094                 }
5095                 goto xputx;
5096             }
5097             makestr(&(pv[n].sval),brstrip(s));
5098             debug(F110,"ftp put /as-name 1",pv[n].sval,0);
5099             if (pv[n].sval) pv[n].ival = 1;
5100             break;
5101
5102 #ifdef PUTARRAY
5103           case SND_ARR:                 /* /ARRAY */
5104             if (!getval) break;
5105             ap = NULL;
5106             if ((x = cmfld("Array name (a single letter will do)",
5107                            "",
5108                            &s,
5109                            NULL
5110                            )) < 0) {
5111                 if (x == -3)
5112                   break;
5113                 else
5114                   return(x);
5115             }
5116             if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) {
5117                 printf("?Bad array: %s\n",s);
5118                 return(-9);
5119             }
5120             if (!(ap = a_ptr[x])) {
5121                 printf("?No such array: %s\n",s);
5122                 return(-9);
5123             }
5124             pv[n].ival = 1;
5125             pv[SND_CMD].ival = 0;       /* Undo any conflicting ones... */
5126             pv[SND_RES].ival = 0;
5127             pv[SND_FIL].ival = 0;
5128             arrayx = x;
5129             break;
5130 #endif /* PUTARRAY */
5131
5132           case SND_BIN:                 /* /BINARY */
5133           case SND_TXT:                 /* /TEXT or /ASCII */
5134           case SND_TEN:                 /* /TENEX */
5135             pv[SND_BIN].ival = 0;
5136             pv[SND_TXT].ival = 0;
5137             pv[SND_TEN].ival = 0;
5138             pv[n].ival = 1;
5139             break;
5140
5141 #ifdef PUTPIPE
5142           case SND_CMD:                 /* These take no args */
5143             if (nopush) {
5144                 printf("?Sorry, system command access is disabled\n");
5145                 x = -9;
5146                 goto xputx;
5147             }
5148 #ifdef PIPESEND
5149             else if (sndfilter) {
5150                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
5151                 x = -9;
5152                 goto xputx;
5153             }
5154 #endif /* PIPESEND */
5155             sw.hlpmsg = "Command, or switch"; /* Change help message */
5156             pv[n].ival = 1;             /* Just set the flag */
5157             pv[SND_ARR].ival = 0;
5158             break;
5159 #endif /* PUTPIPE */
5160
5161 #ifdef CKSYMLINK
5162           case SND_LNK:
5163             nolinks = 0;
5164             goto again;                 /* Because CMIFI params changed... */
5165           case SND_NLK:
5166             nolinks = 2;
5167             goto again;
5168 #endif /* CKSYMLINK */
5169
5170 #ifdef FTP_RESTART
5171           case SND_RES:                 /* /RECOVER (resend) */
5172             pv[SND_ARR].ival = 0;       /* fall thru on purpose... */
5173 #endif /* FTP_RESTART */
5174
5175           case SND_NOB:
5176           case SND_DEL:                 /* /DELETE */
5177           case SND_SHH:                 /* /QUIET */
5178           case SND_UPD:                 /* /UPDATE */
5179           case SND_SIM:                 /* /UPDATE */
5180           case SND_USN:                 /* /UNIQUE */
5181             pv[n].ival = 1;             /* Just set the flag */
5182             break;
5183
5184           case SND_REC:                 /* /RECURSIVE */
5185             recursive = 2;              /* Must be set before cmifi() */
5186             x_recurse = 1;
5187             goto again;                 /* Because CMIFI params changed... */
5188             break;
5189
5190 #ifdef UNIXOROSK
5191           case SND_DOT:                 /* /DOTFILES */
5192             matchdot = 1;
5193             break;
5194           case SND_NOD:                 /* /NODOTFILES */
5195             matchdot = 0;
5196             break;
5197 #endif /* UNIXOROSK */
5198
5199           case SND_ERR:                 /* /ERROR-ACTION */
5200             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
5201               goto xputx;
5202             pv[n].ival = x;
5203             break;
5204
5205           case SND_EXC:                 /* Excludes */
5206             if (!getval) break;
5207             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
5208                 if (x == -3) {
5209                     printf("?Pattern required\n");
5210                     x = -9;
5211                 }
5212                 goto xputx;
5213             }
5214             if (s) if (!*s) s = NULL;
5215             makestr(&(pv[n].sval),s);
5216             if (pv[n].sval)
5217               pv[n].ival = 1;
5218             break;
5219
5220           case SND_PRM:                 /* /PERMISSIONS */
5221             if (!getval)
5222               x = 1;
5223             else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
5224               goto xputx;
5225             pv[SND_PRM].ival = x;
5226             break;
5227
5228 #ifdef PIPESEND
5229           case SND_FLT:                 /* /FILTER */
5230             debug(F101,"ftp put /filter getval","",getval);
5231             if (!getval) break;
5232             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
5233                 if (x == -3)
5234                   s = "";
5235                 else
5236                   goto xputx;
5237             }
5238             if (*s) s = brstrip(s);
5239             y = strlen(s);
5240             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
5241                 if (s[x] != '\\') continue;
5242                 if (s[x+1] == 'v') break;
5243             }
5244             if (x == y) {
5245                 printf(
5246                 "?Filter must contain a replacement variable for filename.\n"
5247                        );
5248                 x = -9;
5249                 goto xputx;
5250             }
5251             if (s) if (!*s) s = NULL;
5252             makestr(&(pv[n].sval),s);
5253             if (pv[n].sval)
5254               pv[n].ival = 1;
5255             break;
5256 #endif /* PIPESEND */
5257
5258           case SND_NAM:                 /* /FILENAMES */
5259             if (!getval) break;
5260             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
5261               goto xputx;
5262             debug(F101,"ftp put /filenames","",x);
5263             pv[n].ival = x;
5264             break;
5265
5266           case SND_SMA:                 /* Smaller / larger than */
5267           case SND_LAR: {
5268               CK_OFF_T y;
5269               if (!getval) break;
5270               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
5271                 goto xputx;
5272               pv[n].wval = y;
5273               break;
5274           }
5275           case SND_FIL:                 /* Name of file containing filenames */
5276             if (!getval) break;
5277             if ((x = cmifi("Name of file containing list of filenames",
5278                                "",&s,&y,xxstring)) < 0) {
5279                 if (x == -3) {
5280                     printf("?Filename required\n");
5281                     x = -9;
5282                 }
5283                 goto xputx;
5284             } else if (y && iswild(s)) {
5285                 printf("?Wildcards not allowed\n");
5286                 x = -9;
5287                 goto xputx;
5288             }
5289             if (s) if (!*s) s = NULL;
5290             makestr(&(pv[n].sval),s);
5291             if (pv[n].sval) {
5292                 pv[n].ival = 1;
5293                 pv[SND_ARR].ival = 0;
5294             } else {
5295                 pv[n].ival = 0;
5296             }
5297             mput = 0;
5298             break;
5299
5300           case SND_MOV:                 /* MOVE after */
5301           case SND_REN:                 /* RENAME after */
5302           case SND_SRN: {               /* SERVER-RENAME after */
5303               char * m = "";
5304               switch (n) {
5305                 case SND_MOV:
5306                   m = "device and/or directory for source file after sending";
5307                   break;
5308                 case SND_REN:
5309                   m = "new name for source file after sending";
5310                   break;
5311                 case SND_SRN:
5312                   m = "new name for destination file after sending";
5313                   break;
5314               }
5315               if (!getval) break;
5316               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
5317                   if (x == -3) {
5318                       printf("%s\n", n == SND_MOV ?
5319                              "?Destination required" :
5320                              "?New name required"
5321                              );
5322                       x = -9;
5323                   }
5324                   goto xputx;
5325               }
5326               if (s) if (!*s) s = NULL;
5327               makestr(&(pv[n].sval),s ? brstrip(s) : NULL);
5328               pv[n].ival = (pv[n].sval) ? 1 : 0;
5329               break;
5330           }
5331           case SND_STA:                 /* Starting position (= PSEND) */
5332             if (!getval) break;
5333             if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0)
5334               goto xputx;
5335             pv[n].ival = y;
5336             break;
5337
5338           case SND_TYP:                 /* /TYPE */
5339             if (!getval) break;
5340             if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0)
5341               goto xputx;
5342             pv[n].ival = (x == 2) ? -1 : x;
5343             break;
5344
5345 #ifndef NOCSETS
5346           case SND_CSL:                 /* Local character set */
5347           case SND_CSR:                 /* Remote (server) charset */
5348             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) {
5349                 return((x == -3) ? -2 : x);
5350             }
5351             if (n == SND_CSL)
5352               x_csl = x;
5353             else
5354               x_csr = x;
5355             x_xla = 1;                  /* Overrides global OFF setting */
5356             break;
5357
5358           case SND_XPA:                 /* Transparent */
5359             x_xla = 0;
5360             x_csr = -1;
5361             x_csl = -1;
5362             break;
5363 #endif /* NOCSETS */
5364         }
5365     }
5366 #ifdef PIPESEND
5367     if (pv[SND_RES].ival > 0) { /* /RECOVER */
5368         if (sndfilter || pv[SND_FLT].ival > 0) {
5369             printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n");
5370             x = -9;
5371             goto xputx;
5372         }
5373         if (sfttab[0] > 0 && sfttab[SFT_REST] == 0)
5374           printf("WARNING: Server says it doesn't support REST.\n");
5375     }
5376 #endif /* PIPESEND */
5377
5378     cmarg = "";
5379     cmarg2 = asnambuf;
5380     line[0] = NUL;
5381     s = line;
5382     wild = 0;
5383
5384     switch (cmresult.fcode) {           /* How did we get out of switch loop */
5385       case _CMIFI:                      /* Input filename */
5386         if (pv[SND_FIL].ival > 0) {
5387             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5388             x = -9;
5389             goto xputx;
5390         }
5391         ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */
5392         if (pv[SND_ARR].ival > 0)
5393           ckstrncpy(asnambuf,line,CKMAXPATH);
5394         else
5395           wild = cmresult.nresult;      /* Wild flag */
5396         debug(F111,"ftp put wild",line,wild);
5397         if (!wild && !recursive && !mput)
5398           nolinks = 0;
5399         break;
5400       case _CMFLD:                      /* Field */
5401         /* Only allowed with /COMMAND and /ARRAY */
5402         if (pv[SND_FIL].ival > 0) {
5403             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5404             x = -9;
5405             goto xputx;
5406         }
5407         /* For MPUT it's OK to have filespecs that don't match any files */
5408         if (mput)
5409           break;
5410         if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) {
5411 #ifdef CKROOT
5412             if (ckrooterr)
5413               printf("?Off limits: %s\n",cmresult.sresult);
5414             else
5415 #endif /* CKROOT */
5416               printf("?%s - \"%s\"\n",
5417                    iswild(cmresult.sresult) ?
5418                    "No files match" : "File not found",
5419                    cmresult.sresult
5420                    );
5421             x = -9;
5422             goto xputx;
5423         }
5424         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
5425         if (pv[SND_ARR].ival > 0)
5426           ckstrncpy(asnambuf,line,CKMAXPATH);
5427         break;
5428       case _CMCFM:                      /* Confirmation */
5429         confirmed = 1;
5430         break;
5431       default:
5432         printf("?Unexpected function code: %d\n",cmresult.fcode);
5433         x = -9;
5434         goto xputx;
5435     }
5436     debug(F110,"ftp put string",s,0);
5437     debug(F101,"ftp put confirmed","",confirmed);
5438
5439     /* Save and change protocol and transfer mode */
5440     /* Global values are restored in main parse loop */
5441
5442     g_displa = fdispla;
5443     if (ftp_dis > -1)
5444       fdispla = ftp_dis;
5445     g_skipbup = skipbup;
5446
5447     if (pv[SND_NOB].ival > -1) {        /* /NOBACKUP (skip backup file) */
5448         g_skipbup = skipbup;
5449         skipbup = 1;
5450     }
5451     if (pv[SND_TYP].ival > -1) {        /* /TYPE */
5452         xfiletype = pv[SND_TYP].ival;
5453         if (xfiletype == 2)
5454           xfiletype = -1;
5455     }
5456     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
5457         forcetype = 1;                  /* So skip file scan */
5458         ftp_typ = FTT_BIN;              /* Set binary */
5459     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
5460         forcetype = 1;
5461         ftp_typ = FTT_ASC;
5462     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
5463         forcetype = 1;
5464         ftp_typ = FTT_TEN;
5465     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
5466         forcetype = 1;
5467         ftp_typ = binary;
5468         g_ftp_typ = binary;
5469     }
5470
5471 #ifdef PIPESEND
5472     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
5473         debug(F110,"PUT /COMMAND before stripping",s,0);
5474         s = brstrip(s);
5475         debug(F110,"PUT /COMMAND after stripping",s,0);
5476         if (!*s) {
5477             printf("?Sorry, a command to send from is required\n");
5478             x = -9;
5479             goto xputx;
5480         }
5481         cmarg = s;
5482     }
5483 #endif /* PIPESEND */
5484
5485 /* Set up /MOVE and /RENAME */
5486
5487     if (pv[SND_DEL].ival > 0 &&
5488         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
5489         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
5490         x = -9;
5491         goto xputx;
5492     }
5493 #ifdef CK_TMPDIR
5494     if (pv[SND_MOV].ival > 0) {
5495         int len;
5496         char * p = pv[SND_MOV].sval;
5497         len = strlen(p);
5498         if (!isdir(p)) {                /* Check directory */
5499 #ifdef CK_MKDIR
5500             char * s = NULL;
5501             s = (char *)malloc(len + 4);
5502             if (s) {
5503                 strcpy(s,p);            /* safe */
5504 #ifdef datageneral
5505                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
5506 #else
5507                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
5508 #endif /* datageneral */
5509                 s[len++] = 'X';
5510                 s[len] = NUL;
5511 #ifdef NOMKDIR
5512                 x = -1;
5513 #else
5514                 x = zmkdir(s);
5515 #endif /* NOMKDIR */
5516                 free(s);
5517                 if (x < 0) {
5518                     printf("?Can't create \"%s\"\n",p);
5519                     x = -9;
5520                     goto xputx;
5521                 }
5522             }
5523 #else
5524             printf("?Directory \"%s\" not found\n",p);
5525             x = -9;
5526             goto xputx;
5527 #endif /* CK_MKDIR */
5528         }
5529         makestr(&snd_move,p);
5530     }
5531 #endif /* CK_TMPDIR */
5532
5533     if (pv[SND_REN].ival > 0) {         /* /RENAME */
5534         char * p = pv[SND_REN].sval;
5535         if (!p) p = "";
5536         if (!*p) {
5537             printf("?New name required for /RENAME\n");
5538             x = -9;
5539             goto xputx;
5540         }
5541         p = brstrip(p);
5542 #ifndef NOSPL
5543     /* If name given is wild, rename string must contain variables */
5544         if (wild) {
5545             char * s = tmpbuf;
5546             x = TMPBUFSIZ;
5547             zzstring(p,&s,&x);
5548             if (!strcmp(tmpbuf,p)) {
5549                 printf(
5550     "?/RENAME for file group must contain variables such as \\v(filename)\n"
5551                        );
5552                 x = -9;
5553                 goto xputx;
5554             }
5555         }
5556 #endif /* NOSPL */
5557         makestr(&snd_rename,p);
5558         debug(F110,"FTP snd_rename",snd_rename,0);
5559     }
5560     if (pv[SND_SRN].ival > 0) {         /* /SERVER-RENAME */
5561         char * p = pv[SND_SRN].sval;
5562         if (!p) p = "";
5563         if (!*p) {
5564             printf("?New name required for /SERVER-RENAME\n");
5565             x = -9;
5566             goto xputx;
5567         }
5568         p = brstrip(p);
5569 #ifndef NOSPL
5570         if (wild) {
5571             char * s = tmpbuf;
5572             x = TMPBUFSIZ;
5573             zzstring(p,&s,&x);
5574             if (!strcmp(tmpbuf,p)) {
5575                 printf(
5576 "?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n"
5577                        );
5578                 x = -9;
5579                 goto xputx;
5580             }
5581         }
5582 #endif /* NOSPL */
5583         makestr(&srv_renam,p);
5584         debug(F110,"ftp put srv_renam",srv_renam,0);
5585     }
5586     if (!confirmed) {                   /* CR not typed yet, get more fields */
5587         char * lp;
5588         if (mput) {                     /* MPUT or MMOVE */
5589             nfils = 0;                  /* We already have the first one */
5590 #ifndef NOMSEND
5591             if (cmresult.fcode == _CMIFI) {
5592                 /* First filespec is valid */
5593                 msfiles[nfils++] = line;    /* Store pointer */
5594                 lp = line + (int)strlen(line) + 1; /* Point past it */
5595                 debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1);
5596             } else {
5597                 /* First filespec matches no files */
5598                 debug(F110,"ftp put mput skipping first filespec",
5599                       cmresult.sresult,
5600                       0
5601                       );
5602                 lp = line;
5603             }
5604             /* Parse a filespec, a "field", or confirmation */
5605
5606             cmfdbi(&sf,                 /* 1st FDB - file to send */
5607                    _CMIFI,              /* fcode */
5608                    "",                  /* hlpmsg */
5609                    "",                  /* default */
5610                    "",                  /* addtl string data */
5611                    nolinks | x_recurse, /* addtl numeric data 1 */
5612                    0,                   /* dirflg 0 means "not dirs only" */
5613                    xxstring,
5614                    NULL,
5615                    &fl
5616                    );
5617             cmfdbi(&fl,                 /* 2nd FDB - local filespec */
5618                    _CMFLD,              /* fcode */
5619                    "",                  /* hlpmsg */
5620                    "",                  /* default */
5621                    "",                  /* addtl string data */
5622                    0,                   /* addtl numeric data 1 */
5623                    0,                   /* addtl numeric data 2 */
5624                    xxstring,
5625                    NULL,
5626                    &cm
5627                    );
5628             cmfdbi(&cm,                 /* 3rd FDB - Confirmation */
5629                    _CMCFM,              /* fcode */
5630                    "",
5631                    "",
5632                    "",
5633                    0,
5634                    0,
5635                    NULL,
5636                    NULL,
5637                    NULL
5638                    );
5639
5640             while (!confirmed) {        /* Get more filenames */
5641                 x = cmfdb(&sf);         /* Parse something */
5642                 debug(F101,"ftp put cmfdb B","",x);
5643                 debug(F101,"ftp put fcode B","",cmresult.fcode);
5644                 if (x < 0)              /* Error */
5645                   goto xputx;           /* or reparse needed */
5646                 switch (cmresult.fcode) {
5647                   case _CMCFM:          /* End of command */
5648                     confirmed++;
5649                     if (nfils < 1) {
5650                         debug(F100,"ftp put mput no files match","",0);
5651                         printf("?No files match MPUT list\n");
5652                         x = -9;
5653                         goto xputx;
5654                     }
5655                     break;
5656                   case _CMFLD:          /* No match */
5657                     debug(F110,"ftp put mput skipping",cmresult.sresult,0);
5658                     continue;
5659                   case _CMIFI:          /* Good match */
5660                     s = cmresult.sresult;
5661                     msfiles[nfils++] = lp; /* Got one, count, point to it, */
5662                     p = lp;                /* remember pointer, */
5663                     while ((*lp++ = *s++)) /* and copy it into buffer */
5664                       if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */
5665                           printf("?MPUT list too long\n");
5666                           line[0] = NUL;
5667                           x = -9;
5668                           goto xputx;
5669                       }
5670                     debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1);
5671                     if (nfils == 1)     /* Take care of \v(filespec) */
5672                       fspec[0] = NUL;
5673 #ifdef ZFNQFP
5674                     zfnqfp(p,TMPBUFSIZ,tmpbuf);
5675                     p = tmpbuf;
5676 #endif /* ZFNQFP */
5677                     if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) {
5678                         strcat(fspec,p);    /* safe */
5679                         strcat(fspec," ");  /* safe */
5680                     } else {
5681 #ifdef COMMENT
5682                         printf("WARNING - \\v(filespec) buffer overflow\n");
5683 #else
5684                         debug(F101,"doxput filespec buffer overflow","",0);
5685 #endif /* COMMENT */
5686                     }
5687                 }
5688             }
5689 #endif /* NOMSEND */
5690         } else {                        /* Regular PUT */
5691             nfils = -1;
5692             if ((x = cmtxt(wild ?
5693 "\nOptional as-name template containing replacement variables \
5694 like \\v(filename)" :
5695                            "Optional name to send it with",
5696                            "",&p,NULL)) < 0)
5697               goto xputx;
5698
5699             if (p) if (!*p) p = NULL;
5700             p = brstrip(p);
5701
5702             if (p && *p) {
5703                 makestr(&(pv[SND_ASN].sval),p);
5704                 if (pv[SND_ASN].sval)
5705                   pv[SND_ASN].ival = 1;
5706                 debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0);
5707             }
5708         }
5709     }
5710     /* Set cmarg2 from as-name, however we got it. */
5711
5712     CHECKCONN();
5713     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
5714         char * p;
5715         p = brstrip(pv[SND_ASN].sval);
5716         ckstrncpy(asnambuf,p,CKMAXPATH+1);
5717     }
5718     debug(F110,"ftp put asnambuf",asnambuf,0);
5719
5720     if (pv[SND_FIL].ival > 0) {
5721         if (confirmed) {
5722             if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
5723                 debug(F110,"ftp put can't open",pv[SND_FIL].sval,0);
5724                 printf("?Failure to open %s\n",pv[SND_FIL].sval);
5725                 x = -9;
5726                 goto xputx;
5727             }
5728             makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */
5729             debug(F110,"ftp PUT /LISTFILE opened",filefile,0);
5730             wild = 1;
5731         }
5732     }
5733     if (confirmed && !line[0] && !filefile) {
5734 #ifndef NOMSEND
5735         if (filehead) {                 /* OK if we have a SEND-LIST */
5736             nfils = filesinlist;
5737             sndsrc = nfils;             /* Like MSEND */
5738             addlist = 1;                /* But using a different list... */
5739             filenext = filehead;
5740             goto doput;
5741         }
5742 #endif /* NOMSEND */
5743         printf("?Filename required but not given\n");
5744         x = -9;
5745         goto xputx;
5746     }
5747 #ifndef NOMSEND
5748     addlist = 0;                        /* Don't use SEND-LIST. */
5749 #endif /* NOMSEND */
5750
5751     if (mput) {                         /* MPUT (rather than PUT) */
5752 #ifndef NOMSEND
5753         cmlist = msfiles;               /* List of filespecs */
5754         sndsrc = nfils;                 /* rather filespec and as-name */
5755 #endif /* NOMSEND */
5756         pipesend = 0;
5757     } else if (filefile) {              /* File contains list of filenames */
5758         s = "";
5759         cmarg = "";
5760         line[0] = NUL;
5761         nfils = 1;
5762         sndsrc = 1;
5763
5764     } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) {
5765
5766         /* Not MSEND, MMOVE, /LIST, or /ARRAY */
5767         nfils = sndsrc = -1;
5768         if (!wild) {
5769             if (zchki(s) < 0) {
5770                 printf("?Read access denied - \"%s\"\n", s);
5771                 x = -9;
5772                 goto xputx;
5773             }
5774         }
5775         if (s != line)                  /* We might already have done this. */
5776           ckstrncpy(line,s,LINBUFSIZ);  /* Copy of string just parsed. */
5777 #ifdef DEBUG
5778         else
5779           debug(F110,"doxput line=s",line,0);
5780 #endif /* DEBUG */
5781         cmarg = line;                   /* File to send */
5782     }
5783 #ifndef NOMSEND
5784     zfnqfp(cmarg,fspeclen,fspec);       /* Get full name */
5785 #endif /* NOMSEND */
5786
5787     if (!mput) {                        /* For all but MPUT... */
5788 #ifdef PIPESEND
5789         if (pv[SND_CMD].ival > 0)       /* /COMMAND sets pipesend flag */
5790           pipesend = 1;
5791         debug(F101,"ftp put /COMMAND pipesend","",pipesend);
5792         if (pipesend && filefile) {
5793             printf("?Invalid switch combination\n");
5794             x = -9;
5795             goto xputx;
5796         }
5797 #endif /* PIPESEND */
5798
5799 #ifndef NOSPL
5800     /* If as-name given and filespec is wild, as-name must contain variables */
5801         if ((wild || mput) && asnambuf[0]) {
5802             char * s = tmpbuf;
5803             x = TMPBUFSIZ;
5804             zzstring(asnambuf,&s,&x);
5805             if (!strcmp(tmpbuf,asnambuf)) {
5806                 printf(
5807     "?As-name for file group must contain variables such as \\v(filename)\n"
5808                        );
5809                 x = -9;
5810                 goto xputx;
5811             }
5812         }
5813 #endif /* NOSPL */
5814     }
5815
5816   doput:
5817
5818     if (pv[SND_SHH].ival > 0) {         /* SEND /QUIET... */
5819         fdispla = 0;
5820         debug(F101,"ftp put display","",fdispla);
5821     } else {
5822         displa = 1;
5823         if (ftp_deb)
5824           fdispla = XYFD_B;
5825     }
5826
5827 #ifdef PUTARRAY                         /* SEND /ARRAY... */
5828     if (pv[SND_ARR].ival > 0) {
5829         if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */
5830         if (range[0] == -1)             /* If low end of range not specified */
5831           range[0] = 1;                 /* default to 1 */
5832         if (range[1] == -1)             /* If high not specified */
5833           range[1] = a_dim[arrayx];     /* default to size of array */
5834         if ((range[0] < 0) ||           /* Check range */
5835             (range[0] > a_dim[arrayx]) ||
5836             (range[1] < range[0]) ||
5837             (range[1] > a_dim[arrayx])) {
5838             printf("?Bad array range - [%d:%d]\n",range[0],range[1]);
5839             x = -9;
5840             goto xputx;
5841         }
5842         sndarray = ap;                  /* Array pointer */
5843         sndxin = arrayx;                /* Array index */
5844         sndxlo = range[0];              /* Array range */
5845         sndxhi = range[1];
5846         sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE);
5847         if (!asnambuf[0])
5848           ckstrncpy(asnambuf,sndxnam,CKMAXPATH);
5849         cmarg = "";
5850     }
5851 #endif /* PUTARRAY */
5852
5853     moving = 0;
5854
5855     if (pv[SND_ARR].ival < 1) {         /* File selection & disposition... */
5856         if (pv[SND_DEL].ival > 0)       /* /DELETE was specified */
5857           moving = 1;
5858         if (pv[SND_AFT].ival > 0)       /* Copy SEND criteria */
5859           ckstrncpy(sndafter,pv[SND_AFT].sval,19);
5860         if (pv[SND_BEF].ival > 0)
5861           ckstrncpy(sndbefore,pv[SND_BEF].sval,19);
5862         if (pv[SND_NAF].ival > 0)
5863           ckstrncpy(sndnafter,pv[SND_NAF].sval,19);
5864         if (pv[SND_NBE].ival > 0)
5865           ckstrncpy(sndnbefore,pv[SND_NBE].sval,19);
5866         if (pv[SND_EXC].ival > 0)
5867           makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT);
5868         if (pv[SND_SMA].ival > -1)
5869           sndsmaller = pv[SND_SMA].wval;
5870         if (pv[SND_LAR].ival > -1)
5871           sndlarger = pv[SND_LAR].wval;
5872         if (pv[SND_NAM].ival > -1)
5873           x_cnv = pv[SND_NAM].ival;
5874         if (pv[SND_USN].ival > -1)
5875           x_usn = pv[SND_USN].ival;
5876         if (pv[SND_ERR].ival > -1)
5877           puterror = pv[SND_ERR].ival;
5878
5879 #ifdef DOUPDATE
5880         if (pv[SND_UPD].ival > 0) {
5881             if (x_usn) {
5882                 printf("?Conflicting switches: /UPDATE /UNIQUE\n");
5883                 x = -9;
5884                 goto xputx;
5885             }
5886             putflags |= PUT_UPD;
5887             ftp_dates |= 2;
5888         }
5889 #ifdef COMMENT
5890         /* This works but it's useless, maybe dangerous */
5891         if (pv[SND_DIF].ival > 0) {
5892             if (x_usn) {
5893                 printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n");
5894                 x = -9;
5895                 goto xputx;
5896             }
5897             putflags |= PUT_DIF;
5898             ftp_dates |= 2;
5899         }
5900 #endif /* COMMENT */
5901 #endif /* DOUPDATE */
5902
5903         if (pv[SND_SIM].ival > 0)
5904           putflags |= PUT_SIM;
5905
5906         if (pv[SND_PRM].ival > -1) {
5907 #ifdef UNIX
5908             if (x_usn) {
5909                 printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n");
5910                 x = -9;
5911                 goto xputx;
5912             }
5913             x_prm = pv[SND_PRM].ival;
5914 #else /* UNIX */
5915             printf("?/PERMISSIONS switch is not supported\n");
5916 #endif /* UNIX */
5917         }
5918 #ifdef FTP_RESTART
5919         if (pv[SND_RES].ival > 0) {
5920             if (!sizeok) {
5921                 printf("?PUT /RESTART can't be used because SIZE disabled.\n");
5922                 x = -9;
5923                 goto xputx;
5924             }
5925             if (x_usn || putflags) {
5926                 printf("?Conflicting switches: /RECOVER %s\n",
5927                        x_usn && putflags ? "/UNIQUE /UPDATE" :
5928                        (x_usn ? "/UNIQUE" : "/UPDATE")
5929                        );
5930                 x = -9;
5931                 goto xputx;
5932             }
5933 #ifndef NOCSETS
5934             if (x_xla &&
5935                 (x_csl == FC_UCS2 ||
5936                  x_csl == FC_UTF8 ||
5937                  x_csr == FC_UCS2 ||
5938                  x_csr == FC_UTF8)) {
5939                 printf("?/RECOVER can not be used with Unicode translation\n");
5940                 x = -9;
5941                 goto xputx;
5942             }
5943 #endif /* NOCSETS */
5944             putflags = PUT_RES;
5945         }
5946 #endif /* FTP_RESTART */
5947     }
5948     debug(F101,"ftp PUT restart","",putflags & PUT_RES);
5949     debug(F101,"ftp PUT update","",putflags & PUT_UPD);
5950
5951 #ifdef PIPESEND
5952     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
5953         if (!pv[SND_FLT].sval) {
5954             sndfilter = NULL;
5955         } else {
5956             sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1);
5957             if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */
5958         }
5959         debug(F110,"ftp put /FILTER", sndfilter, 0);
5960     }
5961     if (sndfilter || pipesend)          /* No /UPDATE or /RESTART */
5962       if (putflags)                     /* with pipes or filters */
5963         putflags = 0;
5964 #endif /* PIPESEND */
5965
5966     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
5967     filcnt = 0;
5968     pktnum = 0;
5969     spackets = 0L;
5970
5971     if (wild)                           /* (is this necessary?) */
5972       cx = FTP_MPU;
5973
5974     t0 = gmstimer();                    /* Record starting time */
5975
5976     done = 0;                           /* Loop control */
5977     cancelgroup = 0;
5978
5979     cdlevel = 0;
5980     cdsimlvl = 0;
5981     while (!done && !cancelgroup) {     /* Loop for all files */
5982                                         /* or until canceled. */
5983 #ifdef FTP_PROXY
5984         /*
5985            If we are using a proxy, we don't use the local file list;
5986            instead we use the list on the remote machine which we want
5987            sent to someone else, and we use remglob() to get the names.
5988            But in that case we shouldn't even be executing this routine;
5989            see ftp_mput().
5990         */
5991 #endif /* FTP_PROXY */
5992
5993         cancelfile = 0;
5994         x = gnfile();                   /* Get next file from list(s) */
5995
5996         if (x == 0)                     /* (see gnfile() comments...) */
5997           x = gnferror;
5998         debug(F111,"FTP PUT gnfile",filnam,x);
5999         debug(F111,"FTP PUT binary",filnam,binary);
6000
6001         switch (x) {
6002           case 1:                       /* File to send */
6003             s2 = asnambuf;
6004 #ifndef NOSPL
6005             if (asnambuf[0]) {          /* As-name */
6006                 int n; char *p;         /* to be evaluated... */
6007                 n = TMPBUFSIZ;
6008                 p = tmpbuf;
6009                 zzstring(asnambuf,&p,&n);
6010                 s2 = tmpbuf;
6011                 debug(F110,"ftp put asname",s2,0);
6012             }
6013 #endif /* NOSPL */
6014             rc = putfile(cx,            /* Function (PUT, APPEND) */
6015                     filnam, s2,         /* Name to send, as-name */
6016                     forcetype, moving,  /* Parameters from switches... */
6017                     snd_move, snd_rename, srv_renam,
6018                     x_cnv, x_usn, xfiletype, x_prm,
6019 #ifndef NOCSETS
6020                     x_csl, (!x_xla ? -1 : x_csr),
6021 #else
6022                     -1, -1,
6023 #endif /* NOCSETS */
6024                     putflags
6025                     );
6026             debug(F111,"ftp put putfile rc",filnam,rc);
6027             debug(F111,"ftp put putfile cancelfile",filnam,cancelfile);
6028             debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup);
6029             if (rc > -1) {
6030                 good++;
6031                 status = 1;
6032             }
6033             if (cancelfile)
6034               continue;
6035             if (rc < 0) {
6036                 ftp_fai++;
6037                 if (puterror) {
6038                     status = 0;
6039                     printf("?Fatal upload error: %s\n",filnam);
6040                     done++;
6041                 }
6042             }
6043             continue;
6044           case 0:                       /* No more files, done */
6045             done++;
6046             continue;
6047           case -1:
6048             printf("?%s: file not found - \"%s\"\n",
6049                    puterror ? "Fatal" : "Warning",
6050                    filnam
6051                    );
6052             if (puterror) {
6053                 status = 0;
6054                 done++;
6055                 break;
6056             }
6057             continue;
6058           case -2:
6059             if (puterror) {
6060                 printf("?Fatal: file not found - \"%s\"\n", filnam);
6061                 status = 0;
6062                 done++;
6063                 break;
6064             }
6065             continue;                   /* Not readable, keep going */
6066           case -3:
6067             if (puterror) {
6068                 printf("?Fatal: Read access denied - \"%s\"\n", filnam);
6069                 status = 0;
6070                 done++;
6071                 break;
6072             }
6073             printf("?Warning access denied - \"%s\"\n", filnam);
6074             continue;
6075 #ifdef COMMENT
6076           case -4:                      /* Canceled */
6077             done++;
6078             break;
6079 #endif /* COMMENT */
6080           case -5:
6081             printf("?Too many files match\n");
6082             done++;
6083             break;
6084           case -6:
6085             if (good < 1)
6086               printf("?No files selected\n");
6087             done++;
6088             break;
6089           default:
6090             printf("?getnextfile() - unknown failure\n");
6091             done++;
6092         }
6093     }
6094     if (cdlevel > 0) {
6095         while (cdlevel--) {
6096             if (cdsimlvl) {
6097                 cdsimlvl--;
6098             } else if (!doftpcdup())
6099               break;
6100         }
6101     }
6102     if (status > 0) {
6103         if (cancelgroup)
6104           status = 0;
6105         else if (cancelfile && good < 1)
6106           status = 0;
6107     }
6108     success = status;
6109     x = success;
6110
6111   xputx:
6112     if (x > -1) {
6113 #ifdef GFTIMER
6114         t1 = gmstimer();                /* End time */
6115         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6116         if (!sec) sec = 0.001;
6117         fptsecs = sec;
6118 #else
6119         sec = (t1 - t0) / 1000;
6120         if (!sec) sec = 1;
6121 #endif /* GFTIMER */
6122         tfcps = (long) (tfc / sec);
6123         tsecs = (int)sec;
6124         lastxfer = W_FTP|W_SEND;
6125         xferstat = success;
6126         if (dpyactive)
6127           ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6128     }
6129     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
6130         if (pv[i].sval)
6131           free(pv[i].sval);
6132     }
6133     ftreset();                          /* Undo switch effects */
6134     dpyactive = 0;
6135     return(x);
6136 }
6137
6138
6139 static char ** mgetlist = NULL;         /* For MGET */
6140 static int mgetn = 0, mgetx = 0;
6141 static char xtmpbuf[4096];
6142
6143 /*
6144   c m d l i n g e t
6145
6146   Get files specified by -g command-line option.
6147   File list is set up in cmlist[] by ckuusy.c; nfils is length of list.
6148 */
6149 int
6150 cmdlinget(stay) int stay; {
6151     int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0;
6152     int lcs = -1, rcs = -1, xlate = 0;
6153     int first = 1;
6154     int mget = 1;
6155     int nc;
6156     char * s, * s2, * s3;
6157     ULONG t0, t1;                       /* Times for stats */
6158 #ifdef GFTIMER
6159     CKFLOAT sec;
6160 #else
6161     int sec = 0;
6162 #endif /* GFTIMER */
6163
6164     if (quiet) {                        /* -q really means quiet */
6165         displa = 0;
6166         fdispla = 0;
6167     } else {
6168         displa = 1;
6169         fdispla = XYFD_B;
6170     }
6171     testing = 0;
6172     dpyactive = 0;
6173     out2screen = 0;
6174     what = W_FTP|W_RECV;
6175     mgetmethod = 0;
6176     mgetforced = 0;
6177
6178     havetype = 0;
6179     havesize = (CK_OFF_T)-1;
6180     makestr(&havemdtm,NULL);
6181
6182     if (ftp_fnc < 0)
6183       ftp_fnc = fncact;
6184
6185 #ifndef NOSPL
6186     cmd_quoting = 0;
6187 #endif /* NOSPL */
6188     debug(F101,"ftp cmdlinget nfils","",nfils);
6189
6190     if (ftp_cnv == CNV_AUTO) {          /* Name conversion is auto */
6191         if (alike) {                    /* If server & client are alike */
6192             nc = 0;                     /* no conversion */
6193         } else {                        /* If they are different */
6194             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6195               nc = -1;                  /* only minimal conversions needed */
6196             else                        /* otherwise */
6197               nc = 1;                   /* full conversion */
6198         }
6199     } else                              /* Not auto - do what user said */
6200       nc = ftp_cnv;
6201
6202     if (nfils < 1)
6203       doexit(BAD_EXIT,-1);
6204
6205     t0 = gmstimer();                    /* Starting time for this batch */
6206
6207 #ifndef NOCSETS
6208     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
6209         lcs = ftp_csl;                  /* Local charset */
6210         if (lcs < 0) lcs = fcharset;
6211         if (lcs < 0) xlate = 0;
6212     }
6213     if (xlate) {                        /* Still ON? */
6214         rcs = ftp_csx;                  /* Remote (Server) charset */
6215         if (rcs < 0) rcs = ftp_csr;
6216         if (rcs < 0) xlate = 0;
6217     }
6218 #endif /* NOCSETS */
6219     /*
6220       If we have only one file and it is a directory, then we ask for a
6221       listing of its contents, rather than retrieving the directory file
6222       itself.  This is what (e.g.) Netscape does.
6223     */
6224     if (nfils == 1) {
6225         if (doftpcwd((char *)cmlist[mgetx],-1)) {
6226             /* If we can CD to it, it must be a directory */
6227             if (recursive) {
6228                 cmlist[mgetx] = "*";
6229             } else {
6230                 status =
6231                   (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0);
6232                 done = 1;
6233             }
6234         }
6235     }
6236 /*
6237   The following is to work around UNIX servers which, when given a command
6238   like "NLST path/blah" (not wild) returns the basename without the path.
6239 */
6240     if (!done && servertype == SYS_UNIX && nfils == 1) {
6241         mget = iswild(cmlist[mgetx]);
6242     }
6243     if (!mget && !done) {               /* Invoked by command-line FTP URL */
6244         if (ftp_deb)
6245           printf("DOING GET...\n");
6246         done++;
6247         cancelfile = 0;                 /* This file not canceled yet */
6248         s = cmlist[mgetx];
6249         rc = 0;                         /* Initial return code */
6250         fsize = (CK_OFF_T)-1;
6251         if (sizeok) {
6252             x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */
6253             if (x == REPLY_COMPLETE)
6254               fsize = ckatofs(&ftp_reply_str[4]);
6255         }
6256         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6257         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6258
6259         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6260         s2 = tmpbuf;
6261
6262         /* If local file already exists, take collision action */
6263
6264         if (zchki(s2) > -1) {
6265             switch (ftp_fnc) {
6266               case XYFX_A:              /* Append */
6267                 append = 1;
6268                 break;
6269               case XYFX_R:              /* Rename */
6270               case XYFX_B: {            /* Backup */
6271                   char * p = NULL;
6272                   int x = -1;
6273                   znewn(s2,&p);         /* Make unique name */
6274                   debug(F110,"ftp cmdlinget znewn",p,0);
6275                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6276                       x = zrename(s2,p);
6277                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6278                   } else {              /* Rename incoming file */
6279                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6280                       s2 = tmpbuf;
6281                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6282                   }
6283                   if (x < 0) {
6284                       printf("?Backup/Rename failed\n");
6285                       return(success = 0);
6286                   }
6287                   break;
6288               }
6289               case XYFX_D:              /* Discard */
6290                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6291                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6292                 tlog(F100," refused: name","",0);
6293                 debug(F110,"ftp cmdlinget skip name",s2,0);
6294                 goto xclget;
6295
6296               case XYFX_X:              /* Overwrite */
6297               case XYFX_U:              /* Update (already handled above) */
6298               case XYFX_M:              /* ditto */
6299                 break;
6300             }
6301         }
6302         rc = getfile(s,                 /* Remote name */
6303                      s2,                /* Local name */
6304                      0,                 /* Recover/Restart */
6305                      append,            /* Append */
6306                      NULL,              /* Pipename */
6307                      0,                 /* Translate charsets */
6308                      -1,                /* File charset (none) */
6309                      -1                 /* Server charset (none) */
6310                      );
6311         debug(F111,"ftp cmdlinget rc",s,rc);
6312         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6313         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6314
6315         if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */
6316             rc = getfile(&s[1],         /* Remote name without leading '/' */
6317                          s2,            /* Local name */
6318                          0,             /* Recover/Restart */
6319                          append,        /* Append */
6320                          NULL,          /* Pipename */
6321                          0,             /* Translate charsets */
6322                          -1,            /* File charset (none) */
6323                          -1             /* Server charset (none) */
6324                          );
6325         if (rc > -1) {
6326             good++;
6327             status = 1;
6328         }
6329         if (cancelfile)
6330           goto xclget;
6331         if (rc < 0) {
6332             ftp_fai++;
6333 #ifdef FTP_TIMEOUT
6334             if (ftp_timed_out)
6335               status = 0;
6336 #endif  /* FTP_TIMEOUT */
6337             if (geterror) {
6338                 status = 0;
6339                 done++;
6340             }
6341         }
6342     }
6343     if (ftp_deb && !done)
6344       printf("DOING MGET...\n");
6345     while (!done && !cancelgroup) {
6346         cancelfile = 0;                 /* This file not canceled yet */
6347         s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6348         if (!s) s = "";
6349         if (!*s) {
6350             first = 1;
6351             mgetx++;
6352             if (mgetx < nfils)
6353               s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6354             else
6355               s = NULL;
6356             debug(F111,"ftp cmdlinget remote_files B",s,0);
6357             if (!s) {
6358                 done = 1;
6359                 break;
6360             }
6361         }
6362         /*
6363           The semantics of NLST are ill-defined.  Suppose we have just sent
6364           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
6365           /path/bar, etc.  But some send back only foo and bar, and subsequent
6366           RETR commands based on the pathless names are not going to work.
6367         */
6368         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
6369             if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) {
6370                 int len, left = 4096;
6371                 char * tmp = xtmpbuf;
6372                 len = s3 - cmlist[mgetx] + 1;
6373                 ckstrncpy(tmp,cmlist[mgetx],left);
6374                 tmp += len;
6375                 left -= len;
6376                 ckstrncpy(tmp,s,left);
6377                 s = xtmpbuf;
6378                 debug(F111,"ftp cmdlinget remote_files X",s,0);
6379             }
6380         }
6381         first = 0;                      /* Not first any more */
6382
6383         debug(F111,"ftp cmdlinget havetype",s,havetype);
6384         if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */
6385             debug(F110,"ftp cmdlinget not-a-file",s,0);
6386             continue;
6387         }
6388         rc = 0;                         /* Initial return code */
6389         if (havesize > (CK_OFF_T)-1) {  /* Already have file size? */
6390             fsize = havesize;
6391         } else {                        /* No - must ask server */
6392             /*
6393               Prior to sending the NLST command we necessarily put the
6394               server into ASCII mode.  We must now put it back into the
6395               the requested mode so the upcoming SIZE command returns
6396               right kind of size; this is especially important for
6397               GET /RECOVER; otherwise the server returns the "ASCII" size
6398               of the file, rather than its true size.
6399             */
6400             changetype(ftp_typ,0);      /* Change to requested type */
6401             fsize = (CK_OFF_T)-1;
6402             if (sizeok) {
6403                 x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm);
6404                 if (x == REPLY_COMPLETE)
6405                   fsize = ckatofs(&ftp_reply_str[4]);
6406             }
6407         }
6408         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6409         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6410
6411         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6412         s2 = tmpbuf;
6413
6414         /* If local file already exists, take collision action */
6415
6416         if (zchki(s2) > -1) {
6417             switch (ftp_fnc) {
6418               case XYFX_A:              /* Append */
6419                 append = 1;
6420                 break;
6421               case XYFX_R:              /* Rename */
6422               case XYFX_B: {            /* Backup */
6423                   char * p = NULL;
6424                   int x = -1;
6425                   znewn(s2,&p);         /* Make unique name */
6426                   debug(F110,"ftp cmdlinget znewn",p,0);
6427                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6428                       x = zrename(s2,p);
6429                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6430                   } else {              /* Rename incoming file */
6431                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6432                       s2 = tmpbuf;
6433                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6434                   }
6435                   if (x < 0) {
6436                       printf("?Backup/Rename failed\n");
6437                       return(success = 0);
6438                   }
6439                   break;
6440               }
6441               case XYFX_D:      /* Discard */
6442                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6443                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6444                 tlog(F100," refused: name","",0);
6445                 debug(F110,"ftp cmdlinget skip name",s2,0);
6446                 continue;
6447               case XYFX_X:              /* Overwrite */
6448               case XYFX_U:              /* Update (already handled above) */
6449               case XYFX_M:              /* ditto */
6450                 break;
6451             }
6452         }
6453                                         /* ^^^ ADD CHARSET STUFF HERE ^^^ */
6454         rc = getfile(s,                 /* Remote name */
6455                      s2,                /* Local name */
6456                      0,                 /* Recover/Restart */
6457                      append,            /* Append */
6458                      NULL,              /* Pipename */
6459                      0,                 /* Translate charsets */
6460                      -1,                /* File charset (none) */
6461                      -1                 /* Server charset (none) */
6462                      );
6463         debug(F111,"ftp cmdlinget rc",s,rc);
6464         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6465         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6466
6467         if (rc > -1) {
6468             good++;
6469             status = 1;
6470         }
6471         if (cancelfile)
6472           continue;
6473         if (rc < 0) {
6474             ftp_fai++;
6475 #ifdef FTP_TIMEOUT
6476             if (ftp_timed_out)
6477               status = 0;
6478 #endif  /* FTP_TIMEOUT */
6479             if (geterror) {
6480                 status = 0;
6481                 done++;
6482             }
6483         }
6484     }
6485
6486   xclget:
6487     if (cancelgroup)
6488       mlsreset();
6489     if (status > 0) {
6490         if (cancelgroup)
6491           status = 0;
6492         else if (cancelfile && good < 1)
6493           status = 0;
6494     }
6495     success = status;
6496
6497 #ifdef GFTIMER
6498     t1 = gmstimer();                    /* End time */
6499     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6500     if (!sec) sec = 0.001;
6501     fptsecs = sec;
6502 #else
6503     sec = (t1 - t0) / 1000;
6504     if (!sec) sec = 1;
6505 #endif /* GFTIMER */
6506
6507     tfcps = (long) (tfc / sec);
6508     tsecs = (int)sec;
6509     lastxfer = W_FTP|W_RECV;
6510     xferstat = success;
6511     if (dpyactive)
6512       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6513     if (!stay)
6514       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
6515     return(success);
6516 }
6517
6518 /*  d o f t p g e t  --  Parse and execute GET, MGET, MDELETE, ...  */
6519
6520 /*
6521   Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use
6522   zstrdat() to convert to UTC-based time_t.  But it doesn't make sense from
6523   the user-interface perspective, since the server's directory listings show
6524   its own local times and since we don't know what timezone it's in, there's
6525   no way to reconcile our local times with the server's.
6526 */
6527 int
6528 doftpget(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
6529     struct FDB fl, sw, cm;
6530     int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0;
6531     int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0;
6532     int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0;
6533     int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0;
6534     int moving = 0, deleting = 0, toscreen = 0, haspath = 0;
6535     int gotsize = 0;
6536     int matchdot = 0;
6537     CK_OFF_T getlarger = (CK_OFF_T)-1;
6538     CK_OFF_T getsmaller = (CK_OFF_T)-1;
6539     char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL;
6540     char * src = "", * local = "";
6541     char * pat = "";
6542
6543     int x_csl = -1, x_csr = -1;         /* Local and remote charsets */
6544     int x_xla = 0;
6545     char c;                             /* Worker char */
6546     ULONG t0 = 0L, t1;                  /* Times for stats */
6547 #ifdef GFTIMER
6548     CKFLOAT sec;
6549 #else
6550     int sec = 0;
6551 #endif /* GFTIMER */
6552
6553     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
6554
6555     success = 0;                        /* Assume failure */
6556     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
6557     restart = 0;                        /* No restart yet */
6558     out2screen = 0;                     /* No TO-SCREEN switch given yet */
6559     mgetmethod = 0;                     /* No NLST or MLSD switch yet */
6560     mgetforced = 0;
6561
6562     g_displa = fdispla;
6563     if (ftp_dis > -1)
6564       fdispla = ftp_dis;
6565
6566     x_cnv = ftp_cnv;                    /* Filename conversion */
6567     if (x_cnv == CNV_AUTO) {            /* Name conversion is auto */
6568         if (alike) {                    /* If server & client are alike */
6569             x_cnv = 0;                  /* no conversion */
6570         } else {                        /* If they are different */
6571             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6572               x_cnv = -1;               /* only minimal conversions needed */
6573             else                        /* otherwise */
6574               x_cnv = 1;                /* full conversion */
6575         }
6576     } else                              /* Not auto - do what user said */
6577       x_cnv = ftp_cnv;
6578
6579     x_prm = ftp_prm;                    /* Permissions */
6580     if (x_prm == SET_AUTO)              /* Permissions AUTO */
6581       x_prm = alike;
6582
6583 #ifndef NOCSETS
6584     x_csr = ftp_csr;                    /* Inherit global server charset */
6585     x_csl = ftp_csl;                    /* Inherit global local charset */
6586     if (x_csl < 0)                      /* If none, use current */
6587       x_csl = fcharset;                 /* file character-set. */
6588     x_xla = ftp_xla;                    /* Translation On/Off */
6589 #endif /* NOCSETS */
6590
6591     geterror = ftp_err;                 /* Inherit global error action. */
6592     asnambuf[0] = NUL;                  /* No as-name yet. */
6593     pipesave = pipesend;
6594     pipesend = 0;
6595
6596     havetype = 0;
6597     havesize = (CK_OFF_T)-1;
6598     makestr(&havemdtm,NULL);
6599
6600     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
6601         ftp_typ = g_ftp_typ;
6602         /* g_ftp_typ = -1; */
6603     }
6604     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
6605         pv[i].sval = NULL;              /* to null pointers */
6606         pv[i].ival = -1;                /* and -1 int values */
6607         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
6608     }
6609     zclose(ZMFILE);                     /* In case it was left open */
6610
6611     x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */
6612
6613     if (fp_nml) {                       /* Reset /NAMELIST */
6614         if (fp_nml != stdout)
6615           fclose(fp_nml);
6616         fp_nml = NULL;
6617     }
6618     makestr(&ftp_nml,NULL);
6619
6620     /* Initialize list of remote filespecs */
6621
6622     if (!mgetlist) {
6623         mgetlist = (char **)malloc(MGETMAX * sizeof(char *));
6624         if (!mgetlist) {
6625             printf("?Memory allocation failure - MGET list\n");
6626             return(-9);
6627         }
6628         for (i = 0; i < MGETMAX; i++)
6629           mgetlist[i] = NULL;
6630     }
6631     mgetn = 0;                          /* Number of mget arguments */
6632     mgetx = 0;                          /* Current arg */
6633
6634     if (who == 0) {                     /* Called with unprefixed command */
6635         if (cx == XXGET || cx == XXREGET || cx == XXRETR)
6636           getone++;
6637         switch (cx) {
6638           case XXREGET: pv[SND_RES].ival = 1; break;
6639           case XXRETR:  pv[SND_DEL].ival = 1; break;
6640           case XXGET:
6641           case XXMGET:  mget++; break;
6642         }
6643     } else {                            /* FTP command */
6644         if (cx == FTP_GET || cx == FTP_RGE)
6645           getone++;
6646         switch (cx) {
6647           case FTP_DEL:                 /* (fall thru on purpose) */
6648           case FTP_MDE: mdel++;         /* (ditto) */
6649           case FTP_GET:                 /* (ditto) */
6650           case FTP_MGE: mget++; break;
6651           case FTP_RGE: pv[SND_RES].ival = 1; break;
6652         }
6653     }
6654     cmfdbi(&sw,                         /* First FDB - command switches */
6655            _CMKEY,                      /* fcode */
6656            "Remote filename;\n or switch", /* hlpmsg */
6657            "",                          /* default */
6658            "",                          /* addtl string data */
6659            mdel ? ndelswi : ngetswi,    /* addtl numeric data 1: tbl size */
6660            4,                           /* addtl numeric data 2: 4 = cmswi */
6661            xxstring,                    /* Processing function */
6662            mdel ? delswi : getswi,      /* Keyword table */
6663            &fl                          /* Pointer to next FDB */
6664            );
6665     cmfdbi(&fl,                         /* 2nd FDB - remote filename */
6666            _CMFLD,                      /* fcode */
6667            "",                          /* hlpmsg */
6668            "",                          /* default */
6669            "",                          /* addtl string data */
6670            0,                           /* addtl numeric data 1 */
6671            0,                           /* addtl numeric data 2 */
6672            xxstring,
6673            NULL,
6674            &cm
6675            );
6676     cmfdbi(&cm,                         /* 3rd FDB - Confirmation */
6677            _CMCFM,                      /* fcode */
6678            "",                          /* hlpmsg */
6679            "",                          /* default */
6680            "",                          /* addtl string data */
6681            0,                           /* addtl numeric data 1 */
6682            0,                           /* addtl numeric data 2 */
6683            NULL,
6684            NULL,
6685            NULL
6686            );
6687
6688     while (1) {                         /* Parse 0 or more switches */
6689         x = cmfdb(&sw);                 /* Parse something */
6690         debug(F101,"ftp get cmfdb","",x);
6691         if (x < 0)                      /* Error */
6692           goto xgetx;                   /* or reparse needed */
6693         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
6694           break;
6695         c = cmgbrk();                   /* Get break character */
6696         getval = (c == ':' || c == '='); /* to see how they ended the switch */
6697         if (getval && !(cmresult.kflags & CM_ARG)) {
6698             printf("?This switch does not take arguments\n");
6699             x = -9;
6700             goto xgetx;
6701         }
6702         n = cmresult.nresult;           /* Numeric result = switch value */
6703         debug(F101,"ftp get switch","",n);
6704
6705         if (!getval && (cmgkwflgs() & CM_ARG)) {
6706             printf("?This switch requires an argument\n");
6707             x = -9;
6708             goto xgetx;
6709         }
6710         switch (n) {                    /* Process the switch */
6711           case SND_ASN:                 /* /AS-NAME: */
6712             debug(F101,"ftp get /as-name getval","",getval);
6713             if (!getval) break;
6714             if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) {
6715                 if (x == -3) {
6716                     printf("?name required\n");
6717                     x = -9;
6718                 }
6719                 goto xgetx;
6720             }
6721             s = brstrip(s);
6722             if (!*s) s = NULL;
6723             makestr(&(pv[n].sval),s);
6724             pv[n].ival = 1;
6725             break;
6726
6727           case SND_BIN:                 /* /BINARY */
6728           case SND_TXT:                 /* /TEXT or /ASCII */
6729           case SND_TEN:                 /* /TENEX */
6730             pv[SND_BIN].ival = 0;
6731             pv[SND_TXT].ival = 0;
6732             pv[SND_TEN].ival = 0;
6733             pv[n].ival = 1;
6734             break;
6735
6736 #ifdef PUTPIPE
6737           case SND_CMD:                 /* These take no args */
6738             if (nopush) {
6739                 printf("?Sorry, system command access is disabled\n");
6740                 x = -9;
6741                 goto xgetx;
6742             }
6743 #ifdef PIPESEND
6744             else if (rcvfilter) {
6745                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
6746                 x = -9;
6747                 goto xgetx;
6748             }
6749 #endif /* PIPESEND */
6750             sw.hlpmsg = "Command, or switch"; /* Change help message */
6751             pv[n].ival = 1;             /* Just set the flag */
6752             pv[SND_ARR].ival = 0;
6753             break;
6754 #endif /* PUTPIPE */
6755
6756           case SND_SHH:                 /* /QUIET */
6757           case SND_RES:                 /* /RECOVER (reget) */
6758           case SND_NOB:                 /* /NOBACKUPFILES */
6759           case SND_DEL:                 /* /DELETE */
6760           case SND_UPD:                 /* /UPDATE */
6761           case SND_USN:                 /* /UNIQUE */
6762           case SND_NOD:                 /* /NODOTFILES */
6763           case SND_REC:                 /* /RECOVER */
6764           case SND_MAI:                 /* /TO-SCREEN */
6765             pv[n].ival = 1;             /* Just set the flag */
6766             break;
6767
6768           case SND_DIF:                 /* /DATES-DIFFER */
6769             pv[SND_COL].ival = XYFX_M;  /* Now it's a collision option */
6770             pv[n].ival = 1;
6771             break;
6772
6773           case SND_COL:                 /* /COLLISION: */
6774             if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
6775               goto xgetx;
6776             if (x == XYFX_M)
6777               pv[SND_DIF].ival = 1;     /* (phase this out) */
6778             pv[n].ival = x;             /* this should be sufficient */
6779             break;
6780
6781           case SND_ERR:                 /* /ERROR-ACTION */
6782             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
6783               goto xgetx;
6784             pv[n].ival = x;
6785             break;
6786
6787           case SND_EXC:                 /* Exception list */
6788             if (!getval) break;
6789             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
6790                 if (x == -3) {
6791                     printf("?Pattern required\n");
6792                     x = -9;
6793                 }
6794                 goto xgetx;
6795             }
6796             if (s) if (!*s) s = NULL;
6797             makestr(&(pv[n].sval),s);
6798             if (pv[n].sval)
6799               pv[n].ival = 1;
6800             break;
6801
6802 #ifdef PIPESEND
6803           case SND_FLT:
6804             debug(F101,"ftp get /filter getval","",getval);
6805             if (!getval) break;
6806             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
6807                 if (x == -3)
6808                   s = "";
6809                 else
6810                   goto xgetx;
6811             }
6812             s = brstrip(s);
6813             if (pv[SND_MAI].ival < 1) {
6814                 y = strlen(s);
6815                 /* Make sure they included "\v(...)" */
6816                 for (x = 0; x < y; x++) {
6817                     if (s[x] != '\\') continue;
6818                     if (s[x+1] == 'v') break;
6819                 }
6820                 if (x == y) {
6821                     printf(
6822                 "?Filter must contain a replacement variable for filename.\n"
6823                            );
6824                     x = -9;
6825                     goto xgetx;
6826                 }
6827             }
6828             if (*s) {
6829                 pv[n].ival = 1;
6830                 makestr(&(pv[n].sval),s);
6831             } else {
6832                 pv[n].ival = 0;
6833                 makestr(&(pv[n].sval),NULL);
6834             }
6835             break;
6836 #endif /* PIPESEND */
6837
6838           case SND_NAM:
6839             if (!getval) break;
6840             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
6841               goto xgetx;
6842             debug(F101,"ftp get /filenames","",x);
6843             pv[n].ival = x;
6844             break;
6845
6846           case SND_SMA:                 /* Smaller / larger than */
6847           case SND_LAR: {
6848               CK_OFF_T y;
6849               if (!getval) break;
6850               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
6851                 goto xgetx;
6852               pv[n].wval = y;
6853               break;
6854           }
6855           case SND_FIL:                 /* Name of file containing filnames */
6856             if (!getval) break;
6857             if ((x = cmifi("Name of file containing list of filenames",
6858                                "",&s,&y,xxstring)) < 0) {
6859                 if (x == -3) {
6860                     printf("?Filename required\n");
6861                     x = -9;
6862                 }
6863                 goto xgetx;
6864             } else if (y && iswild(s)) {
6865                 printf("?Wildcards not allowed BBB\n");
6866                 x = -9;
6867                 goto xgetx;
6868             }
6869             if (s) if (!*s) s = NULL;
6870             makestr(&(pv[n].sval),s);
6871             if (pv[n].sval)
6872               pv[n].ival = 1;
6873             break;
6874
6875           case SND_MOV:                 /* MOVE after */
6876           case SND_REN:                 /* RENAME after */
6877           case SND_SRN: {               /* SERVER-RENAME */
6878               char * m = "";
6879               switch (n) {
6880                 case SND_MOV:
6881                   m =
6882                    "Device and/or directory for incoming file after reception";
6883                   break;
6884                 case SND_REN:
6885                   m = "New name for incoming file after reception";
6886                   break;
6887                 case SND_SRN:
6888                   m = "New name for source file on server after reception";
6889                   break;
6890               }
6891               if (!getval) break;
6892               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
6893                   if (x == -3) {
6894                       printf("%s\n", n == SND_MOV ?
6895                              "?Destination required" :
6896                              "?New name required"
6897                              );
6898                       x = -9;
6899                   }
6900                   goto xgetx;
6901               }
6902               makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6903               pv[n].ival = (pv[n].sval) ? 1 : 0;
6904               break;
6905           }
6906 #ifndef NOCSETS
6907           case SND_CSL:                 /* Local character set */
6908           case SND_CSR:                 /* Remote (server) charset */
6909             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0)
6910               return((x == -3) ? -2 : x);
6911             if (n == SND_CSL)
6912               x_csl = x;
6913             else
6914               x_csr = x;
6915             x_xla = 1;                  /* Overrides global OFF setting */
6916             break;
6917
6918           case SND_XPA:                 /* Transparent */
6919             x_xla =  0;
6920             x_csr = -1;
6921             x_csl = -1;
6922             break;
6923 #endif /* NOCSETS */
6924
6925           case SND_NML:
6926             if ((x = cmofi("Local filename","-",&s,xxstring)) < 0)
6927               goto xgetx;
6928             makestr(&ftp_nml,s);
6929             break;
6930
6931           case SND_PAT:                 /* /PATTERN: */
6932             if (!getval) break;
6933             if ((x = cmfld("Pattern","*", &s, xxstring)) < 0)
6934               goto xgetx;
6935             makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6936             pv[n].ival = (pv[n].sval) ? 1 : 0;
6937             break;
6938
6939           case SND_NLS:                 /* /NLST */
6940             pv[n].ival = 1;             /* Use NLST */
6941             pv[SND_MLS].ival = 0;       /* Don't use MLSD */
6942             break;
6943
6944           case SND_MLS:                 /* /MLSD */
6945             pv[n].ival = 1;             /* Use MLSD */
6946             pv[SND_NLS].ival = 0;       /* Don't use NLST */
6947             break;
6948
6949           default:                      /* /AFTER, /PERMISSIONS, etc... */
6950             printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf);
6951             x = -9;
6952             goto xgetx;
6953         }
6954     }
6955     line[0] = NUL;
6956     cmarg = line;
6957     cmarg2 = asnambuf;
6958     s = line;
6959 /*
6960   For GET, we want to parse an optional as-name, like with PUT.
6961   For MGET, we must parse a list of names, and then send NLST or MLSD
6962   commands for each name separately.
6963 */
6964     switch (cmresult.fcode) {           /* How did we get out of switch loop */
6965       case _CMFLD:                      /* Field */
6966         if (!getone) {
6967             s = brstrip(cmresult.sresult);
6968             makestr(&(mgetlist[mgetn++]),s);
6969             while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) {
6970                 if (x < 0)
6971                   goto xgetx;
6972                 makestr(&(mgetlist[mgetn++]),brstrip(s));
6973                 if (mgetn >= MGETMAX) {
6974                     printf("?Too many items in MGET list\n");
6975                     goto xgetx;
6976                 }
6977             }
6978             if ((x = cmcfm()) < 0)
6979               goto xgetx;
6980         } else {
6981             s = brstrip(cmresult.sresult);
6982             ckstrncpy(line,s,LINBUFSIZ);
6983             if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0)
6984               if (x != -3)
6985                 goto xgetx;
6986             s = brstrip(s);
6987             ckstrncpy(asnambuf,s,CKMAXPATH+1);
6988             if ((x = cmcfm()) < 0)
6989               goto xgetx;
6990         }
6991         break;
6992       case _CMCFM:                      /* Confirmation */
6993         break;
6994       default:
6995         printf("?Unexpected function code: %d\n",cmresult.fcode);
6996         x = -9;
6997         goto xgetx;
6998     }
6999     if (pv[SND_REC].ival > 0)           /* /RECURSIVE */
7000       recursive = 2;
7001
7002     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
7003         forcetype = 1;                  /* So skip the name-pattern match */
7004         ftp_typ = XYFT_B;               /* Set binary */
7005     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
7006         forcetype = 1;
7007         ftp_typ = XYFT_T;
7008     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
7009         forcetype = 1;
7010         ftp_typ = FTT_TEN;
7011     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
7012         forcetype = 1;
7013         ftp_typ = binary;
7014         g_ftp_typ = binary;
7015     }
7016     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
7017         char * p;
7018         p = brstrip(pv[SND_ASN].sval);  /* As-name */
7019         ckstrncpy(asnambuf,p,CKMAXPATH+1);
7020     }
7021     debug(F110,"ftp get asnambuf",asnambuf,0);
7022
7023 #ifdef PIPESEND
7024     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
7025         char * p;
7026         p = asnambuf;
7027         debug(F110,"GET /COMMAND before stripping",p,0);
7028         p = brstrip(p);
7029         debug(F110,"GET /COMMAND after stripping",p,0);
7030         if (!*p) {
7031             printf("?Sorry, a command to write to is required\n");
7032             x = -9;
7033             goto xgetx;
7034         }
7035         pipename = p;
7036         pipesend = 1;
7037     }
7038 #endif /* PIPESEND */
7039
7040 /* Set up /MOVE and /RENAME */
7041
7042 #ifdef COMMENT
7043     /* Conflict exists only for PUT - removed 13 Mar 2006 - fdc */
7044     if (pv[SND_DEL].ival > 0 &&
7045         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
7046         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
7047         x = -9;
7048         goto xgetx;
7049     }
7050 #endif  /* COMMENT */
7051 #ifdef CK_TMPDIR
7052     if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) {
7053         int len;
7054         char * p = pv[SND_MOV].sval;
7055         len = strlen(p);
7056         if (!isdir(p)) {                /* Check directory */
7057 #ifdef CK_MKDIR
7058             char * s = NULL;
7059             s = (char *)malloc(len + 4);
7060             if (s) {
7061                 strcpy(s,p);            /* safe */
7062 #ifdef datageneral
7063                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
7064 #else
7065                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
7066 #endif /* datageneral */
7067                 s[len++] = 'X';
7068                 s[len] = NUL;
7069 #ifdef NOMKDIR
7070                 x = -1;
7071 #else
7072                 x = zmkdir(s);
7073 #endif /* NOMKDIR */
7074                 free(s);
7075                 if (x < 0) {
7076                     printf("?Can't create \"%s\"\n",p);
7077                     x = -9;
7078                     goto xgetx;
7079                 }
7080             }
7081 #else
7082             printf("?Directory \"%s\" not found\n",p);
7083             x = -9;
7084             goto xgetx;
7085 #endif /* CK_MKDIR */
7086         }
7087         makestr(&rcv_move,p);
7088         moving = 1;
7089     }
7090 #endif /* CK_TMPDIR */
7091
7092     if (pv[SND_REN].ival > 0) {         /* /RENAME */
7093         char * p = pv[SND_REN].sval;
7094         if (!p) p = "";
7095         if (!*p) {
7096             printf("?New name required for /RENAME\n");
7097             x = -9;
7098             goto xgetx;
7099         }
7100         p = brstrip(p);
7101 #ifndef NOSPL
7102     /* If name given is wild, rename string must contain variables */
7103         if (mget && !getone) {
7104             char * s = tmpbuf;
7105             x = TMPBUFSIZ;
7106             zzstring(p,&s,&x);
7107             if (!strcmp(tmpbuf,p)) {
7108                 printf(
7109     "?/RENAME for file group must contain variables such as \\v(filename)\n"
7110                        );
7111                 x = -9;
7112                 goto xgetx;
7113             }
7114         }
7115 #endif /* NOSPL */
7116         renaming = 1;
7117         makestr(&rcv_rename,p);
7118         debug(F110,"FTP rcv_rename",rcv_rename,0);
7119     }
7120     if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) {
7121         printf("?Filename required but not given\n");
7122         x = -9;
7123         goto xgetx;
7124     } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) {
7125         printf("?You can't give both /LISTFILE and a remote filename\n");
7126         x = -9;
7127         goto xgetx;
7128     }
7129     CHECKCONN();                        /* Check connection */
7130
7131     if (pv[SND_COL].ival > -1)
7132       x_fnc = pv[SND_COL].ival;
7133
7134 #ifndef NOSPL
7135     /* If as-name given for MGET, as-name must contain variables */
7136     if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) {
7137         char * s = tmpbuf;
7138         x = TMPBUFSIZ;
7139         zzstring(asnambuf,&s,&x);
7140         if (!strcmp(tmpbuf,asnambuf)) {
7141             printf(
7142     "?As-name for MGET must contain variables such as \\v(filename)\n"
7143                    );
7144             x = -9;
7145             goto xgetx;
7146         }
7147     }
7148 #endif /* NOSPL */
7149
7150 /* doget: */
7151
7152     if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */
7153         fdispla = 0;
7154     } else {
7155         displa = 1;
7156         if (mdel || ftp_deb)
7157           fdispla = XYFD_B;
7158     }
7159     deleting = 0;
7160     if (pv[SND_DEL].ival > 0)           /* /DELETE was specified */
7161       deleting = 1;
7162     if (pv[SND_EXC].ival > 0)
7163       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
7164     if (pv[SND_SMA].wval > -1)
7165       getsmaller = pv[SND_SMA].wval;
7166     if (pv[SND_LAR].wval > -1)
7167       getlarger = pv[SND_LAR].wval;
7168     if (pv[SND_NAM].ival > -1)
7169       x_cnv = pv[SND_NAM].ival;
7170     if (pv[SND_ERR].ival > -1)
7171       geterror = pv[SND_ERR].ival;
7172     if (pv[SND_MAI].ival > -1)
7173       toscreen = 1;
7174
7175     if (pv[SND_NLS].ival > 0) {         /* Force NLST or MLSD? */
7176         mgetmethod = SND_NLS;
7177         mgetforced = 1;
7178     } else if (pv[SND_MLS].ival > 0) {
7179         mgetmethod = SND_MLS;
7180         mgetforced = 1;
7181     }
7182
7183 #ifdef FTP_RESTART
7184     if (pv[SND_RES].ival > 0) {
7185         if (!ftp_typ) {
7186             printf("?Sorry, GET /RECOVER requires binary mode\n");
7187             x = -9;
7188             goto xgetx;
7189 #ifdef COMMENT
7190         /* Not true - the fact that the initial REST fails does not mean */
7191         /* it will fail here.  */
7192         } else if (!okrestart) {
7193             printf("WARNING: Server might not support restart...\n");
7194 #endif /* COMMENT */
7195         }
7196         restart = 1;
7197     }
7198 #endif /* FTP_RESTART */
7199
7200 #ifdef PIPESEND
7201     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
7202         if (pipesend) {
7203             printf("?Switch conflict: /FILTER and /COMMAND\n");
7204             x = -9;
7205             goto xgetx;
7206         }
7207         makestr(&rcvfilter,pv[SND_FLT].sval);
7208         debug(F110,"ftp get /FILTER", rcvfilter, 0);
7209     }
7210     if (rcvfilter || pipesend) {        /* /RESTART */
7211 #ifdef FTP_RESTART
7212         if (restart) {                  /* with pipes or filters */
7213             printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n");
7214             x = -9;
7215             goto xgetx;
7216         }
7217 #endif /* FTP_RESTART */
7218         if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) {
7219             printf(
7220                 "?Switch conflict: /FILTER or /COMMAND and Date Checking\n");
7221             x = -9;
7222             goto xgetx;
7223         }
7224     }
7225 #endif /* PIPESEND */
7226
7227     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
7228     filcnt = 0;
7229     pktnum = 0;
7230     rpackets = 0L;
7231
7232     if (pv[SND_FIL].ival > 0) {
7233         if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
7234             debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno);
7235             printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval);
7236             x = -9;
7237             goto xgetx;
7238         }
7239         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */
7240             zclose(ZMFILE);                       /* Failed */
7241             debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0);
7242             printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval);
7243             x = -9;
7244             goto xgetx;
7245         }
7246         listfile = 1;
7247         debug(F110,"ftp get listfile first",tmpbuf,0);
7248         makestr(&(mgetlist[0]),tmpbuf);
7249     }
7250     t0 = gmstimer();                    /* Record starting time */
7251
7252     updating = 0;                       /* Checking dates? */
7253     if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U))
7254       updating = 1;
7255     if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M)
7256       updating = 2;
7257     if (updating)                       /* These switches force FTP DATES ON */
7258       ftp_dates |= 2;
7259
7260     what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */
7261
7262     cancelgroup = 0;                    /* Group not canceled yet */
7263     if (!(ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype))
7264       changetype(ftp_typ,0);            /* Change to requested type */
7265     binary = ftp_typ;                   /* For file-transfer display */
7266     first = 1;                          /* For MGET list */
7267     done = 0;                           /* Loop control */
7268
7269 #ifdef CK_TMPDIR
7270     if (dldir && !f_tmpdir) {           /* If they have a download directory */
7271         if ((s = zgtdir())) {           /* Get current directory */
7272             if (zchdir(dldir)) {        /* Change to download directory */
7273                 ckstrncpy(savdir,s,TMPDIRLEN);
7274                 f_tmpdir = 1;           /* Remember that we did this */
7275             }
7276         }
7277     }
7278 #endif /* CK_TMPDIR */
7279
7280     if (ftp_nml) {                      /* /NAMELIST */
7281         debug(F110,"ftp GET ftp_nml",ftp_nml,0);
7282         if (ftp_nml[0] == '-' && ftp_nml[1] == 0)
7283           fp_nml = stdout;
7284         else
7285           fp_nml = fopen(ftp_nml, "wb");
7286         if (!fp_nml) {
7287             printf("?%s: %s\n",ftp_nml,ck_errstr());
7288             goto xgetx;
7289         }
7290     }
7291     while (!done && !cancelgroup) {     /* Loop for all files */
7292                                         /* or until canceled. */
7293 #ifdef FTP_PROXY
7294         /* do something here if proxy */
7295 #endif /* FTP_PROXY */
7296
7297         rs_len = (CK_OFF_T)0;           /* REGET position */
7298         cancelfile = 0;                 /* This file not canceled yet */
7299         haspath = 0;                    /* Recalculate this each time thru */
7300
7301         if (getone) {                   /* GET */
7302             char * p;
7303             s = line;
7304             src = line;                 /* Server name */
7305             done = 1;
7306             debug(F111,"ftp get file",s,0);
7307         } else if (mget) {              /* MGET */
7308             src = mgetlist[mgetx];
7309             debug(F111,"ftp mget remote_files A",src,first);
7310             s = (char *)remote_files(first,
7311                                      (CHAR *)mgetlist[mgetx],
7312                                      (CHAR *)pv[SND_PAT].sval,
7313                                      0
7314                                      );
7315             debug(F110,"ftp mget remote_files B",s,0);
7316             if (!s) s = "";
7317             if (!*s) {
7318                 first = 1;
7319                 if (listfile) {         /* Names from listfile */
7320                   again:
7321                     tmpbuf[0] = NUL;
7322                     while (!tmpbuf[0]) {
7323                         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) {
7324                             zclose(ZMFILE);
7325                             debug(F110,"ftp get listfile EOF",
7326                                   pv[SND_FIL].sval,0);
7327                             makestr(&(mgetlist[0]),NULL);
7328                             s = NULL;
7329                             done = 1;
7330                             break;
7331                         }
7332                     }
7333                     if (done)
7334                       continue;
7335
7336                     makestr(&(mgetlist[0]),tmpbuf);
7337                     debug(F110,"ftp get listfile next",tmpbuf,0);
7338                     s = (char *)remote_files(first,
7339                                              (CHAR *)mgetlist[0],
7340                                              (CHAR *)pv[SND_PAT].sval,
7341                                              0
7342                                              );
7343                     debug(F110,"ftp mget remote_files C",s,0);
7344                     if (!s) {
7345                         ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7346                         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,"File not found");
7347                         tlog(F110,"ftp get file not found:",s,0);
7348                         goto again;
7349                     }
7350                 } else {                /* Names from command line */
7351                     mgetx++;
7352                     if (mgetx < mgetn)
7353                       s = (char *)remote_files(first,
7354                                                (CHAR *)mgetlist[mgetx],
7355                                                (CHAR *)pv[SND_PAT].sval,
7356                                                0
7357                                                );
7358                     else
7359                       s = NULL;
7360                     if (!s) mgetx++;
7361                     debug(F111,"ftp mget remote_files D",s,mgetx);
7362                 }
7363                 if (!s) {
7364                     if (!first || mgetx >= mgetn) {
7365                         done = 1;
7366                         break;
7367                     } else if (geterror) {
7368                         status = 0;
7369                         done = 1;
7370                         break;
7371                     } else {
7372                         continue;
7373                     }
7374                 }
7375             }
7376         }
7377         debug(F111,"ftp mget remote_files E",s,0);
7378         /*
7379           The semantics of NLST are ill-defined.  Suppose we have just sent
7380           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
7381           /path/bar, etc.  But some send back only foo and bar, and subsequent
7382           RETR commands based on the pathless names are not going to work.
7383         */
7384         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
7385             char * s3;
7386             if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) {
7387                 int len, left = 4096;
7388                 char * tmp = xtmpbuf;
7389                 len = s3 - mgetlist[mgetx] + 1;
7390                 ckstrncpy(tmp,mgetlist[mgetx],left);
7391                 tmp += len;
7392                 left -= len;
7393                 ckstrncpy(tmp,s,left);
7394                 s = xtmpbuf;
7395                 debug(F111,"ftp mget remote_files F",s,0);
7396             }
7397         }
7398         first = 0;
7399         skipthis = 0;                   /* File selection... */
7400         msg = "";
7401         nam = s;                        /* Filename (without path) */
7402         rc = 0;                         /* Initial return code */
7403         s2 = "";
7404
7405         if (!getone && !skipthis) {     /* For MGET and MDELETE... */
7406             char c, * p = s;
7407             int srvpath = 0;
7408             int usrpath = 0;
7409             int i, k = 0;
7410
7411             debug(F111,"ftp mget havetype",s,havetype);
7412             if (havetype > 0 && havetype != FTYP_FILE) {
7413                 /* Server says it's not file... */
7414                 debug(F110,"ftp mget not-a-file",s,0);
7415                 continue;
7416             }
7417 /*
7418   Explanation: Some ftp servers (such as wu-ftpd) return a recursive list.
7419   But if the client did not ask for a recursive list, we have to ignore any
7420   server files that include a pathname that extends beyond any path that
7421   was included in the user's request.
7422
7423   User's filespec is blah or path/blah (or other non-UNIX syntax).  We need to
7424   get the user's path segment.  Then, for each incoming file, if it begins
7425   with the same path segment, we must strip it (point past it).
7426 */
7427             src = mgetlist[mgetx];      /* In case it moved! */
7428             if (src) {
7429                 for (i = 0; src[i]; i++) { /* Find rightmost path separator */
7430                     if (ispathsep(src[i])) /* in user's pathname */
7431                       k = i + 1;
7432                 }
7433             } else {
7434                 src = "";
7435             }
7436             usrpath = k;                /* User path segment length */
7437             debug(F111,"ftp get usrpath",src,usrpath);
7438
7439             p = s;                      /* Server filename */
7440             while ((c = *p++)) {        /* Look for path in server filename */
7441                 if (ispathsep(c)) {
7442                     /* haspath++; */
7443                     nam = p;            /* Pathless name (for ckmatch) */
7444                     srvpath = p - s;    /* Server path segment length */
7445                 }
7446             }
7447             debug(F111,"ftp get srvpath",s,srvpath);
7448
7449             if (usrpath == 0) {
7450 /*
7451   Here we handle the case where the user said "mget foo" where foo is a
7452   directory name, and the server is sending back names like "foo/file1",
7453   "foo/file2", etc.  This is a nasty trick but it's necessary because the
7454   user can't compensate by typing "mget foo/" because then the server is
7455   likely to send back "foo//file1, foo//file2" etc, and we still won't
7456   get a match...
7457 */
7458                 int srclen = 0, srvlen = 0;
7459                 if (src) srclen = strlen(src);
7460                 if (s) srvlen = strlen(s);
7461                 if (src && (srvlen > srclen)) {
7462                     if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) {
7463                         char * tmpsrc = NULL;
7464                         tmpsrc = (char *)malloc(srclen + 2);
7465                         strncpy(tmpsrc,src,srclen);
7466                         tmpsrc[srclen] = s[srclen];
7467                         tmpsrc[srclen+1] = NUL;
7468                         free(mgetlist[mgetx]);
7469                         mgetlist[mgetx] = tmpsrc;
7470                         tmpsrc = NULL;
7471                         src = mgetlist[mgetx];
7472                         usrpath = srclen+1;
7473                     }                         
7474                 }
7475             }
7476 /*
7477   If as-name not given and server filename includes path that matches
7478   the pathname from the user's file specification, we must trim the common
7479   path prefix from the server's name when constructing the local name.
7480 */
7481             if (src &&                  /* Wed Sep 25 17:27:48 2002 */
7482                 !asnambuf[0] &&
7483                 !recursive &&           /* Thu Sep 19 16:11:59 2002 */
7484                 (srvpath > 0) &&
7485                 !strncmp(src,s,usrpath)) {
7486                 s2 = s + usrpath;       /* Local name skips past remote path */
7487             }
7488 #ifdef COMMENT
7489             /* This doesn't work if the path prefix contains wildcards! */
7490             haspath = (srvpath > usrpath);
7491 #else
7492             {                           /* Count path segments instead */
7493                 int x1 = 0, x2 = 0;
7494                 char *p;
7495                 for (p = s; *p; p++)
7496                   if (ispathsep(*p)) x1++;
7497                 for (p = src; *p; p++) {
7498                     if (ispathsep(*p)) x2++;
7499                 }
7500                 haspath = recursive ? x1 || x2 : x1 > x2;
7501                 debug(F111,"ftp get server path segments",s,x1);
7502                 debug(F111,"ftp get user   path segments",src,x2);
7503             }
7504
7505 #endif /* COMMENT */
7506             debug(F111,"ftp get haspath",s+usrpath,haspath);
7507
7508             if (haspath) {              /* Server file has path segments? */
7509                 if (!recursive) {       /* [M]GET /RECURSIVE? */
7510 /*
7511   We did not ask for a recursive listing, but the server is sending us one
7512   anyway (as wu-ftpd is wont to do).  We get here if the current filename
7513   includes a path segment beyond any path segment we asked for in our
7514   non-recursive [M]GET command.  We MUST skip this file.
7515 */
7516                     debug(F111,"ftp get skipping because of path",s,0);
7517                     continue;
7518                 }
7519             }
7520         } else if (getone && !skipthis) { /* GET (not MGET) */
7521             char * p = nam;
7522             while ((c = *p++)) {        /* Handle path in local name */
7523                 if (ispathsep(c)) {
7524                     if (recursive) {    /* If recursive, keep it */
7525                         haspath = 1;
7526                         break;
7527                     } else {            /* Otherwise lose it. */
7528                       nam = p;
7529                     }
7530                 }
7531             }
7532             s2 = nam;
7533         }
7534         if (!*nam)                      /* Name without path */
7535           nam = s;
7536
7537         if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */
7538             if (nam[0] == '.')
7539               continue;
7540         }
7541         if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */
7542             int xx;
7543             for (i = 0; i < NSNDEXCEPT; i++) {
7544                 if (!rcvexcept[i]) {
7545                     break;
7546                 }
7547                 xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1);
7548                 debug(F111,"ftp mget /except match",rcvexcept[i],xx);
7549                 if (xx) {
7550                     tlog(F100," refused: exception list","",0);
7551                     msg = "Refused: Exception List";
7552                     skipthis++;
7553                     break;
7554                 }
7555             }
7556         }
7557         if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */
7558             if (ckmatch(
7559 #ifdef CKREGEX
7560                         "*.~[0-9]*~"
7561 #else
7562                         "*.~*~"
7563 #endif /* CKREGEX */
7564                         ,nam,0,1) > 0)
7565               continue;
7566         }
7567         if (!x_xla) {                   /* If translation is off */
7568             x_csl = -2;                 /* unset the charsets */
7569             x_csr = -2;
7570         }
7571         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
7572         if (!*s2)                       /* Local name */
7573           s2 = asnambuf;                /* As-name */
7574
7575         if (!*s2)                       /* Sat Nov 16 19:19:39 2002 */
7576           s2 = recursive ? s : nam;     /* Fri Jan 10 13:15:19 2003 */
7577
7578         debug(F110,"ftp get filnam  ",s,0);
7579         debug(F110,"ftp get asname A",s2,0);
7580
7581         /* Receiving to real file */
7582         if (!pipesend &&
7583 #ifdef PIPESEND
7584             !rcvfilter &&
7585 #endif /* PIPESEND */
7586             !toscreen) {
7587 #ifndef NOSPL
7588             /* Do this here so we can decide whether to skip */
7589             if (cmd_quoting && !skipthis && asnambuf[0]) {
7590                 int n; char *p;
7591                 n = TMPBUFSIZ;
7592                 p = tmpbuf;
7593                 zzstring(asnambuf,&p,&n);
7594                 s2 = tmpbuf;
7595                 debug(F111,"ftp get asname B",s2,updating);
7596             }
7597 #endif /* NOSPL */
7598
7599             local = *s2 ? s2 : s;
7600
7601             if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */
7602                 CK_OFF_T x;
7603                 x = zchki(local);
7604                 debug(F111,"ftp get DISCARD zchki",local,x);
7605                 if (x > -1) {
7606                     skipthis++;
7607                     debug(F110,"ftp get skip name",local,0);
7608                     tlog(F100," refused: name","",0);
7609                     msg = "Refused: Name";
7610                 }
7611             }
7612
7613 #ifdef DOUPDATE
7614             if (!skipthis && updating) { /* If updating and not yet skipping */
7615                 if (zchki(local) > -1) {
7616                     x = chkmodtime(local,s,0);
7617 #ifdef DEBUG
7618                     if (deblog) {
7619                         if (updating == 2)
7620                           debug(F111,"ftp get /dates-diff chkmodtime",local,x);
7621                         else
7622                           debug(F111,"ftp get /update chkmodtime",local,x);
7623                     }
7624 #endif /* DEBUG */
7625                     if ((updating == 1 && x > 0) ||  /* /UPDATE */
7626                         (updating == 2 && x == 1)) { /* /DATES-DIFFER */
7627                         skipthis++;
7628                         tlog(F100," refused: date","",0);
7629                         msg = "Refused: Date";
7630                         debug(F110,"ftp get skip date",local,0);
7631                     }
7632                 }
7633             }
7634 #endif /* DOUPDATE */
7635         }
7636         /* Initialize file size to -1 in case server doesn't understand */
7637         /* SIZE command, so xxscreen() will know we don't know the size */
7638
7639         fsize = (CK_OFF_T)-1;
7640
7641         /* Ask for size now only if we need it for selection */
7642         /* because if you're going thru a list 100,000 files to select */
7643         /* a small subset, 100,000 SIZE commands can take hours... */
7644
7645         gotsize = 0;
7646         if (!mdel && !skipthis &&        /* Don't need size for DELE... */
7647             (getsmaller >= (CK_OFF_T)0  || getlarger >= (CK_OFF_T)0)) {
7648             if (havesize >= (CK_OFF_T)0) { /* Already have file size? */
7649                 fsize = havesize;
7650                 gotsize = 1;
7651             } else {                    /* No - must ask server */
7652                 /*
7653                   Prior to sending the NLST command we necessarily put the
7654                   server into ASCII mode.  We must now put it back into the
7655                   the requested mode so the upcoming SIZE command returns
7656                   right kind of size; this is especially important for
7657                   GET /RECOVER; otherwise the server returns the "ASCII" size
7658                   of the file, rather than its true size.
7659                 */
7660                 changetype(ftp_typ,0);  /* Change to requested type */
7661                 fsize = (CK_OFF_T)-1;
7662                 if (sizeok) {
7663                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7664                     if (x == REPLY_COMPLETE) {
7665                         fsize = ckatofs(&ftp_reply_str[4]);
7666                         gotsize = 1;
7667                     }
7668                 }
7669             }
7670             if (gotsize) {
7671                 if (getsmaller >= (CK_OFF_T)0 && fsize >= getsmaller)
7672                   skipthis++;
7673                 if (getlarger >= (CK_OFF_T)0 && fsize <= getlarger)
7674                   skipthis++;
7675                 if (skipthis) {
7676                     debug(F111,"ftp get skip size",s,fsize);
7677                     tlog(F100," refused: size","",0);
7678                     msg = "Refused: Size";
7679                 }
7680 #ifdef COMMENT
7681             } else if (getone) {
7682                 /* SIZE can fail for many reasons.  Does the file exist? */
7683                 x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm);
7684                 if (x != REPLY_COMPLETE) {
7685                     printf(">>> FILE NOT FOUND: %s\n",s);
7686                     break;
7687                 }
7688 #endif /* COMMENT */
7689             }
7690         }
7691         if (skipthis) {                 /* Skipping this file? */
7692             ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7693             if (msg)
7694               ftscreen(SCR_ST,ST_ERR,(CK_OFF_T)0,msg);
7695             else
7696               ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,s);
7697             continue;
7698         }
7699         if (fp_nml) {                   /* /NAMELIST only - no transfer */
7700             fprintf(fp_nml,"%s\n",s);
7701             continue;
7702         }
7703         if (recursive && haspath && !pipesend
7704 #ifdef PIPESEND
7705             && !rcvfilter
7706 #endif /* PIPESEND */
7707             ) {
7708             int x;
7709
7710 #ifdef NOMKDIR
7711             x = -1;
7712 #else
7713             x = zmkdir(s);              /* Try to make the directory */
7714 #endif /* NOMKDIR */
7715
7716             if (x < 0) {
7717                 rc = -1;                /* Failure is fatal */
7718                 if (geterror) {
7719                     status = 0;
7720                     ftscreen(SCR_EM,0,(CK_OFF_T)0,
7721                              "Directory creation failure");
7722                     break;
7723                 }
7724             }
7725         }
7726
7727         /* Not skipping */
7728
7729         selected++;                     /* Count this file as selected */
7730         pn = NULL;
7731
7732         if (!gotsize && !mdel) {        /* Didn't get size yet */
7733             if (havesize > (CK_OFF_T)-1) { /* Already have file size? */
7734                 fsize = havesize;
7735                 gotsize = 1;
7736             } else {                    /* No - must ask server */
7737                 fsize = (CK_OFF_T)-1;
7738                 if (sizeok) {
7739                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7740                     if (x == REPLY_COMPLETE) {
7741                         fsize = ckatofs(&ftp_reply_str[4]);
7742                         gotsize = 1;
7743                     }
7744                 }
7745             }
7746         }
7747         if (mdel) {                     /* [M]DELETE */
7748             if (displa && !ftp_vbm)
7749               printf(" %s...",s);
7750             rc =
7751              (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1;
7752             if (rc > -1) {
7753                 tlog(F110,"ftp mdelete",s,0);
7754                 if (displa && !ftp_vbm)
7755                   printf("OK\n");
7756             } else {
7757                 tlog(F110,"ftp mdelete failed:",s,0);
7758                 if (displa)
7759                   printf("Failed\n");
7760             }
7761 #ifndef NOSPL
7762 #ifdef PIPESEND
7763         } else if (rcvfilter) {         /* [M]GET with filter */
7764             int n; char * p;
7765             n = CKMAXPATH;
7766             p = tmpbuf;                 /* Safe - no asname with filter */
7767             zzstring(rcvfilter,&p,&n);
7768             if (n > -1)
7769               pn = tmpbuf;
7770             debug(F111,"ftp get rcvfilter",pn,n);
7771 #endif /* PIPESEND */
7772 #endif /* NOSPL */
7773             if (toscreen) s2 = "-";
7774         } else if (pipesend) {          /* [M]GET /COMMAND */
7775             int n; char * p;
7776             n = CKMAXPATH;
7777             p = tmpbuf;                 /* Safe - no asname with filter */
7778             zzstring(pipename,&p,&n);
7779             if (n > -1)
7780               pn = tmpbuf;
7781             debug(F111,"ftp get pipename",pipename,n);
7782             if (toscreen) s2 = "-";
7783         } else {                        /* [M]GET with no pipes or filters */
7784             debug(F111,"ftp get s2 A",s2,x_cnv);
7785             if (toscreen) {
7786                 s2 = "-";               /* (hokey convention for stdout) */
7787             } else if (!*s2) {          /* No asname? */
7788                 if (x_cnv) {            /* If converting */
7789                     nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */
7790                     s2 = tmpbuf;
7791                     debug(F110,"ftp get nzrtol",s2,0);
7792                 } else                  /* otherwise */
7793                   s2 = s;               /* use incoming file's name */
7794             }
7795             debug(F110,"ftp get s2 B",s2,0);
7796
7797             /* If local file already exists, take collision action */
7798
7799             if (!pipesend &&
7800 #ifdef PIPESEND
7801                 !rcvfilter &&
7802 #endif /* PIPESEND */
7803                 !toscreen) {
7804                 CK_OFF_T x;
7805                 x = zchki(s2);
7806                 debug(F111,"ftp get zchki",s2,x);
7807                 debug(F111,"ftp get x_fnc",s2,x_fnc);
7808
7809                 if (x > (CK_OFF_T)-1 && !restart) {
7810                     int x = -1;
7811                     char * newname = NULL;
7812
7813                     switch (x_fnc) {
7814                       case XYFX_A:      /* Append */
7815                         append = 1;
7816                         break;
7817                       case XYFX_R:      /* Rename */
7818                       case XYFX_B:      /* Backup */
7819                         znewn(s2,&newname); /* Make unique name */
7820                         debug(F110,"ftp get znewn",newname,0);
7821                         if (x_fnc == XYFX_B) { /* Backup existing file */
7822                             x = zrename(s2,newname);
7823                             debug(F111,"ftp get backup zrename",newname,x);
7824                         } else {      /* Rename incoming file */
7825                             x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1);
7826                             s2 = tmpbuf;
7827                             debug(F111,"ftp get rename incoming",newname,x);
7828                         }
7829                         if (x < 0) {
7830                             ftscreen(SCR_EM,0,(CK_OFF_T)0,
7831                                      "Backup/Rename failed");
7832                             x = 0;
7833                             goto xgetx;
7834                         }
7835                         break;
7836                       case XYFX_D:      /* Discard (already handled above) */
7837                       case XYFX_U:      /* Update (ditto) */
7838                       case XYFX_M:      /* Update (ditto) */
7839                       case XYFX_X:      /* Overwrite */
7840                         break;
7841                     }
7842                 }
7843             }
7844         }
7845         if (!mdel) {
7846 #ifdef PIPESEND
7847             debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0);
7848 #endif /* PIPESEND */
7849             if (pipesend && !toscreen)
7850               s2 = NULL;
7851 #ifdef DEBUG
7852             if (deblog) {
7853                 debug(F101,"ftp get x_xla","",x_xla);
7854                 debug(F101,"ftp get x_csl","",x_csl);
7855                 debug(F101,"ftp get x_csr","",x_csr);
7856                 debug(F101,"ftp get append","",append);
7857             }
7858 #endif /* DEBUG */
7859
7860             rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr);
7861
7862 #ifdef DEBUG
7863             if (deblog) {
7864                 debug(F111,"ftp get rc",s,rc);
7865                 debug(F111,"ftp get ftp_timed_out",s,ftp_timed_out);
7866                 debug(F111,"ftp get cancelfile",s,cancelfile);
7867                 debug(F111,"ftp get cancelgroup",s,cancelgroup);
7868                 debug(F111,"ftp get renaming",s,renaming);
7869                 debug(F111,"ftp get moving",s,moving);
7870             }
7871 #endif /* DEBUG */
7872         }
7873         if (rc > -1) {
7874             good++;
7875             status = 1;
7876             if (!cancelfile) {
7877                 if (deleting) {         /* GET /DELETE (source file) */
7878                     rc =
7879                       (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE)
7880                         ? 1 : -1;
7881                     tlog(F110, (rc > -1) ?
7882                          " deleted" : " failed to delete", s, 0);
7883                 }
7884                 if (renaming && rcv_rename && !toscreen) {
7885                     char *p;            /* Rename downloaded file */
7886 #ifndef NOSPL
7887                     char tmpbuf[CKMAXPATH+1];
7888                     int n;
7889                     n = CKMAXPATH;
7890                     p = tmpbuf;
7891                     debug(F111,"ftp get /rename",rcv_rename,0);
7892                     zzstring(rcv_rename,&p,&n);
7893                     debug(F111,"ftp get /rename",rcv_rename,0);
7894                     p = tmpbuf;
7895 #else
7896                     p = rcv_rename;
7897 #endif /* NOSPL */
7898                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7899                     debug(F111,"doftpget /RENAME zrename",p,rc);
7900                     tlog(F110, (rc > -1) ?
7901                          " renamed to" :
7902                          " failed to rename to",
7903                          p,
7904                          0
7905                          );
7906                 } else if (moving && rcv_move && !toscreen) {
7907                     char *p;            /* Move downloaded file */
7908 #ifndef NOSPL
7909                     char tmpbuf[CKMAXPATH+1];
7910                     int n;
7911                     n = TMPBUFSIZ;
7912                     p = tmpbuf;
7913                     debug(F111,"ftp get /move-to",rcv_move,0);
7914                     zzstring(rcv_move,&p,&n);
7915                     p = tmpbuf;
7916 #else
7917                     p = rcv_move;
7918 #endif /* NOSPL */
7919                     debug(F111,"ftp get /move-to",p,0);
7920                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7921                     debug(F111,"doftpget /MOVE zrename",p,rc);
7922                     tlog(F110, (rc > -1) ?
7923                          " moved to" : " failed to move to", p, 0);
7924                 }
7925                 if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) {
7926                     char * s = pv[SND_SRN].sval;
7927                     char * srvrn = pv[SND_SRN].sval;
7928                     char tmpbuf[CKMAXPATH+1];
7929 #ifndef NOSPL
7930                     int y;              /* Pass it thru the evaluator */
7931                     extern int cmd_quoting; /* for \v(filename) */
7932                     debug(F111,"ftp get srv_renam",s,1);
7933
7934                     if (cmd_quoting) {
7935                         y = CKMAXPATH;
7936                         s = (char *)tmpbuf;
7937                         zzstring(srvrn,&s,&y);
7938                         s = (char *)tmpbuf;
7939                     }
7940 #endif /* NOSPL */
7941                     debug(F111,"ftp get srv_renam",s,1);
7942                     if (s) if (*s) {
7943                         int x;
7944                         x = ftp_rename(s2,s);
7945                         debug(F111,"ftp get ftp_rename",s2,x);
7946                         tlog(F110, (x > 0) ?
7947                              " renamed source file to" :
7948                              " failed to rename source file to",
7949                              s,
7950                              0
7951                              );
7952                         if (x < 1)
7953                           return(-1);
7954                     }
7955                 }
7956             }
7957         }
7958         if (cancelfile)
7959           continue;
7960         if (rc < 0) {
7961             ftp_fai++;
7962 #ifdef FTP_TIMEOUT
7963             debug(F101,"ftp get ftp_timed_out","",ftp_timed_out);
7964             if (ftp_timed_out) {
7965                 status = 0;
7966                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"GET timed out");
7967             }
7968 #endif  /* FTP_TIMEOUT */
7969             if (geterror) {
7970                 status = 0;
7971                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"Fatal download error");
7972                 done++;
7973             }
7974         }
7975     }
7976 #ifdef DEBUG
7977     if (deblog) {
7978         debug(F101,"ftp get status","",status);
7979         debug(F101,"ftp get cancelgroup","",cancelgroup);
7980         debug(F101,"ftp get cancelfile","",cancelfile);
7981         debug(F101,"ftp get selected","",selected);
7982         debug(F101,"ftp get good","",good);
7983     }
7984 #endif /* DEBUG */
7985
7986     if (selected == 0) {                /* No files met selection criteria */
7987         status = 1;                     /* which is a kind of success. */
7988     } else if (status > 0) {            /* Some files were selected */
7989         if (cancelgroup)                /* but MGET was canceled */
7990           status = 0;                   /* so MGET failed */
7991         else if (cancelfile && good < 1) /* If file was canceled */
7992           status = 0;                   /* MGET failed if it got no files */
7993     }
7994     success = status;
7995     x = success;
7996     debug(F101,"ftp get success","",success);
7997
7998   xgetx:
7999     pipesend = pipesave;                /* Restore global pipe selection */
8000     if (fp_nml) {                       /* Close /NAMELIST */
8001         if (fp_nml != stdout)
8002           fclose(fp_nml);
8003         fp_nml = NULL;
8004     }
8005     if (
8006 #ifdef COMMENT
8007         x > -1
8008 #else
8009         success
8010 #endif  /* COMMENT */
8011         ) {                             /* Download successful */
8012 #ifdef GFTIMER
8013         t1 = gmstimer();                /* End time */
8014         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
8015         if (!sec) sec = 0.001;
8016         fptsecs = sec;
8017 #else
8018         sec = (t1 - t0) / 1000;
8019         if (!sec) sec = 1;
8020 #endif /* GFTIMER */
8021         tfcps = (long) (tfc / sec);
8022         tsecs = (int)sec;
8023         lastxfer = W_FTP|W_RECV;
8024         xferstat = success;
8025     }
8026     if (dpyactive)
8027       ftscreen(success > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
8028 #ifdef CK_TMPDIR
8029     if (f_tmpdir) {                     /* If we changed to download dir */
8030         zchdir((char *) savdir);        /* Go back where we came from */
8031         f_tmpdir = 0;
8032     }
8033 #endif /* CK_TMPDIR */
8034
8035     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
8036         if (pv[i].sval)
8037           free(pv[i].sval);
8038     }
8039     for (i = 0; i < mgetn; i++)         /* MGET list too */
8040       makestr(&(mgetlist[i]),NULL);
8041
8042     if (cancelgroup)                    /* Clear temp-file stack */
8043       mlsreset();
8044
8045     ftreset();                          /* Undo switch effects */
8046     dpyactive = 0;
8047     return(x);
8048 }
8049
8050 static struct keytab ftprmt[] = {
8051     { "cd",        XZCWD, 0 },
8052     { "cdup",      XZCDU, 0 },
8053     { "cwd",       XZCWD, CM_INV },
8054     { "delete",    XZDEL, 0 },
8055     { "directory", XZDIR, 0 },
8056     { "exit",      XZXIT, 0 },
8057     { "help",      XZHLP, 0 },
8058     { "login",     XZLGI, 0 },
8059     { "logout",    XZLGO, 0 },
8060     { "mkdir",     XZMKD, 0 },
8061     { "pwd",       XZPWD, 0 },
8062     { "rename",    XZREN, 0 },
8063     { "rmdir",     XZRMD, 0 },
8064     { "type",      XZTYP, 0 },
8065     { "", 0, 0 }
8066 };
8067 static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1;
8068
8069 int
8070 doftpsite() {                           /* Send a SITE command */
8071     int reply;
8072     char * s;
8073     int lcs = -1, rcs = -1;
8074     int save_vbm = ftp_vbm;
8075
8076 #ifndef NOCSETS
8077     if (ftp_xla) {
8078         lcs = ftp_csl;
8079         if (lcs < 0) lcs = fcharset;
8080         rcs = ftp_csx;
8081         if (rcs < 0) rcs = ftp_csr;
8082     }
8083 #endif /* NOCSETS */
8084     if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8085       return(x);
8086     CHECKCONN();
8087     ckstrncpy(line,s,LINBUFSIZ);
8088     if (testing) printf(" ftp site \"%s\"...\n",line);
8089     if (!ftp_vbm)
8090         ftp_vbm = !ckstrcmp("HELP",line,4,0);
8091     if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) {
8092         do {
8093             reply = getreply(0,lcs,rcs,ftp_vbm,0);
8094         } while (reply == REPLY_PRELIM);
8095     }
8096     ftp_vbm = save_vbm;
8097     return(success = (reply == REPLY_COMPLETE));
8098 }
8099
8100
8101 int
8102 dosetftppsv() {                         /* Passive mode */
8103     x = seton(&ftp_psv);
8104     if (x > 0) passivemode = ftp_psv;
8105     return(x);
8106 }
8107
8108 /*  d o f t p r m t  --  Parse and execute REMOTE commands  */
8109
8110 int
8111 doftprmt(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
8112     /* cx == 0 means REMOTE */
8113     /* cx != 0 is a XZxxx value */
8114     char * s;
8115
8116     if (who != 0)
8117       return(0);
8118
8119     if (cx == 0) {
8120         if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0)
8121           return(x);
8122         cx = x;
8123     }
8124     switch (cx) {
8125       case XZCDU:                       /* CDUP */
8126         if ((x = cmcfm()) < 0) return(x);
8127         return(doftpcdup());
8128
8129       case XZCWD:                       /* RCD */
8130         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8131           return(x);
8132         ckstrncpy(line,s,LINBUFSIZ);
8133         s = brstrip(line);
8134         return(doftpcwd(s,1));
8135       case XZPWD:                       /* RPWD */
8136         return(doftppwd());
8137       case XZDEL:                       /* RDEL */
8138         return(doftpget(FTP_MDE,1));
8139       case XZDIR:                       /* RDIR */
8140         return(doftpdir(FTP_DIR));
8141       case XZHLP:                       /* RHELP */
8142         return(doftpxhlp());
8143       case XZMKD:                       /* RMKDIR */
8144         return(doftpmkd());
8145       case XZREN:                       /* RRENAME */
8146         return(doftpren());
8147       case XZRMD:                       /* RRMDIR */
8148         return(doftprmd());
8149       case XZLGO:                       /* LOGOUT */
8150         return(doftpres());
8151       case XZXIT:                       /* EXIT */
8152         return(ftpbye());
8153     }
8154     printf("?Not usable with FTP - \"%s\"\n", atmbuf);
8155     return(-9);
8156 }
8157
8158 int
8159 doxftp() {                              /* Command parser for built-in FTP */
8160     int cx, n;
8161     struct FDB kw, fl;
8162     char * s;
8163     int usetls = 0;
8164     int lcs = -1, rcs = -1;
8165
8166 #ifndef NOCSETS
8167     if (ftp_xla) {
8168         lcs = ftp_csl;
8169         if (lcs < 0) lcs = fcharset;
8170         rcs = ftp_csx;
8171         if (rcs < 0) rcs = ftp_csr;
8172     }
8173 #endif /* NOCSETS */
8174
8175     if (inserver)                       /* FTP not allowed in IKSD. */
8176       return(-2);
8177
8178     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8179         ftp_typ = g_ftp_typ;
8180         /* g_ftp_typ = -1; */
8181     }
8182 #ifdef COMMENT
8183 /*
8184   We'll set the collision action locally in doftpget() based on whether
8185   ftp_fnc was ever set to a value.  if not, we'll use the fncact value.
8186 */
8187     if (ftp_fnc < 0)                    /* Inherit global collision action */
8188       ftp_fnc = fncact;                 /* if none specified for FTP */
8189 #endif /* COMMENT */
8190
8191     /* Restore global verbose mode */
8192     if (ftp_deb)
8193       ftp_vbm = 1;
8194     else if (quiet)
8195       ftp_vbm = 0;
8196     else
8197       ftp_vbm = ftp_vbx;
8198
8199     ftp_dates &= 1;                     /* Undo any previous /UPDATE switch */
8200
8201     dpyactive = 0;                      /* Reset global transfer-active flag */
8202     printlines = 0;                     /* Reset printlines */
8203
8204     if (fp_nml) {                       /* Reset /NAMELIST */
8205         if (fp_nml != stdout)
8206           fclose(fp_nml);
8207         fp_nml = NULL;
8208     }
8209     makestr(&ftp_nml,NULL);
8210
8211     cmfdbi(&kw,                         /* First FDB - commands */
8212            _CMKEY,                      /* fcode */
8213            "Hostname; or FTP command",  /* help */
8214            "",                          /* default */
8215            "",                          /* addtl string data */
8216            nftpcmd,                     /* addtl numeric data 1: tbl size */
8217            0,                           /* addtl numeric data 2: none */
8218            xxstring,                    /* Processing function */
8219            ftpcmdtab,                   /* Keyword table */
8220            &fl                          /* Pointer to next FDB */
8221            );
8222     cmfdbi(&fl,                         /* A host name or address */
8223            _CMFLD,                      /* fcode */
8224            "Hostname or address",       /* help */
8225            "",                          /* default */
8226            "",                          /* addtl string data */
8227            0,                           /* addtl numeric data 1 */
8228            0,                           /* addtl numeric data 2 */
8229            xxstring,
8230            NULL,
8231            NULL
8232            );
8233     x = cmfdb(&kw);                     /* Parse a hostname or a keyword */
8234     if (x == -3) {
8235         printf("?ftp what? \"help ftp\" for hints\n");
8236         return(-9);
8237     }
8238     if (x < 0)
8239       return(x);
8240     if (cmresult.fcode == _CMFLD) {     /* If hostname */
8241         return(openftp(cmresult.sresult,0)); /* go open the connection */
8242     } else {
8243         cx = cmresult.nresult;
8244     }
8245     switch (cx) {
8246       case FTP_ACC:                     /* ACCOUNT */
8247         if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
8248           return(x);
8249         CHECKCONN();
8250         makestr(&ftp_acc,s);
8251         if (testing)
8252           printf(" ftp account: \"%s\"\n",ftp_acc);
8253         success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
8254         return(success);
8255
8256       case FTP_GUP:                     /* Go UP */
8257         if ((x = cmcfm()) < 0) return(x);
8258         CHECKCONN();
8259         if (testing) printf(" ftp cd: \"(up)\"\n");
8260         return(success = doftpcdup());
8261
8262       case FTP_CWD:                     /* CD */
8263         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8264           return(x);
8265         CHECKCONN();
8266         ckstrncpy(line,s,LINBUFSIZ);
8267         if (testing)
8268           printf(" ftp cd: \"%s\"\n", line);
8269         return(success = doftpcwd(line,1));
8270
8271       case FTP_CHM:                     /* CHMOD */
8272         if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0)
8273           return(x);
8274         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8275         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8276           return(x);
8277         CHECKCONN();
8278         ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL);
8279         if (testing)
8280           printf(" ftp chmod: %s\n",ftpcmdbuf);
8281         success =
8282           (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8283         return(success);
8284
8285       case FTP_CLS:                     /* CLOSE FTP connection */
8286         if ((y = cmcfm()) < 0)
8287           return(y);
8288         CHECKCONN();
8289         if (testing)
8290           printf(" ftp closing...\n");
8291         ftpclose();
8292         return(success = 1);
8293
8294       case FTP_DIR:                     /* DIRECTORY of remote files */
8295       case FTP_VDI:
8296         return(doftpdir(cx));
8297
8298       case FTP_GET:                     /* GET a remote file */
8299       case FTP_RGE:                     /* REGET */
8300       case FTP_MGE:                     /* MGET */
8301       case FTP_MDE:                     /* MDELETE */
8302         return(doftpget(cx,1));
8303
8304       case FTP_IDL:                     /* IDLE */
8305         if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0)
8306           return(x);
8307         if ((y = cmcfm()) < 0)
8308           return(y);
8309         CHECKCONN();
8310         if (z < 0)  {                   /* Display idle timeout */
8311             if (testing)
8312               printf(" ftp query idle timeout...\n");
8313             success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE);
8314         } else {                        /* Set idle timeout */
8315             if (testing)
8316               printf(" ftp idle timeout set: %d...\n",z);
8317             success =
8318               (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE);
8319         }
8320         return(success);
8321
8322       case FTP_MKD:                     /* MKDIR */
8323         return(doftpmkd());
8324
8325       case FTP_MOD:                     /* MODTIME */
8326         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8327           return(x);
8328         CHECKCONN();
8329         ckstrncpy(line,s,LINBUFSIZ);
8330         if (testing)
8331           printf(" ftp modtime \"%s\"...\n",line);
8332         success = 0;
8333         if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) {
8334             success = 1;
8335             mdtmok = 1;
8336             if (!quiet) {
8337                 int flag = 0;
8338                 char c, * s;
8339                 struct tm tmremote;
8340
8341                 bzero((char *)&tmremote, sizeof(struct tm));
8342                 s = ftp_reply_str;
8343                 while ((c = *s++)) {
8344                     if (c == SP) {
8345                         flag++;
8346                         break;
8347                     }
8348                 }
8349                 if (flag) {
8350                     if (sscanf(s, "%04d%02d%02d%02d%02d%02d",
8351                                &tmremote.tm_year,
8352                                &tmremote.tm_mon,
8353                                &tmremote.tm_mday,
8354                                &tmremote.tm_hour,
8355                                &tmremote.tm_min,
8356                                &tmremote.tm_sec
8357                                ) == 6) {
8358                         printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n",
8359                                line,
8360                                tmremote.tm_year,
8361                                tmremote.tm_mon,
8362                                tmremote.tm_mday,
8363                                tmremote.tm_hour,
8364                                tmremote.tm_min,
8365                                tmremote.tm_sec
8366                                );
8367                     } else {
8368                         success = 0;
8369                     }
8370                 }
8371             }
8372         }
8373         return(success);
8374
8375       case FTP_OPN:                     /* OPEN connection */
8376 #ifdef COMMENT
8377         x = cmfld("IP hostname or address","",&s,xxstring);
8378         if (x < 0) {
8379             success = 0;
8380             return(x);
8381         }
8382         ckstrncpy(line,s,LINBUFSIZ);
8383         s = line;
8384         return(openftp(s,0));
8385 #else
8386         {                               /* OPEN connection */
8387             char name[TTNAMLEN+1], *p;
8388             extern int network;
8389             extern char ttname[];
8390             if (network)                /* If we have a current connection */
8391               ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */
8392             else
8393               *name = '\0';             /* as default host */
8394             for (p = name; *p; p++)     /* Remove ":service" from end. */
8395               if (*p == ':') { *p = '\0'; break; }
8396 #ifndef USETLSTAB
8397             x = cmfld("IP hostname or address",name,&s,xxstring);
8398 #else
8399             cmfdbi(&kw,                 /* First FDB - commands */
8400                    _CMKEY,              /* fcode */
8401                    "Hostname or switch", /* help */
8402                    "",                  /* default */
8403                    "",                  /* addtl string data */
8404                    ntlstab,             /* addtl numeric data 1: tbl size */
8405                    0,                   /* addtl numeric data 2: none */
8406                    xxstring,            /* Processing function */
8407                    tlstab,              /* Keyword table */
8408                    &fl                  /* Pointer to next FDB */
8409                    );
8410             cmfdbi(&fl,                 /* A host name or address */
8411                    _CMFLD,              /* fcode */
8412                    "Hostname or address", /* help */
8413                    "",                  /* default */
8414                    "",                  /* addtl string data */
8415                    0,                   /* addtl numeric data 1 */
8416                    0,                   /* addtl numeric data 2 */
8417                    xxstring,
8418                    NULL,
8419                    NULL
8420                    );
8421
8422             for (n = 0;; n++) {
8423                 x = cmfdb(&kw);         /* Parse a hostname or a keyword */
8424                 if (x == -3) {
8425                   printf("?ftp open what? \"help ftp\" for hints\n");
8426                   return(-9);
8427                 }
8428                 if (x < 0)
8429                   break;
8430                 if (cmresult.fcode == _CMFLD) { /* Hostname */
8431                     s = cmresult.sresult;
8432                     break;
8433                 } else if (cmresult.nresult == OPN_TLS) {
8434                     usetls = 1;
8435                 }
8436             }
8437 #endif /* USETLSTAB */
8438             if (x < 0) {
8439                 success = 0;
8440                 return(x);
8441             }
8442             ckstrncpy(line,s,LINBUFSIZ);
8443             s = line;
8444             return(openftp(s,usetls));
8445         }
8446 #endif /* COMMENT */
8447
8448       case FTP_PUT:                     /* PUT */
8449       case FTP_MPU:                     /* MPUT */
8450       case FTP_APP:                     /* APPEND */
8451       case FTP_REP:                     /* REPUT */
8452         return(doftpput(cx,1));
8453
8454       case FTP_PWD:                     /* PWD */
8455         x = doftppwd();
8456         if (x > -1) success = x;
8457         return(x);
8458
8459       case FTP_REN:                     /* RENAME */
8460         return(doftpren());
8461
8462       case FTP_RES:                     /* RESET */
8463         return(doftpres());
8464
8465       case FTP_HLP:                     /* (remote) HELP */
8466         return(doftpxhlp());
8467
8468       case FTP_RMD:                     /* RMDIR */
8469         return(doftprmd());
8470
8471       case FTP_STA:                     /* STATUS */
8472         if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8473           return(x);
8474         CHECKCONN();
8475         ckstrncpy(line,s,LINBUFSIZ);
8476         if (testing) printf(" ftp status \"%s\"...\n",line);
8477         success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE);
8478         return(success);
8479
8480       case FTP_SIT: {                   /* SITE */
8481           return(doftpsite());
8482       }
8483
8484       case FTP_SIZ:                     /* (ask for) SIZE */
8485         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8486           return(x);
8487         CHECKCONN();
8488         ckstrncpy(line,s,LINBUFSIZ);
8489         if (testing)
8490           printf(" ftp size \"%s\"...\n",line);
8491         success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE);
8492         if (success)
8493           sizeok = 1;
8494         return(success);
8495
8496       case FTP_SYS:                     /* Ask for server's SYSTEM type */
8497         if ((x = cmcfm()) < 0) return(x);
8498         CHECKCONN();
8499         if (testing)
8500           printf(" ftp system...\n");
8501         success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE);
8502         return(success);
8503
8504       case FTP_UMA:                     /* Set/query UMASK */
8505         if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0)
8506           if (x != -3)
8507             return(x);
8508         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8509         if ((x = cmcfm()) < 0) return(x);
8510         CHECKCONN();
8511         if (testing) {
8512             if (tmpbuf[0])
8513               printf(" ftp umask \"%s\"...\n",tmpbuf);
8514             else
8515               printf(" ftp query umask...\n");
8516         }
8517         success = ftp_umask(tmpbuf);
8518         return(success);
8519
8520       case FTP_USR:
8521         return(doftpusr());
8522
8523       case FTP_QUO:
8524         if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0)
8525           return(x);
8526         CHECKCONN();
8527         success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
8528         return(success);
8529
8530       case FTP_TYP:                     /* Type */
8531         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
8532           return(x);
8533         if ((y = cmcfm()) < 0) return(y);
8534         CHECKCONN();
8535         ftp_typ = x;
8536         g_ftp_typ = x;
8537         tenex = (ftp_typ == FTT_TEN);
8538         changetype(ftp_typ,ftp_vbm);
8539         return(1);
8540
8541       case FTP_CHK:                     /* Check if remote file(s) exist(s) */
8542         if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0)
8543           return(x);
8544         CHECKCONN();
8545         success = remote_files(1,(CHAR *)s,(CHAR *)s,0) ? 1 : 0;
8546         return(success);
8547
8548       case FTP_FEA:                     /* RFC2389 */
8549         if ((y = cmcfm()) < 0)
8550           return(y);
8551         CHECKCONN();
8552         success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE);
8553         if (success) {
8554             if (sfttab[0] > 0) {
8555                 ftp_aut = sfttab[SFT_AUTH];
8556                 sizeok  = sfttab[SFT_SIZE];
8557                 mdtmok  = sfttab[SFT_MDTM];
8558                 mlstok  = sfttab[SFT_MLST];
8559             }
8560         }
8561         return(success);
8562
8563       case FTP_OPT:                     /* RFC2389 */
8564         /* Perhaps this should be a keyword list... */
8565         if ((x = cmfld("FTP command","",&s,xxstring)) < 0)
8566           return(x);
8567         CHECKCONN();
8568         ckstrncpy(line,s,LINBUFSIZ);
8569         if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0)
8570           return(x);
8571         success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8572         return(success);
8573
8574       case FTP_ENA:                     /* FTP ENABLE */
8575       case FTP_DIS:                     /* FTP DISABLE */
8576         if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0)
8577           return(x);
8578         if ((y = cmcfm()) < 0) return(y);
8579         switch (x) {
8580           case ENA_AUTH:                /* OK to use autoauthentication */
8581             ftp_aut = (cx == FTP_ENA) ? 1 : 0;
8582             sfttab[SFT_AUTH] = ftp_aut;
8583             break;
8584           case ENA_FEAT:                /* OK to send FEAT command */
8585             featok = (cx == FTP_ENA) ? 1 : 0;
8586             break;
8587           case ENA_MLST:                /* OK to use MLST/MLSD */
8588             mlstok = (cx == FTP_ENA) ? 1 : 0;
8589             sfttab[SFT_MLST] = mlstok;
8590             break;
8591           case ENA_MDTM:                /* OK to use MDTM */
8592             mdtmok = (cx == FTP_ENA) ? 1 : 0;
8593             sfttab[SFT_MDTM] = mdtmok;
8594             break;
8595           case ENA_SIZE:                /* OK to use SIZE */
8596             sizeok = (cx == FTP_ENA) ? 1 : 0;
8597             sfttab[SFT_SIZE] = sizeok;
8598             break;
8599         }
8600         return(success = 1);
8601     }
8602     return(-2);
8603 }
8604
8605 #ifndef NOSHOW
8606 static char *
8607 shopl(x) int x; {
8608     switch (x) {
8609       case FPL_CLR: return("clear");
8610       case FPL_PRV: return("private");
8611       case FPL_SAF: return("safe");
8612       case 0:  return("(not set)");
8613       default: return("(unknown)");
8614     }
8615 }
8616
8617 int
8618 shoftp(brief) int brief; {
8619     char * s = "?";
8620     int n, x;
8621
8622     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8623         ftp_typ = g_ftp_typ;
8624         /* g_ftp_typ = -1; */
8625     }
8626     printf("\n");
8627     printf("FTP connection:                 %s\n",connected ?
8628            ftp_host :
8629            "(none)"
8630            );
8631     n = 2;
8632     if (connected) {
8633         n++;
8634         printf("FTP server type:                %s\n",
8635                ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)");
8636     }
8637     if (loggedin)
8638       printf("Logged in as:                   %s\n",
8639              strval(ftp_logname,"(unknown)"));
8640     else
8641       printf("Not logged in\n");
8642     n++;
8643     if (brief) return(0);
8644
8645     printf("\nSET FTP values:\n\n");
8646     n += 3;
8647
8648     printf(" ftp anonymous-password:        %s\n",
8649            ftp_apw ? ftp_apw : "(default)"
8650            );
8651     printf(" ftp auto-login:                %s\n",showoff(ftp_log));
8652     printf(" ftp auto-authentication:       %s\n",showoff(ftp_aut));
8653     switch (ftp_typ) {
8654       case FTT_ASC: s = "text"; break;
8655       case FTT_BIN: s = "binary"; break;
8656       case FTT_TEN: s = "tenex"; break;
8657     }
8658 #ifdef FTP_TIMEOUT
8659     printf(" ftp timeout:                   %ld\n",ftp_timeout);
8660 #endif  /* FTP_TIMEOUT */
8661     printf(" ftp type:                      %s\n",s);
8662     printf(" ftp get-filetype-switching:    %s\n",showoff(get_auto));
8663     printf(" ftp dates:                     %s\n",showoff(ftp_dates));
8664     printf(" ftp error-action:              %s\n",ftp_err ? "quit":"proceed");
8665     printf(" ftp filenames:                 %s\n",
8666            ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal")
8667            );
8668     printf(" ftp debug                      %s\n",showoff(ftp_deb));
8669
8670     printf(" ftp passive-mode:              %s\n",showoff(ftp_psv));
8671     printf(" ftp permissions:               %s\n",showooa(ftp_prm));
8672     printf(" ftp verbose-mode:              %s\n",showoff(ftp_vbx));
8673     printf(" ftp send-port-commands:        %s\n",showoff(ftp_psv));
8674     printf(" ftp unique-server-names:       %s\n",showoff(ftp_usn));
8675 #ifdef COMMENT
8676     /* See note in doxftp() */
8677     if (ftp_fnc < 0)
8678       ftp_fnc = fncact;
8679 #endif /* COMMENT */
8680     printf(" ftp collision:                 %s\n",
8681            fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]);
8682     printf(" ftp server-time-offset:        %s\n",
8683            fts_sto ? fts_sto : "(none)");
8684     n += 15;
8685
8686 #ifndef NOCSETS
8687     printf(" ftp character-set-translation: %s\n",showoff(ftp_xla));
8688     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8689
8690     printf(" ftp server-character-set:      %s\n",fcsinfo[ftp_csr].keyword);
8691     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8692
8693     printf(" file character-set:            %s\n",fcsinfo[fcharset].keyword);
8694     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8695 #endif /* NOCSETS */
8696
8697     x = ftp_dis;
8698     if (x < 0)
8699       x = fdispla;
8700     switch (x) {
8701       case XYFD_N: s = "none"; break;
8702       case XYFD_R: s = "serial"; break;
8703       case XYFD_C: s = "fullscreen"; break;
8704       case XYFD_S: s = "crt"; break;
8705       case XYFD_B: s = "brief"; break;
8706     }
8707     printf(" ftp display:                   %s\n",s);
8708     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8709
8710     if (mlstok || featok || mdtmok || sizeok || ftp_aut) {
8711         printf(" enabled:                      ");
8712         if (ftp_aut) printf(" AUTH");
8713         if (featok)  printf(" FEAT");
8714         if (mdtmok)  printf(" MDTM");
8715         if (mlstok)  printf(" MLST");
8716         if (sizeok)  printf(" SIZE");
8717         printf("\n");
8718         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8719     }
8720     if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) {
8721         printf(" disabled:                     ");
8722         if (!ftp_aut) printf(" AUTH");
8723         if (!featok)  printf(" FEAT");
8724         if (!mdtmok)  printf(" MDTM");
8725         if (!mlstok)  printf(" MLST");
8726         if (!sizeok)  printf(" SIZE");
8727         printf("\n");
8728         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8729     }
8730     switch (ftpget) {
8731       case 0: s = "kermit"; break;
8732       case 1: s = "ftp"; break;
8733       case 2: s = "auto"; break;
8734       default: s = "?";
8735     }
8736     printf(" get-put-remote:                %s\n",s);
8737     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8738
8739     printf("\n");
8740     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8741
8742 #ifdef FTP_SECURITY
8743     printf("Available security methods:    ");
8744 #ifdef FTP_GSSAPI
8745     printf("GSSAPI ");
8746 #endif /* FTP_GSSAPI */
8747 #ifdef FTP_KRB4
8748     printf("Kerberos4 ");
8749 #endif /* FTP_KRB4 */
8750 #ifdef FTP_SRP
8751     printf("SRP ");
8752 #endif /* FTP_SRP */
8753 #ifdef FTP_SSL
8754     printf("SSL ");
8755 #endif /* FTP_SSL */
8756
8757     n++;
8758     printf("\n\n");
8759     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8760     printf(" ftp authtype:                  %s\n",strval(auth_type,NULL));
8761     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8762     printf(" ftp auto-encryption:           %s\n",showoff(ftp_cry));
8763     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8764     printf(" ftp credential-forwarding:     %s\n",showoff(ftp_cfw));
8765     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8766     printf(" ftp command-protection-level:  %s\n",shopl(ftp_cpl));
8767     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8768     printf(" ftp data-protection-level:     %s\n",shopl(ftp_dpl));
8769     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8770     printf(" ftp secure proxy:              %s\n",shopl(ssl_ftp_proxy));
8771     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8772 #else
8773     printf("Available security methods:     (none)\n");
8774     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8775 #endif /* FTP_SECURITY */
8776
8777     if (n <= cmd_rows - 3)
8778       printf("\n");
8779     return(0);
8780 }
8781 #endif /* NOSHOW */
8782
8783 #ifndef NOHELP
8784 /* FTP HELP text strings */
8785
8786 static char * fhs_ftp[] = {
8787     "Syntax: FTP subcommand [ operands ]",
8788     "  Makes an FTP connection, or sends a command to the FTP server.",
8789     "  To see a list of available FTP subcommands, type \"ftp ?\".",
8790     "  and then use HELP FTP xxx to get help about subcommand xxx.",
8791     "  Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.",
8792     ""
8793 };
8794
8795 static char * fhs_acc[] = {             /* ACCOUNT */
8796     "Syntax: FTP ACCOUNT text",
8797     "  Sends an account designator to an FTP server that needs one.",
8798     "  Most FTP servers do not use accounts; some use them for other",
8799     "  other purposes, such as disk-access passwords.",
8800     ""
8801 };
8802 static char * fhs_app[] = {             /* APPEND */
8803     "Syntax: FTP APPEND filname",
8804     "  Equivalent to [ FTP ] PUT /APPEND.  See HELP FTP PUT.",
8805     ""
8806 };
8807 static char * fhs_cls[] = {             /* BYE, CLOSE */
8808     "Syntax: [ FTP ] BYE",
8809     "  Logs out from the FTP server and closes the FTP connection.",
8810     "  Also see HELP SET GET-PUT-REMOTE.  Synonym: [ FTP ] CLOSE.",
8811     ""
8812 };
8813 static char * fhs_cwd[] = {             /* CD, CWD */
8814     "Syntax: [ FTP ] CD directory",
8815     "  Asks the FTP server to change to the given directory.",
8816     "  Also see HELP SET GET-PUT-REMOTE.  Synonyms: [ FTP ] CWD, RCD, RCWD.",
8817     ""
8818 };
8819 static char * fhs_gup[] = {             /* CDUP, UP */
8820     "Syntax: FTP CDUP",
8821     "  Asks the FTP server to change to the parent directory of its current",
8822     "  directory.  Also see HELP SET GET-PUT-REMOTE.  Synonym: FTP UP.",
8823     ""
8824 };
8825 static char * fhs_chm[] = {             /* CHMOD */
8826     "Syntax: FTP CHMOD filename permissions",
8827     "  Asks the FTP server to change the permissions, protection, or mode of",
8828     "  the given file.  The given permissions must be in the syntax of the",
8829     "  the server's file system, e.g. an octal number for UNIX.  Also see",
8830     "  FTP PUT /PERMISSIONS",
8831     ""
8832 };
8833 static char * fhs_mde[] = {             /* DELETE */
8834     "Syntax: FTP DELETE [ switches ] filespec",
8835     "  Asks the FTP server to delete the given file or files.",
8836     "  Synonym: MDELETE (Kermit makes no distinction between single and",
8837     "  multiple file deletion).  Optional switches:",
8838     " ",
8839     "  /ERROR-ACTION:{PROCEED,QUIT}",
8840     "  /EXCEPT:pattern",
8841     "  /FILENAMES:{AUTO,CONVERTED,LITERAL}",
8842     "  /LARGER-THAN:number",
8843 #ifdef UNIXOROSK
8844     "  /NODOTFILES",
8845 #endif /* UNIXOROSK */
8846     "  /QUIET",
8847 #ifdef RECURSIVE
8848     "  /RECURSIVE (depends on server)",
8849     "  /SUBDIRECTORIES",
8850 #endif /* RECURSIVE */
8851     "  /SMALLER-THAN:number",
8852     ""
8853 };
8854 static char * fhs_dir[] = {             /* DIRECTORY */
8855     "Syntax: FTP DIRECTORY [ filespec ]",
8856     "  Asks the server to send a directory listing of the files that match",
8857     "  the given filespec, or if none is given, all the files in its current",
8858     "  directory.  The filespec, including any wildcards, must be in the",
8859     "  syntax of the server's file system.  Also see HELP SET GET-PUT-REMOTE.",
8860     "  Synonym: RDIRECTORY.",
8861     ""
8862 };
8863 static char * fhs_vdi[] = {             /* VDIRECTORY */
8864     "Syntax: FTP VDIRECTORY [ filespec ]",
8865     "  Asks the server to send a directory listing of the files that match",
8866     "  the given filespec, or if none is given, all the files in its current",
8867     "  directory.  VDIRECTORY is needed for getting verbose directory",
8868     "  listings from certain FTP servers, such as on TOPS-20.  Try it if",
8869     "  FTP DIRECTORY lists only filenames without details.",
8870     ""
8871 };
8872 static char * fhs_fea[] = {             /* FEATURES */
8873     "Syntax: FTP FEATURES",
8874     "  Asks the FTP server to list its special features.  Most FTP servers",
8875     "  do not recognize this command.",
8876     ""
8877 };
8878 static char * fhs_mge[] = {             /* MGET */
8879     "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]",
8880     "  Download a single file or multiple files.  Asks the FTP server to send",
8881     "  the given file or files.  Also see FTP GET.  Optional switches:",
8882     " ",
8883     "  /AS-NAME:text",
8884     "    Name under which to store incoming file.",
8885     "    Pattern required for for multiple files.",
8886     "  /BINARY",                        /* /IMAGE */
8887     "    Force binary mode.  Synonym: /IMAGE.",
8888     "  /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}",
8889    "    What to do if an incoming file has the same name as an existing file.",
8890
8891 #ifdef PUTPIPE
8892     "  /COMMAND",
8893     "    Specifies that the as-name is a command to which the incoming file",
8894     "    is to be piped as standard input.",
8895 #endif /* PUTPIPE */
8896
8897 #ifdef DOUPDATE
8898     "  /DATES-DIFFER",
8899     "    Download only those files whose modification date-times differ from",
8900     "    those of the corresponding local files, or that do not already",
8901     "    exist on the local computer.",
8902 #endif /* DOUPDATE */
8903
8904     "  /DELETE",
8905     "    Specifies that each file is to be deleted from the server after,",
8906     "    and only if, it is successfully downloaded.",
8907     "  /ERROR-ACTION:{PROCEED,QUIT}",
8908     "    When downloading a group of files, what to do upon failure to",
8909     "    transfer a file: quit or proceed to the next one.",
8910     "  /EXCEPT:pattern",
8911     "    Exception list: don't download any files that match this pattern.",
8912     "    See HELP WILDCARD for pattern syntax.",
8913     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
8914     "    Whether to convert incoming filenames to local syntax.",
8915 #ifdef PIPESEND
8916 #ifndef NOSPL
8917     "  /FILTER:command",
8918     "    Pass incoming files through the given command.",
8919 #endif /* NOSPL */
8920 #endif /* PIPESEND */
8921     "  /LARGER-THAN:number",
8922     "    Only download files that are larger than the given number of bytes.",
8923     "  /LISTFILE:filename",
8924     "    Obtain the list of files to download from the given file.",
8925 #ifndef NOCSETS
8926     "  /LOCAL-CHARACTER-SET:name",
8927     "    When downloading in text mode and character-set conversion is",
8928     "    desired, this specifies the target set.",
8929 #endif /* NOCSETS */
8930     "  /MATCH:pattern",
8931     "    Specifies a pattern to be used to select filenames locally from the",
8932     "    server's list.",
8933     "  /MLSD",
8934     "    Forces sending of MLSD (rather than NLST) to get the file list.",
8935 #ifdef CK_TMPDIR
8936     "  /MOVE-TO:directory",
8937     "    Each file that is downloaded is to be moved to the given local",
8938     "    directory immediately after, and only if, it has been received",
8939     "    successfully.",
8940 #endif /* CK_TMPDIR */
8941     "  /NAMELIST:filename",
8942     "    Instead of downloading the files, stores the list of files that",
8943     "    would be downloaded in the given local file, one filename per line.",
8944     "  /NLST",
8945     "    Forces sending of NLST (rather than MLSD) to get the file list.",
8946     "  /NOBACKUPFILES",
8947     "    Don't download any files whose names end with .~<number>~.",
8948     "  /NODOTFILES",
8949     "    Don't download any files whose names begin with period (.).",
8950     "  /QUIET",
8951     "    Suppress the file-transfer display.",
8952 #ifdef FTP_RESTART
8953     "  /RECOVER",                       /* /RESTART */
8954     "    Resume a download that was previously interrupted from the point of",
8955     "    failure.  Works only in binary mode.  Not supported by all servers.",
8956     "    Synonym: /RESTART.",
8957 #endif /* FTP_RESTART */
8958 #ifdef RECURSIVE
8959     "  /RECURSIVE",                     /* /SUBDIRECTORIES */
8960     "    Create subdirectories automatically if the server sends files",
8961     "    recursively and includes pathnames (most don't).",
8962 #endif /* RECURSIVE */
8963     "  /RENAME-TO:text",
8964     "    Each file that is downloaded is to be renamed as indicated just,",
8965     "    after, and only if, it has arrived successfully.",
8966 #ifndef NOCSETS
8967     "  /SERVER-CHARACTER-SET:name",
8968     "    When downloading in text mode and character-set conversion is desired"
8969 ,   "    this specifies the original file's character set on the server.",
8970 #endif /* NOCSETS */
8971     "  /SERVER-RENAME:text",
8972     "    Each server source file is to be renamed on the server as indicated",
8973     "    immediately after, but only if, it has arrived successfully.",
8974     "  /SMALLER-THAN:number",
8975     "    Download only those files smaller than the given number of bytes.",
8976     "  /TEXT",                          /* /ASCII */
8977     "    Force text mode.  Synonym: /ASCII.",
8978     "  /TENEX",
8979     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
8980 #ifndef NOCSETS
8981     "  /TRANSPARENT",
8982     "    When downloading in text mode, do not convert chracter-sets.",
8983 #endif /* NOCSETS */
8984     "  /TO-SCREEN",
8985     "    The downloaded file is to be displayed on the screen.",
8986 #ifdef DOUPDATE
8987     "  /UPDATE",
8988     "    Equivalent to /COLLISION:UPDATE.  Download only those files that are",
8989     "    newer than than their local counterparts, or that do not exist on",
8990     "    the local computer.",
8991 #endif /* DOUPDATE */
8992     ""
8993 };
8994 static char * fhs_hlp[] = {             /* HELP */
8995     "Syntax: FTP HELP [ command [ subcommand... ] ]",
8996     "  Asks the FTP server for help about the given command.  First use",
8997     "  FTP HELP by itself to get a list of commands, then use HELP FTP xxx",
8998     "  to get help for command \"xxx\".  Synonyms: REMOTE HELP, RHELP.",
8999     ""
9000 };
9001 static char * fhs_idl[] = {             /* IDLE */
9002     "Syntax: FTP IDLE [ number ]",
9003     "  If given without a number, this asks the FTP server to tell its",
9004     "  current idle-time limit.  If given with a number, it asks the server",
9005     "  to change its idle-time limit to the given number of seconds.",
9006     ""
9007 };
9008 static char * fhs_usr[] = {             /* USER, LOGIN */
9009     "Syntax: FTP USER username [ password [ account ] ]",
9010     "  Log in to the FTP server.  To be used when connected but not yet",
9011     "  logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.",
9012     "  If you omit the password, and one is required by the server, you are",
9013     "  prompted for it.  If you omit the account, no account is sent.",
9014     "  Synonym: FTP LOGIN.",
9015     ""
9016 };
9017 static char * fhs_get[] = {             /* GET */
9018     "Syntax: [ FTP ] GET [ options ] filename [ as-name ]",
9019     "  Download a single file.  Asks the FTP server to send the given file.",
9020     "  The optional as-name is the name to store it under when it arrives;",
9021     "  if omitted, the file is stored with the name it arrived with, as",
9022     "  modified according to the FTP FILENAMES setting or /FILENAMES: switch",
9023     "  value.  Aside from the file list and as-name, syntax and options are",
9024     "  the same as for FTP MGET, which is used for downloading multiple files."
9025 ,   ""
9026 };
9027 static char * fhs_mkd[] = {             /* MKDIR */
9028     "Syntax: FTP MKDIR directory",
9029     "  Asks the FTP server to create a directory with the given name,",
9030     "  which must be in the syntax of the server's file system.  Synonyms:",
9031     "  REMOTE MKDIR, RMKDIR.",
9032     ""
9033 };
9034 static char * fhs_mod[] = {             /* MODTIME */
9035     "Syntax: FTP MODTIME filename",
9036     "  Asks the FTP server to send the modification time of the given file,",
9037     "  to be displayed on the screen.  The date-time format is all numeric:",
9038     "  yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating",
9039     "  fractions of seconds).",
9040     ""
9041 };
9042 static char * fhs_mpu[] = {             /* MPUT */
9043     "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]",
9044     "  Uploads files.  Sends the given file or files to the FTP server.",
9045     "  Also see FTP PUT.  Optional switches are:",
9046     " ",
9047     "  /AFTER:date-time",
9048     "    Uploads only those files newer than the given date-time.",
9049     "    HELP DATE for info about date-time formats.  Synonym: /SINCE.",
9050 #ifdef PUTARRAY
9051     "  /ARRAY:array-designator",
9052     "    Tells Kermit to upload the contents of the given array, rather than",
9053     "    a file.",
9054 #endif /* PUTARRAY */
9055     "  /AS-NAME:text",
9056     "    Name under which to send files.",
9057     "    Pattern required for for multiple files.",
9058     "  /BEFORE:date-time",
9059     "    Upload only those files older than the given date-time.",
9060     "  /BINARY",
9061     "    Force binary mode.  Synonym: /IMAGE.",
9062 #ifdef PUTPIPE
9063     "  /COMMAND",
9064     "    Specifies that the filespec is a command whose standard output is",
9065     "    to be sent.",
9066 #endif /* PUTPIPE */
9067
9068 #ifdef COMMENT
9069 #ifdef DOUPDATE
9070     "  /DATES-DIFFER",
9071     "    Upload only those files whose modification date-times differ from",
9072     "    those on the server, or that don't exist on the server at all.",
9073 #endif /* DOUPDATE */
9074 #endif /* COMMENT */
9075
9076     "  /DELETE",
9077     "    Specifies that each source file is to be deleted after, and only if,",
9078     "    it is successfully uploaded.",
9079     "  /DOTFILES",
9080     "    Include files whose names begin with period (.).",
9081     "  /ERROR-ACTION:{PROCEED,QUIT}",
9082     "    When uploading a group of files, what to do upon failure to",
9083     "    transfer a file: quit or proceed to the next one.",
9084     "  /EXCEPT:pattern",
9085     "    Exception list: don't upload any files that match this pattern.",
9086     "    See HELP WILDCARD for pattern syntax.",
9087     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
9088     "    Whether to convert outbound filenames to common syntax.",
9089 #ifdef PIPESEND
9090 #ifndef NOSPL
9091     "  /FILTER:command",
9092     "    Pass outbound files through the given command.",
9093 #endif /* NOSPL */
9094 #endif /* PIPESEND */
9095 #ifdef CKSYMLINK
9096     "  /FOLLOWINKS",
9097     "    Send files that are pointed to by symbolic links.",
9098     "  /NOFOLLOWINKS",
9099     "    Skip over symbolic links (default).",
9100 #endif /* CKSYMLINK */
9101     "  /LARGER-THAN:number",
9102     "    Only upload files that are larger than the given number of bytes.",
9103     "  /LISTFILE:filename",
9104     "    Obtain the list of files to upload from the given file.",
9105 #ifndef NOCSETS
9106     "  /LOCAL-CHARACTER-SET:name",
9107     "    When uploading in text mode and character-set conversion is",
9108     "    desired, this specifies the source-file character set.",
9109 #endif /* NOCSETS */
9110 #ifdef CK_TMPDIR
9111     "  /MOVE-TO:directory",
9112     "    Each source file that is uploaded is to be moved to the given local",
9113     "    directory when, and only if, the transfer is successful.",
9114 #endif /* CK_TMPDIR */
9115     "  /NOBACKUPFILES",
9116     "    Don't upload any files whose names end with .~<number>~.",
9117 #ifdef UNIXOROSK
9118     "  /NODOTFILES",
9119     "    Don't upload any files whose names begin with period (.).",
9120 #endif /* UNIXOROSK */
9121     "  /NOT-AFTER:date-time",
9122     "    Upload only files that are not newer than the given date-time",
9123     "  /NOT-BEFORE:date-time",
9124     "    Upload only files that are not older than the given date-time",
9125 #ifdef UNIX
9126     "  /PERMISSIONS",
9127     "    Ask the server to set the permissions of each file it receives",
9128     "    according to the source file's permissions.",
9129 #endif /* UNIX */
9130     "  /QUIET",
9131     "    Suppress the file-transfer display.",
9132 #ifdef FTP_RESTART
9133     "  /RECOVER",
9134     "    Resume an upload that was previously interrupted from the point of",
9135     "    failure.  Synonym: /RESTART.",
9136 #endif /* FTP_RESTART */
9137 #ifdef RECURSIVE
9138     "  /RECURSIVE",
9139     "    Send files from the given directory and all the directories beneath",
9140     "    it.  Synonym: /SUBDIRECTORIES.",
9141 #endif /* RECURSIVE */
9142     "  /RENAME-TO:text",
9143     "    Each source file that is uploaded is to be renamed on the local",
9144     "    local computer as indicated when and only if, the transfer completes",
9145     "    successfully.",
9146 #ifndef NOCSETS
9147     "  /SERVER-CHARACTER-SET:name",
9148     "    When uploading in text mode and character-set conversion is desired,",
9149     "    this specifies the character set to which the file should be",
9150     "    converted for storage on the server.",
9151 #endif /* NOCSETS */
9152     "  /SERVER-RENAME:text",
9153     "    Each file that is uploaded is to be renamed as indicated on the",
9154     "    server after, and only if, if arrives successfully.",
9155     "  /SIMULATE",
9156     "    Show which files would be sent without actually sending them.",
9157     "  /SMALLER-THAN:number",
9158     "    Upload only those files smaller than the given number of bytes.",
9159     "  /TEXT",
9160     "    Force text mode.  Synonym: /ASCII.",
9161     "  /TENEX",
9162     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
9163 #ifndef NOCSETS
9164     "  /TRANSPARENT",
9165     "    When uploading in text mode, do not convert chracter-sets.",
9166 #endif /* NOCSETS */
9167     "  /TYPE:{TEXT,BINARY}",
9168     "    Upload only files of the given type.",
9169 #ifdef DOUPDATE
9170     "  /UPDATE",
9171     "    If a file of the same name exists on the server, upload only if",
9172     "    the local file is newer.",
9173 #endif /* DOUPDATE */
9174     "  /UNIQUE-SERVER-NAMES",
9175     "    Ask the server to compute new names for any incoming file that has",
9176     "    the same name as an existing file.",
9177     ""
9178 };
9179 static char * fhs_opn[] = {             /* OPEN */
9180 #ifdef CK_SSL
9181     "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]",
9182     "  Opens a connection to the FTP server on the given host.  The default",
9183     "  TCP port is 21 (990 if SSL/TLS is used), but a different port number",
9184     "  can be supplied if necessary.  Optional switches are:",
9185 #else /* CK_SSL */
9186     "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]",
9187     "  Opens a connection to the FTP server on the given host.  The default",
9188     "  TCP port is 21, but a different port number can be supplied if",
9189     "  necessary.  Optional switches are:",
9190 #endif /* CK_SSL */
9191     " ",
9192     "  /ANONYMOUS",
9193     "    Logs you in anonymously.",
9194     "  /USER:text",
9195     "    Supplies the given text as your username.",
9196     "  /PASSWORD:text",
9197     "    Supplies the given text as your password.  If you include a username",
9198     "    but omit this switch and the server requires a password, you are",
9199     "    prompted for it.",
9200     "  /ACCOUNT:text",
9201     "    Supplies the given text as your account, if required by the server.",
9202     "  /ACTIVE",
9203     "    Forces an active (rather than passive) connection.",
9204     "  /PASSIVE",
9205     "    Forces a passive (rather than active) connection.",
9206     "  /NOINIT",
9207     "    Inhibits sending initial REST, STRU, and MODE commands, which are",
9208     "    well-known standard commands, but to which some servers react badly.",
9209     "  /NOLOGIN",
9210     "    Inhibits autologin for this connection only.",
9211     ""
9212 };
9213 static char * fhs_opt[] = {             /* OPTS, OPTIONS */
9214     "Syntax: FTP OPTIONS",
9215     "  Asks the FTP server to list its current options.  Advanced, new,",
9216     "  not supported by most FTP servers.",
9217     ""
9218 };
9219 static char * fhs_put[] = {             /* PUT, SEND */
9220     "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]",
9221     "  Like FTP MPUT, but only one filespec is allowed, and if it is followed",
9222     "  by an additional field, this is interpreted as the name under which",
9223     "  to send the file or files.  See HELP FTP MPUT.",
9224     ""
9225 };
9226 static char * fhs_reput[] = {           /* REPUT, RESEND */
9227     "Syntax: [ FTP ] REPUT [ switches ] filespec [ as-name ]",
9228     "  Synonym for FTP PUT /RECOVER.  Recovers an interrupted binary-mode",
9229     "  upload from the point of failure if the FTP server supports recovery.",
9230     "  Synonym: [ FTP ] RESEND.  For details see HELP FTP MPUT.",
9231     ""
9232 };
9233 static char * fhs_pwd[] = {             /* PWD */
9234     "Syntax: FTP PWD",
9235     "  Asks the FTP server to reveal its current working directory.",
9236     "  Synonyms: REMOTE PWD, RPWD.",
9237     ""
9238 };
9239 static char * fhs_quo[] = {             /* QUOTE */
9240     "Syntax: FTP QUOTE text",
9241     "  Sends an FTP protocol command to the FTP server.  Use this command",
9242     "  for sending commands that Kermit might not support.",
9243     ""
9244 };
9245 static char * fhs_rge[] = {             /* REGET */
9246     "Syntax: FTP REGET",
9247     "  Synonym for FTP GET /RECOVER.",
9248     ""
9249 };
9250 static char * fhs_ren[] = {             /* RENAME */
9251     "Syntax: FTP RENAME name1 name1",
9252     "  Asks the FTP server to change the name of the file whose name is name1",
9253     "  and which resides in the FTP server's file system, to name2.  Works",
9254     "  only for single files; wildcards are not accepted.",
9255     ""
9256 };
9257 static char * fhs_res[] = {             /* RESET */
9258     "Syntax: FTP RESET",
9259     "  Asks the server to log out your session, terminating your access",
9260     "  rights, without closing the connection.",
9261     ""
9262 };
9263 static char * fhs_rmd[] = {             /* RMDIR */
9264     "Syntax: FTP RMDIR directory",
9265     "  Asks the FTP server to remove the directory whose name is given.",
9266     "  This usually requires the directory to be empty.  Synonyms: REMOTE",
9267     "  RMDIR, RRMDIR.",
9268     ""
9269 };
9270 static char * fhs_sit[] = {             /* SITE */
9271     "Syntax: FTP SITE text",
9272     "  Sends a site-specific command to the FTP server.",
9273     ""
9274 };
9275 static char * fhs_siz[] = {             /* SIZE */
9276     "Syntax: FTP SIZE filename",
9277     "  Asks the FTP server to send a numeric string representing the size",
9278     "  of the given file.",
9279     ""
9280 };
9281 static char * fhs_sta[] = {             /* STATUS */
9282     "Syntax: FTP STATUS [ filename ]",
9283     "  Asks the FTP server to report its status.  If a filename is given,",
9284     "  the FTP server should report details about the file.",
9285     ""
9286 };
9287 static char * fhs_sys[] = {             /* SYSTEM */
9288     "Syntax: FTP SYSTEM",
9289     "  Asks the FTP server to report its operating system type.",
9290     ""
9291 };
9292 static char * fhs_typ[] = {             /* TYPE */
9293     "Syntax: FTP TYPE { TEXT, BINARY, TENEX }",
9294     "  Puts the client and server in the indicated transfer mode.",
9295     "  ASCII is a synonym for TEXT.  TENEX is used only for uploading 8-bit",
9296     "  binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or",
9297     "  downloading files from TENEX or TOPS-20 that have been uploaded in",
9298     "  TENEX mode.",
9299     ""
9300 };
9301 static char * fhs_uma[] = {             /* UMASK */
9302     "Syntax: FTP UMASK number",
9303     "  Asks the FTP server to set its file creation mode mask.  Applies",
9304     "  only (or mainly) to UNIX-based FTP servers.",
9305     ""
9306 };
9307 static char * fhs_chk[] = {             /* CHECK */
9308     "Syntax: FTP CHECK remote-filespec",
9309     "  Asks the FTP server if the given file or files exist.  If the",
9310     "  remote-filespec contains wildcards, this command fails if no server",
9311     "  files match, and succeeds if at least one file matches.  If the",
9312     "  remote-filespec does not contain wildcards, this command succeeds if",
9313     "  the given file exists and fails if it does not.",
9314     ""
9315 };
9316 static char * fhs_ena[] = {             /* ENABLE */
9317     "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9318     "  Enables the use of the given FTP protocol command in case it has been",
9319     "  disabled (but this is no guarantee that the FTP server understands it)."
9320 ,
9321     "  Use SHOW FTP to see which of these commands is enabled and disabled.",
9322     "  Also see FTP DISABLE.",
9323     ""
9324 };
9325 static char * fhs_dis[] = {             /* DISABLE */
9326     "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9327     "  Disables the use of the given FTP protocol command.",
9328     "  Also see FTP ENABLE.",
9329     ""
9330 };
9331
9332 #endif /* NOHELP */
9333
9334 int
9335 doftphlp() {
9336     int cx;
9337     if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0)
9338       if (cx != -3)
9339         return(cx);
9340     if ((x = cmcfm()) < 0)
9341       return(x);
9342
9343 #ifdef NOHELP
9344     printf("Sorry, no help available\n");
9345 #else
9346     switch (cx) {
9347       case -3:
9348         return(hmsga(fhs_ftp));
9349       case FTP_ACC:                     /* ACCOUNT */
9350         return(hmsga(fhs_acc));
9351       case FTP_APP:                     /* APPEND */
9352         return(hmsga(fhs_app));
9353       case FTP_CLS:                     /* BYE, CLOSE */
9354         return(hmsga(fhs_cls));
9355       case FTP_CWD:                     /* CD, CWD */
9356         return(hmsga(fhs_cwd));
9357       case FTP_GUP:                     /* CDUP, UP */
9358         return(hmsga(fhs_gup));
9359       case FTP_CHM:                     /* CHMOD */
9360         return(hmsga(fhs_chm));
9361       case FTP_MDE:                     /* DELETE, MDELETE */
9362         return(hmsga(fhs_mde));
9363       case FTP_DIR:                     /* DIRECTORY */
9364         return(hmsga(fhs_dir));
9365       case FTP_VDI:                     /* VDIRECTORY */
9366         return(hmsga(fhs_vdi));
9367       case FTP_FEA:                     /* FEATURES */
9368         return(hmsga(fhs_fea));
9369       case FTP_GET:                     /* GET */
9370         return(hmsga(fhs_get));
9371       case FTP_HLP:                     /* HELP */
9372         return(hmsga(fhs_hlp));
9373       case FTP_IDL:                     /* IDLE */
9374         return(hmsga(fhs_idl));
9375       case FTP_USR:                     /* USER, LOGIN */
9376         return(hmsga(fhs_usr));
9377       case FTP_MGE:                     /* MGET */
9378         return(hmsga(fhs_mge));
9379       case FTP_MKD:                     /* MKDIR */
9380         return(hmsga(fhs_mkd));
9381       case FTP_MOD:                     /* MODTIME */
9382         return(hmsga(fhs_mod));
9383       case FTP_MPU:                     /* MPUT */
9384         return(hmsga(fhs_mpu));
9385       case FTP_OPN:                     /* OPEN */
9386         return(hmsga(fhs_opn));
9387       case FTP_OPT:                     /* OPTS, OPTIONS */
9388         return(hmsga(fhs_opt));
9389       case FTP_PUT:                     /* PUT, SEND */
9390         return(hmsga(fhs_put));
9391       case FTP_REP:                     /* REPUT, RESEND */
9392         return(hmsga(fhs_reput));
9393       case FTP_PWD:                     /* PWD */
9394         return(hmsga(fhs_pwd));
9395       case FTP_QUO:                     /* QUOTE */
9396         return(hmsga(fhs_quo));
9397       case FTP_RGE:                     /* REGET */
9398         return(hmsga(fhs_rge));
9399       case FTP_REN:                     /* RENAME */
9400         return(hmsga(fhs_ren));
9401       case FTP_RES:                     /* RESET */
9402         return(hmsga(fhs_res));
9403       case FTP_RMD:                     /* RMDIR */
9404         return(hmsga(fhs_rmd));
9405       case FTP_SIT:                     /* SITE */
9406         return(hmsga(fhs_sit));
9407       case FTP_SIZ:                     /* SIZE */
9408         return(hmsga(fhs_siz));
9409       case FTP_STA:                     /* STATUS */
9410         return(hmsga(fhs_sta));
9411       case FTP_SYS:                     /* SYSTEM */
9412         return(hmsga(fhs_sys));
9413       case FTP_TYP:                     /* TYPE */
9414         return(hmsga(fhs_typ));
9415       case FTP_UMA:                     /* UMASK */
9416         return(hmsga(fhs_uma));
9417       case FTP_CHK:                     /* CHECK */
9418         return(hmsga(fhs_chk));
9419       case FTP_ENA:
9420         return(hmsga(fhs_ena));
9421       case FTP_DIS:
9422         return(hmsga(fhs_dis));
9423       default:
9424         printf("Sorry, help available for this command.\n");
9425         break;
9426     }
9427 #endif /* NOHELP */
9428     return(success = 0);
9429 }
9430
9431 int
9432 dosetftphlp() {
9433     int cx;
9434     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0)
9435       if (cx != -3)
9436         return(cx);
9437     if (cx != -3)
9438       ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ);
9439     if ((x = cmcfm()) < 0)
9440       return(x);
9441
9442 #ifdef NOHELP
9443     printf("Sorry, no help available\n");
9444 #else
9445     switch (cx) {
9446       case -3:
9447         printf("\nSyntax: SET FTP parameter value\n");
9448         printf("  Type \"help set ftp ?\" for a list of parameters.\n");
9449         printf("  Type \"help set ftp xxx\" for information about setting\n");
9450         printf("  parameter xxx.  Type \"show ftp\" for current values.\n\n");
9451         return(0);
9452
9453       case FTS_BUG:
9454         printf("\nSyntax: SET FTP BUG <name> {ON, OFF}\n");
9455         printf(
9456             "  Activates a workaround for the named bug in the FTP server.\n");
9457         printf("  Type SET FTP BUG ? for a list of names.\n");
9458         printf("  For each bug, the default is OFF\n\n");
9459         return(0);
9460
9461 #ifdef FTP_SECURITY
9462       case FTS_ATP:                     /* "authtype" */
9463         printf("\nSyntax: SET FTP AUTHTYPE list\n");
9464         printf("  Specifies an ordered list of authentication methods to be\n"
9465                );
9466         printf("  when FTP AUTOAUTHENTICATION is ON.  The default list is:\n");
9467         printf("  GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n");
9468         return(0);
9469
9470       case FTS_AUT:                     /* "autoauthentication" */
9471         printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n");
9472         printf("  Tells whether authentication should be negotiated by the\n");
9473         printf("  FTP OPEN command.  Default is ON.\n\n");
9474         break;
9475
9476       case FTS_CRY:                     /* "autoencryption" */
9477         printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n");
9478         printf("  Tells whether encryption (privacy) should be negotiated\n");
9479         printf("  by the FTP OPEN command.  Default is ON.\n\n");
9480         break;
9481 #endif /* FTP_SECURITY */
9482
9483       case FTS_LOG:                     /* "autologin" */
9484         printf("\nSET FTP AUTOLOGIN { ON, OFF }\n");
9485         printf("  Tells Kermit whether to try to log you in automatically\n");
9486         printf("  as part of the connection process.\n\n");
9487         break;
9488
9489       case FTS_DIS:
9490         printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n");
9491         printf("  Chooses the file-transfer display style for FTP.\n");
9492         printf("  Like SET TRANSFER DISPLAY but applies only to FTP.\n\n");
9493         break;
9494
9495 #ifndef NOCSETS
9496       case FTS_XLA:                     /* "character-set-translation" */
9497         printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n");
9498         printf("  Whether to translate character sets when transferring\n");
9499         printf("  text files with FTP.  OFF by default.\n\n");
9500         break;
9501
9502 #endif /* NOCSETS */
9503       case FTS_FNC:                     /* "collision" */
9504         printf("\n");
9505         printf(
9506 "Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n"
9507                );
9508         printf("  Tells what do when an incoming file has the same name as\n");
9509         printf("  an existing file when downloading with FTP.\n\n");
9510         break;
9511
9512 #ifdef FTP_SECURITY
9513       case FTS_CPL:                     /* "command-protection-level" */
9514         printf("\n");
9515         printf(
9516 "Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9517                );
9518         printf("\n");
9519         printf(
9520 "  Tells what level of protection is applied to the FTP command channel.\n\n");
9521         break;
9522       case FTS_CFW:                     /* "credential-forwarding" */
9523         printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n");
9524         printf("  Tells whether end-user credentials are to be forwarded\n");
9525         printf("  to the server if supported by the authentication method\n");
9526         printf("  (GSSAPI-KRB5 only).\n\n");
9527         break;
9528       case FTS_DPL:                     /* "data-protection-level" */
9529         printf("\n");
9530         printf(
9531 "Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9532                );
9533         printf("\n");
9534         printf(
9535 "  Tells what level of protection is applied to the FTP data channel.\n\n");
9536         break;
9537 #endif /* FTP_SECURITY */
9538
9539       case FTS_DBG:                     /* "debug" */
9540         printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n");
9541         printf("  Whether to print FTP protocol messages.\n\n");
9542         return(0);
9543
9544       case FTS_ERR:                     /* "error-action" */
9545         printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n");
9546         printf("  What to do when an error occurs when transferring a group\n")
9547           ;
9548         printf("  of files: quit and fail, or proceed to the next file.\n\n");
9549         return(0);
9550
9551       case FTS_CNV:                     /* "filenames" */
9552         printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n");
9553         printf("  What to do with filenames: convert them, take and use them\n"
9554                );
9555         printf("  literally; or choose what to do automatically based on the\n"
9556                );
9557         printf("  OS type of the server.  The default is AUTO.\n\n");
9558         return(0);
9559
9560       case FTS_PSV:                     /* "passive-mode" */
9561         printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n");
9562         printf("  Whether to use passive mode, which helps to get through\n");
9563         printf("  firewalls.  ON by default.\n\n");
9564         return(0);
9565
9566       case FTS_PRM:                     /* "permissions" */
9567         printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n");
9568         printf("  Whether to try to send file permissions when uploading.\n");
9569         printf("  OFF by default.  AUTO means only if client and server\n");
9570         printf("  have the same OS type.\n\n");
9571         return(0);
9572
9573       case FTS_TST:                     /* "progress-messages" */
9574         printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n");
9575         printf("  Whether Kermit should print locally-generated feedback\n");
9576         printf("  messages for each non-file-transfer command.");
9577         printf("  ON by default.\n\n");
9578         return(0);
9579
9580       case FTS_SPC:                     /* "send-port-commands" */
9581         printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n");
9582         printf("  Whether Kermit should send a new PORT command for each");
9583         printf("  task.\n\n");
9584         return(0);
9585
9586 #ifndef NOCSETS
9587       case FTS_CSR:                     /* "server-character-set" */
9588         printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n");
9589         printf("  The name of the character set used for text files on the\n");
9590         printf("  server.  Enter a name of '?' for a menu.\n\n");
9591         return(0);
9592 #endif /* NOCSETS */
9593
9594       case FTS_STO:                     /* "server-time-offset */
9595         printf(
9596 "\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n");
9597         printf(
9598 "  Specifies an offset to apply to the server's file timestamps.\n");
9599         printf(
9600 "  Use this to correct for misconfigured server time or timezone.\n");
9601         printf(
9602 "  Format: must begin with + or - sign.  Hours must be given; minutes\n");
9603         printf(
9604 "  and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n");
9605         return(0);
9606
9607       case FTS_TYP:                     /* "type" */
9608         printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n");
9609         printf("  Establishes the default transfer mode.\n");
9610         printf("  TENEX is used for uploading 8-bit binary files to 36-bit\n");
9611         printf("  platforms such as TENEX and TOPS-20 and for downloading\n");
9612         printf("  them again.  ASCII is a synonym for TEXT.  Normally each\n");
9613         printf("  file's type is determined automatically from its contents\n"
9614                );
9615         printf("  or its name; SET FTP TYPE does not prevent that, it only\n");
9616         printf("  tells which mode to use when the type can't be determined\n"
9617                );
9618         printf("  automatically.  To completely disable automatic transfer-\n"
9619                );
9620         printf("  mode switching and force either text or binary mode, give\n"
9621                );
9622         printf("  the top-level command ASCII or BINARY, as in traditional\n");
9623         printf("  FTP clients.\n\n");
9624         return(0);
9625
9626 #ifdef FTP_TIMEOUT
9627       case FTS_TMO:
9628        printf("\nSyntax: SET FTP TIMEOUT number-of-seconds\n");
9629        printf("  Establishes a timeout for FTP transfers.\n");
9630        printf("  The timeout applies per network read or write on the data\n");
9631        printf("  connection, not to the whole transfer.  By default the\n");
9632        printf("  timeout value is 0, meaning no timeout.  Use a positive\n");
9633        printf("  number to escape gracefully from hung data connections or\n");
9634        printf("  directory listings.\n\n");
9635         return(0);
9636 #endif  /* FTP_TIMEOUT */
9637
9638 #ifdef PATTERNS
9639       case FTS_GFT:
9640         printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n");
9641         printf("  Tells whether GET and MGET should automatically switch\n");
9642         printf("  the appropriate file type, TEXT, BINARY, or TENEX, by\n");
9643         printf("  matching the name of each incoming file with its list of\n");
9644         printf("  FILE TEXT-PATTERNS and FILE BINARY-PATTERNS.  ON by\n");
9645         printf("  default.  SHOW PATTERNS displays the current pattern\n");
9646         printf("  list.  HELP SET FILE to see how to change it.\n");
9647         break;
9648 #endif /* PATTERNS */
9649
9650       case FTS_USN:                     /* "unique-server-names" */
9651         printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n");
9652         printf("  Tells whether to ask the server to create unique names\n");
9653         printf("  for any uploaded file that has the same name as an\n");
9654         printf("  existing file.  Default is OFF.\n\n");
9655         return(0);
9656
9657       case FTS_VBM:                     /* "verbose-mode" */
9658         printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n");
9659         printf("  Whether to display all responses from the FTP server.\n");
9660         printf("  OFF by default.\n\n");
9661         return(0);
9662
9663       case FTS_DAT:
9664         printf("\nSyntax: SET FTP DATES { ON, OFF }\n");
9665         printf("  Whether to set date of incoming files from the file date\n");
9666         printf("  on the server.  ON by default.  Note: there is no way to\n")
9667           ;
9668         printf("  set the date on files uploaded to the server.  Also note\n");
9669         printf("  that not all servers support this feature.\n\n");
9670         return(0);
9671
9672       case FTS_APW:
9673         printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n");
9674         printf("  Password to supply automatically on anonymous FTP\n");
9675         printf("  connections instead of the default user@host.\n");
9676         printf("  Omit optional text to restore default.\n\n");
9677         return(0);
9678
9679       default:
9680         printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf);
9681     }
9682 #endif /* NOHELP */
9683     return(0);
9684 }
9685
9686 #ifndef L_SET
9687 #define L_SET 0
9688 #endif /* L_SET */
9689 #ifndef L_INCR
9690 #define L_INCR 1
9691 #endif /* L_INCR */
9692
9693 #ifdef FTP_SRP
9694 char srp_user[BUFSIZ];                  /* where is BUFSIZ defined? */
9695 char *srp_pass;
9696 char *srp_acct;
9697 #endif /* FTP_SRP */
9698
9699 static int kerror;                      /* Needed for all auth types */
9700
9701 static struct   sockaddr_in hisctladdr;
9702 static struct   sockaddr_in hisdataaddr;
9703 static struct   sockaddr_in data_addr;
9704 static int      data = -1;
9705 static int      ptflag = 0;
9706 static struct   sockaddr_in myctladdr;
9707
9708 #ifdef COMMENT
9709 #ifndef OS2
9710 UID_T getuid();
9711 #endif /* OS2 */
9712 #endif /* COMMENT */
9713
9714
9715 static int cpend = 0;                   /* No pending replies */
9716
9717 #ifdef CK_SSL
9718 extern SSL *ssl_ftp_con;
9719 extern SSL_CTX *ssl_ftp_ctx;
9720 extern SSL *ssl_ftp_data_con;
9721 extern int ssl_ftp_active_flag;
9722 extern int ssl_ftp_data_active_flag;
9723 #endif /* CK_SSL */
9724
9725 /*  f t p c m d  --  Send a command to the FTP server  */
9726 /*
9727   Call with:
9728     char * cmd: The command to send.
9729     char * arg: The argument (e.g. a filename).
9730     int lcs: The local character set index.
9731     int rcs: The remote (server) character set index.
9732     int vbm: Verbose mode:
9733       0 = force verbosity off
9734      >0 = force verbosity on
9735
9736   If arg is given (not NULL or empty) and lcs != rcs and both are > -1,
9737   and neither lcs or rcs is UCS-2, the arg is translated from the local
9738   character set to the remote one before sending the result to the server.
9739
9740    Returns:
9741     0 on failure with ftpcode = -1
9742     >= 0 on success (getreply() result) with ftpcode = 0.
9743 */
9744 static char xcmdbuf[RFNBUFSIZ];
9745
9746 static int
9747 ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; {
9748     char * s = NULL;
9749     int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1;
9750     sig_t oldintr;
9751
9752     if (ftp_deb)                        /* DEBUG */
9753       vbm = 1;
9754     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
9755       vbm = 0;
9756     else if (vbm < 0)                   /* VERBOSE */
9757       vbm = ftp_vbm;
9758
9759     cancelfile = 0;
9760     if (!cmd) cmd = "";
9761     if (!arg) arg = "";
9762     cmdlen = (int)strlen(cmd);
9763     len = cmdlen + (int)strlen(arg) + 1;
9764
9765     if (ftp_deb /* && !dpyactive */ ) {
9766 #ifdef FTP_PROXY
9767         if (ftp_prx) printf("%s ", ftp_host);
9768 #endif /* FTP_PROXY */
9769         printf("---> ");
9770         if (!anonymous && strcmp("PASS",cmd) == 0)
9771           printf("PASS XXXX");
9772         else
9773           printf("%s %s",cmd,arg);
9774         printf("\n");
9775     }
9776     /* bzero(xcmdbuf,RFNBUFSIZ); */
9777     ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL);
9778
9779 #ifdef DEBUG
9780     if (deblog) {
9781         debug(F110,"ftpcmd cmd",cmd,0);
9782         debug(F110,"ftpcmd arg",arg,0);
9783         debug(F101,"ftpcmd lcs","",lcs);
9784         debug(F101,"ftpcmd rcs","",rcs);
9785     }
9786 #endif /* DEBUG */
9787
9788     if (csocket == -1) {
9789         perror("No control connection for command");
9790         ftpcode = -1;
9791         return(0);
9792     }
9793     havesigint = 0;
9794     oldintr = signal(SIGINT, cmdcancel);
9795
9796 #ifndef NOCSETS
9797     if (*arg &&                         /* If an arg was given */
9798         lcs > -1 &&                     /* and a local charset */
9799         rcs > -1 &&                     /* and a remote charset */
9800         lcs != rcs &&                   /* and the two are not the same */
9801         lcs != FC_UCS2 &&               /* and neither one is UCS-2 */
9802         rcs != FC_UCS2                  /* ... */
9803         ) {
9804         initxlate(lcs,rcs);             /* Translate arg from lcs to rcs */
9805         xgnbp = arg;                    /* Global pointer to input string */
9806         rfnptr = rfnbuf;                /* Global pointer to output buffer */
9807
9808         while (1) {
9809             if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break;
9810             if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break;
9811         }
9812         /*
9813           We have to copy here instead of translating directly into
9814           xcmdbuf[] so strputc() can check length.  Alternatively we could
9815           write yet another xpnbyte() output function.
9816         */
9817         if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) {
9818             printf("?FTP command too long: %s + arg\n",cmd);
9819             ftpcode = -1;
9820             return(0);
9821         }
9822         x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1));
9823     }
9824 #endif /* NOCSETS */
9825
9826     s = xcmdbuf;                        /* Command to send to server */
9827
9828 #ifdef DEBUG
9829     if (deblog) {                       /* Log it */
9830         if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) {
9831             /* But don't log passwords */
9832             debug(F110,"FTP SENT ","PASS XXXX",0);
9833         } else {
9834             debug(F110,"FTP SENT ",s,0);
9835         }
9836     }
9837 #endif /* DEBUG */
9838
9839 #ifdef CK_ENCRYPTION
9840   again:
9841 #endif /* CK_ENCRYPTION */
9842     if (scommand(s) == 0) {              /* Send it. */
9843       signal(SIGINT, oldintr);
9844       return(0);
9845     }
9846     cpend = 1;
9847     x = !strcmp(cmd,"QUIT");            /* Is it the QUIT command? */
9848     if (x)                              /* In case we're interrupted */
9849       connected = 0;                    /* while waiting for the reply... */
9850
9851     fc = 0;                             /* Function code for getreply() */
9852     if (!strncmp(cmd,"AUTH ",5)         /* Must parse AUTH reply */
9853 #ifdef FTPHOST
9854         && strncmp(cmd, "HOST ",5)
9855 #endif /* FTPHOST */
9856         ) {
9857         fc = GRF_AUTH;
9858     } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */
9859         fc = GRF_FEAT;                  /* But FEAT not widely understood */
9860         if (!ftp_deb)                   /* So suppress error messages */
9861           vbm = 9;
9862     }
9863     r = getreply(x,                     /* Expect connection to close */
9864                  lcs,rcs,               /* Charsets */
9865                  vbm,                   /* Verbosity */
9866                  fc                     /* Function code */
9867                  );
9868     if (q > -1)
9869       quiet = q;
9870
9871 #ifdef CK_ENCRYPTION
9872     if (ftpcode == 533 && ftp_cpl == FPL_PRV) {
9873         fprintf(stderr,
9874                "ENC command not supported at server; retrying under MIC...\n");
9875         ftp_cpl = FPL_SAF;
9876         goto again;
9877     }
9878 #endif /* CK_ENCRYPTION */
9879 #ifdef COMMENT
9880     if (cancelfile && oldintr != SIG_IGN)
9881       (*oldintr)(SIGINT);
9882 #endif /* COMMENT */
9883     signal(SIGINT, oldintr);
9884     return(r);
9885 }
9886
9887 static VOID
9888 lostpeer() {
9889     debug(F100,"lostpeer","",0);
9890     if (connected) {
9891         if (csocket != -1) {
9892 #ifdef CK_SSL
9893             if (ssl_ftp_active_flag) {
9894                 SSL_shutdown(ssl_ftp_con);
9895                 SSL_free(ssl_ftp_con);
9896                 ssl_ftp_proxy = 0;
9897                 ssl_ftp_active_flag = 0;
9898                 ssl_ftp_con = NULL;
9899             }
9900 #endif /* CK_SSL */
9901 #ifdef TCPIPLIB
9902             socket_close(csocket);
9903 #else /* TCPIPLIB */
9904 #ifdef USE_SHUTDOWN
9905             shutdown(csocket, 1+1);
9906 #endif /* USE_SHUTDOWN */
9907             close(csocket);
9908 #endif /* TCPIPLIB */
9909             csocket = -1;
9910         }
9911         if (data != -1) {
9912 #ifdef CK_SSL
9913             if (ssl_ftp_data_active_flag) {
9914                 SSL_shutdown(ssl_ftp_data_con);
9915                 SSL_free(ssl_ftp_data_con);
9916                 ssl_ftp_data_active_flag = 0;
9917                 ssl_ftp_data_con = NULL;
9918             }
9919 #endif /* CK_SSL */
9920 #ifdef TCPIPLIB
9921             socket_close(data);
9922 #else /* TCPIPLIB */
9923 #ifdef USE_SHUTDOWN
9924             shutdown(data, 1+1);
9925 #endif /* USE_SHUTDOWN */
9926             close(data);
9927 #endif /* TCPIPLIB */
9928             data = -1;
9929             globaldin = -1;
9930         }
9931         connected = 0;
9932         anonymous = 0;
9933         loggedin = 0;
9934         auth_type = NULL;
9935         ftp_cpl = ftp_dpl = FPL_CLR;
9936 #ifdef CKLOGDIAL
9937         ftplogend();
9938 #endif /* CKLOGDIAL */
9939
9940 #ifdef LOCUS
9941         if (autolocus)                  /* Auotomatic locus switching... */
9942           setlocus(1,1);                /* Switch locus to local. */
9943 #endif /* LOCUS */
9944 #ifdef OS2
9945         DialerSend(OPT_KERMIT_HANGUP, 0);
9946 #endif /* OS2 */
9947     }
9948 #ifdef FTP_PROXY
9949     pswitch(1);
9950     if (connected) {
9951         if (csocket != -1) {
9952 #ifdef TCPIPLIB
9953             socket_close(csocket);
9954 #else /* TCPIPLIB */
9955 #ifdef USE_SHUTDOWN
9956             shutdown(csocket, 1+1);
9957 #endif /* USE_SHUTDOWN */
9958             close(csocket);
9959 #endif /* TCPIPLIB */
9960             csocket = -1;
9961         }
9962         connected = 0;
9963         anonymous = 0;
9964         loggedin = 0;
9965         auth_type = NULL;
9966         ftp_cpl = ftp_dpl = FPL_CLR;
9967     }
9968     proxflag = 0;
9969     pswitch(0);
9970 #endif /* FTP_PROXY */
9971 }
9972
9973 int
9974 ftpisopen() {
9975     return(connected);
9976 }
9977
9978 static int
9979 ftpclose() {
9980     extern int quitting;
9981     if (!connected)
9982       return(0);
9983     ftp_xfermode = xfermode;
9984     if (!ftp_vbm && !quiet)
9985       printlines = 1;
9986     ftpcmd("QUIT",NULL,0,0,ftp_vbm);
9987     if (csocket) {
9988 #ifdef CK_SSL
9989         if (ssl_ftp_active_flag) {
9990             SSL_shutdown(ssl_ftp_con);
9991             SSL_free(ssl_ftp_con);
9992             ssl_ftp_proxy = 0;
9993             ssl_ftp_active_flag = 0;
9994             ssl_ftp_con = NULL;
9995         }
9996 #endif /* CK_SSL */
9997 #ifdef TCPIPLIB
9998         socket_close(csocket);
9999 #else /* TCPIPLIB */
10000 #ifdef USE_SHUTDOWN
10001         shutdown(csocket, 1+1);
10002 #endif /* USE_SHUTDOWN */
10003         close(csocket);
10004 #endif /* TCPIPLIB */
10005     }
10006     csocket = -1;
10007     connected = 0;
10008     anonymous = 0;
10009     loggedin = 0;
10010     mdtmok = 1;
10011     sizeok = 1;
10012     featok = 1;
10013     stouarg = 1;
10014     typesent = 0;
10015     data = -1;
10016     globaldin = -1;
10017 #ifdef FTP_PROXY
10018     if (!proxy)
10019       macnum = 0;
10020 #endif /* FTP_PROXY */
10021     auth_type = NULL;
10022     ftp_dpl = FPL_CLR;
10023 #ifdef CKLOGDIAL
10024     ftplogend();
10025 #endif /* CKLOGDIAL */
10026 #ifdef LOCUS
10027     /* Unprefixed file management commands are executed locally */
10028     if (autolocus && !ftp_cmdlin && !quitting) {
10029         setlocus(1,1);
10030     }
10031 #endif /* LOCUS */
10032 #ifdef OS2
10033     DialerSend(OPT_KERMIT_HANGUP, 0);
10034 #endif /* OS2 */
10035     return(0);
10036 }
10037
10038 int
10039 ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; {
10040     char * host;
10041
10042     if (connected) {
10043         printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host);
10044         ftpcode = -1;
10045         return(0);
10046     }
10047 #ifdef FTPHOST
10048     hostcmd = 0;
10049 #endif /* FTPHOST */
10050     alike = 0;
10051     ftp_srvtyp[0] = NUL;
10052     if (!service) service = "";
10053     if (!*service) service = use_tls ? "ftps" : "ftp";
10054
10055     if (!isdigit(service[0])) {
10056         struct servent *destsp;
10057         destsp = getservbyname(service, "tcp");
10058         if (!destsp) {
10059             if (!ckstrcmp(service,"ftp",-1,0)) {
10060                 ftp_port = 21;
10061             } else if (!ckstrcmp(service,"ftps",-1,0)) {
10062                 ftp_port = 990;
10063             } else {
10064                 printf("?Bad port name - \"%s\"\n", service);
10065                 ftpcode = -1;
10066                 return(0);
10067             }
10068         } else {
10069             ftp_port = destsp->s_port;
10070             ftp_port = ntohs((unsigned short)ftp_port); /* SMS 2007/02/15 */
10071         }
10072     } else
10073         ftp_port = atoi(service);
10074     if (ftp_port <= 0) {
10075         printf("?Bad port name - \"%s\"\n", service);
10076         ftpcode = -1;
10077         return(0);
10078     }
10079     host = ftp_hookup(remote, ftp_port, use_tls);
10080     if (host) {
10081         ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN);
10082         connected = 1;                  /* Set FTP defaults */
10083         ftp_cpl = ftp_dpl = FPL_CLR;
10084         curtype = FTT_ASC;              /* Server uses ASCII mode */
10085         form = FORM_N;
10086         mode = MODE_S;
10087         stru = STRU_F;
10088         strcpy(bytename, "8");
10089         bytesize = 8;
10090
10091 #ifdef FTP_SECURITY
10092         if (ftp_aut) {
10093             if (ftp_auth()) {
10094                 if (ftp_cry 
10095 #ifdef OS2
10096                      && ck_crypt_is_installed()
10097 #endif /* OS2 */
10098                      ) {
10099                     if (!quiet)
10100                       printf("FTP Command channel is Private (encrypted)\n");
10101                     ftp_cpl = FPL_PRV;
10102                     if (setpbsz(DEFAULT_PBSZ) < 0) {
10103                         /* a failure here is most likely caused by a mixup */
10104                         /* in the session key used by client and server    */
10105                         printf("?Protection buffer size negotiation failed\n");
10106                         return(0);
10107                     }
10108                     if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) {
10109                         if (!quiet)
10110                           printf("FTP Data channel is Private (encrypted)\n");
10111                         ftp_dpl = FPL_PRV;
10112                     } else
10113                       printf("?Unable to enable encryption on data channel\n");
10114                 } else {
10115                     ftp_cpl = FPL_SAF;
10116                 }
10117             }
10118             if (!connected)
10119               goto fail;
10120         }
10121 #endif /* FTP_SECURITY */
10122         if (ftp_log)                    /* ^^^ */
10123           ftp_login(remote);
10124
10125         if (!connected)
10126           goto fail;
10127
10128         ftp_xfermode = xfermode;
10129
10130 #ifdef CKLOGDIAL
10131         dologftp();
10132 #endif /* CKLOGDIAL */
10133 #ifdef OS2
10134         DialerSend(OPT_KERMIT_CONNECT, 0);
10135 #endif /* OS2 */
10136         passivemode = ftp_psv;
10137         sendport = ftp_spc;
10138         mdtmok = 1;
10139         sizeok = 1;
10140         stouarg = 1;
10141         typesent = 0;
10142
10143         if (ucbuf == NULL) {
10144             actualbuf = DEFAULT_PBSZ;
10145             while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL)
10146               actualbuf >>= 2;
10147         }
10148         if (!maxbuf)
10149           ucbufsiz = actualbuf - FUDGE_FACTOR;
10150         debug(F101,"ftpopen ucbufsiz","",ucbufsiz);
10151         return(1);
10152     }
10153   fail:
10154     printf("?Can't FTP connect to %s:%s\n",remote,service);
10155     ftpcode = -1;
10156     return(0);
10157 }
10158
10159 #ifdef CK_SSL
10160 int
10161 ssl_auth() {
10162     int i;
10163     char* p;
10164
10165     if (ssl_debug_flag) {
10166         fprintf(stderr,"SSL DEBUG ACTIVE\n");
10167         fflush(stderr);
10168         /* for the moment I want the output on screen */
10169     }
10170     if (ssl_ftp_data_con != NULL) {
10171         SSL_free(ssl_ftp_data_con);
10172         ssl_ftp_data_con = NULL;
10173     }
10174     if (ssl_ftp_con != NULL) {
10175         SSL_free(ssl_ftp_con);
10176         ssl_ftp_con=NULL;
10177     }
10178     if (ssl_ftp_ctx != NULL) {
10179         SSL_CTX_free(ssl_ftp_ctx);
10180         ssl_ftp_ctx = NULL;
10181     }
10182
10183     /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 
10184      * was added to OpenSSL 0.9.6e and 0.9.7.  It does not exist in previous
10185      * versions
10186      */
10187 #ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
10188 #define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L
10189 #endif
10190     if (auth_type && !strcmp(auth_type,"TLS")) {
10191         ssl_ftp_ctx=SSL_CTX_new(SSLv3_client_method());
10192         if (!ssl_ftp_ctx)
10193           return(0);
10194         SSL_CTX_set_options(ssl_ftp_ctx,
10195                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10196                             );
10197     } else {
10198         ssl_ftp_ctx = SSL_CTX_new(ftp_bug_use_ssl_v2 ? SSLv23_client_method() : 
10199                                   SSLv3_client_method());
10200         if (!ssl_ftp_ctx)
10201           return(0);
10202         SSL_CTX_set_options(ssl_ftp_ctx,
10203                             (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
10204                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10205                             );
10206     }
10207     SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
10208                                   (pem_password_cb *)ssl_passwd_callback);
10209     SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
10210     SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
10211
10212 #ifdef OS2
10213 #ifdef NT
10214     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10215     {
10216         char path[CKMAXPATH];
10217         extern char exedir[];
10218
10219         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10220         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10221             debug(F110,"ftp ssl_auth unable to load path",path,0);
10222             if (ssl_debug_flag)
10223                 printf("?Unable to load verify-dir: %s\r\n",path);
10224         }
10225
10226         ckmakmsg(path,CKMAXPATH,
10227                  (char *)GetAppData(1),"kermit 95/certs",NULL,NULL);
10228         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10229             debug(F110,"ftp ssl_auth unable to load path",path,0);
10230             if (ssl_debug_flag)
10231                 printf("?Unable to load verify-dir: %s\r\n",path);
10232         }
10233
10234         ckmakmsg(path,CKMAXPATH,
10235                  (char *)GetAppData(0),"kermit 95/certs",NULL,NULL);
10236         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10237             debug(F110,"ftp ssl_auth unable to load path",path,0);
10238             if (ssl_debug_flag)
10239                 printf("?Unable to load verify-dir: %s\r\n",path);
10240         }
10241
10242         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10243         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10244             debug(F110,"ftp ssl_auth unable to load path",path,0);
10245             if (ssl_debug_flag)
10246                 printf("?Unable to load verify-file: %s\r\n",path);
10247         }
10248
10249         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10250                  "kermit 95/ca_certs.pem",NULL,NULL);
10251         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10252             debug(F110,"ftp ssl_auth unable to load path",path,0);
10253             if (ssl_debug_flag)
10254                 printf("?Unable to load verify-file: %s\r\n",path);
10255         }
10256
10257         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10258                  "kermit 95/ca_certs.pem",NULL,NULL);
10259         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10260             debug(F110,"ftp ssl_auth unable to load path",path,0);
10261             if (ssl_debug_flag)
10262                 printf("?Unable to load verify-file: %s\r\n",path);
10263         }
10264     }
10265 #else /* NT */
10266     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10267     {
10268
10269         char path[CKMAXPATH];
10270         extern char exedir[];
10271
10272         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10273         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10274             debug(F110,"ftp ssl_auth unable to load path",path,0);
10275             if (ssl_debug_flag)
10276                 printf("?Unable to load verify-dir: %s\r\n",path);
10277         }
10278         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10279         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10280             debug(F110,"ftp ssl_auth unable to load path",path,0);
10281             if (ssl_debug_flag)
10282                 printf("?Unable to load verify-file: %s\r\n",path);
10283         }
10284     }
10285 #endif /* NT */
10286 #else /* OS2 */
10287     SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
10288 #endif /* OS2 */
10289
10290     if (ssl_verify_file &&
10291         SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
10292         debug(F110,
10293               "ftp ssl auth unable to load ssl_verify_file",
10294               ssl_verify_file,
10295               0
10296               );
10297         if (ssl_debug_flag)
10298           printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
10299     }
10300     if (ssl_verify_dir &&
10301         SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
10302         debug(F110,
10303               "ftp ssl auth unable to load ssl_verify_dir",
10304               ssl_verify_dir,
10305               0
10306               );
10307         if (ssl_debug_flag)
10308           printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
10309     }
10310
10311     /* set up the new CRL Store */
10312     crl_store = (X509_STORE *)X509_STORE_new();
10313     if (crl_store) {
10314 #ifdef OS2
10315         char path[CKMAXPATH];
10316         extern char exedir[];
10317
10318         ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
10319         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10320             debug(F110,"ftp ssl auth unable to load dir",path,0);
10321             if (ssl_debug_flag)
10322                 printf("?Unable to load crl-dir: %s\r\n",path);
10323         }
10324 #ifdef NT
10325         ckmakmsg(path,CKMAXPATH,
10326                  (char *)GetAppData(1),"kermit 95/crls",NULL,NULL);
10327         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10328             debug(F110,"ftp ssl auth unable to load dir",path,0);
10329             if (ssl_debug_flag)
10330                 printf("?Unable to load crl-dir: %s\r\n",path);
10331         }
10332         ckmakmsg(path,CKMAXPATH,
10333                  (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
10334         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10335             debug(F110,"ftp ssl auth unable to load dir",path,0);
10336             if (ssl_debug_flag)
10337                 printf("?Unable to load crl-dir: %s\r\n",path);
10338         }
10339 #endif /* NT */
10340         
10341         ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
10342         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10343             debug(F110,"ftp ssl auth unable to load file",path,0);
10344             if (ssl_debug_flag)
10345                 printf("?Unable to load crl-file: %s\r\n",path);
10346         }
10347 #ifdef NT
10348         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10349                  "kermit 95/ca_crls.pem",NULL,NULL);
10350         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10351             debug(F110,"ftp ssl auth unable to load file",path,0);
10352             if (ssl_debug_flag)
10353                 printf("?Unable to load crl-file: %s\r\n",path);
10354         }
10355         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10356                  "kermit 95/ca_crls.pem",NULL,NULL);
10357         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10358             debug(F110,"ftp ssl auth unable to load file",path,0);
10359             if (ssl_debug_flag)
10360                 printf("?Unable to load crl-file: %s\r\n",path);
10361         }
10362 #endif /* NT */
10363 #endif /* OS2 */
10364
10365         if (ssl_crl_file || ssl_crl_dir) {
10366             if (ssl_crl_file &&
10367                 X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
10368                 debug(F110,
10369                       "ftp ssl auth unable to load ssl_crl_file",
10370                       ssl_crl_file,
10371                       0
10372                       );
10373                 if (ssl_debug_flag)
10374                   printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
10375             }
10376             if (ssl_crl_dir &&
10377                 X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
10378                 debug(F110,
10379                       "ftp ssl auth unable to load ssl_crl_dir",
10380                       ssl_crl_dir,
10381                       0
10382                       );
10383                 if (ssl_debug_flag)
10384                   printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
10385             }
10386         } else {
10387             X509_STORE_set_default_paths(crl_store);
10388         }
10389     }
10390     SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
10391                        ssl_client_verify_callback);
10392     ssl_verify_depth = -1;
10393     ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
10394     tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
10395     SSL_set_fd(ssl_ftp_con,csocket);
10396     SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
10397     if (ssl_cipher_list) {
10398         SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
10399     } else {
10400         char * p;
10401         if (p = getenv("SSL_CIPHER")) {
10402             SSL_set_cipher_list(ssl_ftp_con,p);
10403         } else {
10404             SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
10405         }
10406     }
10407     if (ssl_debug_flag) {
10408         fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
10409         fflush(stderr);
10410     }
10411     if (SSL_connect(ssl_ftp_con) <= 0) {
10412         static char errbuf[1024];
10413         ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
10414                  ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
10415         fprintf(stderr,"%s\n", errbuf);
10416         fflush(stderr);
10417         ssl_ftp_active_flag=0;
10418         SSL_free(ssl_ftp_con);
10419         ssl_ftp_con = NULL;
10420     } else {
10421         ssl_ftp_active_flag = 1;
10422
10423         if (!ssl_certsok_flag && !tls_is_krb5(1)) {
10424             char *subject = ssl_get_subject_name(ssl_ftp_con);
10425
10426             if (!subject) {
10427                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
10428                     debug(F110,"ssl_auth","[SSL - FAILED]",0);
10429                     return(ssl_ftp_active_flag = 0);
10430                 } else {
10431                     if (uq_ok("Warning: Server didn't provide a certificate\n",
10432                                "Continue? (Y/N)",3,NULL,0) <= 0) {
10433                         debug(F110, "ssl_auth","[SSL - FAILED]",0);
10434                         return(ssl_ftp_active_flag = 0);
10435                     }
10436                 }
10437             } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
10438                 debug(F110,"ssl_auth","[SSL - FAILED]",0);
10439                 return(ssl_ftp_active_flag = 0);
10440             }
10441         }
10442         debug(F110,"ssl_auth","[SSL - OK]",0);
10443         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
10444     }
10445     if (ssl_debug_flag) {
10446         fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
10447         fflush(stderr);
10448     }
10449     return(ssl_ftp_active_flag);
10450 }
10451 #endif /* CK_SSL */
10452
10453 static sigtype
10454 cmdcancel(sig) int sig; {
10455 #ifdef OS2
10456     /* In Unix we "chain" to trap(), which prints this */
10457     printf("^C...\n");
10458 #endif /* OS2 */
10459     debug(F100,"ftp cmdcancel caught SIGINT ","",0);
10460     fflush(stdout);
10461     secure_getc(0,1);                   /* Initialize net input buffers */
10462     cancelfile++;
10463     cancelgroup++;
10464     mlsreset();
10465 #ifndef OS2
10466 #ifdef FTP_PROXY
10467     if (ptflag)                         /* proxy... */
10468       longjmp(ptcancel,1);
10469 #endif /* FTP_PROXY */
10470     debug(F100,"ftp cmdcancel chain to trap()...","",0);
10471     trap(SIGINT);
10472     /* NOTREACHED */
10473     debug(F100,"ftp cmdcancel return from trap()...","",0);
10474 #else
10475     debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
10476     PostCtrlCSem();
10477 #endif /* OS2 */
10478 }
10479
10480 static int
10481 #ifdef CK_ANSIC
10482 scommand(char * s)                      /* Was secure_command() */
10483 #else
10484 scommand(s) char * s;
10485 #endif /* CK_ANSIC */
10486 {
10487     int length = 0, len2;
10488     char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
10489 #ifdef CK_SSL
10490     if (ssl_ftp_active_flag) {
10491         int error, rc;
10492         length = strlen(s) + 2;
10493         length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10494         rc = SSL_write(ssl_ftp_con,out,length);
10495         error = SSL_get_error(ssl_ftp_con,rc);
10496         switch (error) {
10497           case SSL_ERROR_NONE:
10498             return(1);
10499           case SSL_ERROR_WANT_WRITE:
10500           case SSL_ERROR_WANT_READ:
10501           case SSL_ERROR_SYSCALL:
10502 #ifdef NT
10503             {
10504                 int gle = GetLastError();
10505             }
10506 #endif /* NT */
10507           case SSL_ERROR_WANT_X509_LOOKUP:
10508           case SSL_ERROR_SSL:
10509           case SSL_ERROR_ZERO_RETURN:
10510           default:
10511             lostpeer();
10512         }
10513         return(0);
10514     }
10515 #endif /* CK_SSL */
10516
10517     if (auth_type && ftp_cpl != FPL_CLR) {
10518 #ifdef FTP_SRP
10519         if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
10520           if ((length = srp_encode(ftp_cpl == FPL_PRV,
10521                                    (CHAR *)s,
10522                                    (CHAR *)out,
10523                                    strlen(s))) < 0) {
10524               fprintf(stderr, "SRP failed to encode message\n");
10525               return(0);
10526           }
10527 #endif /* FTP_SRP */
10528 #ifdef FTP_KRB4
10529         if (ck_krb4_is_installed() &&
10530             (strcmp(auth_type, "KERBEROS_V4") == 0)) {
10531             if (ftp_cpl == FPL_PRV) {
10532                 length =
10533                   krb_mk_priv((CHAR *)s, (CHAR *)out,
10534                               strlen(s), ftp_sched,
10535 #ifdef KRB524
10536                               ftp_cred.session,
10537 #else /* KRB524 */
10538                               &ftp_cred.session,
10539 #endif /* KRB524 */
10540                               &myctladdr, &hisctladdr);
10541             } else {
10542                 length =
10543                   krb_mk_safe((CHAR *)s,
10544                               (CHAR *)out,
10545                               strlen(s),
10546 #ifdef KRB524
10547                               ftp_cred.session,
10548 #else /* KRB524 */
10549                               &ftp_cred.session,
10550 #endif /* KRB524 */
10551                               &myctladdr, &hisctladdr);
10552             }
10553             if (length == -1) {
10554                 fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
10555                         ftp_cpl == FPL_PRV ? "priv" : "safe");
10556                 return(0);
10557             }
10558         }
10559 #endif /* FTP_KRB4 */
10560 #ifdef FTP_GSSAPI
10561         /* Scommand (based on level) */
10562         if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
10563             gss_buffer_desc in_buf, out_buf;
10564             OM_uint32 maj_stat, min_stat;
10565             int conf_state;
10566             in_buf.value = s;
10567             in_buf.length = strlen(s) + 1;
10568             maj_stat = gss_seal(&min_stat, gcontext,
10569                                 (ftp_cpl==FPL_PRV), /* private */
10570                                 GSS_C_QOP_DEFAULT,
10571                                 &in_buf, &conf_state,
10572                                 &out_buf);
10573             if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */
10574                 user_gss_error(maj_stat, min_stat,
10575                                (ftp_cpl==FPL_PRV)?
10576                                "gss_seal ENC didn't complete":
10577                                "gss_seal MIC didn't complete");
10578             } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
10579                 fprintf(stderr, "GSSAPI didn't encrypt message");
10580             } else {
10581                 if (ftp_deb)
10582                   fprintf(stderr, "sealed (%s) %d bytes\n",
10583                           ftp_cpl==FPL_PRV?"ENC":"MIC",
10584                           out_buf.length);
10585                 memcpy(out, out_buf.value,
10586                        length=out_buf.length);
10587                 gss_release_buffer(&min_stat, &out_buf);
10588             }
10589         }
10590 #endif /* FTP_GSSAPI */
10591         /* Other auth types go here ... */
10592
10593         len2 = FTP_BUFSIZ;
10594         if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
10595                                    length, &len2, RADIX_ENCODE))
10596             ) {
10597             fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
10598                     radix_error(kerror));
10599             return(0);
10600         }
10601         if (ftp_deb)
10602           fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
10603         len2 = ckmakmsg(out,
10604                         FTP_BUFSIZ,
10605                         ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
10606                         in,
10607                         "\r\n",
10608                         NULL
10609                         );
10610         send(csocket,(SENDARG2TYPE)out,len2,0);
10611     } else {
10612         char out[FTP_BUFSIZ];
10613         int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10614         send(csocket,(SENDARG2TYPE)out,len,0);
10615     }
10616     return(1);
10617 }
10618
10619 static int
10620 mygetc() {
10621     static char inbuf[4096];
10622     static int bp = 0, ep = 0;
10623     int rc;
10624
10625     if (bp == ep) {
10626         bp = ep = 0;
10627 #ifdef CK_SSL
10628         if (ssl_ftp_active_flag) {
10629             int error;
10630             rc = SSL_read(ssl_ftp_con,inbuf,4096);
10631             error = SSL_get_error(ssl_ftp_con,rc);
10632             switch (error) {
10633               case SSL_ERROR_NONE:
10634                 break;
10635               case SSL_ERROR_WANT_WRITE:
10636               case SSL_ERROR_WANT_READ:
10637                 return(0);
10638               case SSL_ERROR_SYSCALL:
10639                 if (rc == 0) {          /* EOF */
10640                     break;
10641                 } else {
10642 #ifdef NT
10643                     int gle = GetLastError();
10644 #endif /* NT */
10645                     break;
10646                 }
10647               case SSL_ERROR_WANT_X509_LOOKUP:
10648               case SSL_ERROR_SSL:
10649               case SSL_ERROR_ZERO_RETURN:
10650               default:
10651                 break;
10652             }
10653         } else
10654 #endif /* CK_SSL */
10655           rc = recv(csocket,(char *)inbuf,4096,0);
10656         if (rc <= 0)
10657           return(EOF);
10658         ep = rc;
10659     }
10660     return(inbuf[bp++]);
10661 }
10662
10663 /*  x l a t e c  --  Translate a character  */
10664 /*
10665     Call with:
10666       fc    = Function code: 0 = translate, 1 = initialize.
10667       c     = Character (as int).
10668       incs  = Index of charset to translate from.
10669       outcs = Index of charset to translate to.
10670
10671     Returns:
10672       0: OK
10673      -1: Error
10674 */
10675 static int
10676 xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
10677 #ifdef NOCSETS
10678     return(c);
10679 #else
10680     static char buf[128];
10681     static int cx;
10682     int c0, c1;
10683
10684     if (fc == 1) {                      /* Initialize */
10685         cx = 0;                         /* Catch-up buffer write index */
10686         xgnbp = buf;                    /* Catch-up buffer read pointer */
10687         buf[0] = NUL;                   /* Buffer is empty */
10688         return(0);
10689     }
10690     if (cx >= 127) {                    /* Catch-up buffer full */
10691         debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
10692         printf("?Translation buffer overflow\n");
10693         return(-1);
10694     }
10695     /* Add char to buffer. */
10696     /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
10697
10698     debug(F000,"xlatec buf",ckitoa(cx),c);
10699     buf[cx++] = c;
10700     buf[cx] = NUL;
10701
10702     while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
10703         if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
10704           return(-1);
10705     }
10706     /* If we're caught up, reinitialize the buffer */
10707     return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
10708 #endif /* NOCSETS */
10709 }
10710
10711
10712 /*  p a r s e f e a t  */
10713
10714 /* Note: for convenience we align keyword values with table indices */
10715 /* If you need to insert a new keyword, adjust the SFT_xxx definitions */
10716
10717 static struct keytab feattab[] = {
10718     { "$$$$", 0,        0 },            /* Dummy for sfttab[0] */
10719     { "AUTH", SFT_AUTH, 0 },
10720     { "LANG", SFT_LANG, 0 },
10721     { "MDTM", SFT_MDTM, 0 },
10722     { "MLST", SFT_MLST, 0 },
10723     { "PBSZ", SFT_PBSZ, 0 },
10724     { "PROT", SFT_PROT, 0 },
10725     { "REST", SFT_REST, 0 },
10726     { "SIZE", SFT_SIZE, 0 },
10727     { "TVFS", SFT_TVFS, 0 },
10728     { "UTF8", SFT_UTF8, 0 }
10729 };
10730 static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
10731
10732 #define FACT_CSET  1
10733 #define FACT_CREA  2
10734 #define FACT_LANG  3
10735 #define FACT_MTYP  4
10736 #define FACT_MDTM  5
10737 #define FACT_PERM  6
10738 #define FACT_SIZE  7
10739 #define FACT_TYPE  8
10740 #define FACT_UNIQ  9
10741
10742 static struct keytab facttab[] = {
10743     { "CHARSET",    FACT_CSET, 0 },
10744     { "CREATE",     FACT_CREA, 0 },
10745     { "LANG",       FACT_LANG, 0 },
10746     { "MEDIA-TYPE", FACT_MTYP, 0 },
10747     { "MODIFY",     FACT_MDTM, 0 },
10748     { "PERM",       FACT_PERM, 0 },
10749     { "SIZE",       FACT_SIZE, 0 },
10750     { "TYPE",       FACT_TYPE, 0 },
10751     { "UNIQUE",     FACT_UNIQ, 0 }
10752 };
10753 static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
10754
10755 static struct keytab ftyptab[] = {
10756     { "CDIR", FTYP_CDIR, 0 },
10757     { "DIR",  FTYP_DIR,  0 },
10758     { "FILE", FTYP_FILE, 0 },
10759     { "PDIR", FTYP_PDIR, 0 }
10760 };
10761 static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
10762
10763 static VOID
10764 parsefeat(s) char * s; {                /* Parse a FEATURE response */
10765     char kwbuf[8];
10766     int i, x;
10767     if (!s) return;
10768     if (!*s) return;
10769     while (*s < '!')
10770       s++;
10771     for (i = 0; i < 4; i++) {
10772         if (s[i] < '!')
10773           break;
10774         kwbuf[i] = s[i];
10775     }
10776     if (s[i] && s[i] != SP && s[i] != CR && s[i] != LF)
10777       return;
10778     kwbuf[i] = NUL;
10779     /* xlookup requires a full (but case independent) match */
10780     i = xlookup(feattab,kwbuf,nfeattab,&x);
10781     debug(F111,"ftp parsefeat",s,i);
10782     if (i < 0 || i > 15)
10783       return;
10784
10785     switch (i) {
10786       case SFT_MDTM:                    /* Controlled by ENABLE/DISABLE */
10787         sfttab[i] = mdtmok;
10788         if (mdtmok) sfttab[0]++;
10789         break;
10790       case SFT_MLST:                    /* ditto */
10791         sfttab[i] = mlstok;
10792         if (mlstok) sfttab[0]++;
10793         break;
10794       case SFT_SIZE:                    /* ditto */
10795         sfttab[i] = sizeok;
10796         if (sizeok) sfttab[0]++;
10797         break;
10798       case SFT_AUTH:                    /* ditto */
10799         sfttab[i] = ftp_aut;
10800         if (ftp_aut) sfttab[0]++;
10801         break;
10802       default:                          /* Others */
10803         sfttab[0]++;
10804         sfttab[i]++;
10805     }
10806 }
10807
10808 static char *
10809 parsefacts(s) char * s; {               /* Parse MLS[DT] File Facts */
10810     char * p;
10811     int i, j, x;
10812     if (!s) return(NULL);
10813     if (!*s) return(NULL);
10814
10815     /* Maybe we should make a copy of s so we can poke it... */
10816
10817     while ((p = ckstrchr(s,'='))) {
10818         *p = NUL;                       /* s points to fact */
10819         i = xlookup(facttab,s,nfacttab,&x); 
10820         debug(F111,"ftp parsefact fact",s,i);
10821         *p = '=';
10822         s = p+1;                        /* Now s points to arg */
10823         p = ckstrchr(s,';');
10824         if (!p)
10825           p = ckstrchr(s,SP);
10826         if (!p) {
10827             debug(F110,"ftp parsefact end-of-val search fail",s,0);
10828             break;
10829         }
10830         *p = NUL;
10831         debug(F110,"ftp parsefact valu",s,0);
10832         switch (i) {
10833           case FACT_CSET:               /* Ignore these for now */
10834           case FACT_CREA:
10835           case FACT_LANG:
10836           case FACT_PERM:
10837           case FACT_MTYP:
10838           case FACT_UNIQ:
10839             break;
10840           case FACT_MDTM:               /* Modtime */
10841             makestr(&havemdtm,s);
10842             debug(F110,"ftp parsefact mdtm",havemdtm,0);
10843             break;
10844           case FACT_SIZE:               /* Size */
10845             havesize = ckatofs(s);
10846             debug(F101,"ftp parsefact size","",havesize);
10847             break;
10848           case FACT_TYPE:               /* Type */
10849             j = xlookup(ftyptab,s,nftyptab,NULL);
10850             debug(F111,"ftp parsefact type",s,j);
10851             havetype = (j < 1) ? 0 : j;
10852             break;
10853         }
10854         *p = ';';
10855         s = p+1;                        /* s points next fact or name */
10856     }
10857     while (*s == SP)                    /* Skip past spaces. */
10858       s++;
10859     if (!*s)                            /* Make sure we still have a name */
10860       s = NULL;
10861     debug(F110,"ftp parsefact name",s,0);
10862     return(s);
10863 }
10864
10865 /*  g e t r e p l y  --  (to an FTP command sent to server)  */
10866
10867 /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
10868
10869 static int
10870 getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
10871     /* lcs, rcs, vbm parameters as in ftpcmd() */
10872     register int i, c, n;
10873     register int dig;
10874     register char *cp;
10875     int xlate = 0;
10876     int count = 0;
10877     int auth = 0;
10878     int originalcode = 0, continuation = 0;
10879     sig_t oldintr;
10880     int pflag = 0;
10881     char *pt = pasv;
10882     char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
10883     int safe = 0;
10884     int xquiet = 0;
10885
10886     auth = (fc == GRF_AUTH);
10887
10888 #ifndef NOCSETS
10889     debug(F101,"ftp getreply lcs","",lcs);
10890     debug(F101,"ftp getreply rcs","",rcs);
10891     if (lcs > -1 && rcs > -1 && lcs != rcs) {
10892         xlate = 1;
10893         initxlate(rcs,lcs);
10894         xlatec(1,0,rcs,lcs);
10895     }
10896 #endif /* NOCSETS */
10897     debug(F101,"ftp getreply fc","",fc);
10898
10899     if (quiet)
10900       xquiet = 1;
10901     if (vbm == 9) {
10902         xquiet = 1;
10903         vbm = 0;
10904     }
10905     if (ftp_deb)                        /* DEBUG */
10906       vbm = 1;
10907     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
10908       vbm = 0;
10909     else if (vbm < 0)                   /* VERBOSE */
10910       vbm = ftp_vbm;
10911
10912     ibuf[0] = '\0';
10913     if (reply_parse)
10914       reply_ptr = reply_buf;
10915     havesigint = 0;
10916     oldintr = signal(SIGINT, cmdcancel);
10917     for (count = 0;; count++) {
10918         obuf[0] = '\0';
10919         dig = n = ftpcode = i = 0;
10920         cp = ftp_reply_str;
10921         while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
10922             if (c == IAC) {             /* Handle telnet commands */
10923                 switch (c = mygetc()) {
10924                   case WILL:
10925                   case WONT:
10926                     c = mygetc();
10927                     obuf[0] = IAC;
10928                     obuf[1] = DONT;
10929                     obuf[2] = c;
10930                     obuf[3] = NUL;
10931 #ifdef CK_SSL
10932                     if (ssl_ftp_active_flag) {
10933                         int error, rc;
10934                         rc = SSL_write(ssl_ftp_con,obuf,3);
10935                         error = SSL_get_error(ssl_ftp_con,rc);
10936                         switch (error) {
10937                           case SSL_ERROR_NONE:
10938                             break;
10939                           case SSL_ERROR_WANT_WRITE:
10940                           case SSL_ERROR_WANT_READ:
10941                             return(0);
10942                           case SSL_ERROR_SYSCALL:
10943                             if (rc == 0) { /* EOF */
10944                                 break;
10945                             } else {
10946 #ifdef NT
10947                                 int gle = GetLastError();
10948 #endif /* NT */
10949                                 break;
10950                             }
10951                           case SSL_ERROR_WANT_X509_LOOKUP:
10952                           case SSL_ERROR_SSL:
10953                           case SSL_ERROR_ZERO_RETURN:
10954                           default:
10955                             break;
10956                         }
10957                     } else
10958 #endif /* CK_SSL */
10959                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10960                     break;
10961                   case DO:
10962                   case DONT:
10963                     c = mygetc();
10964                     obuf[0] = IAC;
10965                     obuf[1] = WONT;
10966                     obuf[2] = c;
10967                     obuf[3] = NUL;
10968 #ifdef CK_SSL
10969                     if (ssl_ftp_active_flag) {
10970                         int error, rc;
10971                         rc = SSL_write(ssl_ftp_con,obuf,3);
10972                         error = SSL_get_error(ssl_ftp_con,rc);
10973                         switch (error) {
10974                           case SSL_ERROR_NONE:
10975                             break;
10976                           case SSL_ERROR_WANT_WRITE:
10977                           case SSL_ERROR_WANT_READ:
10978                               signal(SIGINT,oldintr);
10979                               return(0);
10980                           case SSL_ERROR_SYSCALL:
10981                             if (rc == 0) { /* EOF */
10982                                 break;
10983                             } else {
10984 #ifdef NT
10985                                 int gle = GetLastError();
10986 #endif /* NT */
10987                                 break;
10988                             }
10989                           case SSL_ERROR_WANT_X509_LOOKUP:
10990                           case SSL_ERROR_SSL:
10991                           case SSL_ERROR_ZERO_RETURN:
10992                           default:
10993                             break;
10994                         }
10995                     } else
10996 #endif /* CK_SSL */
10997                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10998                     break;
10999                   default:
11000                     break;
11001                 }
11002                 continue;
11003             }
11004             dig++;
11005             if (c == EOF) {
11006                 if (expecteof) {
11007                     signal(SIGINT,oldintr);
11008                     ftpcode = 221;
11009                     debug(F101,"ftp getreply EOF","",ftpcode);
11010                     return(0);
11011                 }
11012                 lostpeer();
11013                 if (!xquiet) {
11014                     if (ftp_deb)
11015                       printf("421 ");
11016                     printf(
11017                       "Service not available, connection closed by server\n");
11018                     fflush(stdout);
11019                 }
11020                 signal(SIGINT,oldintr);
11021                 ftpcode = 421;
11022                 debug(F101,"ftp getreply EOF","",ftpcode);
11023                 return(4);
11024             }
11025             if (n == 0) {               /* First digit */
11026                 n = c;                  /* Save it */
11027             }
11028             if (auth_type &&
11029 #ifdef CK_SSL
11030                 !ssl_ftp_active_flag &&
11031 #endif /* CK_SSL */
11032                 !ibuf[0] && (n == '6' || continuation)) {
11033                 if (c != '\r' && dig > 4)
11034                   obuf[i++] = c;
11035             } else {
11036                 if (auth_type &&
11037 #ifdef CK_SSL
11038                     !ssl_ftp_active_flag &&
11039 #endif /* CK_SSL */
11040                     !ibuf[0] && dig == 1 && vbm)
11041                   printf("Unauthenticated reply received from server:\n");
11042                 if (reply_parse) {
11043                     *reply_ptr++ = c;
11044                     *reply_ptr = NUL;
11045                 }
11046                 if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
11047                     ftp_cmdlin < 2) {
11048                     if ((c != '\r') &&
11049                         (ftp_deb || ((vbm || (!auth && n == '5')) &&
11050                         (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
11051                         )))))
11052                     {
11053 #ifdef FTP_PROXY
11054                         if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
11055                           printf("%s:",ftp_host);
11056 #endif /* FTP_PROXY */
11057
11058                         if (!xquiet) {
11059 #ifdef NOCSETS
11060                             printf("%c",c);
11061 #else
11062                             if (xlate) {
11063                                 xlatec(0,c,rcs,lcs);
11064                             } else {
11065                                 printf("%c",c);
11066                             }
11067 #endif /* NOCSETS */
11068                         }
11069                     }
11070                 }
11071             }
11072             if (auth_type &&
11073 #ifdef CK_SSL
11074                 !ssl_ftp_active_flag &&
11075 #endif /* CK_SSL */
11076                 !ibuf[0] && n != '6')
11077               continue;
11078             if (dig < 4 && isdigit(c))
11079               ftpcode = ftpcode * 10 + (c - '0');
11080             if (!pflag && ftpcode == 227)
11081               pflag = 1;
11082             if (dig > 4 && pflag == 1 && isdigit(c))
11083               pflag = 2;
11084             if (pflag == 2) {
11085                 if (c != '\r' && c != ')')
11086                   *pt++ = c;
11087                 else {
11088                     *pt = '\0';
11089                     pflag = 3;
11090                 }
11091             }
11092             if (dig == 4 && c == '-' && n != '6') {
11093                 if (continuation)
11094                   ftpcode = 0;
11095                 continuation++;
11096             }
11097             if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
11098                 *cp++ = c;
11099                 *cp = NUL;
11100             }
11101         }
11102         if (deblog ||
11103 #ifdef COMMENT
11104 /*
11105   Sometimes we need to print the server reply.  printlines is nonzero for any
11106   command where the results are sent back on the control connection rather
11107   than the data connection, e.g. STAT.  In the TOPS-20 case, each file line
11108   has ftpcode 213.  But if you do this with a UNIX server, it sends "213-Start
11109   STAT", <line with ftpcode == 0>, "213-End" or somesuch.  So when printlines
11110   is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
11111   lines from UNIX.  Further experimentation needed with other servers.  Of
11112   course RFC959 is mute as to the format of the server reply.
11113
11114   'printlines' is also true for PWD and BYE.
11115 */
11116             (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
11117 #else
11118 /* No, we can't be that clever -- it breaks other things like RPWD... */
11119             (printlines &&
11120              (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
11121 #endif /* COMMENT */
11122             ) {
11123             char * q = cp;
11124             char *r = ftp_reply_str;
11125             *q-- = NUL;                 /* NUL-terminate */
11126             while (*q < '!' && q > r)   /* Strip CR, etc */
11127               *q-- = NUL;
11128             if (!ftp_deb && printlines) { /* If printing */
11129                 if (ftpcode != 0)       /* strip ftpcode if any */
11130                   r += 4;
11131 #ifdef NOCSETS
11132                 printf("%s\n",r);       /* and print */
11133 #else
11134                 if (!xlate) {
11135                     printf("%s\n",r);
11136                 } else {                /* Translating */
11137                     xgnbp = r;          /* Set up strgetc() */
11138                     while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
11139                         if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
11140                             signal(SIGINT,oldintr);
11141                             return(-1);
11142                         }
11143                     }
11144                     printf("\n");
11145                 }
11146 #endif /* NOCSETS */
11147             }
11148         }
11149         debug(F110,"FTP RCVD ",ftp_reply_str,0);
11150
11151         if (fc == GRF_FEAT) {           /* Parsing FEAT command response? */
11152             if (count == 0 && n == '2') {
11153                 int i;                  /* (Re)-init server FEATure table */
11154                 debug(F100,"ftp getreply clearing feature table","",0);
11155                 for (i = 0; i < 16; i++)
11156                   sfttab[i] = 0;
11157             } else {
11158                 parsefeat((char *)ftp_reply_str);
11159             }
11160         }
11161         if (auth_type &&
11162 #ifdef CK_SSL
11163             !ssl_ftp_active_flag &&
11164 #endif /* CK_SSL */
11165              !ibuf[0] && n != '6') {
11166             signal(SIGINT,oldintr);
11167             return(getreply(expecteof,lcs,rcs,vbm,auth));
11168         }
11169         ibuf[0] = obuf[i] = '\0';
11170         if (ftpcode && n == '6')
11171           if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
11172               printf("Unknown reply: %d %s\n", ftpcode, obuf);
11173               n = '5';
11174           } else safe = (ftpcode == 631);
11175         if (obuf[0]                     /* if there is a string to decode */
11176 #ifdef CK_SSL
11177             && !ssl_ftp_active_flag     /* and not SSL/TLS */
11178 #endif /* CK_SSL */
11179             ) {
11180             if (!auth_type) {
11181                 printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
11182                 n = '5';
11183             }
11184 #ifndef CK_ENCRYPTION
11185             else if (ftpcode == 632) {
11186                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11187                 n = '5';
11188             }
11189 #endif /* CK_ENCRYPTION */
11190 #ifdef NOCONFIDENTIAL
11191             else if (ftpcode == 633) {
11192                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11193                 n = '5';
11194             }
11195 #endif /* NOCONFIDENTIAL */
11196             else {
11197                 int len = FTP_BUFSIZ;
11198                 if ((kerror = radix_encode((CHAR *)obuf,
11199                                            (CHAR *)ibuf,
11200                                            0,
11201                                            &len,
11202                                            RADIX_DECODE))
11203                     ) {
11204                     printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
11205                            ftpcode, radix_error(kerror), obuf);
11206                     n = '5';
11207                 }
11208 #ifdef FTP_SRP
11209                 else if (strcmp(auth_type, "SRP") == 0) {
11210                     int outlen;
11211                     outlen = srp_decode(!safe, (CHAR *)ibuf,
11212                                         (CHAR *) ibuf, len);
11213                     if (outlen < 0) {
11214                         printf("Warning: %d reply %s!\n",
11215                                ftpcode, safe ? "modified" : "garbled");
11216                         n = '5';
11217                     } else {
11218                         ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
11219                         if (ftp_deb)
11220                           printf("%c:", safe ? 'S' : 'P');
11221                         continue;
11222                     }
11223                 }
11224 #endif /* FTP_SRP */
11225 #ifdef FTP_KRB4
11226                 else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
11227                     if (safe) {
11228                         kerror = krb_rd_safe((CHAR *)ibuf, len,
11229 #ifdef KRB524
11230                                              ftp_cred.session,
11231 #else /* KRB524 */
11232                                              &ftp_cred.session,
11233 #endif /* KRB524 */
11234                                              &hisctladdr,
11235                                              &myctladdr,
11236                                              &ftp_msg_data
11237                                              );
11238                     } else {
11239                         kerror = krb_rd_priv((CHAR *)ibuf, len,
11240                                              ftp_sched,
11241 #ifdef KRB524
11242                                              ftp_cred.session,
11243 #else /* KRB524 */
11244                                              &ftp_cred.session,
11245 #endif /* KRB524 */
11246                                              &hisctladdr,
11247                                              &myctladdr,
11248                                              &ftp_msg_data
11249                                              );
11250                     }
11251                     if (kerror != KSUCCESS) {
11252                         printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
11253                                safe ? "modified" : "garbled",
11254                                safe ? "safe" : "priv",
11255                                krb_get_err_text(kerror));
11256                         n = '5';
11257                     } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
11258                         kerror = KFAILURE;
11259                         n = '5';
11260                         printf("reply data too large for buffer\n");
11261                     } else {
11262                         if (ftp_deb)
11263                           printf("%c:", safe ? 'S' : 'P');
11264                         memcpy(ibuf,ftp_msg_data.app_data,
11265                                ftp_msg_data.app_length);
11266                         ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
11267                                   FTP_BUFSIZ - ftp_msg_data.app_length);
11268                         continue;
11269                     }
11270                 }
11271 #endif /* FTP_KRB4 */
11272 #ifdef FTP_GSSAPI
11273                 else if (strcmp(auth_type, "GSSAPI") == 0) {
11274                     gss_buffer_desc xmit_buf, msg_buf;
11275                     OM_uint32 maj_stat, min_stat;
11276                     int conf_state;
11277                     xmit_buf.value = ibuf;
11278                     xmit_buf.length = len;
11279                     /* decrypt/verify the message */
11280                     conf_state = safe;
11281                     maj_stat = gss_unseal(&min_stat, gcontext,
11282                                           &xmit_buf, &msg_buf,
11283                                           &conf_state, NULL);
11284                     if (maj_stat != GSS_S_COMPLETE) {
11285                         user_gss_error(maj_stat, min_stat,
11286                                        "failed unsealing reply");
11287                         n = '5';
11288                     } else {
11289                         memcpy(ibuf, msg_buf.value, msg_buf.length);
11290                         ckstrncpy(&ibuf[msg_buf.length], "\r\n",
11291                                   FTP_BUFSIZ-msg_buf.length);
11292                         gss_release_buffer(&min_stat,&msg_buf);
11293                         if (ftp_deb)
11294                           printf("%c:", safe ? 'S' : 'P');
11295                         continue;
11296                     }
11297                 }
11298 #endif /* FTP_GSSAPI */
11299                 /* Other auth types go here... */
11300             }
11301         } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
11302                    !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
11303 #ifdef NOCSETS
11304             printf("%c",c);
11305 #else
11306             if (xlate) {
11307                 xlatec(0,c,rcs,lcs);
11308             } else {
11309                 printf("%c",c);
11310             }
11311 #endif /* NOCSETS */
11312             fflush (stdout);
11313         }
11314         if (continuation && ftpcode != originalcode) {
11315             if (originalcode == 0)
11316               originalcode = ftpcode;
11317             continue;
11318         }
11319         *cp = '\0';
11320         if (n != '1')
11321           cpend = 0;
11322         signal(SIGINT,oldintr);
11323         if (ftpcode == 421 || originalcode == 421) {
11324             lostpeer();
11325             if (!xquiet && !ftp_deb)
11326               printf("%s\n",reply_buf);
11327         }
11328         if ((cancelfile != 0) &&
11329 #ifndef ULTRIX3
11330             /* Ultrix 3.0 cc objects violently to this clause */
11331             (oldintr != cmdcancel) &&
11332 #endif /* ULTRIX3 */
11333             (oldintr != SIG_IGN)) {
11334             if (oldintr)
11335               (*oldintr)(SIGINT);
11336         }
11337         if (reply_parse) {
11338             *reply_ptr = '\0';
11339             if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
11340                 reply_parse = reply_ptr + strlen(reply_parse);
11341                 if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
11342                   *reply_ptr = '\0';
11343             } else
11344               reply_parse = reply_ptr;
11345         }
11346         while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
11347           *cp-- = NUL;
11348         debug(F111,"ftp getreply",ftp_reply_str,n - '0');
11349         return(n - '0');
11350     } /* for (;;) */
11351 }
11352
11353 #ifdef BSDSELECT
11354 static int
11355 #ifdef CK_ANSIC
11356 empty(fd_set * mask, int sec)
11357 #else
11358 empty(mask, sec) fd_set * mask; int sec;
11359 #endif /* CK_ANSIC */
11360 {
11361     struct timeval t;
11362     t.tv_sec = (long) sec;
11363     t.tv_usec = 0L;
11364     debug(F100,"ftp empty calling select...","",0);
11365 #ifdef INTSELECT
11366     x = select(32, (int *)mask, NULL, NULL, &t);
11367 #else
11368     x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
11369 #endif /* INTSELECT */
11370     debug(F101,"ftp empty select","",x);
11371     return(x);
11372 }
11373 #else /* BSDSELECT */
11374 #ifdef IBMSELECT
11375 static int
11376 empty(mask, cnt, sec) int * mask, sec;
11377                       int   cnt;
11378 {
11379     return(select(mask,cnt,0,0,sec*1000));
11380 }
11381 #endif /* IBMSELECT */
11382 #endif /* BSDSELECT */
11383
11384 static sigtype
11385 cancelsend(sig) int sig; {
11386     havesigint++;
11387     cancelgroup++;
11388     cancelfile = 0;
11389     printf(" Canceled...\n");
11390     secure_getc(0,1);                   /* Initialize net input buffers */
11391     debug(F100,"ftp cancelsend caught SIGINT ","",0);
11392     fflush(stdout);
11393 #ifndef OS2
11394     longjmp(sendcancel, 1);
11395 #else
11396     PostCtrlCSem();
11397 #endif /* OS2 */
11398 }
11399
11400 static VOID
11401 #ifdef CK_ANSIC
11402 secure_error(char *fmt, ...)
11403 #else
11404 /* VARARGS1 */
11405 secure_error(fmt, p1, p2, p3, p4, p5)
11406    char *fmt; int p1, p2, p3, p4, p5;
11407 #endif /* CK_ANSIC */
11408 {
11409 #ifdef CK_ANSIC
11410     va_list ap;
11411
11412     va_start(ap, fmt);
11413     vfprintf(stderr, fmt, ap);
11414     va_end(ap);
11415 #else
11416     fprintf(stderr, fmt, p1, p2, p3, p4, p5);
11417 #endif
11418     fprintf(stderr, "\n");
11419 }
11420
11421 /*
11422  * Internal form of settype; changes current type in use with server
11423  * without changing our notion of the type for data transfers.
11424  * Used to change to and from ascii for listings.
11425  */
11426 static VOID
11427 changetype(newtype, show) int newtype, show; {
11428     int rc;
11429     char * s;
11430
11431     if ((newtype == curtype) && typesent++)
11432       return;
11433     switch (newtype) {
11434       case FTT_ASC:
11435         s = "A";
11436         break;
11437       case FTT_BIN:
11438         s = "I";
11439         break;
11440       case FTT_TEN:
11441         s = "L 8";
11442         break;
11443       default:
11444         s = "I";
11445         break;
11446     }
11447     rc = ftpcmd("TYPE",s,-1,-1,show);
11448     if (rc == REPLY_COMPLETE)
11449       curtype = newtype;
11450 }
11451
11452 /* PUT a file.  Returns -1 on error, 0 on success, 1 if file skipped */
11453
11454 static VOID
11455 #ifdef CK_ANSIC
11456 doftpsend(void * threadinfo)
11457 #else
11458 doftpsend(threadinfo) VOID * threadinfo;
11459 #endif
11460 {
11461 #ifdef NTSIG
11462     if (threadinfo) {                   /* Thread local storage... */
11463         TlsSetValue(TlsIndex,threadinfo);
11464         debug(F100, "doftpsend called with threadinfo block","", 0);
11465     } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
11466 #endif /* NTSIG */
11467 #ifdef CK_LOGIN
11468 #ifdef IKSD
11469 #ifdef NT
11470     if (inserver)
11471       setntcreds();
11472 #endif /* NT */
11473 #endif /* IKSD */
11474 #endif /* CK_LOGIN */
11475
11476     if (initconn()) {
11477 #ifndef NOHTTP
11478         int y = -1;
11479         /* debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy); */
11480
11481        /*  If the connection failed and we are using an HTTP Proxy
11482         *  and the reason for the failure was an authentication
11483         *  error, then we need to give the user to ability to
11484         *  enter a username and password, just like a browser.
11485         *
11486         *  I tried to do all of this within the netopen() call
11487         *  but it is much too much work.
11488         */
11489         while (y != 0 && tcp_http_proxy != NULL ) {
11490
11491             if (tcp_http_proxy_errno == 401 ||
11492                  tcp_http_proxy_errno == 407 ) {
11493                 char uid[UIDBUFLEN];
11494                 char pwd[PWDSIZ];
11495                 struct txtbox tb[2];
11496                 int ok;
11497
11498                 tb[0].t_buf = uid;
11499                 tb[0].t_len = UIDBUFLEN;
11500                 tb[0].t_lbl = "Proxy Userid: ";
11501                 tb[0].t_dflt = NULL;
11502                 tb[0].t_echo = 1;
11503                 tb[1].t_buf = pwd;
11504                 tb[1].t_len = 256;
11505                 tb[1].t_lbl = "Proxy Passphrase: ";
11506                 tb[1].t_dflt = NULL;
11507                 tb[1].t_echo = 2;
11508
11509                 ok = uq_mtxt("Proxy Server Authentication Required\n",
11510                               NULL, 2, tb);
11511                 if (ok && uid[0]) {
11512                     char * proxy_user, * proxy_pwd;
11513
11514                     proxy_user = tcp_http_proxy_user;
11515                     proxy_pwd  = tcp_http_proxy_pwd;
11516
11517                     tcp_http_proxy_user = uid;
11518                     tcp_http_proxy_pwd = pwd;
11519
11520                     y = initconn();
11521
11522                     debug(F101,"doftpsend","initconn",y);
11523                     memset(pwd,0,PWDSIZ);
11524                     tcp_http_proxy_user = proxy_user;
11525                     tcp_http_proxy_pwd = proxy_pwd;
11526                 } else
11527                     break;
11528             } else
11529                 break;
11530         }
11531
11532         if ( y != 0 ) {
11533 #endif /* NOHTTP */
11534             signal(SIGINT, ftpsnd.oldintr);
11535 #ifdef SIGPIPE
11536             if (ftpsnd.oldintp)
11537               signal(SIGPIPE, ftpsnd.oldintp);
11538 #endif /* SIGPIPE */
11539             ftpcode = -1;
11540             zclose(ZIFILE);
11541             ftpsndret = -1;
11542 #ifdef NTSIG
11543             ckThreadEnd(threadinfo);
11544 #endif /* NTSIG */
11545             return;
11546 #ifndef NOHTTP
11547         }
11548 #endif /* NOHTTP */
11549     }
11550     ftpsndret = 0;
11551 #ifdef NTSIG
11552      ckThreadEnd(threadinfo);
11553 #endif /* NTSIG */
11554 }
11555
11556 static VOID
11557 #ifdef CK_ANSIC
11558 failftpsend(void * threadinfo)
11559 #else
11560 failftpsend(threadinfo) VOID * threadinfo;
11561 #endif /* CK_ANSIC */
11562 {
11563 #ifdef NTSIG
11564     if (threadinfo) {                   /* Thread local storage... */
11565         TlsSetValue(TlsIndex,threadinfo);
11566         debug(F100, "docmdfile called with threadinfo block","", 0);
11567     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11568 #endif /* NTSIG */
11569 #ifdef CK_LOGIN
11570 #ifdef IKSD
11571 #ifdef NT
11572     if (inserver)
11573       setntcreds();
11574 #endif /* NT */
11575 #endif /* IKSD */
11576 #endif /* CK_LOGIN */
11577
11578     while (cpend) {
11579         ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11580         debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
11581     }
11582     if (data >= 0) {
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 #ifdef TCPIPLIB
11592         socket_close(data);
11593 #else /* TCPIPLIB */
11594 #ifdef USE_SHUTDOWN
11595         shutdown(data, 1+1);
11596 #endif /* USE_SHUTDOWN */
11597         close(data);
11598 #endif /* TCPIPLIB */
11599         data = -1;
11600         globaldin = -1;
11601     }
11602     if (ftpsnd.oldintr)
11603         signal(SIGINT,ftpsnd.oldintr);
11604 #ifdef SIGPIPE
11605     if (ftpsnd.oldintp)
11606         signal(SIGPIPE,ftpsnd.oldintp);
11607 #endif /* SIGPIPE */
11608     ftpcode = -1;
11609 #ifndef OS2
11610     /* TEST ME IN K95 */
11611     if (havesigint) {
11612         havesigint = 0;
11613         debug(F100,"ftp failftpsend chain to trap()...","",0);
11614         if (ftpsnd.oldintr != SIG_IGN)
11615           (*ftpsnd.oldintr)(SIGINT);
11616         /* NOTREACHED (I hope!) */
11617         debug(F100,"ftp failftpsend return from trap()...","",0);
11618     }
11619 #endif /* OS2 */
11620 }
11621
11622 static VOID
11623 #ifdef CK_ANSIC
11624 failftpsend2(void * threadinfo)
11625 #else
11626 failftpsend2(threadinfo) VOID * threadinfo;
11627 #endif /* CK_ANSIC */
11628 {
11629 #ifdef NTSIG
11630     if (threadinfo) {                   /* Thread local storage... */
11631         TlsSetValue(TlsIndex,threadinfo);
11632         debug(F100, "docmdfile called with threadinfo block","", 0);
11633     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11634 #endif /* NTSIG */
11635 #ifdef CK_LOGIN
11636 #ifdef IKSD
11637 #ifdef NT
11638     if (inserver)
11639       setntcreds();
11640 #endif /* NT */
11641 #endif /* IKSD */
11642 #endif /* CK_LOGIN */
11643
11644     debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
11645     tfc += ffc;
11646 #ifdef GFTIMER
11647     fpfsecs = gftimer();
11648 #endif /* GFTIMER */
11649     zclose(ZIFILE);
11650 #ifdef PIPESEND
11651     if (sndfilter)
11652       pipesend = 0;
11653 #endif /* PIPESEND */
11654     signal(SIGINT, ftpsnd.oldintr);
11655 #ifdef SIGPIPE
11656     if (ftpsnd.oldintp)
11657       signal(SIGPIPE, ftpsnd.oldintp);
11658 #endif /* SIGPIPE */
11659     if (!cpend) {
11660         ftpcode = -1;
11661         ftpsndret = -1;
11662 #ifdef NTSIG
11663         ckThreadEnd(threadinfo);
11664 #endif /* NTSIG */
11665         return;
11666     }
11667     if (data >= 0) {
11668 #ifdef CK_SSL
11669         if (ssl_ftp_data_active_flag) {
11670             SSL_shutdown(ssl_ftp_data_con);
11671             SSL_free(ssl_ftp_data_con);
11672             ssl_ftp_data_active_flag = 0;
11673             ssl_ftp_data_con = NULL;
11674         }
11675 #endif /* CK_SSL */
11676 #ifdef TCPIPLIB
11677         socket_close(data);
11678 #else /* TCPIPLIB */
11679 #ifdef USE_SHUTDOWN
11680         shutdown(data, 1+1);
11681 #endif /* USE_SHUTDOWN */
11682         close(data);
11683 #endif /* TCPIPLIB */
11684         data = -1;
11685         globaldin = -1;
11686     }
11687     if (dout) {
11688 #ifdef TCPIPLIB
11689         socket_close(dout);
11690 #else /* TCPIPLIB */
11691 #ifdef USE_SHUTDOWN
11692         shutdown(dout, 1+1);
11693 #endif /* USE_SHUTDOWN */
11694         close(dout);
11695 #endif /* TCPIPLIB */
11696     }
11697     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11698     ftpcode = -1;
11699     ftpsndret = -1;
11700
11701 #ifndef OS2
11702     /* TEST ME IN K95 */
11703     if (havesigint) {
11704         havesigint = 0;
11705         debug(F100,"ftp failftpsend2 chain to trap()...","",0);
11706         if (ftpsnd.oldintr != SIG_IGN)
11707           (*ftpsnd.oldintr)(SIGINT);
11708         /* NOTREACHED (I hope!) */
11709         debug(F100,"ftp failftpsend2 return from trap()...","",0);
11710     }
11711 #endif /* OS2 */
11712 }
11713
11714 static VOID
11715 #ifdef CK_ANSIC
11716 doftpsend2(void * threadinfo)
11717 #else
11718 doftpsend2(threadinfo) VOID * threadinfo;
11719 #endif
11720 {
11721     register int c, d = 0;
11722     int n, t, x, notafile, unique = 0;
11723     char *buf, *bufp;
11724     
11725 #ifdef NTSIG
11726     if (threadinfo) {                   /* Thread local storage... */
11727         TlsSetValue(TlsIndex,threadinfo);
11728         debug(F100, "doftpsend2 called with threadinfo block","", 0);
11729     } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
11730 #endif /* NTSIG */
11731 #ifdef CK_LOGIN
11732 #ifdef IKSD
11733 #ifdef NT
11734     if (inserver)
11735       setntcreds();
11736 #endif /* NT */
11737 #endif /* IKSD */
11738 #endif /* CK_LOGIN */
11739
11740     buf = ftpsndbuf;                    /* (not on stack) */
11741
11742     unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
11743     notafile = sndarray || pipesend;
11744
11745 #ifdef FTP_RESTART
11746     if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
11747         char * p;
11748         changetype(FTT_BIN,0);          /* Change to binary */
11749
11750         /* Ask for remote file's size */
11751         x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
11752
11753         if (x == REPLY_COMPLETE) {      /* Have ftpsnd.reply */
11754             p = &ftp_reply_str[4];      /* Parse it */
11755             while (isdigit(*p)) {
11756                 sendstart = sendstart * 10 + (int)(*p - '0');
11757                 p++;
11758             }
11759             if (*p && *p != CR) {       /* Bad number */
11760                 debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
11761                 sendstart = (CK_OFF_T)0;
11762             } else if (sendstart > fsize) { /* Remote file bigger than local */
11763                 debug(F110,"doftpsend2 big size",ckfstoa(fsize),sendstart);
11764                 sendstart = (CK_OFF_T)0;
11765             }
11766             /* Local is newer */
11767             debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
11768             if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
11769                 debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
11770                 sendstart = (CK_OFF_T)0; /* Send the whole file */
11771             }
11772         }
11773         changetype(ftp_typ,0);          /* Change back to appropriate type */
11774         if (sendstart > (CK_OFF_T)0) {  /* Still restarting? */
11775             if (sendstart == fsize) {   /* Same size - no need to send */
11776                 debug(F111,"doftpsend2 /restart SKIP",
11777                       ckfstoa(fsize),sendstart);
11778                 zclose(ZIFILE);
11779                 ftpsndret = SKP_RES;
11780 #ifdef NTSIG
11781                 ckThreadEnd(threadinfo);
11782 #endif /* NTSIG */
11783                 return;
11784             }
11785             errno = 0;                  /* Restart needed, seek to the spot */
11786             if (zfseek((long)sendstart) < 0) {
11787                 debug(F111,"doftpsend2 zfseek fails",
11788                       ftpsnd.local,sendstart);
11789                 fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
11790                 sendstart = 0;
11791                 zclose(ZIFILE);
11792                 ftpsndret = -1;
11793 #ifdef NTSIG
11794                 ckThreadEnd(threadinfo);
11795 #endif /* NTSIG */
11796                 return;
11797             }
11798 #ifdef COMMENT
11799             debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
11800             x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
11801             if (x != REPLY_CONTINUE) {
11802                 sendstart = 0;
11803                 zclose(ZIFILE);
11804                 ftpsndret = -1;
11805 #ifdef NTSIG
11806                 ckThreadEnd(threadinfo);
11807 #endif /* NTSIG */
11808                 return;
11809             } else {
11810                 ftpsnd.cmd = "STOR";
11811             }
11812 #else
11813             sendmode = SM_RESEND;
11814             ftpsnd.cmd = "APPE";
11815 #endif /* COMMENT */
11816             /* sendstart = (CK_OFF_T)0; */
11817         }
11818     }
11819 #endif /* FTP_RESTART */
11820
11821     if (unique && !stouarg)             /* If we know STOU accepts no arg */
11822       ftpsnd.remote = NULL;             /* don't include one. */
11823
11824     x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
11825     debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
11826     debug(F101,"doftpsend2 ftpcmd","",x);
11827
11828     if (x != REPLY_PRELIM && unique) {
11829         /*
11830           RFC959 says STOU does not take an argument.  But every FTP server
11831           I've encountered but one accepts the arg and constructs the unique
11832           name from it, which is better than making up a totally random name
11833           for the file, which is what RFC959 calls for.  Especially because
11834           there is no way for the client to find out the name chosen by the
11835           server.  So we try STOU with the argument first, which works with
11836           most servers, and if it fails we retry it without the arg, for
11837           the benefit of the one picky server that is not "liberal in what
11838           it accepts" UNLESS the first STOU got a 502 code ("not implemented")
11839           which means STOU is not accepted, period.
11840         */
11841         if ((x == 5) && stouarg && (ftpcode != 502)) {
11842             x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); 
11843             if (x == REPLY_PRELIM)      /* If accepted */
11844               stouarg = 0;              /* flag no STOU arg for this server */
11845         }
11846     }
11847     if (x != REPLY_PRELIM) {
11848         signal(SIGINT, ftpsnd.oldintr);
11849 #ifdef SIGPIPE
11850         if (ftpsnd.oldintp)
11851           signal(SIGPIPE, ftpsnd.oldintp);
11852 #endif /* SIGPIPE */
11853         debug(F101,"doftpsend2 not REPLY_PRELIM","",x);
11854         zclose(ZIFILE);
11855 #ifdef PIPESEND
11856         if (sndfilter)
11857           pipesend = 0;
11858 #endif /* PIPESEND */
11859         ftpsndret = -1;
11860 #ifdef NTSIG
11861         ckThreadEnd(threadinfo);
11862 #endif /* NTSIG */
11863         return;
11864     }
11865     debug(F100,"doftpsend2 getting data connection...","",0);
11866     dout = dataconn(ftpsnd.lmode);             /* Get data connection */
11867     debug(F101,"doftpsend2 dataconn","",dout);
11868     if (dout == -1) {
11869         failftpsend2(threadinfo);
11870 #ifdef NTSIG
11871         ckThreadEnd(threadinfo);
11872 #endif /* NTSIG */
11873         return;
11874     }
11875     /* Initialize per-file stats */
11876     ffc = (CK_OFF_T)0;                  /* Character counter */
11877     cps = oldcps = 0L;                  /* Thruput */
11878     n = 0;
11879 #ifdef GFTIMER
11880     rftimer();                          /* reset f.p. timer */
11881 #endif /* GFTIMER */
11882
11883 #ifdef SIGPIPE
11884     ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
11885 #endif /* SIGPIPE */
11886     debug(F101,"doftpsend2 curtype","",curtype);
11887     switch (curtype) {
11888       case FTT_BIN:                     /* Binary mode */
11889       case FTT_TEN:
11890         errno = d = 0;
11891 #ifdef VMS
11892         /*
11893           This is because VMS zxin() is C-Library fread() 
11894           but the file was opened with zopeni(), which is RMS.
11895         */
11896         while (((c = zminchar()) > -1) && !cancelfile) {
11897             ffc++;
11898             if (zzout(dout,c) < 0)
11899               break;
11900         }
11901 #else  /* VMS */
11902         while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
11903             ftpsnd.bytes += n;
11904             ffc += n;
11905             debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
11906             ckhexdump("doftpsend2 zxin",buf,16);
11907 #ifdef CK_SSL
11908             if (ssl_ftp_data_active_flag) {
11909                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11910                     if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
11911                       break;
11912                     spackets++;
11913                     pktnum++;
11914                     if (fdispla != XYFD_B) {
11915                         spktl = d;
11916                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11917                     }
11918                 }
11919             } else {
11920 #endif /* CK_SSL */
11921                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11922                     if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
11923                         || iscanceled())
11924                       break;
11925                     spackets++;
11926                     pktnum++;
11927                     if (fdispla != XYFD_B) {
11928                         spktl = d;
11929                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11930                     }
11931                 }
11932 #ifdef CK_SSL
11933             }
11934 #endif /* CK_SSL */
11935             if (d <= 0)
11936               break;
11937         }
11938 #endif  /* VMS */
11939
11940         debug(F111,"doftpsend2 XX zxin",ckltoa(n),ffc);
11941         if (n < 0)
11942           fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
11943         if (d < 0 || (d = secure_flush(dout)) < 0) {
11944             if (d == -1 && errno && errno != EPIPE)
11945               perror("netout");
11946             ftpsnd.bytes = -1;
11947         }
11948         break;
11949
11950       case FTT_ASC:                     /* Text mode */
11951 #ifndef NOCSETS
11952         if (ftpsnd.xlate) {             /* With translation */
11953             initxlate(ftpsnd.incs,ftpsnd.outcs);
11954             while (!cancelfile) {
11955                 if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
11956                 if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
11957             }
11958         } else {
11959 #endif /* NOCSETS */
11960             /* Text mode, no translation */
11961             while (((c = zminchar()) > -1) && !cancelfile) {
11962                 ffc++;
11963                 if (xxout(c) < 0)
11964                   break;
11965             }
11966             d = 0;
11967 #ifndef NOCSETS
11968         }
11969 #endif /* NOCSETS */
11970         if (dout == -1 || (d = secure_flush(dout)) < 0) {
11971             if (d == -1 && errno && errno != EPIPE)
11972               perror("netout");
11973             ftpsnd.bytes = -1;
11974         }
11975         break;
11976     }
11977     tfc += ffc;                         /* Total file chars */
11978 #ifdef GFTIMER
11979     fpfsecs = gftimer();
11980 #endif /* GFTIMER */
11981     zclose(ZIFILE);                     /* Close input file */
11982 #ifdef PIPESEND
11983     if (sndfilter)                      /* Undo this (it's per file) */
11984       pipesend = 0;
11985 #endif /* PIPESEND */
11986
11987 #ifdef CK_SSL
11988         if (ssl_ftp_data_active_flag) {
11989             SSL_shutdown(ssl_ftp_data_con);
11990             SSL_free(ssl_ftp_data_con);
11991             ssl_ftp_data_active_flag = 0;
11992             ssl_ftp_data_con = NULL;
11993         }
11994 #endif /* CK_SSL */
11995
11996 #ifdef TCPIPLIB
11997     socket_close(dout);                 /* Close data connection */
11998 #else /* TCPIPLIB */
11999 #ifdef USE_SHUTDOWN
12000     shutdown(dout, 1+1);
12001 #endif /* USE_SHUTDOWN */
12002     close(dout);
12003 #endif /* TCPIPLIB */
12004     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
12005     signal(SIGINT, ftpsnd.oldintr);            /* Put back interrupts */
12006 #ifdef SIGPIPE
12007     if (ftpsnd.oldintp)
12008       signal(SIGPIPE, ftpsnd.oldintp);
12009 #endif /* SIGPIPE */
12010     if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
12011         debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
12012         ftpsndret = -1;
12013 #ifdef NTSIG
12014         ckThreadEnd(threadinfo);
12015 #endif /* NTSIG */
12016         return;
12017     } else if (cancelfile) {
12018         debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
12019         ftpsndret = -1;
12020 #ifdef NTSIG
12021         ckThreadEnd(threadinfo);
12022 #endif /* NTSIG */
12023         return;
12024     }
12025     debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
12026     ftpsndret = 0;
12027 #ifdef NTSIG
12028      ckThreadEnd(threadinfo);
12029 #endif /* NTSIG */
12030 }
12031
12032 static int
12033 sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
12034     char *cmd, *local, *remote; int xlate, incs, outcs, restart;
12035 {
12036     if (!remote) remote = "";           /* Check args */
12037     if (!*remote) remote = local;
12038     if (!local) local = "";
12039     if (!*local) return(-1);
12040     if (!cmd) cmd = "";
12041     if (!*cmd) cmd = "STOR";
12042
12043     debug(F111,"ftp sendrequest restart",local,restart);
12044
12045     nout = 0;                           /* Init output buffer count */
12046     ftpsnd.bytes = 0;                   /* File input byte count */
12047     dout = -1;
12048
12049 #ifdef FTP_PROXY
12050     if (proxy) {
12051         proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
12052         return(0);
12053     }
12054 #endif /* FTP_PROXY */
12055
12056     changetype(ftp_typ,0);              /* Change type for this file */
12057
12058     ftpsnd.oldintr = NULL;              /* Set up interrupt handler */
12059     ftpsnd.oldintp = NULL;
12060     ftpsnd.restart = restart;
12061     ftpsnd.xlate = xlate;
12062     ftpsnd.lmode = "wb";
12063
12064 #ifdef PIPESEND                         /* Use Kermit API for file i/o... */
12065     if (sndfilter) {
12066         char * p = NULL, * q;
12067 #ifndef NOSPL
12068         int n = CKMAXPATH;
12069         if (cmd_quoting && (p = (char *) malloc(n + 1))) {
12070             q = p;
12071             debug(F110,"sendrequest pipesend filter",sndfilter,0);
12072             zzstring(sndfilter,&p,&n);
12073             debug(F111,"sendrequest pipename",q,n);
12074             if (n <= 0) {
12075                 printf("?Sorry, send filter + filename too long, %d max.\n",
12076                        CKMAXPATH
12077                        );
12078                 free(q);
12079                 return(-1);
12080             }
12081             ckstrncpy(filnam,q,CKMAXPATH+1);
12082             free(q);
12083             local = filnam;
12084         }
12085 #endif /* NOSPL */
12086     }
12087
12088     if (sndfilter)                      /* If sending thru a filter */
12089       pipesend = 1;                     /* set this for open and i/o */
12090 #endif /* PIPESEND */
12091     
12092 #ifdef VMS
12093     debug(F101,"XXX before openi binary","",binary);
12094     debug(F101,"XXX before openi ftp_typ","",ftp_typ);
12095 #endif  /* VMS */
12096
12097     if (openi(local) == 0)              /* Try to open the input file */
12098       return(-1);
12099
12100 #ifdef VMS
12101     debug(F101,"XXX after openi binary","",binary);
12102     debug(F101,"XXX after openi ftp_typ","",ftp_typ);
12103     if (!forcetype) {
12104         if (binary != ftp_typ) {        /* VMS zopeni() sets binary */
12105             debug(F101,"XXX changing type","",binary);
12106             doftptyp(binary);
12107             debug(F101,"XXX after doftptyp","",ftp_typ);
12108
12109             /* **** */
12110             if (displa && fdispla) {    /* Update file type display */
12111                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,local);
12112             }
12113         }
12114     }
12115 #endif  /* VMS */
12116     ftpsndret = 0;
12117     ftpsnd.incs = incs;
12118     ftpsnd.outcs = outcs;
12119     ftpsnd.cmd = cmd;
12120     ftpsnd.local = local;
12121     ftpsnd.remote = remote;
12122     ftpsnd.oldintr = signal(SIGINT, cancelsend);
12123     havesigint = 0;
12124
12125     if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
12126       return(-1);
12127     if (ftpsndret < 0)
12128       return(-1);
12129     if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
12130       return(-1);
12131
12132     return(ftpsndret);
12133 }
12134
12135 static sigtype
12136 cancelrecv(sig) int sig; {
12137     havesigint++;
12138     cancelfile = 0;
12139     cancelgroup++;
12140     secure_getc(0,1);                   /* Initialize net input buffers */
12141     printf(" Canceling...\n");
12142     debug(F100,"ftp cancelrecv caught SIGINT","",0);
12143     fflush(stdout);
12144     if (fp_nml) {
12145         if (fp_nml != stdout)
12146           fclose(fp_nml);
12147         fp_nml = NULL;
12148     }
12149 #ifndef OS2
12150     longjmp(recvcancel, 1);
12151 #else
12152     PostCtrlCSem();
12153 #endif /* OS2 */
12154 }
12155
12156 /* Argumentless front-end for secure_getc() */
12157
12158 static int
12159 netgetc() {
12160     return(secure_getc(globaldin,0));
12161 }
12162
12163 /* Returns -1 on failure, 0 on success, 1 if file skipped */
12164
12165 /*
12166   Sets ftpcode < 0 on failure if failure reason is not server reply code:
12167     -1: interrupted by user.
12168     -2: error opening or writing output file (reason in errno).
12169     -3: failure to make data connection.
12170     -4: network read error (reason in errno).
12171 */
12172
12173 struct xx_ftprecv {
12174     int reply;
12175     int fcs;
12176     int rcs;
12177     int recover;
12178     int xlate;
12179     int din;
12180     int is_retr;
12181     sig_t oldintr, oldintp;
12182     char * cmd;
12183     char * local;
12184     char * remote;
12185     char * lmode;
12186     char * pipename;
12187     int    tcrflag;
12188     CK_OFF_T localsize;
12189 };
12190 static struct xx_ftprecv ftprecv;
12191
12192 static int ftprecvret = 0;
12193
12194 static VOID
12195 #ifdef CK_ANSIC
12196 failftprecv(VOID * threadinfo)
12197 #else
12198 failftprecv(threadinfo) VOID * threadinfo;
12199 #endif /* CK_ANSIC */
12200 {
12201 #ifdef NTSIG
12202     if (threadinfo) {                   /* Thread local storage... */
12203         TlsSetValue(TlsIndex,threadinfo);
12204         debug(F100, "docmdfile called with threadinfo block","", 0);
12205     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12206 #endif /* NTSIG */
12207
12208 #ifdef CK_LOGIN
12209 #ifdef IKSD
12210 #ifdef NT
12211     if (inserver)
12212       setntcreds();
12213 #endif /* NT */
12214 #endif /* IKSD */
12215 #endif /* CK_LOGIN */
12216
12217     while (cpend) {
12218         ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12219     }
12220     if (data >= 0) {
12221 #ifdef CK_SSL
12222         if (ssl_ftp_data_active_flag) {
12223             SSL_shutdown(ssl_ftp_data_con);
12224             SSL_free(ssl_ftp_data_con);
12225             ssl_ftp_data_active_flag = 0;
12226             ssl_ftp_data_con = NULL;
12227         }
12228 #endif /* CK_SSL */
12229 #ifdef TCPIPLIB
12230         socket_close(data);
12231 #else /* TCPIPLIB */
12232 #ifdef USE_SHUTDOWN
12233         shutdown(data, 1+1);
12234 #endif /* USE_SHUTDOWN */
12235         close(data);
12236 #endif /* TCPIPLIB */
12237         data = -1;
12238         globaldin = -1;
12239     }
12240     if (ftprecv.oldintr)
12241       signal(SIGINT, ftprecv.oldintr);
12242     ftpcode = -1;
12243     ftprecvret = -1;
12244
12245 #ifndef OS2
12246     /* TEST ME IN K95 */
12247     if (havesigint) {
12248         havesigint = 0;
12249         debug(F100,"ftp failftprecv chain to trap()...","",0);
12250         if (ftprecv.oldintr != SIG_IGN)
12251           (*ftprecv.oldintr)(SIGINT);
12252         /* NOTREACHED (I hope!) */
12253         debug(F100,"ftp failftprecv return from trap()...","",0);
12254     }
12255 #endif /* OS2 */
12256     return;
12257 }
12258
12259 static VOID
12260 #ifdef CK_ANSIC
12261 doftprecv(VOID * threadinfo)
12262 #else
12263 doftprecv(threadinfo) VOID * threadinfo;
12264 #endif /* CK_ANSIC */
12265 {
12266 #ifdef NTSIG
12267     if (threadinfo) {                   /* Thread local storage... */
12268         TlsSetValue(TlsIndex,threadinfo);
12269         debug(F100, "docmdfile called with threadinfo block","", 0);
12270     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12271 #endif /* NTSIG */
12272 #ifdef CK_LOGIN
12273 #ifdef IKSD
12274 #ifdef NT
12275     if (inserver)
12276       setntcreds();
12277 #endif /* NT */
12278 #endif /* IKSD */
12279 #endif /* CK_LOGIN */
12280
12281 #ifndef COMMENT
12282     if (!out2screen && !ftprecv.pipename) {
12283         int x;
12284         char * local;
12285         local = ftprecv.local;
12286         x = zchko(local);
12287         if (x < 0) {
12288             if ((!dpyactive || ftp_deb))
12289               fprintf(stderr,
12290                       "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
12291             signal(SIGINT, ftprecv.oldintr);
12292             ftpcode = -2;
12293             ftprecvret = -1;
12294 #ifdef NTSIG
12295             ckThreadEnd(threadinfo);
12296 #endif /* NTSIG */
12297             return;
12298         }
12299     }
12300 #endif /* COMMENT */
12301     changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
12302     if (initconn()) {                   /* Initialize the data connection */
12303         signal(SIGINT, ftprecv.oldintr);
12304         ftpcode = -1;
12305         ftprecvret = -3;
12306 #ifdef NTSIG
12307         ckThreadEnd(threadinfo);
12308 #endif /* NTSIG */
12309         return;
12310     }
12311     secure_getc(0,1);                   /* Initialize net input buffers */
12312     ftprecvret = 0;
12313
12314 #ifdef NTSIG
12315     ckThreadEnd(threadinfo);
12316 #endif /* NTSIG */
12317 }
12318
12319 static VOID
12320 #ifdef CK_ANSIC
12321 failftprecv2(VOID * threadinfo)
12322 #else
12323 failftprecv2(threadinfo) VOID * threadinfo;
12324 #endif /* CK_ANSIC */
12325 {
12326 #ifdef NTSIG
12327     if (threadinfo) {                   /* Thread local storage... */
12328         TlsSetValue(TlsIndex,threadinfo);
12329         debug(F100, "docmdfile called with threadinfo block","", 0);
12330     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12331 #endif /* NTSIG */
12332 #ifdef CK_LOGIN
12333 #ifdef IKSD
12334 #ifdef NT
12335     if (inserver)
12336       setntcreds();
12337 #endif /* NT */
12338 #endif /* IKSD */
12339 #endif /* CK_LOGIN */
12340
12341     /* Cancel using RFC959 recommended IP,SYNC sequence  */
12342
12343     debug(F100,"ftp recvrequest CANCEL","",0);
12344 #ifdef GFTIMER
12345     fpfsecs = gftimer();
12346 #endif /* GFTIMER */
12347 #ifdef SIGPIPE
12348     if (ftprecv.oldintp)
12349       signal(SIGPIPE, ftprecv.oldintr);
12350 #endif /* SIGPIPE */
12351     signal(SIGINT, SIG_IGN);
12352     if (!cpend) {
12353         ftpcode = -1;
12354         signal(SIGINT, ftprecv.oldintr);
12355         ftprecvret = -1;
12356 #ifdef NTSIG
12357         ckThreadEnd(threadinfo);
12358 #endif /* NTSIG */
12359         return;
12360     }
12361     cancel_remote(ftprecv.din);
12362
12363 #ifdef FTP_TIMEOUT
12364     if (ftp_timed_out && out2screen && !quiet)
12365       printf("\n?Timed out.\n");
12366 #endif  /* FTP_TIMEOUT */
12367
12368     if (ftpcode > -1)
12369       ftpcode = -1;
12370     if (data >= 0) {
12371 #ifdef CK_SSL
12372         if (ssl_ftp_data_active_flag) {
12373             SSL_shutdown(ssl_ftp_data_con);
12374             SSL_free(ssl_ftp_data_con);
12375             ssl_ftp_data_active_flag = 0;
12376             ssl_ftp_data_con = NULL;
12377         }
12378 #endif /* CK_SSL */
12379 #ifdef TCPIPLIB
12380         socket_close(data);
12381 #else /* TCPIPLIB */
12382 #ifdef USE_SHUTDOWN
12383         shutdown(data, 1+1);
12384 #endif /* USE_SHUTDOWN */
12385         close(data);
12386 #endif /* TCPIPLIB */
12387         data = -1;
12388         globaldin = -1;
12389     }
12390     if (!out2screen) {
12391         int x = 0;
12392         debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
12393         zclose(ZOFILE);
12394         switch (keep) {                 /* which is... */
12395           case SET_AUTO:                /* AUTO */
12396             if (curtype == FTT_ASC)     /* Delete file if TYPE A. */
12397               x = 1;
12398             break;
12399           case SET_OFF:                 /* DISCARD */
12400             x = 1;                      /* Delete file, period. */
12401             break;
12402           default:                      /* KEEP */
12403             break;
12404         }
12405         if (x) {
12406             x = zdelet(ftprecv.local);
12407             debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
12408         }
12409     }
12410     if (ftprecv.din) {
12411 #ifdef TCPIPLIB
12412         socket_close(ftprecv.din);
12413 #else /* TCPIPLIB */
12414 #ifdef USE_SHUTDOWN
12415         shutdown(ftprecv.din, 1+1);
12416 #endif /* USE_SHUTDOWN */
12417         close(ftprecv.din);
12418 #endif /* TCPIPLIB */
12419     }
12420     signal(SIGINT, ftprecv.oldintr);
12421     ftprecvret = -1;
12422
12423     if (havesigint) {
12424         havesigint = 0;
12425         debug(F100,"FTP failftprecv2 chain to trap()...","",0);
12426 #ifdef OS2
12427         debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
12428         PostCtrlCSem();
12429 #else /* OS2 */
12430         if (ftprecv.oldintr != SIG_IGN)
12431           (*ftprecv.oldintr)(SIGINT);
12432         /* NOTREACHED (I hope!) */
12433         debug(F100,"ftp failftprecv2 return from trap()...","",0);
12434 #endif /* OS2 */
12435     }
12436 }
12437
12438 static VOID
12439 #ifdef CK_ANSIC
12440 doftprecv2(VOID * threadinfo)
12441 #else
12442 doftprecv2(threadinfo) VOID * threadinfo;
12443 #endif /* CK_ANSIC */
12444 {
12445     register int c, d;
12446     CK_OFF_T bytes = (CK_OFF_T)0;
12447     int bare_lfs = 0;
12448     int blksize = 0;
12449     ULONG start = 0L, stop;
12450     char * p;
12451     static char * rcvbuf = NULL;
12452     static int rcvbufsiz = 0;
12453 #ifdef CK_URL
12454     char newname[CKMAXPATH+1];          /* For file dialog */
12455 #endif /* CK_URL */
12456     extern int adl_ask;
12457
12458 #ifdef FTP_TIMEOUT
12459     ftp_timed_out = 0;
12460 #endif  /* FTP_TIMEOUT */
12461
12462     ftprecv.din = -1;
12463 #ifdef NTSIG
12464     if (threadinfo) {                   /* Thread local storage... */
12465         TlsSetValue(TlsIndex,threadinfo);
12466         debug(F100, "docmdfile called with threadinfo block","", 0);
12467     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12468 #endif /* NTSIG */
12469 #ifdef CK_LOGIN
12470 #ifdef IKSD
12471 #ifdef NT
12472     if (inserver)
12473       setntcreds();
12474 #endif /* NT */
12475 #endif /* IKSD */
12476 #endif /* CK_LOGIN */
12477
12478     if (ftprecv.recover) {                      /* Initiate recovery */
12479         x = ftpcmd("REST",ckfstoa(ftprecv.localsize),-1,-1,ftp_vbm);
12480         debug(F111,"ftp reply","REST",x);
12481         if (x == REPLY_CONTINUE) {
12482             ftprecv.lmode = "ab";
12483             rs_len = ftprecv.localsize;
12484         } else {
12485             ftprecv.recover = 0;
12486         }
12487     }
12488     /* IMPORTANT: No FTP commands can come between REST and RETR! */
12489
12490     debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
12491
12492     /* Send the command and get reply */
12493     debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
12494     debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
12495
12496     if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
12497         != REPLY_PRELIM) {
12498         signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
12499         ftprecvret = -1;                /* ftpcode is set by ftpcmd() */
12500 #ifdef NTSIG
12501         ckThreadEnd(threadinfo);
12502 #endif /* NTSIG */
12503         return;
12504     }
12505     ftprecv.din = dataconn("r");        /* Good reply, open data connection */
12506     globaldin = ftprecv.din;            /* Global copy of file descriptor */
12507     if (ftprecv.din == -1) {            /* Check for failure */
12508         ftpcode = -3;                   /* Code for no data connection */
12509         ftprecvret = -1;
12510 #ifdef NTSIG
12511         ckThreadEnd(threadinfo);
12512 #endif /* NTSIG */
12513         return;
12514     }
12515 #ifdef CK_URL
12516     /* In K95 GUI put up a file box */
12517     if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
12518         int x;
12519         char * preface =
12520 "\r\nIncoming file from FTP server...\r\n\
12521 Please confirm output file specification or supply an alternative:";
12522
12523         x = uq_file(preface,            /* K95 GUI: Put up file box. */
12524                     NULL,
12525                     4,
12526                     NULL,
12527                     ftprecv.local ? ftprecv.local : ftprecv.remote,
12528                     newname,
12529                     CKMAXPATH+1
12530                     );
12531         if (x > 0) {
12532             ftprecv.local = newname;    /* Substitute user's file name */
12533             if (x == 2)                 /* And append if user said to */
12534               ftprecv.lmode = "ab";
12535         }
12536     }
12537 #endif /* CK_URL */
12538     x = 1;                              /* Output file open OK? */
12539     if (ftprecv.pipename) {             /* Command */
12540         x = zxcmd(ZOFILE,ftprecv.pipename);
12541         debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
12542     } else if (!out2screen) {           /* File */
12543         struct filinfo xx;
12544         xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
12545         xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
12546         /* Append or New */
12547         xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
12548         x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
12549         debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
12550     }
12551     if (x < 1) {                        /* Failure to open output file */
12552         if ((!dpyactive || ftp_deb))
12553           fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
12554         ftprecvret = -1;
12555 #ifdef NTSIG
12556         ckThreadEnd(threadinfo);
12557 #endif /* NTSIG */
12558         return;
12559     }
12560     blksize = FTP_BUFSIZ;               /* Allocate input buffer */
12561
12562     debug(F101,"ftp recvrequest blksize","",blksize);
12563     debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
12564
12565     if (rcvbufsiz < blksize) {          /* if necessary */
12566         if (rcvbuf) {
12567             free(rcvbuf);
12568             rcvbuf = NULL;
12569         }
12570         rcvbuf = (char *)malloc((unsigned)blksize);
12571         if (!rcvbuf) {
12572             debug(F100,"ftp get rcvbuf malloc failed","",0);
12573             ftpcode = -2;
12574 #ifdef ENOMEM
12575             errno = ENOMEM;
12576 #endif /* ENOMEM */
12577             if ((!dpyactive || ftp_deb))
12578               perror("malloc");
12579             rcvbufsiz = 0;
12580             ftprecvret = -1;
12581 #ifdef NTSIG
12582             ckThreadEnd(threadinfo);
12583 #endif /* NTSIG */
12584             return;
12585         }
12586         debug(F101,"ftp get rcvbuf malloc ok","",blksize);
12587         rcvbufsiz = blksize;
12588     }
12589     debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
12590
12591     ffc = (CK_OFF_T)0;                  /* Character counter */
12592     cps = oldcps = 0L;                  /* Thruput */
12593     start = gmstimer();                 /* Start time (msecs) */
12594 #ifdef GFTIMER
12595     rftimer();                          /* Start time (float) */
12596 #endif /* GFTIMER */
12597
12598     debug(F111,"ftp get type",ftprecv.local,curtype);
12599     debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
12600     switch (curtype) {
12601       case FTT_BIN:                     /* Binary mode */
12602       case FTT_TEN:                     /* TENEX mode */
12603         d = 0;
12604         while (1) {
12605             errno = 0;
12606             c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
12607             if (cancelfile) {
12608                 failftprecv2(threadinfo);
12609 #ifdef NTSIG
12610                 ckThreadEnd(threadinfo);
12611 #endif /* NTSIG */
12612                 return;
12613             }
12614             if (c < 1)
12615               break;
12616 #ifdef printf                           /* (What if it isn't?) */
12617             if (out2screen && !ftprecv.pipename) {
12618                 int i;
12619                 for (i = 0; i < c; i++)
12620                   printf("%c",rcvbuf[i]);
12621             } else
12622 #endif /* printf */
12623               {
12624                 register int i;
12625                 i = 0;
12626                 errno = 0;
12627                 while (i < c) {
12628                     if (zmchout(rcvbuf[i++]) < 0) {
12629                         d = i;
12630                         break;
12631                     }
12632                 }
12633             }
12634             bytes += c;
12635             ffc += c;
12636         }
12637 #ifdef FTP_TIMEOUT
12638         if (c == -3) {
12639             debug(F100,"ftp recvrequest timeout","",0); 
12640             bytes = (CK_OFF_T)-1;
12641             ftp_timed_out = 1;
12642             ftpcode = -3;
12643         } else
12644 #endif  /* FTP_TIMEOUT */
12645         if (c < 0) {
12646             debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
12647             if (c == -1 && errno != EPIPE)
12648               if ((!dpyactive || ftp_deb))
12649                 perror("netin");
12650             bytes = (CK_OFF_T)-1;
12651             ftpcode = -4;
12652         }
12653         if (d < c) {
12654             ftpcode = -2;
12655             if ((!dpyactive || ftp_deb)) {
12656                 char * p;
12657                 p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
12658                 if (d < 0)
12659                   fprintf(stderr,
12660                           "local(3): %s: %s\n", ftprecv.local, ck_errstr());
12661                 else
12662                   fprintf(stderr,
12663                           "%s: short write\n", ftprecv.local);
12664             }
12665         }
12666         break;
12667
12668       case FTT_ASC:                     /* Text mode */
12669         debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
12670 #ifndef NOCSETS
12671         if (ftprecv.xlate) {
12672             int t;
12673 #ifdef CK_ANSIC
12674             int (*fn)(char);
12675 #else
12676             int (*fn)();
12677 #endif /* CK_ANSIC */
12678             debug(F110,"ftp recvrequest (data)","initxlate",0);
12679             initxlate(ftprecv.rcs,ftprecv.fcs);         /* (From,To) */
12680             if (ftprecv.pipename) {
12681                 fn = pipeout;
12682                 debug(F110,"ftp recvrequest ASCII","pipeout",0);
12683             } else {
12684                 fn = out2screen ? scrnout : putfil;
12685                 debug(F110,"ftp recvrequest ASCII",
12686                       out2screen ? "scrnout" : "putfil",0);
12687             }
12688             while (1) {
12689                 /* Get byte from net */
12690                 c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12691                 if (cancelfile) {
12692                     failftprecv2(threadinfo);
12693 #ifdef NTSIG
12694                     ckThreadEnd(threadinfo);
12695 #endif /* NTSIG */
12696                     return;
12697                 }
12698                 if (c0 < 0)
12699                   break;
12700                 /* Second byte from net */
12701                 c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12702                 if (cancelfile) {
12703                     failftprecv2(threadinfo);
12704 #ifdef NTSIG
12705                     ckThreadEnd(threadinfo);
12706 #endif /* NTSIG */
12707                     return;
12708                 }
12709                 if (c1 < 0)
12710                   break;
12711 #ifdef COMMENT
12712                 /* K95: Check whether we need this */
12713                 if (fileorder > 0)      /* Little Endian */
12714                   bytswap(&c0,&c1);     /* swap bytes*/
12715 #endif /* COMMENT */
12716
12717 #ifdef OS2
12718                 if ( out2screen &&            /* we're translating to UCS-2 */ 
12719                      !k95stdout && !inserver) /* for the real screen... */     
12720                 {
12721                     union {
12722                         USHORT ucs2;
12723                         UCHAR  bytes[2];
12724                     } output;
12725
12726                     output.bytes[0] = c1;
12727                     output.bytes[1] = c0;
12728
12729                     VscrnWrtUCS2StrAtt(VCMD,
12730                                        &output.ucs2,
12731                                        1,
12732                                        wherey[VCMD],
12733                                        wherex[VCMD],
12734                                        &colorcmd
12735                                        );
12736
12737                 } else 
12738 #endif /* OS2 */
12739                 {
12740                     if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12741                     if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12742                 }
12743             }
12744         } else {
12745 #endif /* NOCSETS */
12746             while (1) {
12747                 c = secure_getc(ftprecv.din,0);
12748                 if (cancelfile
12749 #ifdef FTP_TIMEOUT
12750                     || ftp_timed_out
12751 #endif  /* FTP_TIMEOUT */
12752                     ) {
12753                     failftprecv2(threadinfo);
12754 #ifdef NTSIG
12755                     ckThreadEnd(threadinfo);
12756 #endif /* NTSIG */
12757                     return;
12758                 }
12759                 if (c < 0 || c == EOF)
12760                   break;
12761 #ifdef UNIX
12762                 /* Record format conversion for Unix */
12763                 /* SKIP THIS FOR WINDOWS! */
12764                 if (c == '\n')
12765                   bare_lfs++;
12766                 while (c == '\r') {
12767                     bytes++;
12768                     if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
12769                         ftprecv.tcrflag) {
12770                         if (cancelfile) {
12771                             failftprecv2(threadinfo);
12772 #ifdef NTSIG
12773                             ckThreadEnd(threadinfo);
12774 #endif /* NTSIG */
12775                             return;
12776                         }
12777                         if (c < 0 || c == EOF)
12778                           goto break2;
12779                         if (c == '\0') {
12780                             bytes++;
12781                             goto contin2;
12782                         }
12783                     }
12784                 }
12785                 if (c < 0)
12786                   break;
12787 #endif /* UNX */
12788
12789                 if (out2screen && !ftprecv.pipename)
12790 #ifdef printf
12791                   printf("%c",(char)c);
12792 #else
12793                   putchar((char)c);
12794 #endif /* printf */
12795                 else
12796                   if ((d = zmchout(c)) < 0)
12797                     break;
12798                 bytes++;
12799                 ffc++;
12800               contin2:
12801                 ;
12802             }
12803           break2:
12804             if (bare_lfs && (!dpyactive || ftp_deb)) {
12805                 printf("WARNING! %d bare linefeeds received in ASCII mode\n",
12806                        bare_lfs);
12807                 printf("File might not have transferred correctly.\n");
12808             }
12809             if (ftprecv.din == -1) {
12810                 bytes = (CK_OFF_T)-1;
12811             }
12812             if (c == -2)
12813               bytes = (CK_OFF_T)-1;
12814             break;
12815 #ifndef NOCSETS
12816         }
12817 #endif /* NOCSETS */
12818     }
12819     if (ftprecv.pipename || !out2screen) {
12820         zclose(ZOFILE);                 /* Close the file */
12821         debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
12822         if (ftpcode < 0) {              /* If download failed */
12823             int x = 0;
12824             switch (keep) {             /* which is... */
12825               case SET_AUTO:            /* AUTO */
12826                 if (curtype == FTT_ASC) /* Delete file if TYPE A. */
12827                   x = 1;
12828                 break;
12829               case SET_OFF:             /* DISCARD */
12830                 x = 1;                  /* Delete file, period. */
12831                 break;
12832               default:                  /* KEEP */
12833                 break;
12834             }
12835             if (x) {
12836                 x = zdelet(ftprecv.local);
12837                 debug(F111,"ftp get delete incomplete",ftprecv.local,x);
12838             }
12839         }
12840     }
12841     signal(SIGINT, ftprecv.oldintr);
12842 #ifdef SIGPIPE
12843     if (ftprecv.oldintp)
12844       signal(SIGPIPE, ftprecv.oldintp);
12845 #endif /* SIGPIPE */
12846     stop = gmstimer();
12847 #ifdef GFTIMER
12848     fpfsecs = gftimer();
12849 #endif /* GFTIMER */
12850     tfc += ffc;
12851
12852 #ifdef TCPIPLIB
12853     socket_close(ftprecv.din);
12854 #else /* TCPIPLIB */
12855 #ifdef USE_SHUTDOWN
12856     shutdown(ftprecv.din, 1+1);
12857 #endif /* USE_SHUTDOWN */
12858     close(ftprecv.din);
12859 #endif /* TCPIPLIB */
12860     ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12861     ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || 
12862                    ftprecv.reply == REPLY_ERROR) ? -1 : 0);
12863 #ifdef NTSIG
12864      ckThreadEnd(threadinfo);
12865 #endif /* NTSIG */
12866 }
12867
12868 static int
12869 recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
12870             xlate, fcs, rcs)
12871     char *cmd, *local, *remote, *lmode, *pipename;
12872     int printnames, recover, xlate, fcs, rcs;
12873 {
12874 #ifdef NT
12875     struct _stat stbuf;
12876 #else /* NT */
12877     struct stat stbuf;
12878 #endif /* NT */
12879
12880 #ifdef DEBUG
12881     if (deblog) {
12882         debug(F111,"ftp recvrequest cmd",cmd,recover);
12883         debug(F110,"ftp recvrequest local ",local,0);
12884         debug(F111,"ftp recvrequest remote",remote,ftp_typ);
12885         debug(F110,"ftp recvrequest pipename ",pipename,0);
12886         debug(F101,"ftp recvrequest xlate","",xlate);
12887         debug(F101,"ftp recvrequest fcs","",fcs);
12888         debug(F101,"ftp recvrequest rcs","",rcs);
12889     }
12890 #endif /* DEBUG */
12891
12892     ftprecv.localsize = (CK_OFF_T)0;
12893
12894     if (remfile) {                      /* See remcfm(), remtxt() */
12895         if (rempipe) {
12896             pipename = remdest;
12897         } else {
12898             local = remdest;
12899             if (remappd) lmode = "ab";
12900         }
12901     }
12902     out2screen = 0;
12903     if (!cmd) cmd = "";                 /* Core dump prevention */
12904     if (!remote) remote = "";
12905     if (!lmode) lmode = "";
12906
12907     if (pipename) {                     /* No recovery for pipes. */
12908         recover = 0;
12909         if (!local)
12910           local = pipename;
12911     } else {
12912         if (!local)                     /* Output to screen? */
12913           local = "-";
12914         out2screen = !strcmp(local,"-");
12915     }
12916     debug(F101,"ftp recvrequest out2screen","",out2screen);
12917
12918 #ifdef OS2
12919     if ( ftp_xla && out2screen && !k95stdout && !inserver )
12920         fcs = FC_UCS2;
12921 #endif /* OS2 */
12922
12923     if (out2screen)                     /* No recovery to screen */
12924       recover = 0;
12925     if (!ftp_typ)                       /* No recovery in text mode */
12926       recover = 0;
12927     ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
12928
12929     if (!ftprecv.is_retr)               /* No recovery except for RETRieve */
12930       recover = 0;
12931
12932 #ifdef COMMENT
12933     if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
12934         if (recursive && ckstrchr(local,'/')) {
12935             
12936         }
12937     }
12938 #endif /* COMMENT */
12939
12940     ftprecv.localsize = (CK_OFF_T)0;    /* Local file size */
12941     rs_len = (CK_OFF_T)0;               /* Recovery point */
12942
12943     debug(F101,"ftp recvrequest recover","",recover);
12944     if (recover) {                      /* Recovering... */
12945         if (stat(local, &stbuf) < 0) {  /* Can't stat local file */
12946             debug(F101,"ftp recvrequest recover stat failed","",errno);
12947             recover = 0;                /* So cancel recovery */
12948         } else {                        /* Have local file info */
12949             ftprecv.localsize = stbuf.st_size;  /* Get size */
12950             /* Remote file smaller than local */
12951             if (fsize < ftprecv.localsize) {
12952                 debug(F101,"ftp recvrequest recover remote smaller","",fsize);
12953                 recover = 0;            /* Recovery can't work */
12954             } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
12955                 debug(F111,"ftp recvrequest recover equal size",
12956                       remote,ftprecv.localsize);
12957                 return(1);
12958             }
12959 #ifdef COMMENT
12960 /*
12961   The problem here is that the original partial file never got its date
12962   set, either because FTP DATES was OFF, or because the partial file was
12963   downloaded by some other program that doesn't set local file dates, or
12964   because Kermit only sets the file's date when the download was complete
12965   and successful.  In all these cases, the local file has a later time
12966   than the remote.
12967 */
12968             if (recover) {              /* Remote is bigger */
12969                 x = chkmodtime(local,remote,0); /* Check file dates */
12970                 debug(F111,"ftp recvrequest chkmodtime",remote,x);
12971                 if (x != 1)             /* Dates must be equal! */
12972                   recover = 0;          /* If not, get whole file */
12973             }
12974 #endif /* COMMENT */
12975         }
12976         debug(F111,"ftp recvrequest recover",remote,recover);
12977     }
12978
12979 #ifdef FTP_PROXY
12980     if (proxy && ftprecv.is_retr)
12981       return(proxtrans(cmd, local ? local : remote, remote));
12982 #endif /* FTP_PROXY */
12983
12984     ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
12985
12986     ftprecv.reply = 0;
12987     ftprecv.fcs = fcs;
12988     ftprecv.rcs = rcs;
12989     ftprecv.recover = recover;
12990     ftprecv.xlate = xlate;
12991     ftprecv.cmd = cmd;
12992     ftprecv.local = local;
12993     ftprecv.remote = remote;
12994     ftprecv.lmode = lmode;
12995     ftprecv.pipename = pipename;
12996     ftprecv.oldintp = NULL;
12997     ftpcode = 0;
12998
12999     havesigint = 0;
13000     ftprecv.oldintr = signal(SIGINT, cancelrecv);
13001     if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
13002       return -1;
13003
13004 #ifdef FTP_TIMEOUT
13005     debug(F111,"ftp recvrequest ftprecvret",remote,ftprecvret);
13006     debug(F111,"ftp recvrequest ftp_timed_out",remote,ftp_timed_out);
13007     if (ftp_timed_out)
13008       ftprecvret = -1;
13009 #endif  /* FTP_TIMEOUT */
13010
13011     if (ftprecvret < 0)
13012       return -1;
13013
13014     if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
13015       return -1;
13016     return ftprecvret;
13017 }
13018
13019 /*
13020  * Need to start a listen on the data channel before we send the command,
13021  * otherwise the server's connect may fail.
13022  */
13023 static int
13024 initconn() {
13025     register char *p, *a;
13026     int result, tmpno = 0;
13027     int on = 1;
13028     GSOCKNAME_T len;
13029
13030 #ifndef NO_PASSIVE_MODE
13031     int a1,a2,a3,a4,p1,p2;
13032
13033     if (passivemode) {
13034         data = socket(AF_INET, SOCK_STREAM, 0);
13035         globaldin = data;
13036         if (data < 0) {
13037             perror("ftp: socket");
13038             return(-1);
13039         }
13040         if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13041             printf("Passive mode refused\n");
13042             passivemode = 0;
13043             return(initconn());
13044         }
13045 /*
13046   Now we have a string of comma-separated one-byte unsigned integer values,
13047   The first four are the an IP address.  The fifth is the MSB of the port
13048   number, the sixth is the LSB.  From that we can make a sockaddr_in.
13049 */
13050         if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
13051             printf("Passive mode address scan failure\n");
13052             return(-1);
13053         };
13054 #ifndef NOHTTP
13055         if (tcp_http_proxy) {
13056 #ifdef OS2
13057             char * agent = "Kermit 95"; /* Default user agent */
13058 #else
13059             char * agent = "C-Kermit";
13060 #endif /* OS2 */
13061             register struct hostent *hp = 0;
13062             struct servent *destsp;
13063             char host[512], *p, *q;
13064 #ifdef IP_TOS
13065 #ifdef IPTOS_THROUGHPUT
13066             int tos;
13067 #endif /* IPTOS_THROUGHPUT */
13068 #endif /* IP_TOS */
13069             int s;
13070 #ifdef DEBUG
13071             extern int debtim;
13072             int xdebtim;
13073             xdebtim = debtim;
13074             debtim = 1;
13075 #endif /* DEBUG */
13076
13077             ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
13078                       ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
13079                       NULL,NULL,NULL
13080                       );
13081             memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
13082             for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
13083               *q = *p;
13084             *q = '\0';
13085
13086             hisctladdr.sin_addr.s_addr = inet_addr(host);
13087             if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
13088             {
13089                 debug(F110,"initconn A",host,0);
13090                 hisctladdr.sin_family = AF_INET;
13091             } else {
13092                 debug(F110,"initconn B",host,0);
13093                 hp = gethostbyname(host);
13094 #ifdef HADDRLIST
13095                 hp = ck_copyhostent(hp); /* make safe copy that won't change */
13096 #endif /* HADDRLIST */
13097                 if (hp == NULL) {
13098                     fprintf(stderr, "ftp: %s: Unknown host\n", host);
13099                     ftpcode = -1;
13100 #ifdef DEBUG
13101                     debtim = xdebtim;
13102 #endif /* DEBUG */
13103                     return(0);
13104                 }
13105                 hisctladdr.sin_family = hp->h_addrtype;
13106 #ifdef HADDRLIST
13107                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
13108                        sizeof(hisctladdr.sin_addr));
13109 #else /* HADDRLIST */
13110                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
13111                        sizeof(hisctladdr.sin_addr));
13112 #endif /* HADDRLIST */
13113             }
13114             data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13115             debug(F101,"initconn socket","",data);
13116             if (data < 0) {
13117                 perror("ftp: socket");
13118                 ftpcode = -1;
13119 #ifdef DEBUG
13120                 debtim = xdebtim;
13121 #endif /* DEBUG */
13122                 return(0);
13123             }
13124             if (*p == ':')
13125               p++;
13126             else
13127               p = "http";
13128
13129             destsp = getservbyname(p,"tcp");
13130             if (destsp)
13131               hisctladdr.sin_port = destsp->s_port;
13132             else if (p)
13133               hisctladdr.sin_port = htons(atoi(p));
13134             else
13135               hisctladdr.sin_port = htons(80);
13136             errno = 0;
13137 #ifdef HADDRLIST
13138             debug(F100,"initconn HADDRLIST","",0);
13139             while
13140 #else
13141             debug(F100,"initconn no HADDRLIST","",0);
13142             if
13143 #endif /* HADDRLIST */
13144               (connect(data, (struct sockaddr *)&hisctladdr,
13145                        sizeof (hisctladdr)) < 0) {
13146                   debug(F101,"initconn connect failed","",errno);
13147 #ifdef HADDRLIST
13148                   if (hp && hp->h_addr_list[1]) {
13149                       int oerrno = errno;
13150
13151                       fprintf(stderr,
13152                               "ftp: connect to address %s: ",
13153                               inet_ntoa(hisctladdr.sin_addr)
13154                               );
13155                       errno = oerrno;
13156                       perror((char *)0);
13157                       hp->h_addr_list++;
13158                       memcpy((char *)&hisctladdr.sin_addr,
13159                              hp->h_addr_list[0],
13160                              sizeof(hisctladdr.sin_addr));
13161                       fprintf(stdout, "Trying %s...\n",
13162                               inet_ntoa(hisctladdr.sin_addr));
13163 #ifdef TCPIPLIB
13164                       socket_close(data);
13165 #else /* TCPIPLIB */
13166                       close(data);
13167 #endif /* TCPIPLIB */
13168                       data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13169                       if (data < 0) {
13170                           perror("ftp: socket");
13171                           ftpcode = -1;
13172 #ifdef DEBUG
13173                           debtim = xdebtim;
13174 #endif /* DEBUG */
13175                           return(0);
13176                       }
13177                       continue;
13178                   }
13179 #endif /* HADDRLIST */
13180                   perror("ftp: connect");
13181                   ftpcode = -1;
13182                   goto bad;
13183               }
13184             if (http_connect(data,
13185                              tcp_http_proxy_agent ?
13186                                tcp_http_proxy_agent :
13187                                  agent,
13188                              NULL,
13189                              tcp_http_proxy_user,
13190                              tcp_http_proxy_pwd,
13191                              0,
13192                              proxyhost
13193                              ) < 0) {
13194 #ifdef TCPIPLIB
13195                 socket_close(data);
13196 #else /* TCPIPLIB */
13197                 close(data);
13198 #endif /* TCPIPLIB */
13199                 perror("ftp: connect");
13200                 ftpcode = -1;
13201                 goto bad;
13202             }
13203         } else
13204 #endif /* NOHTTP */
13205         {
13206             data_addr.sin_family = AF_INET;
13207             data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
13208             data_addr.sin_port = htons((p1<<8)|p2);
13209
13210             if (connect(data,
13211                         (struct sockaddr *)&data_addr,
13212                         sizeof(data_addr)) < 0
13213                 ) {
13214                 perror("ftp: connect");
13215                 return(-1);
13216             }
13217         }
13218         debug(F100,"initconn connect ok","",0);
13219 #ifdef IP_TOS
13220 #ifdef IPTOS_THROUGHPUT
13221         on = IPTOS_THROUGHPUT;
13222         if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13223           perror("ftp: setsockopt TOS (ignored)");
13224 #endif /* IPTOS_THROUGHPUT */
13225 #endif /* IP_TOS */
13226         memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
13227         return(0);
13228     }
13229 #endif /* NO_PASSIVE_MODE */
13230
13231   noport:
13232     memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
13233     if (sendport)
13234       data_addr.sin_port = 0;   /* let system pick one */
13235     if (data != -1) {
13236 #ifdef TCPIPLIB
13237         socket_close(data);
13238 #else /* TCPIPLIB */
13239 #ifdef USE_SHUTDOWN
13240         shutdown(data, 1+1);
13241 #endif /* USE_SHUTDOWN */
13242         close(data);
13243 #endif /* TCPIPLIB */
13244     }
13245     data = socket(AF_INET, SOCK_STREAM, 0);
13246     globaldin = data;
13247     if (data < 0) {
13248         perror("ftp: socket");
13249         if (tmpno)
13250           sendport = 1;
13251         return(-1);
13252     }
13253     if (!sendport) {
13254         if (setsockopt(data,
13255                        SOL_SOCKET,
13256                        SO_REUSEADDR,
13257                        (char *)&on,
13258                        sizeof (on)
13259                        ) < 0
13260             ) {
13261             perror("ftp: setsockopt (reuse address)");
13262             goto bad;
13263         }
13264     }
13265     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
13266         perror("ftp: bind");
13267         goto bad;
13268     }
13269     len = sizeof (data_addr);
13270     if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
13271         perror("ftp: getsockname");
13272         goto bad;
13273     }
13274     if (listen(data, 1) < 0) {
13275         perror("ftp: listen");
13276         goto bad;
13277     }
13278     if (sendport) {
13279         a = (char *)&data_addr.sin_addr;
13280         p = (char *)&data_addr.sin_port;
13281         ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
13282                   UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
13283                   UC(p[0]),",", UC(p[1]));
13284         result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
13285         if (result == REPLY_ERROR && sendport) {
13286             sendport = 0;
13287             tmpno = 1;
13288             goto noport;
13289         }
13290         return(result != REPLY_COMPLETE);
13291     }
13292     if (tmpno)
13293       sendport = 1;
13294 #ifdef IP_TOS
13295 #ifdef IPTOS_THROUGHPUT
13296     on = IPTOS_THROUGHPUT;
13297     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13298       perror("ftp: setsockopt TOS (ignored)");
13299 #endif
13300 #endif
13301     return(0);
13302   bad:
13303 #ifdef TCPIPLIB
13304     socket_close(data);
13305 #else /* TCPIPLIB */
13306 #ifdef USE_SHUTDOWN
13307     shutdown(data, 1+1);
13308 #endif /* USE_SHUTDOWN */
13309     close(data);
13310 #endif /* TCPIPLIB */
13311     data = -1;
13312     globaldin = data;
13313     if (tmpno)
13314       sendport = 1;
13315     return(-1);
13316 }
13317
13318 #ifdef CK_SSL
13319 static int
13320 ssl_dataconn() {
13321     if (ssl_ftp_data_con!=NULL) {       /* Do SSL */
13322         SSL_free(ssl_ftp_data_con);
13323         ssl_ftp_data_con=NULL;
13324     }
13325     ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
13326
13327     SSL_set_fd(ssl_ftp_data_con,data);
13328     SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
13329
13330     SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
13331
13332     if (ssl_debug_flag) {
13333         fprintf(stderr,"=>START SSL connect on DATA\n");
13334         fflush(stderr);
13335     }
13336     if (SSL_connect(ssl_ftp_data_con) <= 0) {
13337         static char errbuf[1024];
13338         ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
13339                   ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
13340         fprintf(stderr,"%s\n", errbuf);
13341         fflush(stderr);
13342 #ifdef TCPIPLIB
13343         socket_close(data);
13344 #else /* TCPIPLIB */
13345 #ifdef USE_SHUTDOWN
13346         shutdown(data, 1+1);
13347 #endif /* USE_SHUTDOWN */
13348         close(data);
13349 #endif /* TCPIPLIB */
13350         data = -1;
13351         globaldin = data;
13352         return(-1);
13353     } else {
13354         ssl_ftp_data_active_flag=1;
13355
13356         if (!ssl_certsok_flag && !tls_is_krb5(2)) {
13357             char *subject = ssl_get_subject_name(ssl_ftp_data_con);
13358
13359             if (!subject) {
13360                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
13361                     debug(F110,"dataconn","[SSL _- FAILED]",0);
13362
13363                     ssl_ftp_data_active_flag = 0;
13364 #ifdef TCPIPLIB
13365                     socket_close(data);
13366 #else /* TCPIPLIB */
13367 #ifdef USE_SHUTDOWN
13368                     shutdown(data, 1+1);
13369 #endif /* USE_SHUTDOWN */
13370                     close(data);
13371 #endif /* TCPIPLIB */
13372                     data = -1;
13373                     globaldin = data;
13374                     return(-1);
13375                 } else {
13376                     if (!out2screen && displa && fdispla) {
13377                         ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13378                         /* fdispla = XYFD_B; */
13379                     }
13380
13381                     if (uq_ok(
13382           "Warning: Server didn't provide a certificate on data connection\n",
13383                                "Continue with file transfer? (Y/N)",
13384                               3,NULL,0) <= 0) {
13385                         debug(F110, "dataconn","[SSL - FAILED]",0);
13386                         ssl_ftp_data_active_flag = 0;
13387 #ifdef TCPIPLIB
13388                         socket_close(data);
13389 #else /* TCPIPLIB */
13390 #ifdef USE_SHUTDOWN
13391                         shutdown(data, 1+1);
13392 #endif /* USE_SHUTDOWN */
13393                         close(data);
13394 #endif /* TCPIPLIB */
13395                         data = -1;
13396                         globaldin = data;
13397                         return(-1);
13398                     }
13399                 }
13400             } else {
13401                 if (!out2screen && displa && fdispla == XYFD_C) {
13402                     ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13403                     /* fdispla = XYFD_B; */
13404                 }
13405
13406                 if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
13407                     debug(F110,"dataconn","[SSL - FAILED]",0);
13408                     ssl_ftp_data_active_flag = 0;
13409 #ifdef TCPIPLIB
13410                     socket_close(data);
13411 #else /* TCPIPLIB */
13412 #ifdef USE_SHUTDOWN
13413                     shutdown(data, 1+1);
13414 #endif /* USE_SHUTDOWN */
13415                     close(data);
13416 #endif /* TCPIPLIB */
13417                     data = -1;
13418                     globaldin = data;
13419                     return(-1);
13420                 }
13421             }
13422         }
13423         debug(F110,"dataconn","[SSL - OK]",0);
13424 #ifdef COMMENT
13425         /* This messes up the full screen file transfer display */
13426         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
13427 #endif /* COMMENT */
13428     }
13429     if (ssl_debug_flag) {
13430         fprintf(stderr,"=>DONE SSL connect on DATA\n");
13431         fflush(stderr);
13432     }
13433     return(data);
13434 }
13435 #endif /* CK_SSL */
13436
13437 static int
13438 dataconn(lmode) char *lmode; {
13439     int s;
13440 #ifdef IP_TOS
13441     int tos;
13442 #endif /* IP_TOS */
13443 #ifdef UCX50
13444     static u_int fromlen;
13445 #else
13446     static SOCKOPT_T fromlen;
13447 #endif /* UCX50 */
13448
13449     fromlen = sizeof(hisdataaddr);
13450
13451 #ifndef NO_PASSIVE_MODE
13452     if (passivemode) {
13453 #ifdef CK_SSL
13454         ssl_ftp_data_active_flag=0;
13455         if (ssl_ftp_active_flag &&
13456             (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13457           return(ssl_dataconn());
13458 #endif /* CK_SSL */
13459         return(data);
13460     }
13461 #endif /* NO_PASSIVE_MODE */
13462
13463     s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
13464     if (s < 0) {
13465         perror("ftp: accept");
13466 #ifdef TCPIPLIB
13467         socket_close(data);
13468 #else /* TCPIPLIB */
13469 #ifdef USE_SHUTDOWN
13470         shutdown(data, 1+1);
13471 #endif /* USE_SHUTDOWN */
13472         close(data);
13473 #endif /* TCPIPLIB */
13474         data = -1;
13475         globaldin = data;
13476         return(-1);
13477     }
13478 #ifdef TCPIPLIB
13479     socket_close(data);
13480 #else /* TCPIPLIB */
13481 #ifdef USE_SHUTDOWN
13482     shutdown(data, 1+1);
13483 #endif /* USE_SHUTDOWN */
13484     close(data);
13485 #endif /* TCPIPLIB */
13486     data = s;
13487     globaldin = data;
13488 #ifdef IP_TOS
13489 #ifdef IPTOS_THROUGHPUT
13490     tos = IPTOS_THROUGHPUT;
13491     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
13492       perror("ftp: setsockopt TOS (ignored)");
13493 #endif /* IPTOS_THROUGHPUT */
13494 #endif /* IP_TOS */
13495
13496 #ifdef CK_SSL
13497     ssl_ftp_data_active_flag=0;
13498     if (ssl_ftp_active_flag &&
13499         (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13500       return(ssl_dataconn());
13501 #endif /* CK_SSL */
13502     return(data);
13503 }
13504
13505 #ifdef FTP_PROXY
13506 static sigtype
13507 pscancel(sig) int sig; {
13508     cancelfile++;
13509 }
13510
13511 static VOID
13512 pswitch(flag) int flag; {
13513     extern int proxy;
13514     sig_t oldintr;
13515     static struct comvars {
13516         int connect;
13517         char name[MAXHOSTNAMELEN];
13518         struct sockaddr_in mctl;
13519         struct sockaddr_in hctl;
13520         FILE *in;
13521         FILE *out;
13522         int tpe;
13523         int curtpe;
13524         int cpnd;
13525         int sunqe;
13526         int runqe;
13527         int mcse;
13528         int ntflg;
13529         char nti[17];
13530         char nto[17];
13531         int mapflg;
13532         char mi[CKMAXPATH];
13533         char mo[CKMAXPATH];
13534         char *authtype;
13535         int clvl;
13536         int dlvl;
13537 #ifdef FTP_KRB4
13538         des_cblock session;
13539         des_key_schedule ftp_sched;
13540 #endif /* FTP_KRB4 */
13541 #ifdef FTP_GSSAPI
13542         gss_ctx_id_t gcontext;
13543 #endif /* GSSAPI */
13544     } proxstruct, tmpstruct;
13545     struct comvars *ip, *op;
13546
13547     cancelfile = 0;
13548     oldintr = signal(SIGINT, pscancel);
13549     if (flag) {
13550         if (proxy)
13551           return;
13552         ip = &tmpstruct;
13553         op = &proxstruct;
13554         proxy++;
13555     } else {
13556         if (!proxy)
13557           return;
13558         ip = &proxstruct;
13559         op = &tmpstruct;
13560         proxy = 0;
13561     }
13562     ip->connect = connected;
13563     connected = op->connect;
13564     if (ftp_host) {
13565         strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
13566         ip->name[MAXHOSTNAMELEN - 1] = '\0';
13567         ip->name[strlen(ip->name)] = '\0';
13568     } else
13569       ip->name[0] = 0;
13570     ftp_host = op->name;
13571     ip->hctl = hisctladdr;
13572     hisctladdr = op->hctl;
13573     ip->mctl = myctladdr;
13574     myctladdr = op->mctl;
13575     ip->in = csocket;
13576     csocket = op->in;
13577     ip->out = csocket;
13578     csocket = op->out;
13579     ip->tpe = ftp_typ;
13580     ftp_typ = op->tpe;
13581     ip->curtpe = curtype;
13582     curtype = op->curtpe;
13583     ip->cpnd = cpend;
13584     cpend = op->cpnd;
13585     ip->sunqe = ftp_usn;
13586     ftp_usn = op->sunqe;
13587     ip->mcse = mcase;
13588     mcase = op->mcse;
13589     ip->ntflg = ntflag;
13590     ntflag = op->ntflg;
13591     strncpy(ip->nti, ntin, 16);
13592     (ip->nti)[strlen(ip->nti)] = '\0';
13593     strcpy(ntin, op->nti);
13594     strncpy(ip->nto, ntout, 16);
13595     (ip->nto)[strlen(ip->nto)] = '\0';
13596     strcpy(ntout, op->nto);
13597     ip->mapflg = mapflag;
13598     mapflag = op->mapflg;
13599     strncpy(ip->mi, mapin, CKMAXPATH - 1);
13600     (ip->mi)[strlen(ip->mi)] = '\0';
13601     strcpy(mapin, op->mi);
13602     strncpy(ip->mo, mapout, CKMAXPATH - 1);
13603     (ip->mo)[strlen(ip->mo)] = '\0';
13604     strcpy(mapout, op->mo);
13605     ip->authtype = auth_type;
13606     auth_type = op->authtype;
13607     ip->clvl = ftp_cpl;
13608     ftp_cpl = op->clvl;
13609     ip->dlvl = ftp_dpl;
13610     ftp_dpl = op->dlvl;
13611     if (!ftp_cpl)
13612       ftp_cpl = FPL_CLR;
13613     if (!ftp_dpl)
13614       ftp_dpl = FPL_CLR;
13615 #ifdef FTP_KRB4
13616     memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
13617     memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
13618     memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
13619     memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
13620 #endif /* FTP_KRB4 */
13621 #ifdef FTP_GSSAPI
13622     ip->gcontext = gcontext;
13623     gcontext = op->gcontext;
13624 #endif /* GSSAPI */
13625     signal(SIGINT, oldintr);
13626     if (cancelfile) {
13627         cancelfile = 0;
13628         debug(F101,"pswitch cancelfile B","",cancelfile);
13629         (*oldintr)(SIGINT);
13630     }
13631 }
13632
13633 static sigtype
13634 cancelpt(sig) int sig; {
13635     printf("\n");
13636     fflush(stdout);
13637     ptabflg++;
13638     cancelfile = 0;
13639 #ifndef OS2
13640     longjmp(ptcancel, 1);
13641 #else
13642     PostCtrlCSem();
13643 #endif /* OS2 */
13644 }
13645
13646 void
13647 proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
13648     sig_t oldintr;
13649     int secndflag = 0, prox_type, nfnd;
13650     char *cmd2;
13651 #ifdef BSDSELECT
13652     fd_set mask;
13653 #endif /* BSDSELECT */
13654     sigtype cancelpt();
13655
13656     if (strcmp(cmd, "RETR"))
13657       cmd2 = "RETR";
13658     else
13659       cmd2 = unique ? "STOU" : "STOR";
13660     if ((prox_type = type) == 0) {
13661         if (servertype == SYS_UNIX && unix_proxy)
13662           prox_type = FTT_BIN;
13663         else
13664           prox_type = FTT_ASC;
13665     }
13666     if (curtype != prox_type)
13667       changetype(prox_type, 1);
13668     if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13669         printf("Proxy server does not support third party transfers.\n");
13670         return;
13671     }
13672     pswitch(0);
13673     if (!connected) {
13674         printf("No primary connection\n");
13675         pswitch(1);
13676         ftpcode = -1;
13677         return;
13678     }
13679     if (curtype != prox_type)
13680       changetype(prox_type, 1);
13681
13682     if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
13683         pswitch(1);
13684         return;
13685     }
13686
13687     /* Replace with calls to cc_execute() */
13688     if (setjmp(ptcancel))
13689       goto cancel;
13690     oldintr = signal(SIGINT, cancelpt);
13691     if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
13692         signal(SIGINT, oldintr);
13693         pswitch(1);
13694         return;
13695     }
13696     sleep(2000);
13697     pswitch(1);
13698     secndflag++;
13699     if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
13700       goto cancel;
13701     ptflag++;
13702     getreply(0,-1,-1,ftp_vbm,0);
13703     pswitch(0);
13704     getreply(0,-1,-1,ftp_vbm,0);
13705     signal(SIGINT, oldintr);
13706     pswitch(1);
13707     ptflag = 0;
13708     return;
13709
13710   cancel:
13711     signal(SIGINT, SIG_IGN);
13712     ptflag = 0;
13713     if (strcmp(cmd, "RETR") && !proxy)
13714       pswitch(1);
13715     else if (!strcmp(cmd, "RETR") && proxy)
13716       pswitch(0);
13717     if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
13718         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13719             pswitch(0);
13720             if (cpend)
13721               cancel_remote(0);
13722         }
13723         pswitch(1);
13724         if (ptabflg)
13725           ftpcode = -1;
13726         signal(SIGINT, oldintr);
13727         return;
13728     }
13729     if (cpend)
13730       cancel_remote(0);
13731     pswitch(!proxy);
13732     if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
13733         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13734             pswitch(0);
13735             if (cpend)
13736               cancel_remote(0);
13737             pswitch(1);
13738             if (ptabflg)
13739               ftpcode = -1;
13740             signal(SIGINT, oldintr);
13741             return;
13742         }
13743     }
13744     if (cpend)
13745       cancel_remote(0);
13746     pswitch(!proxy);
13747     if (cpend) {
13748 #ifdef BSDSELECT
13749         FD_ZERO(&mask);
13750         FD_SET(csocket, &mask);
13751         if ((nfnd = empty(&mask, 10)) <= 0) {
13752             if (nfnd < 0) {
13753                 perror("cancel");
13754             }
13755             if (ptabflg)
13756               ftpcode = -1;
13757             lostpeer();
13758         }
13759 #else /* BSDSELECT */
13760 #ifdef IBMSELECT
13761         if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
13762             if (nfnd < 0) {
13763                 perror("cancel");
13764             }
13765             if (ptabflg)
13766               ftpcode = -1;
13767             lostpeer();
13768         }
13769 #endif /* IBMSELECT */
13770 #endif /* BSDSELECT */
13771         getreply(0,-1,-1,ftp_vbm,0);
13772         getreply(0,-1,-1,ftp_vbm,0);
13773     }
13774     if (proxy)
13775       pswitch(0);
13776     pswitch(1);
13777     if (ptabflg)
13778       ftpcode = -1;
13779     signal(SIGINT, oldintr);
13780 }
13781 #endif /* FTP_PROXY */
13782
13783 #ifdef FTP_SECURITY
13784 #ifdef FTP_GSSAPI
13785
13786 #ifdef COMMENT
13787 /* ck_gss_mech_krb5 is not declared anywhere */
13788 struct {
13789     CONST gss_OID_desc * CONST * mech_type;
13790     char *service_name;
13791 } gss_trials[] = {
13792     { &ck_gss_mech_krb5, "ftp" },
13793     { &ck_gss_mech_krb5, "host" },
13794 };
13795 #else
13796 /* This matches what is declared above */
13797 struct {
13798     CONST gss_OID_desc * CONST * mech_type;
13799     char *service_name;
13800 } gss_trials[] = {
13801     { &gss_mech_krb5, "ftp" },
13802     { &gss_mech_krb5, "host" },
13803 };
13804 #endif  /* COMMENT */
13805
13806
13807 int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
13808 #endif /* FTP_GSSAPI */
13809
13810 static int
13811 ftp_auth() {
13812     extern int setsafe();
13813     int j = 0, n;
13814 #ifdef FTP_KRB4
13815     char *service, inst[INST_SZ];
13816     ULONG cksum;
13817     ULONG checksum = (ULONG) getpid();
13818     CHAR out_buf[FTP_BUFSIZ];
13819     int i;
13820 #else /* FTP_KRB4 */
13821 #ifdef FTP_GSSAPI
13822     CHAR out_buf[FTP_BUFSIZ];
13823     int i;
13824 #endif /* FTP_GSSAPI */
13825 #endif /* FTP_KRB4 */
13826
13827     if (ssl_ftp_proxy)                  /* Do not allow AUTH over SSL proxy */
13828         return(0);
13829
13830     if (auth_type)
13831       return(1);                        /* auth already succeeded */
13832
13833     /* Try each auth type as specified by the end user */
13834     for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
13835 #ifdef FTP_GSSAPI
13836         if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
13837             n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
13838             if (n == REPLY_CONTINUE) {
13839                 OM_uint32 maj_stat, min_stat;
13840                 gss_name_t target_name;
13841                 gss_buffer_desc send_tok, recv_tok, *token_ptr;
13842                 char stbuf[FTP_BUFSIZ];
13843                 int comcode, trial;
13844                 struct gss_channel_bindings_struct chan;
13845                 char * realm = NULL;
13846                 char tgt[256];
13847
13848                 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
13849                 chan.initiator_address.length = 4;
13850                 chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
13851                 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
13852                 chan.acceptor_address.length = 4;
13853                 chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
13854                 chan.application_data.length = 0;
13855                 chan.application_data.value = 0;
13856
13857                 if (!quiet)
13858                   printf("GSSAPI accepted as authentication type\n");
13859
13860                 realm = ck_krb5_realmofhost(ftp_user_host);
13861                 if (realm) {
13862                     ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
13863                     debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
13864                     if ( krb5_autoget &&
13865                          !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
13866                             (ck_krb5_is_tgt_valid() > 0)) )
13867                         ck_krb5_autoget_TGT(realm);
13868                 }
13869
13870                 /* Blob from gss-client */
13871                 for (trial = 0; trial < n_gss_trials; trial++) {
13872                     /* ftp@hostname first, the host@hostname */
13873                     /* the V5 GSSAPI binding canonicalizes this for us... */
13874                     ckmakmsg(stbuf,FTP_BUFSIZ,
13875                              gss_trials[trial].service_name,
13876                              "@",
13877                              ftp_user_host,
13878                              NULL
13879                              );
13880                     if (ftp_deb)
13881                       fprintf(stderr,
13882                               "Authenticating to <%s>...\n", stbuf);
13883                     send_tok.value = stbuf;
13884                     send_tok.length = strlen(stbuf);
13885                     maj_stat = gss_import_name(&min_stat, &send_tok,
13886                                                gss_nt_service_name,
13887                                                &target_name
13888                                                );
13889                     if (maj_stat != GSS_S_COMPLETE) {
13890                         user_gss_error(maj_stat, min_stat, "parsing name");
13891                         secure_error("name parsed <%s>\n", stbuf);
13892                         continue;
13893                     }
13894                     token_ptr = GSS_C_NO_BUFFER;
13895                     gcontext = GSS_C_NO_CONTEXT; /* structure copy */
13896
13897                     do {
13898                         if (ftp_deb)
13899                           fprintf(stderr, "calling gss_init_sec_context\n");
13900                         maj_stat =
13901                           gss_init_sec_context(&min_stat,
13902                                                GSS_C_NO_CREDENTIAL,
13903                                                &gcontext,
13904                                                target_name,
13905                                                (gss_OID) *
13906                                                  gss_trials[trial].mech_type,
13907                                                GSS_C_MUTUAL_FLAG |
13908                                                GSS_C_REPLAY_FLAG |
13909                                                (ftp_cfw ?
13910                                                 GSS_C_DELEG_FLAG : 0),
13911                                                0,
13912                                                 /* channel bindings */
13913                                                 (krb5_d_no_addresses ?
13914                                                   GSS_C_NO_CHANNEL_BINDINGS :
13915                                                   &chan),
13916                                                 token_ptr,
13917                                                NULL,    /* ignore mech type */
13918                                                &send_tok,
13919                                                NULL,    /* ignore ret_flags */
13920                                                NULL
13921                                                );       /* ignore time_rec */
13922
13923                         if (maj_stat != GSS_S_COMPLETE &&
13924                             maj_stat != GSS_S_CONTINUE_NEEDED) {
13925                             if (trial == n_gss_trials-1)
13926                               user_gss_error(maj_stat,
13927                                              min_stat,
13928                                              "initializing context"
13929                                              );
13930                             gss_release_name(&min_stat, &target_name);
13931                             /* maybe we missed on the service name */
13932                             goto outer_loop;
13933                         }
13934                         if (send_tok.length != 0) {
13935                             int len;
13936                             reply_parse = "ADAT="; /* for ftpcmd() later */
13937                             len = FTP_BUFSIZ;
13938                             kerror =
13939                               radix_encode(send_tok.value,
13940                                            out_buf,
13941                                            send_tok.length,
13942                                            &len,
13943                                            RADIX_ENCODE
13944                                            );
13945                             if (kerror)  {
13946                                 fprintf(stderr,
13947                                         "Base 64 encoding failed: %s\n",
13948                                         radix_error(kerror)
13949                                         );
13950                                 goto gss_complete_loop;
13951                             }
13952                             comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
13953                             if (comcode != REPLY_COMPLETE
13954                                 && comcode != REPLY_CONTINUE /* (335) */
13955                                 ) {
13956                                 if (trial == n_gss_trials-1) {
13957                                     fprintf(stderr, "GSSAPI ADAT failed\n");
13958                                     /* force out of loop */
13959                                     maj_stat = GSS_S_FAILURE;
13960                                 }
13961                                 /*
13962                                   Backoff to the v1 gssapi is still possible.
13963                                   Send a new AUTH command.  If that fails,
13964                                   terminate the loop.
13965                                 */
13966                                 if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
13967                                     != REPLY_CONTINUE) {
13968                                     fprintf(stderr,
13969                                 "GSSAPI ADAT failed, AUTH restart failed\n");
13970                                     /* force out of loop */
13971                                     maj_stat = GSS_S_FAILURE;
13972                                 }
13973                                 goto outer_loop;
13974                             }
13975                             if (!reply_parse) {
13976                                 fprintf(stderr,
13977                               "No authentication data received from server\n");
13978                                 if (maj_stat == GSS_S_COMPLETE) {
13979                                     fprintf(stderr,
13980                                             "...but no more was needed\n");
13981                                     goto gss_complete_loop;
13982                                 } else {
13983                                     user_gss_error(maj_stat,
13984                                                    min_stat,
13985                                                    "no reply, huh?"
13986                                                    );
13987                                     goto gss_complete_loop;
13988                                 }
13989                             }
13990                             len = FTP_BUFSIZ;
13991                             kerror = radix_encode(reply_parse,out_buf,i,&len,
13992                                                   RADIX_DECODE);
13993                             if (kerror) {
13994                                 fprintf(stderr,
13995                                         "Base 64 decoding failed: %s\n",
13996                                         radix_error(kerror));
13997                                 goto gss_complete_loop;
13998                             }
13999
14000                             /* everything worked */
14001                             token_ptr = &recv_tok;
14002                             recv_tok.value = out_buf;
14003                             recv_tok.length = len;
14004                             continue;
14005
14006                             /* get out of loop clean */
14007                           gss_complete_loop:
14008                             trial = n_gss_trials-1;
14009                             gss_release_buffer(&min_stat, &send_tok);
14010                             gss_release_name(&min_stat, &target_name);
14011                             goto outer_loop;
14012                         }
14013                     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
14014
14015                   outer_loop:
14016                     if (maj_stat == GSS_S_COMPLETE)
14017                       break;
14018                 }
14019                 if (maj_stat == GSS_S_COMPLETE) {
14020                     printf("GSSAPI authentication succeeded\n");
14021                     reply_parse = NULL;
14022                     auth_type = "GSSAPI";
14023                     return(1);
14024                 } else {
14025                     fprintf(stderr, "GSSAPI authentication failed\n");
14026                     reply_parse = NULL;
14027                 }
14028             } else {
14029                 if (ftp_deb)
14030                 fprintf(stderr, "GSSAPI rejected as an authentication type\n");
14031                 if (ftpcode == 500 || ftpcode == 502)
14032                     return(0);
14033             }
14034         }
14035 #endif /* FTP_GSSAPI */
14036 #ifdef FTP_SRP
14037         if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
14038             if (srp_ftp_auth(ftp_user_host,NULL,NULL))
14039               return(1);
14040             else if (ftpcode == 500 || ftpcode == 502)
14041               return(0);
14042         }
14043 #endif /* FTP_SRP */
14044 #ifdef FTP_KRB4
14045         if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
14046             n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
14047             if (n == REPLY_CONTINUE) {
14048                 char tgt[4*REALM_SZ+1];
14049                 int rc;
14050
14051                 if (!quiet)
14052                   printf("KERBEROS_V4 accepted as authentication type\n");
14053                 ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
14054                 ckstrncpy(ftp_realm,
14055                           (char *)ck_krb4_realmofhost(ftp_user_host),
14056                           REALM_SZ
14057                           );
14058
14059                 ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
14060                 rc = ck_krb4_tkt_isvalid(tgt);
14061
14062                 if (rc <= 0 && krb4_autoget)
14063                   ck_krb4_autoget_TGT(ftp_realm);
14064
14065                 service = "ftp";
14066                 kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
14067                 if (kerror == KDC_PR_UNKNOWN) {
14068                     service = "rcmd";
14069                     kerror = krb_mk_req(&ftp_tkt,
14070                                         service,
14071                                         inst,
14072                                         ftp_realm,
14073                                         checksum
14074                                         );
14075                 }
14076                 if (kerror)
14077                   fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
14078                           krb_get_err_text(kerror));
14079                 if (!kerror) {
14080                     kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
14081                     if (kerror)
14082                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
14083                               krb_get_err_text(kerror));
14084                 }
14085                 if (!kerror) {
14086                     int rc;
14087                     rc = des_key_sched(ftp_cred.session, ftp_sched);
14088                     if (rc == -1) {
14089                        printf("?Invalid DES key specified in credentials\r\n");
14090                        debug(F110,"ftp_auth",
14091                              "invalid DES Key specified in credentials",0);
14092                     } else if ( rc == -2 ) {
14093                         printf("?Weak DES key specified in credentials\r\n");
14094                         debug(F110,"ftp_auth",
14095                               "weak DES Key specified in credentials",0);
14096                     } else if ( rc != 0 ) {
14097                         printf("?DES Key Schedule not set by credentials\r\n");
14098                         debug(F110,"ftp_auth",
14099                               "DES Key Schedule not set by credentials",0);
14100                     }
14101                     reply_parse = "ADAT=";
14102                     i = FTP_BUFSIZ;
14103                     kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
14104                                           &i, RADIX_ENCODE);
14105                     if (kerror) {
14106                         fprintf(stderr, "Base 64 encoding failed: %s\n",
14107                                 radix_error(kerror));
14108                         goto krb4_err;
14109                     }
14110                     if (i > FTP_BUFSIZ - 6)
14111                       printf("?ADAT data too long\n");
14112                     if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
14113                         REPLY_COMPLETE) {
14114                         fprintf(stderr, "Kerberos V4 authentication failed\n");
14115                         goto krb4_err;
14116                     }
14117                     if (!reply_parse) {
14118                         fprintf(stderr,
14119                              "No authentication data received from server\n");
14120                         goto krb4_err;
14121                     }
14122                     i = sizeof(out_buf);
14123                     kerror =
14124                       radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
14125                     if (kerror) {
14126                         fprintf(stderr, "Base 64 decoding failed: %s\n",
14127                                 radix_error(kerror));
14128                         goto krb4_err;
14129                     }
14130                     kerror = krb_rd_safe(out_buf, i,
14131 #ifdef KRB524
14132                                          ftp_cred.session,
14133 #else /* KRB524 */
14134                                          &ftp_cred.session,
14135 #endif /* KRB524 */
14136                                          &hisctladdr,
14137                                          &myctladdr,
14138                                          &ftp_msg_data
14139                                          );
14140                     if (kerror) {
14141                         fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
14142                                 krb_get_err_text(kerror));
14143                         goto krb4_err;
14144                     }
14145
14146                     /* fetch the (modified) checksum */
14147                     memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
14148                     if (ntohl(cksum) == checksum + 1) {
14149                         if (ftp_vbm)
14150                           printf("Kerberos V4 authentication succeeded\n");
14151                         reply_parse = NULL;
14152                         auth_type = "KERBEROS_V4";
14153                         return(1);
14154                     } else
14155                       fprintf(stderr,
14156                               "Kerberos V4 mutual authentication failed\n");
14157                   krb4_err:
14158                     reply_parse = NULL;
14159                 }
14160             } else {
14161                 if (ftp_deb)
14162                   fprintf(stderr,
14163                       "KERBEROS_V4 rejected as an authentication type\n");
14164                 if (ftpcode == 500 || ftpcode == 502)
14165                     return(0);
14166             }
14167         }
14168 #endif /* FTP_KRB4 */
14169 #ifdef CK_SSL
14170         if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
14171 #ifdef FTPHOST
14172             if (!hostcmd) {
14173                 ftpcmd("HOST",ftp_user_host,0,0,0);
14174                 hostcmd = 1;
14175             }
14176 #endif /* FTPHOST */
14177             n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
14178             if (n != REPLY_COMPLETE)
14179               n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
14180             if (n == REPLY_COMPLETE) {
14181                 if (!quiet)
14182                   printf("TLS accepted as authentication type\n");
14183
14184                 auth_type = "TLS";
14185                 ssl_auth();
14186                 if (ssl_ftp_active_flag ) {
14187                     ftp_dpl = FPL_CLR;
14188                     ftp_cpl = FPL_PRV;
14189                     return(1);
14190                 } else {
14191                     fprintf(stderr,"TLS authentication failed\n");
14192                     auth_type = NULL;
14193 #ifdef TCPIPLIB
14194                     socket_close(csocket);
14195 #else /* TCPIPLIB */
14196 #ifdef USE_SHUTDOWN
14197                     shutdown(csocket, 1+1);
14198 #endif /* USE_SHUTDOWN */
14199                     close(csocket);
14200 #endif /* TCPIPLIB */
14201                     csocket = -1;
14202                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14203                       return(0);
14204                 }
14205             } else {
14206                 if (ftp_deb)
14207                   fprintf(stderr,"TLS rejected as an authentication type\n");
14208                 if (ftpcode == 500 || ftpcode == 502)
14209                     return(0);
14210             }
14211         }
14212         if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
14213 #ifdef FTPHOST
14214             if (!hostcmd) {
14215                 ftpcmd("HOST",ftp_user_host,0,0,0);
14216                 hostcmd = 1;
14217             }
14218 #endif /* FTPHOST */
14219             n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
14220             if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
14221                 if (!quiet)
14222                   printf("SSL accepted as authentication type\n");
14223                 auth_type = "SSL";
14224                 ssl_auth();
14225                 if (ssl_ftp_active_flag) {
14226                     ftp_dpl = FPL_PRV;
14227                     ftp_cpl = FPL_PRV;
14228                     setprotbuf(1<<20);
14229                     return(1);
14230                 } else {
14231                     fprintf(stderr,"SSL authentication failed\n");
14232                     auth_type = NULL;
14233 #ifdef TCPIPLIB
14234                     socket_close(csocket);
14235 #else /* TCPIPLIB */
14236 #ifdef USE_SHUTDOWN
14237                     shutdown(csocket, 1+1);
14238 #endif /* USE_SHUTDOWN */
14239                     close(csocket);
14240 #endif /* TCPIPLIB */
14241                     csocket = -1;
14242                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14243                       return(0);
14244                 }
14245             } else {
14246                 if (ftp_deb)
14247                   fprintf(stderr, "SSL rejected as an authentication type\n");
14248                 if (ftpcode == 500 || ftpcode == 502)
14249                   return(0);
14250             }
14251         }
14252 #endif /* CK_SSL */
14253         /* Other auth types go here ... */
14254     } /* for (j;;) */
14255     return(0);
14256 }
14257 #endif /* FTP_SECURITY */
14258
14259 static int
14260 #ifdef CK_ANSIC
14261 setprotbuf(unsigned int size)
14262 #else
14263 setprotbuf(size) unsigned int size;
14264 #endif /* CK_ANSIC */
14265 /* setprotbuf */ {
14266     if (ucbuf)
14267       free(ucbuf);
14268     ucbuf = NULL;
14269     ucbufsiz = 0;
14270     actualbuf = size;
14271     while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
14272         if (actualbuf)
14273           actualbuf /= 2;
14274         else
14275           return(0);
14276     }
14277     ucbufsiz = actualbuf - FUDGE_FACTOR;
14278     debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
14279     if (ucbufsiz < 128) {
14280         printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
14281     } else if (ucbufsiz < 0) {
14282         printf("ERROR: ucbuf allocation failure\n");
14283         return(-1);
14284     }
14285     maxbuf = actualbuf;
14286     return(1);
14287 }
14288
14289 static int
14290 #ifdef CK_ANSIC
14291 setpbsz(unsigned int size)
14292 #else
14293 setpbsz(size) unsigned int size;
14294 #endif /* CK_ANSIC */
14295 /* setpbsz */ {
14296     if (!setprotbuf(size)) {
14297         perror("?Error while trying to malloc PROT buffer:");
14298 #ifdef FTP_SRP
14299         srp_reset();
14300 #endif /* FTP_SRP */
14301         ftpclose();
14302         return(-1);
14303     }
14304     reply_parse = "PBSZ=";
14305     ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
14306 #ifdef CK_SSL
14307              ssl_ftp_active_flag ? "0" :
14308 #endif /* CK_SSL */
14309              ckuitoa(actualbuf),NULL,NULL);
14310     if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
14311         if (connected) {
14312             printf("?Unable to negotiate PROT buffer size with FTP server\n");
14313             ftpclose();
14314         }
14315         return(-1);
14316     }
14317     if (reply_parse) {
14318         if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
14319           maxbuf = actualbuf;
14320     } else
14321       maxbuf = actualbuf;
14322     ucbufsiz = maxbuf - FUDGE_FACTOR;
14323     debug(F101,"setpbsz ucbufsiz","",ucbufsiz);    
14324     reply_parse = NULL;
14325     return(0);
14326 }
14327
14328 static VOID
14329 cancel_remote(din) int din; {
14330     CHAR buf[FTP_BUFSIZ];
14331     int x, nfnd;
14332 #ifdef BSDSELECT
14333     fd_set mask;
14334 #endif /* BSDSELECT */
14335 #ifdef IBMSELECT
14336     int fds[2], fdcnt = 0;
14337 #endif /* IBMSELECT */
14338 #ifdef DEBUG
14339     extern int debtim;
14340     int xdebtim;
14341     xdebtim = debtim;
14342     debtim = 1;
14343 #endif /* DEBUG */
14344     debug(F100,"ftp cancel_remote entry","",0);
14345 #ifdef CK_SSL
14346     if (ssl_ftp_active_flag) {
14347         /*
14348          * Send Telnet IP, Telnet DM but do so inline and within the
14349          * TLS channel
14350          */
14351         int count, error;
14352
14353         buf[0] = IAC;
14354         buf[1] = TN_IP;
14355         buf[2] = IAC;
14356         buf[3] = TN_DM;
14357         buf[4] = NUL;
14358
14359         count = SSL_write(ssl_ftp_con, buf, 4);
14360         debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
14361         error = SSL_get_error(ssl_ftp_con,count);
14362         debug(F111,"ftp cancel_remote","SSL_get_error()",error);
14363         switch (error) {
14364           case SSL_ERROR_NONE:
14365             break;
14366           case SSL_ERROR_WANT_WRITE:
14367           case SSL_ERROR_WANT_READ:
14368           case SSL_ERROR_SYSCALL:
14369 #ifdef NT
14370             {
14371                 int gle = GetLastError();
14372             }
14373 #endif /* NT */
14374           case SSL_ERROR_WANT_X509_LOOKUP:
14375           case SSL_ERROR_SSL:
14376           case SSL_ERROR_ZERO_RETURN:
14377           default:
14378             lostpeer();
14379             return;
14380         }
14381     } else
14382 #endif /* CK_SSL */
14383     {
14384         /*
14385          * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
14386          * after urgent byte rather than before as is protocol now.
14387          */
14388         buf[0] = IAC;
14389         buf[1] = TN_IP;
14390         buf[2] = IAC;
14391         buf[3] = NUL;
14392         if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
14393           perror("cancel");
14394         debug(F101,"ftp cancel_remote send 1","",x);
14395         buf[0] = TN_DM;
14396         x = send(csocket,(SENDARG2TYPE)buf,1,0);
14397         debug(F101,"ftp cancel_remote send 2","",x);
14398     }
14399     x = scommand("ABOR");
14400     debug(F101,"ftp cancel_remote scommand","",x);
14401 #ifdef BSDSELECT
14402     FD_ZERO(&mask);
14403     FD_SET(csocket, &mask);
14404     if (din) {
14405         FD_SET(din, &mask);
14406     }
14407     nfnd = empty(&mask, 10);
14408     debug(F101,"ftp cancel_remote empty","",nfnd);
14409     if ((nfnd) <= 0) {
14410         if (nfnd < 0) {
14411             perror("cancel");
14412         }
14413 #ifdef FTP_PROXY
14414         if (ptabflg)
14415           ftpcode = -1;
14416 #endif /* FTP_PROXY */
14417         lostpeer();
14418     }
14419     debug(F110,"ftp cancel_remote","D",0);
14420     if (din && FD_ISSET(din, &mask)) {
14421         /* Security: No threat associated with this read. */
14422         /* But you can't simply read the TLS data stream  */
14423 #ifdef CK_SSL
14424         if (ssl_ftp_data_active_flag) {
14425             int count, error;
14426             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14427                     /* LOOP */ ;
14428         } else
14429 #endif /* CK_SSL */
14430         {
14431             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14432                 /* LOOP */ ;
14433         }
14434     }
14435     debug(F110,"ftp cancel_remote","E",0);
14436 #else /* BSDSELECT */
14437 #ifdef IBMSELECT
14438     fds[0] = csocket;
14439     fdcnt++;
14440     if (din) {
14441         fds[1] = din;
14442         fdcnt++;
14443     }
14444     nfnd = empty(fds, fdcnt, 10);
14445     debug(F101,"ftp cancel_remote empty","",nfnd);
14446     if ((nfnd) <= 0) {
14447         if (nfnd < 0) {
14448             perror("cancel");
14449         }
14450 #ifdef FTP_PROXY
14451         if (ptabflg)
14452           ftpcode = -1;
14453 #endif /* FTP_PROXY */
14454         lostpeer();
14455     }
14456     debug(F110,"ftp cancel_remote","D",0);
14457     if (din && select(&din, 1,0,0,1) ) {
14458 #ifdef CK_SSL
14459         if (ssl_ftp_data_active_flag) {
14460             int count, error;
14461             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14462                     /* LOOP */ ;
14463         } else
14464 #endif /* CK_SSL */
14465         {
14466             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14467                 /* LOOP */ ;
14468         }
14469     }
14470     debug(F110,"ftp cancel_remote","E",0);
14471 #else /* IBMSELECT */
14472     Some form of select is required.
14473 #endif /* IBMSELECT */
14474 #endif /* BSDSELECT */
14475     if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
14476         debug(F110,"ftp cancel_remote","F",0);
14477         /* 552 needed for NIC style cancel */
14478         getreply(0,-1,-1,ftp_vbm,0);
14479         debug(F110,"ftp cancel_remote","G",0);
14480     }
14481     debug(F110,"ftp cancel_remote","H",0);
14482     getreply(0,-1,-1,ftp_vbm,0);
14483     debug(F110,"ftp cancel_remote","I",0);
14484 #ifdef DEBUG
14485     debtim = xdebtim;
14486 #endif /* DEBUG */
14487 }
14488
14489 static int
14490 fts_dpl(x) int x; {
14491     if (!auth_type
14492 #ifdef OS2
14493          || !ck_crypt_is_installed()
14494 #endif /* OS2 */
14495          ) {
14496         switch ( x ) {
14497           case FPL_PRV:
14498             printf("?Cannot set protection level to PRIVATE\n");
14499             return(0);
14500           case FPL_SAF:
14501             printf("?Cannot set protection level to SAFE\n");
14502             return(0);
14503         }
14504         ftp_dpl = x;
14505         return(1);
14506     }
14507
14508 #ifdef CK_SSL
14509     if (x == FPL_SAF &&
14510         (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
14511         printf("Cannot set protection level to safe\n");
14512         return(0);
14513     }
14514 #endif /* CK_SSL */
14515     /* Start with a PBSZ of 1 meg */
14516     if (x != FPL_CLR) {
14517         if (setpbsz(DEFAULT_PBSZ) < 0)
14518           return(0);
14519     }
14520     y = ftpcmd(x == FPL_CLR ? "PROT C" :
14521                (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
14522     if (y == REPLY_COMPLETE) {
14523         ftp_dpl = x;
14524         return(1);
14525     }
14526     return(0);
14527 }
14528
14529 static int
14530 fts_cpl(x) int x; {
14531     if (!auth_type 
14532 #ifdef OS2
14533          || !ck_crypt_is_installed()
14534 #endif /* OS2 */
14535          ) {
14536         switch ( x ) {
14537           case FPL_PRV:
14538             printf("?Cannot set protection level to PRIVATE\n");
14539             return(0);
14540           case FPL_SAF:
14541             printf("?Cannot set protection level to SAFE\n");
14542             return(0);
14543         }
14544         ftp_cpl = x;
14545         return(1);
14546     }
14547     if (x == FPL_CLR) {
14548         y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
14549         if (y == REPLY_COMPLETE) {
14550             ftp_cpl = x;
14551             return(1);
14552         }
14553         return(0);
14554     }
14555     ftp_cpl = x;
14556     return(1);
14557 }
14558
14559 #ifdef FTP_GSSAPI
14560 static VOID
14561 user_gss_error(maj_stat, min_stat, s)
14562     OM_uint32 maj_stat, min_stat;
14563     char *s;
14564 {
14565     /* a lot of work just to report the error */
14566     OM_uint32 gmaj_stat, gmin_stat, msg_ctx;
14567     gss_buffer_desc msg;
14568     msg_ctx = 0;
14569     while (!msg_ctx) {
14570         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
14571                                        GSS_C_GSS_CODE,
14572                                        GSS_C_NULL_OID,
14573                                        &msg_ctx,
14574                                        &msg
14575                                        );
14576         if ((gmaj_stat == GSS_S_COMPLETE)||
14577             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14578             fprintf(stderr, "GSSAPI error major: %s\n",
14579                     (char*)msg.value);
14580             gss_release_buffer(&gmin_stat, &msg);
14581         }
14582         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14583           break;
14584     }
14585     msg_ctx = 0;
14586     while (!msg_ctx) {
14587         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
14588                                        GSS_C_MECH_CODE,
14589                                        GSS_C_NULL_OID,
14590                                        &msg_ctx,
14591                                        &msg
14592                                        );
14593         if ((gmaj_stat == GSS_S_COMPLETE)||
14594             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14595             fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value);
14596             gss_release_buffer(&gmin_stat, &msg);
14597         }
14598         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14599           break;
14600     }
14601     fprintf(stderr, "GSSAPI error: %s\n", s);
14602 }
14603 #endif /* FTP_GSSAPI */
14604
14605 #ifndef NOMHHOST
14606 #ifdef datageneral
14607 #define NOMHHOST
14608 #else
14609 #ifdef HPUX5WINTCP
14610 #define NOMHHOST
14611 #endif /* HPUX5WINTCP */
14612 #endif /* datageneral */
14613 #endif /* NOMHHOST */
14614
14615 #ifdef INADDRX
14616 static struct in_addr inaddrx;
14617 #endif /* INADDRX */
14618
14619 static char *
14620 ftp_hookup(host, port, tls) char * host; int port; int tls; {
14621     register struct hostent *hp = 0;
14622 #ifdef IP_TOS
14623 #ifdef IPTOS_THROUGHPUT
14624     int tos;
14625 #endif /* IPTOS_THROUGHPUT */
14626 #endif /* IP_TOS */
14627     int s;
14628     GSOCKNAME_T len;
14629     static char hostnamebuf[MAXHOSTNAMELEN];
14630     char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
14631     int  cport;
14632 #ifdef DEBUG
14633     extern int debtim;
14634     int xdebtim;
14635     xdebtim = debtim;
14636     debtim = 1;
14637 #endif /* DEBUG */
14638
14639     debug(F111,"ftp_hookup",host,port);
14640
14641 #ifndef NOHTTP
14642     if (tcp_http_proxy) {
14643         struct servent *destsp;
14644         char *p, *q;
14645
14646         ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
14647         for (p = tcp_http_proxy, q = hostname;
14648              *p != '\0' && *p != ':';
14649              p++, q++
14650              )
14651           *q = *p;
14652         *q = '\0';
14653
14654         if (*p == ':')
14655           p++;
14656         else
14657           p = "http";
14658
14659         destsp = getservbyname(p,"tcp");
14660         if (destsp)
14661           cport = ntohs(destsp->s_port);
14662         else if (p) {
14663           cport = atoi(p);
14664         } else
14665           cport = 80;
14666     } else
14667 #endif /* NOHTTP */
14668     {
14669         ckstrncpy(hostname,host,MAXHOSTNAMELEN);
14670         cport = port;
14671     }
14672     memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
14673     hisctladdr.sin_addr.s_addr = inet_addr(host);
14674     if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
14675     {
14676         debug(F110,"ftp hookup A",hostname,0);
14677         hisctladdr.sin_family = AF_INET;
14678         ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
14679     } else {
14680         debug(F110,"ftp hookup B",hostname,0);
14681         hp = gethostbyname(hostname);
14682 #ifdef HADDRLIST
14683         hp = ck_copyhostent(hp);        /* make safe copy that won't change */
14684 #endif /* HADDRLIST */
14685         if (hp == NULL) {
14686             fprintf(stderr, "ftp: %s: Unknown host\n", host);
14687             ftpcode = -1;
14688 #ifdef DEBUG
14689             debtim = xdebtim;
14690 #endif /* DEBUG */
14691             return((char *) 0);
14692         }
14693         hisctladdr.sin_family = hp->h_addrtype;
14694 #ifdef HADDRLIST
14695         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
14696                sizeof(hisctladdr.sin_addr));
14697 #else /* HADDRLIST */
14698         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
14699                sizeof(hisctladdr.sin_addr));
14700 #endif /* HADDRLIST */
14701         ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
14702     }
14703     debug(F110,"ftp hookup C",hostnamebuf,0);
14704     ftp_host = hostnamebuf;
14705     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14706     debug(F101,"ftp hookup socket","",s);
14707     if (s < 0) {
14708         perror("ftp: socket");
14709         ftpcode = -1;
14710 #ifdef DEBUG
14711         debtim = xdebtim;
14712 #endif /* DEBUG */
14713         return(0);
14714     }
14715     hisctladdr.sin_port = htons(cport);
14716     errno = 0;
14717
14718 #ifdef COMMENT
14719   printf("hisctladdr=%d\n",sizeof(hisctladdr));
14720   printf("hisctladdr.sin_addr=%d\n",sizeof(hisctladdr.sin_addr));
14721   printf("sockaddr_in=%d\n",sizeof(struct sockaddr_in));
14722   printf("hisctladdr.sin_addr.s_addr=%d\n",sizeof(hisctladdr.sin_addr.s_addr));
14723 #endif  /* COMMENT */
14724
14725 #ifdef HADDRLIST
14726     debug(F100,"ftp hookup HADDRLIST","",0);
14727     while
14728 #else
14729     debug(F100,"ftp hookup no HADDRLIST","",0);
14730     if
14731 #endif /* HADDRLIST */
14732       (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
14733           debug(F101,"ftp hookup connect failed","",errno);
14734 #ifdef HADDRLIST
14735           if (hp && hp->h_addr_list[1]) {
14736               int oerrno = errno;
14737
14738               fprintf(stderr, "ftp: connect to address %s: ",
14739                       inet_ntoa(hisctladdr.sin_addr));
14740               errno = oerrno;
14741               perror((char *) 0);
14742               hp->h_addr_list++;
14743               memcpy((char *)&hisctladdr.sin_addr,
14744                      hp->h_addr_list[0],
14745                      sizeof(hisctladdr.sin_addr));
14746               fprintf(stdout, "Trying %s...\n",
14747                       inet_ntoa(hisctladdr.sin_addr));
14748 #ifdef TCPIPLIB
14749               socket_close(s);
14750 #else /* TCPIPLIB */
14751               close(s);
14752 #endif /* TCPIPLIB */
14753               s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14754               if (s < 0) {
14755                   perror("ftp: socket");
14756                   ftpcode = -1;
14757 #ifdef DEBUG
14758                   debtim = xdebtim;
14759 #endif /* DEBUG */
14760                   return(0);
14761               }
14762               continue;
14763           }
14764 #endif /* HADDRLIST */
14765           perror("ftp: connect");
14766           ftpcode = -1;
14767           goto bad;
14768       }
14769     debug(F100,"ftp hookup connect ok","",0);
14770
14771     len = sizeof (myctladdr);
14772     errno = 0;
14773     if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
14774         debug(F101,"ftp hookup getsockname failed","",errno);
14775         perror("ftp: getsockname");
14776         ftpcode = -1;
14777         goto bad;
14778     }
14779     debug(F100,"ftp hookup getsockname ok","",0);
14780
14781 #ifndef NOHTTP
14782     if (tcp_http_proxy) {
14783 #ifdef OS2
14784         char * agent = "Kermit 95";     /* Default user agent */
14785 #else
14786         char * agent = "C-Kermit";
14787 #endif /* OS2 */
14788
14789         if (http_connect(s,agent,NULL,
14790                          tcp_http_proxy_user,
14791                          tcp_http_proxy_pwd,
14792                          0,
14793                          proxyhost
14794                          ) < 0) {
14795             char * foo = NULL;
14796 #ifdef TCPIPLIB
14797             socket_close(s);
14798 #else /* TCPIPLIB */
14799             close(s);
14800 #endif /* TCPIPLIB */
14801
14802             while (foo == NULL && tcp_http_proxy != NULL ) {
14803
14804                 if (tcp_http_proxy_errno == 401 ||
14805                      tcp_http_proxy_errno == 407 ) {
14806                     char uid[UIDBUFLEN];
14807                     char pwd[PWDSIZ];
14808                     struct txtbox tb[2];
14809                     int ok;
14810
14811                     tb[0].t_buf = uid;
14812                     tb[0].t_len = UIDBUFLEN;
14813                     tb[0].t_lbl = "Proxy Userid: ";
14814                     tb[0].t_dflt = NULL;
14815                     tb[0].t_echo = 1;
14816                     tb[1].t_buf = pwd;
14817                     tb[1].t_len = 256;
14818                     tb[1].t_lbl = "Proxy Passphrase: ";
14819                     tb[1].t_dflt = NULL;
14820                     tb[1].t_echo = 2;
14821
14822                     ok = uq_mtxt("Proxy Server Authentication Required\n",
14823                                   NULL, 2, tb);
14824
14825                     if (ok && uid[0]) {
14826                         char * proxy_user, * proxy_pwd;
14827
14828                         proxy_user = tcp_http_proxy_user;
14829                         proxy_pwd  = tcp_http_proxy_pwd;
14830
14831                         tcp_http_proxy_user = uid;
14832                         tcp_http_proxy_pwd = pwd;
14833
14834                         foo = ftp_hookup(host, port, 0);
14835
14836                         debug(F110,"ftp_hookup()",foo,0);
14837                         memset(pwd,0,PWDSIZ);
14838                         tcp_http_proxy_user = proxy_user;
14839                         tcp_http_proxy_pwd = proxy_pwd;
14840                     } else
14841                         break;
14842                 } else
14843                     break;
14844             }
14845             if (foo != NULL)
14846               return(foo);
14847             perror("ftp: connect");
14848             ftpcode = -1;
14849             goto bad;
14850         }
14851         ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
14852     }
14853 #endif /* NOHTTP */
14854
14855     csocket = s;
14856
14857 #ifdef CK_SSL
14858     if (tls) {
14859         /* FTP over SSL
14860          * If the connection is over an SSL proxy then the
14861          * auth_type will be NULL.  However, I'm not sure
14862          * whether we should protect the data channel in
14863          * that case or not.
14864          */
14865
14866         debug(F100,"ftp hookup use_tls","",0);
14867         if (!ssl_auth()) {
14868             debug(F100,"ftp hookup ssl_auth failed","",0);
14869             auth_type = NULL;
14870             ftpcode = -1;
14871             csocket = -1;
14872             goto bad;
14873         }
14874         ssl_ftp_proxy = 1;
14875     }
14876 #endif /* CK_SSL */
14877
14878 #ifdef IP_TOS
14879 #ifdef IPTOS_LOWDELAY
14880     tos = IPTOS_LOWDELAY;
14881     if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
14882       perror("ftp: setsockopt TOS (ignored)");
14883 #endif
14884 #endif
14885     if (!quiet)
14886       printf("Connected to %s.\n", host);
14887
14888     /* Read greeting from server */
14889     if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
14890         debug(F100,"ftp hookup bad reply","",0);
14891 #ifdef TCPIPLIB
14892         socket_close(csocket);
14893 #else /* TCPIPLIB */
14894         close(csocket);
14895 #endif /* TCPIPLIB */
14896         ftpcode = -1;
14897         goto bad;
14898     }
14899 #ifdef SO_OOBINLINE
14900     {
14901         int on = 1;
14902         errno = 0;
14903         if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
14904                        sizeof(on)) < 0) {
14905             perror("ftp: setsockopt");
14906             debug(F101,"ftp hookup setsockopt failed","",errno);
14907         }
14908 #ifdef DEBUG
14909         else
14910           debug(F100,"ftp hookup setsockopt ok","",0);
14911 #endif /* DEBUG */
14912     }
14913 #endif /* SO_OOBINLINE */
14914
14915 #ifdef DEBUG
14916     debtim = xdebtim;
14917 #endif /* DEBUG */
14918     return(ftp_host);
14919
14920   bad:
14921     debug(F100,"ftp hookup bad","",0);
14922 #ifdef TCPIPLIB
14923     socket_close(s);
14924 #else /* TCPIPLIB */
14925     close(s);
14926 #endif /* TCPIPLIB */
14927 #ifdef DEBUG
14928     debtim = xdebtim;
14929 #endif /* DEBUG */
14930     csocket = -1;
14931     return((char *)0);
14932 }
14933
14934 static VOID
14935 ftp_init() {
14936     int i, n;
14937
14938     /* The purpose of the initial REST 0 is not clear, but other FTP */
14939     /* clients do it.  In any case, failure of this command is not a */
14940     /* reliable indication that the server does not support Restart. */
14941
14942     okrestart = 0;
14943     if (!noinit) {
14944         n = ftpcmd("REST 0",NULL,0,0,0);
14945         if (n == REPLY_COMPLETE)
14946           okrestart = 1;
14947 #ifdef COMMENT
14948         else if (ftp_deb)
14949           printf("WARNING: Unable to restore file pointer.\n");
14950 #endif /* COMMENT */
14951     }
14952     n = ftpcmd("SYST",NULL,0,0,0);      /* Get server system type */
14953     if (n == REPLY_COMPLETE) {
14954         register char *cp, c = NUL;
14955         cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
14956         if (cp == NULL)
14957           cp = ckstrchr(ftp_reply_str+4,'\r');
14958         if (cp) {
14959             if (cp[-1] == '.')
14960               cp--;
14961             c = *cp;                    /* Save this char */
14962             *cp = '\0';                 /* Replace it with NUL */
14963         }
14964         if (!quiet)
14965           printf("Remote system type is %s.\n",ftp_reply_str+4);
14966         ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
14967         if (cp)                         /* Put back saved char */
14968           *cp = c;
14969     }
14970     alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
14971
14972     if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
14973     else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
14974     else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
14975     else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
14976     else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
14977     else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
14978     else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
14979
14980 #ifdef FTP_PROXY
14981     unix_proxy = 0;
14982     if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
14983 #endif /* FTP_PROXY */
14984
14985     if (ftp_cmdlin && ftp_xfermode == XMODE_M)
14986       ftp_typ = binary;                 /* Type given on command line */
14987     else                                /* Otherwise set it automatically */
14988       ftp_typ = alike ? FTT_BIN : FTT_ASC;
14989     changetype(ftp_typ,0);              /* Change to this type */
14990     g_ftp_typ = ftp_typ;                /* Make it the global type */
14991     if (!quiet)
14992       printf("Default transfer mode is %s\n",
14993              ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
14994              );
14995     for (i = 0; i < 16; i++)            /* Init server FEATure table */
14996       sfttab[i] = 0;
14997     if (!noinit) {
14998         n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
14999 #ifdef COMMENT
15000         if (n != REPLY_COMPLETE)
15001           printf("WARNING: Server does not accept MODE S(TREAM)\n");
15002 #endif /* COMMENT */
15003         n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
15004 #ifdef COMMENT
15005         if (n != REPLY_COMPLETE)
15006           printf("WARNING: Server does not accept STRU F(ILE)\n");
15007 #endif /* COMMENT */
15008         if (featok) {
15009             n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
15010             if (n == REPLY_COMPLETE) {
15011                 debug(F101,"ftp_init FEAT","",sfttab[0]);
15012                 if (deblog || ftp_deb) {
15013                     int i;
15014                     for (i = 1; i < 16 && i < nfeattab; i++) {
15015                         debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
15016                         if (ftp_deb)
15017                           printf("  Server %s %s\n",
15018                                  sfttab[i] ? "supports" : "does not support",
15019                                  feattab[i].kwd
15020                                  );
15021                     }
15022                     /* Deal with disabled MLST opts here if necessary */
15023                     /* But why would it be? */
15024                 }
15025             }
15026         }
15027     }
15028 }
15029
15030 static int
15031 ftp_login(host) char * host; {          /* (also called from ckuusy.c) */
15032     static char ftppass[PASSBUFSIZ]="";
15033     char tmp[PASSBUFSIZ];
15034     char *user = NULL, *pass = NULL, *acct = NULL;
15035     int n, aflag = 0;
15036     extern char uidbuf[];
15037     extern char pwbuf[];
15038     extern int  pwflg, pwcrypt;
15039
15040     debug(F111,"ftp_login",ftp_logname,ftp_log);
15041
15042     if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
15043       anonymous = 1;
15044     if (!ckstrcmp(ftp_logname,"ftp",-1,0))
15045       anonymous = 1;
15046
15047 #ifdef FTP_SRP
15048     if (auth_type && !strcmp(auth_type, "SRP")) {
15049         user = srp_user;
15050         pass = srp_pass;
15051         acct = srp_acct;
15052     } else
15053 #endif /* FTP_SRP */
15054       if (anonymous) {
15055           user = "anonymous";
15056           if (ftp_tmp) {                /* They gave a password */
15057               pass = ftp_tmp;
15058           } else if (ftp_apw) {         /* SET FTP ANONYMOUS-PASSWORD */
15059               pass = ftp_apw;
15060           } else {                      /* Supply user@host */
15061               ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
15062               pass = tmp;
15063           }
15064           debug(F110,"ftp anonymous",pass,0);
15065       } else {
15066 #ifdef USE_RUSERPASS
15067           if (ruserpass(host, &user, &pass, &acct) < 0) {
15068               ftpcode = -1;
15069               return(0);
15070           }
15071 #endif /* USE_RUSERPASS */
15072           if (ftp_logname) {
15073               user = ftp_logname;
15074               pass = ftp_tmp;
15075           } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
15076               user = uidbuf;
15077               if (ftp_tmp) {
15078                   pass = ftp_tmp;
15079               } else if (pwbuf[0] && pwflg) {
15080                   ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
15081 #ifdef OS2
15082                   if ( pwcrypt )
15083                       ck_encrypt((char *)ftppass);
15084 #endif /* OS2 */
15085                   pass = ftppass;
15086               }
15087           }
15088           acct = ftp_acc;
15089           while (user == NULL) {
15090               char *myname, prompt[PROMPTSIZ];
15091               int ok;
15092
15093               myname = whoami();
15094               if (myname)
15095                 ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
15096                           NULL,NULL,NULL,NULL,NULL,NULL,NULL);
15097               else
15098                 ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
15099               tmp[0] = '\0';
15100               
15101               ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
15102                           DEFAULT_UQ_TIMEOUT);
15103               if (!ok || *tmp == '\0')
15104                 user = myname;
15105               else
15106                 user = brstrip(tmp);
15107           }
15108       }
15109     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15110     if (n == REPLY_COMPLETE) {
15111         /* determine if we need to send a dummy password */
15112         if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
15113           ftpcmd("PASS dummy",NULL,0,0,1);
15114     } else if (n == REPLY_CONTINUE) {
15115 #ifdef CK_ENCRYPTION
15116         int oldftp_cpl;
15117 #endif /* CK_ENCRYPTION */
15118
15119         if (pass == NULL) {
15120             int ok;
15121             setint();
15122             ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
15123                         DEFAULT_UQ_TIMEOUT);
15124             if (ok)
15125                 pass = brstrip(ftppass);
15126         }
15127
15128 #ifdef CK_ENCRYPTION
15129         oldftp_cpl = ftp_cpl;
15130         ftp_cpl = FPL_PRV;
15131 #endif /* CK_ENCRYPTION */
15132         n = ftpcmd("PASS",pass,-1,-1,1);
15133         if (!anonymous && pass) {
15134             char * p = pass;
15135             while (*p++) *(p-1) = NUL;
15136             makestr(&ftp_tmp,NULL);
15137         }
15138 #ifdef CK_ENCRYPTION
15139         /* level may have changed */
15140         if (ftp_cpl == FPL_PRV)
15141           ftp_cpl = oldftp_cpl;
15142 #endif /* CK_ENCRYPTION */
15143     }
15144     if (n == REPLY_CONTINUE) {
15145         aflag++;
15146         if (acct == NULL) {
15147             static char ftpacct[80];
15148             int ok;
15149             setint();
15150             ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
15151                         DEFAULT_UQ_TIMEOUT);
15152             if (ok)
15153                 acct = brstrip(ftpacct);
15154         }
15155         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15156     }
15157     if (n != REPLY_COMPLETE) {
15158         fprintf(stderr, "FTP login failed.\n");
15159         if (haveurl)
15160           doexit(BAD_EXIT,-1);
15161         return(0);
15162     }
15163     if (!aflag && acct != NULL) {
15164         ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15165     }
15166     makestr(&ftp_logname,user);
15167     loggedin = 1;
15168 #ifdef LOCUS
15169     /* Unprefixed file management commands go to server */
15170     if (autolocus && !ftp_cmdlin) {
15171         setlocus(0,1);
15172     }
15173 #endif /* LOCUS */
15174     ftp_init();
15175
15176     if (anonymous && !quiet) {
15177         printf(" Logged in as anonymous (%s)\n",pass);
15178         memset(pass, 0, strlen(pass));
15179     }
15180     if (ftp_rdir) {
15181         if (doftpcwd(ftp_rdir,-1) < 1)
15182           doexit(BAD_EXIT,-1);
15183     }
15184
15185 #ifdef FTP_PROXY
15186     if (proxy)
15187       return(1);
15188 #endif /* FTP_PROXY */
15189     return(1);
15190 }
15191
15192 static int
15193 ftp_reset() {
15194     int rc;
15195 #ifdef BSDSELECT
15196     int nfnd = 1;
15197     fd_set mask;
15198     FD_ZERO(&mask);
15199     while (nfnd > 0) {
15200         FD_SET(csocket, &mask);
15201         if ((nfnd = empty(&mask,0)) < 0) {
15202             perror("reset");
15203             ftpcode = -1;
15204             lostpeer();
15205             return(0);
15206         } else if (nfnd) {
15207             getreply(0,-1,-1,ftp_vbm,0);
15208         }
15209     }
15210 #else /* BSDSELECT */
15211 #ifdef IBMSELECT
15212     int nfnd = 1;
15213     while (nfnd > 0) {
15214         if ((nfnd = empty(&csocket,1,0)) < 0) {
15215             perror("reset");
15216             ftpcode = -1;
15217             lostpeer();
15218             return(0);
15219         } else if (nfnd) {
15220             getreply(0,-1,-1,ftp_vbm,0);
15221         }
15222     }
15223 #endif /* IBMSELECT */
15224 #endif /* BSDSELECT */
15225     rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
15226     if (rc > 0)
15227       loggedin = 0;
15228     return(rc);
15229 }
15230
15231 static int
15232 ftp_rename(from, to) char * from, * to; {
15233     int lcs = -1, rcs = -1;
15234 #ifndef NOCSETS
15235     if (ftp_xla) {
15236         lcs = ftp_csl;
15237         if (lcs < 0) lcs = fcharset;
15238         rcs = ftp_csx;
15239         if (rcs < 0) rcs = ftp_csr;
15240     }
15241 #endif /* NOCSETS */
15242     if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
15243         return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
15244     }
15245     return(0);                          /* Failure */
15246 }
15247
15248 static int
15249 ftp_umask(mask) char * mask; {
15250     int rc;
15251     rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
15252     return(rc);
15253 }
15254
15255 static int
15256 ftp_user(user,pass,acct) char * user, * pass, * acct; {
15257     int n = 0, aflag = 0;
15258     char pwd[PWDSIZ];
15259
15260     if (!auth_type && ftp_aut) {
15261 #ifdef FTP_SRP
15262         if (ck_srp_is_installed()) {
15263             if (srp_ftp_auth( NULL, user, pass)) {
15264                 makestr(&pass,srp_pass);
15265             }
15266         }
15267 #endif /* FTP_SRP */
15268     }
15269     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15270     if (n == REPLY_COMPLETE)
15271       n = ftpcmd("PASS dummy",NULL,0,0,1);
15272     else if (n == REPLY_CONTINUE) {
15273 #ifdef CK_ENCRYPTION
15274         int oldftp_cpl;
15275 #endif /* CK_ENCRYPTION */
15276         if (pass == NULL || !pass[0]) {
15277             int ok;
15278             pwd[0] = '\0';
15279             setint();
15280             ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
15281                         DEFAULT_UQ_TIMEOUT);
15282             if (ok)
15283                 pass = brstrip(pwd);
15284         }
15285
15286 #ifdef CK_ENCRYPTION
15287         if ((oldftp_cpl = ftp_cpl) == PROT_S)
15288           ftp_cpl = PROT_P;
15289 #endif /* CK_ENCRYPTION */
15290         n = ftpcmd("PASS",pass,-1,-1,1);
15291         memset(pass, 0, strlen(pass));
15292 #ifdef CK_ENCRYPTION
15293         /* level may have changed */
15294         if (ftp_cpl == PROT_P)
15295           ftp_cpl = oldftp_cpl;
15296 #endif /* CK_ENCRYPTION */
15297     }
15298     if (n == REPLY_CONTINUE) {
15299         if (acct == NULL || !acct[0]) {
15300             int ok;
15301             pwd[0] = '\0';
15302             setint();
15303             ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
15304                         DEFAULT_UQ_TIMEOUT);
15305             if (ok)
15306                 acct = pwd;
15307         }
15308         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15309         aflag++;
15310     }
15311     if (n != REPLY_COMPLETE) {
15312         printf("Login failed.\n");
15313         return(0);
15314     }
15315     if (!aflag && acct != NULL && acct[0]) {
15316         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15317     }
15318     if (n == REPLY_COMPLETE) {
15319         makestr(&ftp_logname,user);
15320         loggedin = 1;
15321         ftp_init();
15322         return(1);
15323     }
15324     return(0);
15325 }
15326
15327 char *
15328 ftp_authtype() {
15329     if (!connected)
15330       return("NULL");
15331     return(auth_type ? auth_type : "NULL");
15332 }
15333
15334 char *
15335 ftp_cpl_mode() {
15336     switch (ftp_cpl) {
15337       case FPL_CLR:
15338         return("clear");
15339       case FPL_SAF:
15340         return("safe");
15341       case FPL_PRV:
15342         return("private");
15343       case FPL_CON:
15344         return("confidential");
15345       default:
15346         return("(error)");
15347     }
15348 }
15349
15350 char *
15351 ftp_dpl_mode() {
15352     switch (ftp_dpl) {
15353       case FPL_CLR:
15354         return("clear");
15355       case FPL_SAF:
15356         return("safe");
15357       case FPL_PRV:
15358         return("private");
15359       case FPL_CON:
15360         return("confidential");
15361       default:
15362         return("(error)");
15363     }
15364 }
15365
15366
15367 /* remote_files() */
15368 /*
15369    Returns next remote filename on success;
15370    NULL on error or no more files with global rfrc set to:
15371      -1: Bad argument
15372      -2: Server error response to NLST, e.g. file not found
15373      -3: No more files
15374      -9: Internal error
15375 */
15376 #define FTPNAMBUFLEN CKMAXPATH+1024
15377
15378 /* Check: ckmaxfiles CKMAXOPEN */
15379
15380 #define MLSDEPTH 128                    /* Stack of open temp files */
15381 static int mlsdepth = 0;                /* Temp file stack depth */
15382 static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
15383 static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
15384
15385 static VOID
15386 mlsreset() {                            /* Reset MGET temp-file stack */
15387     int i;
15388     for (i = 0; i <= mlsdepth; i++) {
15389         if (tmpfilptr[i]) {
15390             fclose(tmpfilptr[i]);
15391             tmpfilptr[i] = NULL;
15392             if (tmpfilnam[i]) {
15393 #ifdef OS2
15394                 unlink(tmpfilnam[i]);
15395 #endif /* OS2 */
15396                 free(tmpfilnam[i]);
15397             }
15398         }
15399     }
15400     mlsdepth = 0;
15401 }
15402
15403 static CHAR *
15404 #ifdef CK_ANSIC
15405 remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
15406 #else /* CK_ANSIC */
15407 remote_files(new_query, arg, pattern, proxy_switch)
15408     int new_query;
15409     CHAR * arg;                         /* That we send to the server */
15410     CHAR * pattern;                     /* That we use locally */
15411     int proxy_switch;
15412 #endif /* CK_ANSIC */
15413 /* remote_files */ {
15414     static CHAR buf[FTPNAMBUFLEN];
15415     CHAR *cp, *whicharg;
15416     char * cdto = NULL;
15417     char * p;
15418     int i, x, forced = 0;
15419     int lcs = 0, rcs = 0, xlate = 0;
15420
15421     debug(F101,"ftp remote_files new_query","",new_query);
15422     debug(F110,"ftp remote_files arg",arg,0);
15423     debug(F110,"ftp remote_files pattern",pattern,0);
15424
15425     rfrc = -1;
15426     if (pattern)                        /* Treat empty pattern same as NULL */
15427       if (!*pattern)
15428         pattern = NULL;
15429     if (arg)                            /* Ditto for arg */
15430       if (!*arg)
15431         arg = NULL;
15432
15433   again:
15434
15435     if (new_query) {
15436         if (tmpfilptr[mlsdepth]) {
15437             fclose(tmpfilptr[mlsdepth]);
15438             tmpfilptr[mlsdepth] = NULL;
15439 #ifdef OS2
15440             if (!ftp_deb && !deblog)
15441               unlink(tmpfilnam[mlsdepth]);
15442 #endif /* OS2 */
15443         }
15444     }
15445     if (tmpfilptr[mlsdepth] == NULL) {
15446         extern char * tempdir;
15447         char * p;
15448         debug(F110,"ftp remote_files tempdir",tempdir,0);
15449         if (tempdir) {
15450             p = tempdir;
15451         } else {
15452 #ifdef OS2
15453 #ifdef NT
15454             p = getenv("K95TMP");
15455 #else
15456             p = getenv("K2TMP");
15457 #endif /* NT */
15458             if (!p)
15459 #endif /* OS2 */
15460               p = getenv("CK_TMP");
15461             if (!p)
15462               p = getenv("TMPDIR");
15463             if (!p) p = getenv("TEMP");
15464             if (!p) p = getenv("TMP");
15465 #ifdef OS2ORUNIX
15466             if (p) {
15467                 int len = strlen(p);
15468                 if (p[len-1] != '/'
15469 #ifdef OS2
15470                     && p[len-1] != '\\'
15471 #endif /* OS2 */
15472                      ) {
15473                     static char foo[CKMAXPATH];
15474                     ckstrncpy(foo,p,CKMAXPATH);
15475                     ckstrncat(foo,"/",CKMAXPATH);
15476                     p = foo;
15477                 }
15478             } else
15479 #else /* OS2ORUNIX */
15480             if (!p)
15481 #endif /* OS2ORUNIX */
15482 #ifdef UNIX                             /* Systems that have a standard */
15483                 p = "/tmp/";            /* temporary directory... */
15484 #else
15485 #ifdef datageneral
15486             p = ":TMP:";
15487 #else
15488             p = "";
15489 #endif /* datageneral */
15490 #endif /* UNIX */
15491         }
15492         debug(F110,"ftp remote_files p",p,0);
15493
15494         /* Get temp file */
15495
15496         if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
15497             ckmakmsg((char *)tmpfilnam[mlsdepth],
15498                      CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
15499         } else {
15500             printf("?Malloc failure: remote_files()\n");
15501             return(NULL);
15502         }
15503
15504 #ifdef NT
15505         {
15506             char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
15507             if ( tmpfil )
15508                 ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
15509         }
15510 #else /* NT */
15511 #ifdef MKTEMP
15512 #ifdef MKSTEMP
15513         x = mkstemp((char *)tmpfilnam[mlsdepth]);
15514         if (x > -1) close(x);           /* We just want the name. */
15515 #else
15516         mktemp((char *)tmpfilnam[mlsdepth]);
15517 #endif /* MKSTEMP */
15518         /* if no mktmpnam() the name will just be "ckXXXXXX"... */
15519 #endif /* MKTEMP */
15520 #endif /* NT */
15521
15522         debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
15523               tmpfilnam[mlsdepth],mlsdepth);
15524
15525 #ifdef FTP_PROXY
15526         if (proxy_switch) {
15527             pswitch(!proxy);
15528         }
15529 #endif /* FTP_PROXY */
15530
15531         debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
15532         debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
15533         debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
15534
15535 #ifndef NOCSETS
15536         xlate = ftp_xla;                /* SET FTP CHARACTER-SET-TRANSLATION */
15537         if (xlate) {                    /* ON? */
15538             lcs = ftp_csl;              /* Local charset */
15539             if (lcs < 0) lcs = fcharset;
15540             if (lcs < 0) xlate = 0;
15541         }
15542         if (xlate) {                    /* Still ON? */
15543             rcs = ftp_csx;              /* Remote (Server) charset */
15544             if (rcs < 0) rcs = ftp_csr;
15545             if (rcs < 0) xlate = 0;
15546         }
15547 #endif /* NOCSETS */
15548
15549         forced = mgetforced;            /* MGET method forced? */
15550         if (!forced || !mgetmethod)     /* Not forced... */
15551           mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
15552               SND_MLS :
15553               SND_NLS; 
15554 /*                                           
15555   User's Command:                 Result:
15556     mget /nlst                     NLST (NULL)
15557     mget /nlst foo                 NLST foo
15558     mget /nlst *.txt               NLST *.txt 
15559     mget /nlst /match:*.txt        NLST (NULL)
15560     mget /nlst /match:*.txt  foo   NLST foo   
15561     mget /mlsd                     MLSD (NULL)
15562     mget /mlsd foo                 MLSD foo
15563     mget /mlsd *.txt               MLSD (NULL)
15564     mget /mlsd /match:*.txt        MLSD (NULL)
15565     mget /mlsd /match:*.txt  foo   MLSD foo
15566 */
15567         x = -1;
15568         while (x < 0) {
15569             if (pattern) {              /* Don't simplify this! */
15570                 whicharg = arg;
15571             } else if (mgetmethod == SND_MLS) {
15572                 if (arg)
15573                   whicharg = iswild((char *)arg) ? NULL : arg;
15574                 else
15575                   whicharg = NULL;
15576             } else {
15577                 whicharg = arg;
15578             }
15579             debug(F110,"ftp remote_files mgetmethod",
15580                   mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
15581             debug(F110,"ftp remote_files whicharg",whicharg,0);
15582
15583             x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
15584                             (char *)tmpfilnam[mlsdepth],
15585                             (char *)whicharg,
15586                             "wb",
15587                             0,
15588                             0,
15589                             NULL,
15590                             xlate,
15591                             lcs,
15592                             rcs
15593                             );
15594             if (x < 0) {                /* Chosen method wasn't accepted */
15595                 if (forced) {
15596                     if (ftpcode > 500 && ftpcode < 505 && !quiet)
15597                       printf("?%s: Not supported by server\n",
15598                              mgetmethod == SND_MLS ? "MLSD" : "NLST"
15599                              );
15600                     rfrc = -2;          /* Fail */
15601                     return(NULL);
15602                 }
15603                 /* Not forced - if MLSD failed, try NLST */
15604                 if (mgetmethod == SND_MLS) {  /* Server lied about MLST */
15605                     sfttab[SFT_MLST] = 0;     /* So disable it */
15606                     mlstok = 0;               /* and */
15607                     mgetmethod = SND_NLS;     /* try NLST */
15608                     continue;
15609                 }
15610                 rfrc = -2;
15611                 return(NULL);
15612             }
15613         }
15614 #ifdef FTP_PROXY
15615         if (proxy_switch) {
15616             pswitch(!proxy);
15617         }
15618 #endif /* FTP_PROXY */
15619         tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
15620 #ifndef OS2
15621         if (tmpfilptr[mlsdepth]) {
15622             if (!ftp_deb && !deblog)
15623               unlink(tmpfilnam[mlsdepth]);
15624         }
15625 #endif /* OS2 */
15626       notemp:
15627         if (!tmpfilptr[mlsdepth]) {
15628             debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
15629             if ((!dpyactive || ftp_deb))
15630               printf("?Can't find list of remote files, oops\n");
15631             rfrc = -9;
15632             return(NULL);
15633         }
15634         if (ftp_deb)
15635           printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
15636     }
15637     buf[0] = NUL;
15638     buf[FTPNAMBUFLEN-1] = NUL;
15639     buf[FTPNAMBUFLEN-2] = NUL;
15640
15641     /* We have to redo all this because the first time was only for */
15642     /* for getting the file list, now it's for getting each file */
15643
15644     if (arg && mgetmethod == SND_MLS) { /* MLSD */
15645         if (!pattern && iswild((char *)arg)) {
15646             pattern = arg;              /* Wild arg is really a pattern */
15647             if (pattern)
15648               if (!*pattern)
15649                 pattern = NULL;
15650             arg = NULL;                 /* and not an arg */
15651         }
15652         if (new_query) {                /* Initial query? */
15653             cdto = (char *)arg;         /* (nonwild) arg given? */
15654             if (cdto)
15655               if (!*cdto)
15656                 cdto = NULL;
15657             if (cdto)                   /* If so, then CD to it */
15658               doftpcwd(cdto,0);
15659         }
15660     }
15661     new_query = 0;
15662
15663     if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
15664         fclose(tmpfilptr[mlsdepth]);
15665         tmpfilptr[mlsdepth] = NULL;
15666
15667 #ifdef OS2
15668         if (!ftp_deb && !deblog)
15669           unlink(tmpfilnam[mlsdepth]);
15670 #endif /* OS2 */
15671         if (ftp_deb && !deblog) {
15672             printf("(Temporary file %s NOT deleted)\n",
15673                    (char *)tmpfilnam[mlsdepth]);
15674         }
15675         if (mlsdepth <= 0) {            /* EOF at depth 0 */
15676             rfrc = -3;                  /* means we're done */
15677             return(NULL);
15678         }
15679         printf("POPPING(%d)...\n",mlsdepth-1); 
15680         if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
15681         mlsdepth--;
15682         doftpcdup();
15683         zchdir("..");                   /* <-- Not portable */
15684         goto again;
15685     }
15686     if (buf[FTPNAMBUFLEN-1]) {
15687         printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
15688                FTPNAMBUFLEN
15689                );
15690         debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
15691         return(NULL);
15692     }
15693     /* debug(F110,"ftp remote_files buf 1",buf,0); */
15694     if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
15695       *cp = '\0';
15696     if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
15697       *cp = '\0';
15698     debug(F110,"ftp remote_files buf",buf,0);
15699     rfrc = 0;
15700
15701     if (ftp_deb)
15702       printf("[%s]\n",(char *)buf);
15703
15704     havesize = (CK_OFF_T)-1;            /* Initialize file facts... */
15705     havetype = 0;
15706     makestr(&havemdtm,NULL);
15707     p = (char *)buf;
15708
15709     if (mgetmethod == SND_NLS) {        /* NLST... */
15710         if (pattern) {
15711             if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15712               goto again;
15713         }
15714     } else {                            /* MLSD... */
15715         p = parsefacts((char *)buf);
15716         switch (havetype) {
15717           case FTYP_FILE:               /* File: Get it if it matches */
15718             if (pattern) {
15719                 if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15720                   goto again;
15721             }
15722             break;
15723           case FTYP_CDIR:               /* Current directory */
15724           case FTYP_PDIR:               /* Parent directory */
15725             goto again;                 /* Skip */
15726           case FTYP_DIR:                /* (Sub)Directory */
15727             if (!recursive)             /* If not /RECURSIVE */
15728               goto again;               /* Skip */
15729             if (mlsdepth < MLSDEPTH) {
15730                 char * p2 = NULL;
15731                 mlsdepth++;
15732                 printf("RECURSING [%s](%d)...\n",p,mlsdepth); 
15733                 if (doftpcwd(p,0) > 0) {
15734                     int x;
15735                     if (!ckstrchr(p,'/')) {
15736                         /* zmkdir() needs dirsep */
15737                         if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
15738                             strcpy(p2,p);       /* SAFE */
15739                             strcat(p2,"/");     /* SAFE */
15740                             p = p2;
15741                         }
15742                     }
15743 #ifdef NOMKDIR
15744                     x = -1;
15745 #else
15746                     x = zmkdir(p);
15747 #endif /* NOMKDIR */
15748                     if (x > -1) {
15749                         zchdir(p);
15750                         p = (char *)remote_files(1,arg,pattern,0);
15751                         if (p2) free(p2);
15752                     } else {
15753                         printf("?mkdir failed: [%s] Depth=%d\n",
15754                                p,
15755                                mlsdepth
15756                                );
15757                         mlsreset();
15758                         if (p2) free(p2);
15759                         return(NULL);
15760                     }
15761                 } else {
15762                     printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
15763                     mlsreset();
15764                     return(NULL);
15765                 }
15766             } else {
15767                 printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
15768                        mlsdepth
15769                        );
15770                 mlsreset();
15771                 return(NULL);
15772             }
15773         }
15774     }
15775
15776 #ifdef DEBUG
15777     if (deblog) {
15778         debug(F101,"remote_files havesize","",havesize);
15779         debug(F101,"remote_files havetype","",havetype);
15780         debug(F110,"remote_files havemdtm",havemdtm,0); 
15781         debug(F110,"remote_files name",p,0);    
15782     }
15783 #endif /* DEBUG */
15784     return((CHAR *)p);
15785 }
15786
15787 /* N O T  P O R T A B L E !!! */
15788
15789 #if (SIZEOF_SHORT == 4)
15790 typedef unsigned short ftp_uint32;
15791 typedef short ftp_int32;
15792 #else
15793 #if (SIZEOF_INT == 4)
15794 typedef unsigned int ftp_uint32;
15795 typedef int ftp_int32;
15796 #else
15797 #if (SIZEOF_LONG == 4)
15798 typedef ULONG ftp_uint32;
15799 typedef long ftp_int32;
15800 #endif
15801 #endif
15802 #endif
15803
15804 /* Perhaps use these in general, certainly use them for GSSAPI */
15805
15806 #ifndef looping_write
15807 #define ftp_int32 int
15808 #define ftp_uint32 unsigned int
15809 static int
15810 looping_write(fd, buf, len)
15811     int fd;
15812     register CONST char *buf;
15813     int len;
15814 {
15815     int cc;
15816     register int wrlen = len;
15817     do {
15818         cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
15819         if (cc < 0) {
15820             if (errno == EINTR)
15821               continue;
15822             return(cc);
15823         } else {
15824             buf += cc;
15825             wrlen -= cc;
15826         }
15827     } while (wrlen > 0);
15828     return(len);
15829 }
15830 #endif
15831 #ifndef looping_read
15832 static int
15833 looping_read(fd, buf, len)
15834     int fd;
15835     register char *buf;
15836     register int len;
15837 {
15838     int cc, len2 = 0;
15839
15840     do {
15841         cc = recv(fd, (char *)buf, len,0);
15842         if (cc < 0) {
15843             if (errno == EINTR)
15844               continue;
15845             return(cc);                 /* errno is already set */
15846         } else if (cc == 0) {
15847             return(len2);
15848         } else {
15849             buf += cc;
15850             len2 += cc;
15851             len -= cc;
15852         }
15853     } while (len > 0);
15854     return(len2);
15855 }
15856 #endif /* looping_read */
15857
15858 #define ERR -2
15859
15860 #ifdef COMMENT
15861 static
15862 secure_putbyte(fd, c) int fd; CHAR c; {
15863     int ret;
15864
15865     ucbuf[nout++] = c;
15866     if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
15867         nout = 0;
15868         if (!ftpissecure())
15869           ret = send(fd, (SENDARG2TYPE)ucbuf,
15870                      (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
15871         else
15872           ret = secure_putbuf(fd,
15873                               ucbuf,
15874                               (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
15875                               );
15876         return(ret?ret:c);
15877     }
15878     return(c);
15879 }
15880 #endif /* COMMENT */
15881
15882 /* returns:
15883  *       0  on success
15884  *      -1  on error (errno set)
15885  *      -2  on security error
15886  */
15887 static int
15888 secure_flush(fd) int fd; {
15889     int rc = 0;
15890     int len = 0;
15891
15892     if (nout > 0) {
15893         len = nout;
15894         if (!ftpissecure()) {
15895             rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
15896             nout = 0;
15897             goto xflush;
15898         } else {
15899             rc = secure_putbuf(fd, ucbuf, nout);
15900             if (rc)
15901               goto xflush;
15902         }
15903     }
15904     rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
15905
15906   xflush:
15907     if (rc > -1 && len > 0 && fdispla != XYFD_B) {
15908         spackets++;
15909         spktl = len;
15910         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
15911     }
15912     return(rc);
15913 }
15914
15915 #ifdef COMMENT                          /* (not used) */
15916 /* returns:
15917  *      c>=0  on success
15918  *      -1    on error
15919  *      -2    on security error
15920  */
15921 static int
15922 #ifdef CK_ANSIC
15923 secure_putc(char c, int fd)
15924 #else
15925 secure_putc(c, fd) char c; int fd;
15926 #endif /* CK_ANSIC */
15927 /* secure_putc */ {
15928     return(secure_putbyte(fd, (CHAR) c));
15929 }
15930 #endif /* COMMENT */
15931
15932 /* returns:
15933  *      nbyte on success
15934  *      -1  on error (errno set)
15935  *      -2  on security error
15936  */
15937 static int
15938 #ifdef CK_ANSIC
15939 secure_write(int fd, CHAR * buf, unsigned int nbyte)
15940 #else
15941 secure_write(fd, buf, nbyte)
15942     int fd;
15943     CHAR * buf;
15944     unsigned int nbyte;
15945 #endif /* CK_ANSIC */
15946 {
15947     int ret;
15948
15949 #ifdef FTP_TIMEOUT    
15950     ftp_timed_out = 0;
15951     if (check_data_connection(fd,1) < 0) {
15952         ftp_timed_out = 1;
15953         return(-3);
15954     }
15955 #endif  /* FTP_TIMEOUT */
15956
15957     if (!ftpissecure()) {
15958         if (nout > 0) {
15959             if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
15960               return(ret);
15961             nout = 0;
15962         }
15963         return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
15964     } else {
15965         int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
15966         int bsent = 0;
15967
15968         while (bsent < nbyte) {
15969             int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
15970                         (ucbuflen - nout) : (nbyte - bsent));
15971 #ifdef DEBUG
15972             if (deblog) {
15973                 debug(F101,"secure_write ucbuflen","",ucbuflen);
15974                 debug(F101,"secure_write ucbufsiz","",ucbufsiz);
15975                 debug(F101,"secure_write bsent","",bsent);
15976                 debug(F101,"secure_write b2cp","",b2cp);
15977             }
15978 #endif /* DEBUG */
15979             memcpy(&ucbuf[nout],&buf[bsent],b2cp);
15980             nout += b2cp;
15981             bsent += b2cp;
15982
15983             if (nout == ucbuflen) {
15984                 nout = 0;
15985                 ret = secure_putbuf(fd, ucbuf, ucbuflen);
15986                 if (ret < 0)
15987                   return(ret);
15988             }
15989         }
15990         return(bsent);
15991     }
15992 }
15993
15994 /* returns:
15995  *       0  on success
15996  *      -1  on error (errno set)
15997  *      -2  on security error
15998  */
15999 static int
16000 #ifdef CK_ANSIC
16001 secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
16002 #else
16003 secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
16004 #endif /* CK_ANSIC */
16005 {
16006     static char *outbuf = NULL;         /* output ciphertext */
16007 #ifdef FTP_SECURITY
16008     static unsigned int bufsize = 0;    /* size of outbuf */
16009 #endif /* FTP_SECURITY */
16010     ftp_int32 length   = 0;
16011     ftp_uint32 net_len = 0;
16012
16013     /* Other auth types go here ... */
16014 #ifdef CK_SSL
16015     if (ssl_ftp_data_active_flag) {
16016         int count, error;
16017
16018         /* there is no need to send an empty buffer when using SSL/TLS */
16019         if ( nbyte == 0 )
16020           return(0);
16021
16022         count = SSL_write(ssl_ftp_data_con, buf, nbyte);
16023         error = SSL_get_error(ssl_ftp_data_con,count);
16024         switch (error) {
16025           case SSL_ERROR_NONE:
16026             return(0);
16027           case SSL_ERROR_WANT_WRITE:
16028           case SSL_ERROR_WANT_READ:
16029           case SSL_ERROR_SYSCALL:
16030 #ifdef NT
16031             {
16032                 int gle = GetLastError();
16033                 if (gle == 0)
16034                   return(0);
16035                 debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
16036             }
16037 #endif /* NT */
16038           case SSL_ERROR_WANT_X509_LOOKUP:
16039           case SSL_ERROR_SSL:
16040           case SSL_ERROR_ZERO_RETURN:
16041           default:
16042             SSL_shutdown(ssl_ftp_data_con);
16043             SSL_free(ssl_ftp_data_con);
16044             ssl_ftp_data_active_flag = 0;
16045             ssl_ftp_data_con = NULL;
16046 #ifdef TCPIPLIB
16047             socket_close(data);
16048 #else /* TCPIPLIB */
16049 #ifdef USE_SHUTDOWN
16050             shutdown(data, 1+1);
16051 #endif /* USE_SHUTDOWN */
16052             close(data);
16053 #endif /* TCPIPLIB */
16054             data = -1;
16055             globaldin = data;
16056             return(-1);
16057         }
16058         return(-1);
16059     }
16060 #endif /* CK_SSL */
16061
16062 #ifdef FTP_SRP
16063     if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
16064         if (bufsize < nbyte + FUDGE_FACTOR) {
16065             if (outbuf?
16066                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16067                 (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16068                 bufsize = nbyte + FUDGE_FACTOR;
16069             } else {
16070                 bufsize = 0;
16071                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16072                 return(ERR);
16073             }
16074         }
16075         if ((length =
16076              srp_encode(ftp_dpl == FPL_PRV,
16077                         (CHAR *) buf,
16078                         (CHAR *) outbuf,
16079                         nbyte
16080                         )
16081              ) < 0) {
16082             secure_error ("srp_encode failed");
16083             return ERR;
16084         }
16085     }
16086 #endif /* FTP_SRP */
16087 #ifdef FTP_KRB4
16088     if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
16089         struct sockaddr_in myaddr, hisaddr;
16090         GSOCKNAME_T len;
16091         len = sizeof(myaddr);
16092         if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16093             secure_error("secure_putbuf: getsockname failed");
16094             return(ERR);
16095         }
16096         len = sizeof(hisaddr);
16097         if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16098             secure_error("secure_putbuf: getpeername failed");
16099             return(ERR);
16100         }
16101         if (bufsize < nbyte + FUDGE_FACTOR) {
16102             if (outbuf ?
16103                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16104                  (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16105                 bufsize = nbyte + FUDGE_FACTOR;
16106             } else {
16107                 bufsize = 0;
16108                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16109                 return(ERR);
16110             }
16111         }
16112         if (ftp_dpl == FPL_PRV) {
16113             length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
16114                                  ftp_sched,
16115 #ifdef KRB524
16116                                  ftp_cred.session,
16117 #else /* KRB524 */
16118                                  &ftp_cred.session,
16119 #endif /* KRB524 */
16120                                  &myaddr,
16121                                  &hisaddr
16122                                  );
16123         } else {
16124             length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
16125 #ifdef KRB524
16126                                  ftp_cred.session,
16127 #else /* KRB524 */
16128                                  &ftp_cred.session,
16129 #endif /* KRB524 */
16130                                  &myaddr,
16131                                  &hisaddr
16132                                  );
16133         }
16134         if (length == -1) {
16135             secure_error("krb_mk_%s failed for KERBEROS_V4",
16136                          ftp_dpl == FPL_PRV ? "priv" : "safe");
16137             return(ERR);
16138         }
16139     }
16140 #endif /* FTP_KRB4 */
16141 #ifdef FTP_GSSAPI
16142     if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
16143         gss_buffer_desc in_buf, out_buf;
16144         OM_uint32 maj_stat, min_stat;
16145         int conf_state;
16146
16147         in_buf.value = buf;
16148         in_buf.length = nbyte;
16149         maj_stat = gss_seal(&min_stat, gcontext,
16150                             (ftp_dpl == FPL_PRV), /* confidential */
16151                             GSS_C_QOP_DEFAULT,
16152                             &in_buf,
16153                             &conf_state,
16154                             &out_buf
16155                             );
16156         if (maj_stat != GSS_S_COMPLETE) {
16157             /* generally need to deal */
16158             /* ie. should loop, but for now just fail */
16159             user_gss_error(maj_stat, min_stat,
16160                            ftp_dpl == FPL_PRV?
16161                            "GSSAPI seal failed":
16162                            "GSSAPI sign failed");
16163             return(ERR);
16164         }
16165         if (bufsize < out_buf.length) {
16166             if (outbuf ?
16167                 (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
16168                 (outbuf = malloc((unsigned) out_buf.length))) {
16169                 bufsize = out_buf.length;
16170             } else {
16171                 bufsize = 0;
16172                 secure_error("%s (in malloc of PROT buffer)",
16173                              ck_errstr());
16174                 return(ERR);
16175             }
16176         }
16177         memcpy(outbuf, out_buf.value, length=out_buf.length);
16178         gss_release_buffer(&min_stat, &out_buf);
16179     }
16180 #endif /* FTP_GSSAPI */
16181     net_len = htonl((ULONG) length);
16182     if (looping_write(fd, (char *)&net_len, 4) == -1)
16183       return(-1);
16184     if (looping_write(fd, outbuf, length) != length)
16185       return(-1);
16186     return(0);
16187 }
16188
16189
16190 /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
16191
16192 static int
16193 secure_getbyte(fd,fc) int fd,fc; {
16194     /* number of chars in ucbuf, pointer into ucbuf */
16195     static unsigned int nin = 0, bufp = 0;
16196     int kerror;
16197     ftp_uint32 length;
16198
16199     if (fc) {
16200         nin = bufp = 0;
16201         ucbuf[0] = NUL;
16202         return(0);
16203     }
16204     if (nin == 0) {
16205         if (iscanceled())
16206           return(-9);
16207
16208 #ifdef FTP_TIMEOUT
16209         if (check_data_connection(fd,0) < 0)
16210           return(-3);
16211 #endif  /* FTP_TIMEOUT */
16212
16213 #ifdef CK_SSL
16214         if (ssl_ftp_data_active_flag) {
16215             int count, error;
16216             count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
16217             error = SSL_get_error(ssl_ftp_data_con,count);
16218 #ifdef DEBUG
16219             if (error != SSL_ERROR_NONE)
16220               debug(F101,"ftp secure_getbyte error","",error);
16221             if (count == 0)
16222               debug(F101,"ftp secure_getbyte count","",count);
16223 #endif  /* DEBUG */
16224             switch (error) {
16225               case SSL_ERROR_NONE:
16226                 if (count > 0) {
16227                     nin = bufp = count;
16228                     rpackets++;
16229                     pktnum++;
16230                     if (fdispla != XYFD_B) {
16231                         rpktl = count;
16232                         ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16233                     }
16234                     break;
16235                 }
16236               case SSL_ERROR_WANT_WRITE:
16237               case SSL_ERROR_WANT_READ:
16238               case SSL_ERROR_SYSCALL:
16239 #ifdef NT
16240                 {
16241                     int gle = GetLastError();
16242                 }
16243 #endif /* NT */
16244               case SSL_ERROR_WANT_X509_LOOKUP:
16245               case SSL_ERROR_SSL:
16246               case SSL_ERROR_ZERO_RETURN:
16247               default:
16248                 nin = bufp = count = 0;
16249                 SSL_shutdown(ssl_ftp_data_con);
16250                 SSL_free(ssl_ftp_data_con);
16251                 ssl_ftp_data_active_flag = 0;
16252                 ssl_ftp_data_con = NULL;
16253 #ifdef TCPIPLIB
16254                 socket_close(data);
16255 #else /* TCPIPLIB */
16256 #ifdef USE_SHUTDOWN
16257                 shutdown(data, 1+1);
16258 #endif /* USE_SHUTDOWN */
16259                 close(data);
16260 #endif /* TCPIPLIB */
16261                 data = -1;
16262                 globaldin = data;
16263                 break;
16264             }
16265         } else
16266 #endif /* CK_SSL */
16267           {
16268               kerror = looping_read(fd, (char *)&length, sizeof(length));
16269               if (kerror != sizeof(length)) {
16270                   secure_error("Couldn't read PROT buffer length: %d/%s",
16271                                kerror,
16272                                kerror == -1 ? ck_errstr()
16273                                : "premature EOF"
16274                                );
16275                   return(ERR);
16276               }
16277               debug(F101,"secure_getbyte length","",length);
16278               debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
16279
16280               length = (ULONG) ntohl(length);
16281               if (length > maxbuf) {
16282                   secure_error("Length (%d) of PROT buffer > PBSZ=%u",
16283                                length,
16284                                maxbuf
16285                                );
16286                   return(ERR);
16287               }
16288               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
16289                   secure_error("Couldn't read %u byte PROT buffer: %s",
16290                                length,
16291                                kerror == -1 ? ck_errstr() : "premature EOF"
16292                                );
16293                   return(ERR);
16294               }
16295
16296               /* Other auth types go here ... */
16297 #ifdef FTP_SRP
16298               if (strcmp(auth_type, "SRP") == 0) {
16299                   if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
16300                                                 (CHAR *) ucbuf,
16301                                                 ucbuf,
16302                                                 length
16303                                                 )
16304                        ) == -1) {
16305                       secure_error ("srp_encode failed" );
16306                       return ERR;
16307                   }
16308               }
16309 #endif /* FTP_SRP */
16310 #ifdef FTP_KRB4
16311               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
16312                   struct sockaddr_in myaddr, hisaddr;
16313                   GSOCKNAME_T len;
16314                   len = sizeof(myaddr);
16315                   if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16316                       secure_error("secure_putbuf: getsockname failed");
16317                       return(ERR);
16318                   }
16319                   len = sizeof(hisaddr);
16320                   if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16321                       secure_error("secure_putbuf: getpeername failed");
16322                       return(ERR);
16323                   }
16324                   if (ftp_dpl) {
16325                       kerror = krb_rd_priv(ucbuf, length, ftp_sched,
16326 #ifdef KRB524
16327                                            ftp_cred.session,
16328 #else /* KRB524 */
16329                                            &ftp_cred.session,
16330 #endif /* KRB524 */
16331                                            &hisaddr, &myaddr, &ftp_msg_data);
16332                   } else {
16333                       kerror = krb_rd_safe(ucbuf, length,
16334 #ifdef KRB524
16335                                            ftp_cred.session,
16336 #else /* KRB524 */
16337                                            &ftp_cred.session,
16338 #endif /* KRB524 */
16339                                            &hisaddr, &myaddr, &ftp_msg_data);
16340                   }
16341                   if (kerror) {
16342                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
16343                                    ftp_dpl == FPL_PRV ? "priv" : "safe",
16344                                    krb_get_err_text(kerror));
16345                       return(ERR);
16346                   }
16347                   memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
16348                   nin = bufp = ftp_msg_data.app_length;
16349               }
16350 #endif /* FTP_KRB4 */
16351 #ifdef FTP_GSSAPI
16352               if (strcmp(auth_type, "GSSAPI") == 0) {
16353                   gss_buffer_desc xmit_buf, msg_buf;
16354                   OM_uint32 maj_stat, min_stat;
16355                   int conf_state;
16356
16357                   xmit_buf.value = ucbuf;
16358                   xmit_buf.length = length;
16359                   conf_state = (ftp_dpl == FPL_PRV);
16360                   /* decrypt/verify the message */
16361                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
16362                                         &msg_buf, &conf_state, NULL);
16363                   if (maj_stat != GSS_S_COMPLETE) {
16364                       user_gss_error(maj_stat, min_stat,
16365                                      (ftp_dpl == FPL_PRV)?
16366                                      "failed unsealing ENC message":
16367                                      "failed unsealing MIC message");
16368                       return ERR;
16369                   }
16370                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
16371                   gss_release_buffer(&min_stat, &msg_buf);
16372               }
16373 #endif /* FTP_GSSAPI */
16374               /* Other auth types go here ... */
16375
16376               /* Update file transfer display */
16377               rpackets++;
16378               pktnum++;
16379               if (fdispla != XYFD_B) {
16380                   rpktl = nin;
16381                   ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16382               }
16383           }
16384     }
16385     if (nin == 0)
16386       return(EOF);
16387     else
16388       return(ucbuf[bufp - nin--]);
16389 }
16390
16391 /* secure_getc(fd,fc)
16392  * Call with:
16393  *   fd = file descriptor for connection.
16394  *   fc = 0 to get a character, fc != 0 to initialize buffer pointers.
16395  * Returns:
16396  *   c>=0 on success (character value)
16397  *   -1   on EOF
16398  *   -2   on security error
16399  *   -3   on timeout (if built with FTP_TIMEOUT defined)
16400  */
16401 static int
16402 secure_getc(fd,fc) int fd,fc; {         /* file descriptor, function code */
16403
16404     if (!ftpissecure()) {
16405         static unsigned int nin = 0, bufp = 0;
16406         if (fc) {
16407             nin = bufp = 0;
16408             ucbuf[0] = NUL;
16409             return(0);
16410         }
16411         if (nin == 0) {
16412             if (iscanceled())
16413               return(-9);
16414
16415 #ifdef FTP_TIMEOUT
16416             if (check_data_connection(fd,0) < 0) {
16417                 debug(F100,"secure_getc TIMEOUT","",0);
16418                 nin = bufp = 0;
16419                 ftp_timed_out = 1;
16420                 return(-3);
16421             }           
16422 #endif  /* FTP_TIMEOUT */
16423
16424             nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
16425             if ((nin == 0) || (nin == (unsigned int)-1)) {
16426                 debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
16427                 debug(F101,"secure_getc returns EOF","",EOF);
16428                 nin = bufp = 0;
16429                 return(EOF);
16430             }
16431             debug(F101,"ftp secure_getc recv","",nin);
16432             ckhexdump("ftp secure_getc recv",ucbuf,16);
16433             rpackets++;
16434             pktnum++;
16435             if (fdispla != XYFD_B) {
16436                 rpktl = nin;
16437                 ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16438             }
16439         }
16440         return(ucbuf[bufp - nin--]);
16441     } else
16442       return(secure_getbyte(fd,fc));
16443 }
16444
16445 /* returns:
16446  *     n>0  on success (n == # of bytes read)
16447  *       0  on EOF
16448  *      -1  on error (errno set), only for FPL_CLR
16449  *      -2  on security error
16450  */
16451 static int
16452 secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
16453     static int c = 0;
16454     int i;
16455
16456     debug(F101,"secure_read bytes requested","",nbyte);
16457     if (c == EOF)
16458       return(c = 0);
16459     for (i = 0; nbyte > 0; nbyte--) {
16460         c = secure_getc(fd,0);
16461         switch (c) {
16462           case -9:                      /* Canceled from keyboard */
16463             debug(F101,"ftp secure_read interrupted","",c);
16464             return(0);
16465           case ERR:
16466             debug(F101,"ftp secure_read error","",c);
16467             return(c);
16468           case EOF:
16469             debug(F101,"ftp secure_read EOF","",c);
16470             if (!i)
16471               c = 0;
16472             return(i);
16473 #ifdef FTP_TIMEOUT
16474           case -3:
16475             debug(F101,"ftp secure_read timeout","",c);
16476             return(c);
16477 #endif  /* FTP_TIMEOUT */
16478           default:
16479             buf[i++] = c;
16480         }
16481     }
16482     return(i);
16483 }
16484
16485 #ifdef USE_RUSERPASS
16486 /* BEGIN_RUSERPASS
16487  *
16488  * Copyright (c) 1985 Regents of the University of California.
16489  * All rights reserved.
16490  *
16491  * Redistribution and use in source and binary forms, with or without
16492  * modification, are permitted provided that the following conditions
16493  * are met:
16494  * 1. Redistributions of source code must retain the above copyright
16495  *    notice, this list of conditions and the following disclaimer.
16496  * 2. Redistributions in binary form must reproduce the above copyright
16497  *    notice, this list of conditions and the following disclaimer in the
16498  *    documentation and/or other materials provided with the distribution.
16499  * 3. All advertising materials mentioning features or use of this software
16500  *    must display the following acknowledgement:
16501  *      This product includes software developed by the University of
16502  *      California, Berkeley and its contributors.
16503  * 4. Neither the name of the University nor the names of its contributors
16504  *    may be used to endorse or promote products derived from this software
16505  *    without specific prior written permission.
16506  *
16507  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16508  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16509  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16510  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
16511  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16512  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
16513  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16514  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16515  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16516  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
16517  * SUCH DAMAGE.
16518  */
16519
16520 #ifndef lint
16521 static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
16522 #endif /* not lint */
16523
16524 #ifndef MAXHOSTNAMELEN
16525 #define MAXHOSTNAMELEN 64
16526 #endif
16527
16528 char * renvlook();
16529 static FILE * cfile;
16530
16531 #define DEFAULT 1
16532 #define LOGIN   2
16533 #define PASSWD  3
16534 #define ACCOUNT 4
16535 #define MACDEF  5
16536 #define ID      10
16537 #define MACH    11
16538
16539 static char tokval[100];
16540
16541 static struct toktab {
16542     char *tokstr;
16543     int tval;
16544 } toktab[]= {
16545     "default",  DEFAULT,
16546     "login",    LOGIN,
16547     "password", PASSWD,
16548     "passwd",   PASSWD,
16549     "account",  ACCOUNT,
16550     "machine",  MACH,
16551     "macdef",   MACDEF,
16552     0,          0
16553 };
16554
16555 static int
16556 token() {
16557     char *cp;
16558     int c;
16559     struct toktab *t;
16560
16561     if (feof(cfile))
16562       return(0);
16563     while ((c = getc(cfile)) != EOF &&
16564            (c == '\n' || c == '\t' || c == ' ' || c == ','))
16565       continue;
16566     if (c == EOF)
16567       return(0);
16568     cp = tokval;
16569     if (c == '"') {
16570         while ((c = getc(cfile)) != EOF && c != '"') {
16571             if (c == '\\')
16572               c = getc(cfile);
16573             *cp++ = c;
16574         }
16575     } else {
16576         *cp++ = c;
16577         while ((c = getc(cfile)) != EOF
16578                && c != '\n' && c != '\t' && c != ' ' && c != ',') {
16579             if (c == '\\')
16580               c = getc(cfile);
16581             *cp++ = c;
16582         }
16583     }
16584     *cp = 0;
16585     if (tokval[0] == 0)
16586       return(0);
16587     for (t = toktab; t->tokstr; t++)
16588       if (!strcmp(t->tokstr, tokval))
16589         return(t->tval);
16590     return(ID);
16591 }
16592
16593 ruserpass(host, aname, apass, aacct)
16594     char *host, **aname, **apass, **aacct;
16595 {
16596     char *hdir, buf[FTP_BUFSIZ], *tmp;
16597     char myname[MAXHOSTNAMELEN], *mydomain;
16598     int t, i, c, usedefault = 0;
16599 #ifdef NT
16600     struct _stat stb;
16601 #else /* NT */
16602     struct stat stb;
16603 #endif /* NT */
16604
16605     hdir = getenv("HOME");
16606     if (hdir == NULL)
16607         hdir = ".";
16608     ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
16609     cfile = fopen(buf, "r");
16610     if (cfile == NULL) {
16611         if (errno != ENOENT)
16612           perror(buf);
16613         return(0);
16614     }
16615     if (gethostname(myname, MAXHOSTNAMELEN) < 0)
16616       myname[0] = '\0';
16617     if ((mydomain = ckstrchr(myname, '.')) == NULL)
16618       mydomain = "";
16619
16620   next:
16621     while ((t = token())) switch(t) {
16622
16623       case DEFAULT:
16624         usedefault = 1;
16625         /* FALL THROUGH */
16626
16627       case MACH:
16628         if (!usedefault) {
16629             if (token() != ID)
16630               continue;
16631             /*
16632              * Allow match either for user's input host name
16633              * or official hostname.  Also allow match of
16634              * incompletely-specified host in local domain.
16635              */
16636             if (ckstrcmp(host, tokval,-1,1) == 0)
16637               goto match;
16638             if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
16639               goto match;
16640             if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
16641                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16642                 ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
16643                 tokval[tmp - ftp_host] == '\0')
16644               goto match;
16645             if ((tmp = ckstrchr(host, '.')) != NULL &&
16646                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16647                 ckstrcmp(host, tokval, tmp - host, 0) == 0 &&
16648                 tokval[tmp - host] == '\0')
16649               goto match;
16650             continue;
16651         }
16652
16653       match:
16654         while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
16655
16656           case LOGIN:
16657             if (token())
16658               if (*aname == 0) {
16659                   *aname = malloc((unsigned) strlen(tokval) + 1);
16660                   strcpy(*aname, tokval);      /* safe */
16661               } else {
16662                   if (strcmp(*aname, tokval))
16663                     goto next;
16664               }
16665             break;
16666           case PASSWD:
16667             if (strcmp(*aname, "anonymous") &&
16668                 fstat(fileno(cfile), &stb) >= 0 &&
16669                 (stb.st_mode & 077) != 0) {
16670                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16671                 fprintf(stderr, "Remove password or correct mode.\n");
16672                 goto bad;
16673             }
16674             if (token() && *apass == 0) {
16675                 *apass = malloc((unsigned) strlen(tokval) + 1);
16676                 strcpy(*apass, tokval);          /* safe */
16677             }
16678             break;
16679           case ACCOUNT:
16680             if (fstat(fileno(cfile), &stb) >= 0
16681                 && (stb.st_mode & 077) != 0) {
16682                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16683                 fprintf(stderr, "Remove account or correct mode.\n");
16684                 goto bad;
16685             }
16686             if (token() && *aacct == 0) {
16687                 *aacct = malloc((unsigned) strlen(tokval) + 1);
16688                 strcpy(*aacct, tokval);          /* safe */
16689             }
16690             break;
16691
16692           default:
16693             fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
16694             break;
16695         }
16696         goto done;
16697     }
16698
16699   done:
16700     fclose(cfile);
16701     return(0);
16702
16703   bad:
16704     fclose(cfile);
16705     return(-1);
16706 }
16707 #endif /* USE_RUSERPASS */
16708
16709 static char *radixN =
16710   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16711
16712 static char pad = '=';
16713
16714 static int
16715 radix_encode(inbuf, outbuf, inlen, outlen, decode)
16716     CHAR inbuf[], outbuf[];
16717     int inlen, *outlen, decode;
16718 {
16719     int i, j, D = 0;
16720     char *p;
16721     CHAR c = NUL;
16722
16723     if (decode) {
16724         for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
16725             if ((p = ckstrchr(radixN, inbuf[i])) == NULL)
16726               return(1);
16727             D = p - radixN;
16728             switch (i&3) {
16729               case 0:
16730                 outbuf[j] = D<<2;
16731                 break;
16732               case 1:
16733                 outbuf[j++] |= D>>4;
16734                 outbuf[j] = (D&15)<<4;
16735                 break;
16736               case 2:
16737                 outbuf[j++] |= D>>2;
16738                 outbuf[j] = (D&3)<<6;
16739                 break;
16740               case 3:
16741                 outbuf[j++] |= D;
16742             }
16743             if (j == *outlen)
16744               return(4);
16745         }
16746         switch (i&3) {
16747           case 1: return(3);
16748           case 2: if (D&15) return(3);
16749             if (strcmp((char *)&inbuf[i], "==")) return(2);
16750             break;
16751           case 3: if (D&3) return(3);
16752             if (strcmp((char *)&inbuf[i], "="))  return(2);
16753         }
16754         *outlen = j;
16755     } else {
16756         for (i = 0, j = 0; i < inlen; i++) {
16757             switch (i%3) {
16758               case 0:
16759                 outbuf[j++] = radixN[inbuf[i]>>2];
16760                 c = (inbuf[i]&3)<<4;
16761                 break;
16762               case 1:
16763                 outbuf[j++] = radixN[c|inbuf[i]>>4];
16764                 c = (inbuf[i]&15)<<2;
16765                 break;
16766               case 2:
16767                 outbuf[j++] = radixN[c|inbuf[i]>>6];
16768                 outbuf[j++] = radixN[inbuf[i]&63];
16769                 c = 0;
16770             }
16771             if (j == *outlen)
16772               return(4);
16773         }
16774         if (i%3) outbuf[j++] = radixN[c];
16775         switch (i%3) {
16776           case 1: outbuf[j++] = pad;
16777           case 2: outbuf[j++] = pad;
16778         }
16779         outbuf[*outlen = j] = '\0';
16780     }
16781     return(0);
16782 }
16783
16784 static char *
16785 radix_error(e) int e;
16786 {
16787     switch (e) {
16788       case 0:  return("Success");
16789       case 1:  return("Bad character in encoding");
16790       case 2:  return("Encoding not properly padded");
16791       case 3:  return("Decoded # of bits not a multiple of 8");
16792       case 4:  return("Output buffer too small");
16793       default: return("Unknown error");
16794     }
16795 }
16796 /* END_RUSERPASS */
16797
16798 #ifdef FTP_SRP
16799 /*---------------------------------------------------------------------------+
16800  |                                                                           |
16801  |   Package: srpftp                                                         |
16802  |   Author: Eugene Jhong                                                    |
16803  |                                                                           |
16804  +---------------------------------------------------------------------------*/
16805
16806 /*
16807  * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
16808  * All Rights Reserved.
16809  *
16810  * Permission is hereby granted, free of charge, to any person obtaining
16811  * a copy of this software and associated documentation files (the
16812  * "Software"), to deal in the Software without restriction, including
16813  * without limitation the rights to use, copy, modify, merge, publish,
16814  * distribute, sublicense, and/or sell copies of the Software, and to
16815  * permit persons to whom the Software is furnished to do so, subject to
16816  * the following conditions:
16817  *
16818  * The above copyright notice and this permission notice shall be
16819  * included in all copies or substantial portions of the Software.
16820  *
16821  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16822  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16823  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16824  *
16825  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
16826  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
16827  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
16828  * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
16829  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16830  *
16831  * In addition, the following conditions apply:
16832  *
16833  * 1. Any software that incorporates the SRP authentication technology
16834  *    must display the following acknowlegment:
16835  *    "This product uses the 'Secure Remote Password' cryptographic
16836  *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
16837  *
16838  * 2. Any software that incorporates all or part of the SRP distribution
16839  *    itself must also display the following acknowledgment:
16840  *    "This product includes software developed by Tom Wu and Eugene
16841  *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
16842  *
16843  * 3. Redistributions in source or binary form must retain an intact copy
16844  *    of this copyright notice and list of conditions.
16845  */
16846
16847 #define SRP_PROT_VERSION        1
16848
16849 #ifdef CK_ENCRYPTION
16850 #define SRP_DEFAULT_CIPHER      CIPHER_ID_CAST5_CBC
16851 #else
16852 #define SRP_DEFAULT_CIPHER      CIPHER_ID_NONE
16853 #endif /* CK_ENCRYPTION */
16854
16855 #define SRP_DEFAULT_HASH        HASH_ID_SHA
16856
16857 CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
16858 CHAR srp_pref_hash = HASH_ID_SHA;
16859
16860 static struct t_client *tc = NULL;
16861 static CHAR *skey = NULL;
16862 static krypto_context *incrypt = NULL;
16863 static krypto_context *outcrypt = NULL;
16864
16865 typedef unsigned int srp_uint32;
16866
16867 /*--------------------------------------------------------------+
16868  | srp_selcipher: select cipher                                 |
16869  +--------------------------------------------------------------*/
16870 static int
16871 srp_selcipher (cname) char *cname; {
16872     cipher_desc *cd;
16873
16874     if (!(cd = cipher_getdescbyname (cname))) {
16875         int i;
16876         CHAR *list = cipher_getlist ();
16877
16878         fprintf (stderr, "ftp: supported ciphers:\n\n");
16879         for (i = 0; i < strlen (list); i++)
16880           fprintf (stderr, "    %s\n", (cipher_getdescbyid(list[i]))->name);
16881         fprintf (stderr, "\n");
16882         return -1;
16883     }
16884     srp_pref_cipher = cd->id;
16885     return 0;
16886 }
16887
16888 /*--------------------------------------------------------------+
16889  | srp_selhash: select hash                                     |
16890  +--------------------------------------------------------------*/
16891 static int
16892 srp_selhash (hname) char *hname; {
16893     hash_desc *hd;
16894
16895     if (!(hd = hash_getdescbyname (hname))) {
16896         int i;
16897         CHAR *list = hash_getlist ();
16898
16899         fprintf (stderr, "ftp: supported hash functions:\n\n");
16900         for (i = 0; i < strlen (list); i++)
16901           fprintf (stderr, "    %s\n", (hash_getdescbyid(list[i]))->name);
16902         fprintf (stderr, "\n");
16903         return -1;
16904     }
16905     srp_pref_hash = hd->id;
16906     return 0;
16907 }
16908
16909 /*--------------------------------------------------------------+
16910  | srp_userpass: get username and password                      |
16911  +--------------------------------------------------------------*/
16912 static int
16913 srp_userpass (host) char *host; {
16914     char tmp[BUFSIZ], prompt[PROMPTSIZ];
16915     char *user;
16916
16917     user = NULL;
16918 #ifdef USE_RUSERPASS
16919     ruserpass (host, &user, &srp_pass, &srp_acct);
16920 #endif /* USE_RUSERPASS */
16921
16922     while (user == NULL)     {
16923         char *myname;
16924         int ok;
16925
16926         myname = whoami();
16927         if (!myname) myname = "";
16928         if (myname[0])
16929           ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
16930                     NULL,NULL,NULL,NULL,NULL,NULL,NULL);
16931         else
16932           ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
16933         tmp[0] = '\0';
16934         ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
16935                     DEFAULT_UQ_TIMEOUT);
16936         if (!ok || *tmp == '\0')
16937           user = myname;
16938         else
16939           user = brstrip(tmp);
16940     }
16941     ckstrncpy (srp_user, user,BUFSIZ);
16942     return(0);
16943 }
16944
16945 /*--------------------------------------------------------------+
16946  | srp_reset: reset srp information                             |
16947  +--------------------------------------------------------------*/
16948 static int
16949 srp_reset () {
16950     if (tc) { t_clientclose (tc); tc = NULL; }
16951     if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
16952     if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
16953     return(0);
16954 }
16955
16956 /*--------------------------------------------------------------+
16957  | srp_ftp_auth: perform srp authentication                         |
16958  +--------------------------------------------------------------*/
16959 static int
16960 srp_ftp_auth(host, user, pass)
16961     char *host;
16962     char *user;
16963     char *pass;
16964 {
16965     struct t_num *wp;
16966     struct t_num N;
16967     struct t_num g;
16968     struct t_num s;
16969     struct t_num yp;
16970     CHAR buf[FTP_BUFSIZ];
16971     CHAR tmp[FTP_BUFSIZ];
16972     CHAR *bp, *cp;
16973     int n, e, clen, blen, len, i;
16974     CHAR cid = 0;
16975     CHAR hid = 0;
16976
16977     srp_pass = srp_acct = 0;
16978
16979     n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
16980     if (n != REPLY_CONTINUE) {
16981         if (ftp_deb)
16982             fprintf(stderr, "SRP rejected as an authentication type\n");
16983         return(0);
16984     } else {                            /* Send protocol version */
16985         CHAR vers[4];
16986         memset (vers, 0, 4);
16987         vers[3] = SRP_PROT_VERSION;
16988         if (!quiet)
16989           printf ("SRP accepted as authentication type.\n");
16990         bp = tmp; blen = 0;
16991         srp_put (vers, &bp, 4, &blen);
16992         len = FTP_BUFSIZ;
16993         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
16994           goto encode_error;
16995         reply_parse = "ADAT=";
16996         n = ftpcmd("ADAT",buf,-1,-1,0);
16997     }
16998     if (n == REPLY_CONTINUE) {          /* Get protocol version */
16999         bp = buf;
17000         if (!reply_parse)
17001           goto data_error;
17002         blen = FTP_BUFSIZ;
17003         if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
17004           goto decode_error;
17005         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17006           goto data_error;
17007
17008         if (host) {                     /* Get username/password if needed */
17009             srp_userpass (host);
17010         } else {
17011             ckstrncpy (srp_user, user, BUFSIZ);
17012             srp_pass = pass;
17013         }
17014         bp = tmp; blen = 0;             /* Send username */
17015         srp_put (srp_user, &bp, strlen (srp_user), &blen);
17016         len = FTP_BUFSIZ;
17017         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17018           goto encode_error;
17019         reply_parse = "ADAT=";
17020         n = ftpcmd("ADAT",buf,-1,-1,0);
17021     }
17022     if (n == REPLY_CONTINUE) {          /* Get N, g and s */
17023         bp = buf;
17024         if (!reply_parse)
17025           goto data_error;
17026         blen = FTP_BUFSIZ;
17027         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17028           goto decode_error;
17029         if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
17030           goto data_error;
17031         if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
17032           goto data_error;
17033         if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
17034           goto data_error;
17035         if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
17036             fprintf (stderr, "Unable to open SRP client structure.\n");
17037             goto bad;
17038         }
17039         wp = t_clientgenexp (tc);       /* Send wp */
17040         bp = tmp; blen = 0;
17041         srp_put (wp->data, &bp, wp->len, &blen);
17042         len = FTP_BUFSIZ;
17043         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17044           goto encode_error;
17045         reply_parse = "ADAT=";
17046         n = ftpcmd("ADAT",buf,-1,-1,0);
17047     }
17048     if (n == REPLY_CONTINUE) {          /* Get yp */
17049         bp = buf;
17050         if (!reply_parse)
17051           goto data_error;
17052         blen = FTP_BUFSIZ;
17053         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17054           goto decode_error;
17055         if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
17056           goto data_error;
17057         if (!srp_pass) {
17058             static char ftppass[PASSBUFSIZ];
17059             int ok;
17060             setint();
17061             ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
17062                         DEFAULT_UQ_TIMEOUT);
17063             if (ok)
17064               srp_pass = brstrip(ftppass);
17065         }
17066         t_clientpasswd (tc, srp_pass);
17067         memset (srp_pass, 0, strlen (srp_pass));
17068         skey = t_clientgetkey (tc, &yp); /* Send response */
17069         bp = tmp; blen = 0;
17070         srp_put (t_clientresponse (tc), &bp, 20, &blen);
17071         len = FTP_BUFSIZ;
17072         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17073           goto encode_error;
17074         reply_parse = "ADAT=";
17075         n = ftpcmd("ADAT",buf,-1,-1,0);
17076     }
17077     if (n == REPLY_CONTINUE) {          /* Get response */
17078         bp = buf;
17079         if (!reply_parse)
17080           goto data_error;
17081         blen = FTP_BUFSIZ;
17082         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17083           goto encode_error;
17084         if (srp_get (&bp, &cp, &blen, &clen) != 20)
17085           goto data_error;
17086         if (t_clientverify (tc, cp)) {
17087             fprintf (stderr, "WARNING: bad response to client challenge.\n");
17088             goto bad;
17089         }
17090         bp = tmp; blen = 0;             /* Send nothing */
17091         srp_put ("\0", &bp, 1, &blen);
17092         len = FTP_BUFSIZ;
17093         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17094           goto encode_error;
17095         reply_parse = "ADAT=";
17096         n = ftpcmd("ADAT",buf,-1,-1,0);
17097     }
17098     if (n == REPLY_CONTINUE) {          /* Get cipher & hash lists, seqnum */
17099         CHAR seqnum[4];
17100         CHAR *clist;
17101         CHAR *hlist;
17102         CHAR *p1;
17103         int clist_len, hlist_len;
17104         bp = buf;
17105         if (!reply_parse)
17106           goto data_error;
17107         blen = FTP_BUFSIZ;
17108         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17109           goto encode_error;
17110         if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
17111           goto data_error;
17112         if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
17113           goto data_error;
17114         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17115           goto data_error;
17116         memcpy (seqnum, cp, 4);
17117         if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
17118           cid = srp_pref_cipher;
17119         if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
17120           cid = SRP_DEFAULT_CIPHER;
17121         if (!cid) {
17122             CHAR *loclist = cipher_getlist ();
17123             for (i = 0; i < strlen (loclist); i++)
17124               if (cipher_supported (clist, loclist[i])) {
17125                   cid = loclist[i];
17126                   break;
17127               }
17128         }
17129         if (!cid) {
17130             fprintf (stderr, "Unable to agree on cipher.\n");
17131             goto bad;
17132         }
17133         /* Choose hash */
17134
17135         if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
17136           hid = srp_pref_hash;
17137
17138         if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
17139           hid = SRP_DEFAULT_HASH;
17140
17141         if (!hid) {
17142             CHAR *loclist = hash_getlist ();
17143             for (i = 0; i < strlen (loclist); i++)
17144               if (hash_supported (hlist, loclist[i])) {
17145                   hid = loclist[i];
17146                   break;
17147               }
17148         }
17149         if (!hid) {
17150             fprintf (stderr, "Unable to agree on hash.\n");
17151             goto bad;
17152         }
17153         /* Set incrypt */
17154
17155         if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
17156                                     KRYPTO_DECODE)))
17157           goto bad;
17158
17159         /* Generate random number for outkey and outseqnum */
17160
17161         t_random (seqnum, 4);
17162
17163         /* Send cid, hid, outkey, outseqnum */
17164
17165         bp = tmp; blen = 0;
17166         srp_put (&cid, &bp, 1, &blen);
17167         srp_put (&hid, &bp, 1, &blen);
17168         srp_put (seqnum, &bp, 4, &blen);
17169         len = FTP_BUFSIZ;
17170         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17171           goto encode_error;
17172         reply_parse = "ADAT=";
17173         n = ftpcmd("ADAT",buf,-1,-1,0);
17174
17175         /* Set outcrypt */
17176
17177         if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
17178                                      KRYPTO_ENCODE)))
17179           goto bad;
17180
17181         t_clientclose (tc);
17182         tc = NULL;
17183     }
17184     if (n != REPLY_COMPLETE)
17185       goto bad;
17186
17187     if (ftp_vbm) {
17188         if (ftp_deb)
17189           printf("\n");
17190         printf ("SRP authentication succeeded.\n");
17191         printf ("Using cipher %s and hash function %s.\n",
17192                 (cipher_getdescbyid(cid))->name,
17193                 (hash_getdescbyid(hid))->name
17194                 );
17195     }
17196     reply_parse = NULL;
17197     auth_type = "SRP";
17198     return(1);
17199
17200   encode_error:
17201     fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
17202     goto bad;
17203
17204   decode_error:
17205     fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
17206     goto bad;
17207
17208   data_error:
17209     fprintf (stderr, "Unable to unmarshal authentication data.\n");
17210     goto bad;
17211
17212   bad:
17213     fprintf (stderr, "SRP authentication failed, trying regular login.\n");
17214     reply_parse = NULL;
17215     return(0);
17216 }
17217
17218 /*--------------------------------------------------------------+
17219  | srp_put: put item to send buffer                             |
17220  +--------------------------------------------------------------*/
17221 static int
17222 srp_put (in, out, inlen, outlen)
17223     CHAR *in;
17224     CHAR **out;
17225     int inlen;
17226     int *outlen;
17227 {
17228     srp_uint32 net_len;
17229
17230     net_len = htonl (inlen);
17231     memcpy (*out, &net_len, 4);
17232
17233     *out += 4; *outlen += 4;
17234
17235     memcpy (*out, in, inlen);
17236
17237     *out += inlen; *outlen += inlen;
17238     return(0);
17239 }
17240
17241 /*--------------------------------------------------------------+
17242  | srp_get: get item from receive buffer                        |
17243  +--------------------------------------------------------------*/
17244 static int
17245 srp_get (in, out, inlen, outlen)
17246     CHAR **in;
17247     CHAR **out;
17248     int *inlen;
17249     int *outlen;
17250 {
17251     srp_uint32 net_len;
17252
17253     if (*inlen < 4) return -1;
17254
17255     memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
17256     *outlen = ntohl (net_len);
17257
17258     if (*inlen < *outlen) return -1;
17259
17260     *out = *in; *inlen -= *outlen; *in += *outlen;
17261
17262     return *outlen;
17263 }
17264
17265 /*--------------------------------------------------------------+
17266  | srp_encode: encode control message                           |
17267  +--------------------------------------------------------------*/
17268 static int
17269 srp_encode (private, in, out, len)
17270     int private;
17271     CHAR *in;
17272     CHAR *out;
17273     unsigned len;
17274 {
17275     if (private)
17276       return krypto_msg_priv (outcrypt, in, out, len);
17277     else
17278       return krypto_msg_safe (outcrypt, in, out, len);
17279 }
17280
17281 /*--------------------------------------------------------------+
17282  | srp_decode: decode control message                           |
17283  +--------------------------------------------------------------*/
17284 static int
17285 srp_decode (private, in, out, len)
17286     int private;
17287     CHAR *in;
17288     CHAR *out;
17289     unsigned len;
17290 {
17291     if (private)
17292       return krypto_msg_priv (incrypt, in, out, len);
17293     else
17294       return krypto_msg_safe (incrypt, in, out, len);
17295 }
17296
17297 #endif /* FTP_SRP */
17298
17299
17300
17301 #ifdef NOT_USED
17302 /*
17303   The following code is from the Unix FTP client.  Be sure to
17304   make sure that the functionality is not lost.  Especially
17305   the Proxy stuff even though we have not yet implemented it.
17306 */
17307
17308 /* Send multiple files  */
17309
17310 static int
17311 ftp_mput(argc, argv) int argc; char **argv; {
17312     register int i;
17313     sig_t oldintr;
17314     int ointer;
17315     char *tp;
17316     sigtype mcancel();
17317
17318     if (argc < 2 && !another(&argc, &argv, "local-files")) {
17319         printf("usage: %s local-files\n", argv[0]);
17320         ftpcode = -1;
17321         return;
17322     }
17323     mname = argv[0];
17324     mflag = 1;
17325     oldintr = signal(SIGINT, mcancel);
17326
17327     /* Replace with calls to cc_execute() */
17328     setjmp(jcancel);
17329 #ifdef FTP_PROXY
17330     if (proxy) {
17331         char *cp, *tp2, tmpbuf[CKMAXPATH];
17332
17333         while ((cp = remglob(argv,0)) != NULL) {
17334             if (*cp == 0) {
17335                 mflag = 0;
17336                 continue;
17337             }
17338             if (mflag && confirm(argv[0], cp)) {
17339                 tp = cp;
17340                 if (mcase) {
17341                     while (*tp && !islower(*tp)) {
17342                         tp++;
17343                     }
17344                     if (!*tp) {
17345                         tp = cp;
17346                         tp2 = tmpbuf;
17347                         while ((*tp2 = *tp) != 0) {
17348                             if (isupper(*tp2)) {
17349                                 *tp2 = 'a' + *tp2 - 'A';
17350                             }
17351                             tp++;
17352                             tp2++;
17353                         }
17354                     }
17355                     tp = tmpbuf;
17356                 }
17357                 if (ntflag) {
17358                     tp = dotrans(tp);
17359                 }
17360                 if (mapflag) {
17361                     tp = domap(tp);
17362                 }
17363                 sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0);
17364                 if (!mflag && fromatty) {
17365                     ointer = interactive;
17366                     interactive = 1;
17367                     if (confirm("Continue with","mput")) {
17368                         mflag++;
17369                     }
17370                     interactive = ointer;
17371                 }
17372             }
17373         }
17374         signal(SIGINT, oldintr);
17375         mflag = 0;
17376         return;
17377     }
17378 #endif /* FTP_PROXY */
17379     for (i = 1; i < argc; i++) {
17380         register char **cpp, **gargs;
17381
17382         if (mflag && confirm(argv[0], argv[i])) {
17383             tp = argv[i];
17384             sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
17385             if (!mflag && fromatty) {
17386                 ointer = interactive;
17387                 interactive = 1;
17388                 if (confirm("Continue with","mput")) {
17389                     mflag++;
17390                 }
17391                 interactive = ointer;
17392             }
17393         }
17394         continue;
17395
17396         gargs = ftpglob(argv[i]);
17397         if (globerr != NULL) {
17398             printf("%s\n", globerr);
17399             if (gargs) {
17400                 blkfree(gargs);
17401                 free((char *)gargs);
17402             }
17403             continue;
17404         }
17405         for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
17406             if (mflag && confirm(argv[0], *cpp)) {
17407                 tp = *cpp;
17408                 sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
17409                 if (!mflag && fromatty) {
17410                     ointer = interactive;
17411                     interactive = 1;
17412                     if (confirm("Continue with","mput")) {
17413                         mflag++;
17414                     }
17415                     interactive = ointer;
17416                 }
17417             }
17418         }
17419         if (gargs != NULL) {
17420             blkfree(gargs);
17421             free((char *)gargs);
17422         }
17423     }
17424     signal(SIGINT, oldintr);
17425     mflag = 0;
17426 }
17427
17428 /* Get multiple files */
17429
17430 static int
17431 ftp_mget(argc, argv) int argc; char **argv; {
17432     int rc = -1;
17433     sig_t oldintr;
17434     int ointer;
17435     char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
17436     sigtype mcancel();
17437
17438     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17439         printf("usage: %s remote-files\n", argv[0]);
17440         ftpcode = -1;
17441         return(-1);
17442     }
17443     mname = argv[0];
17444     mflag = 1;
17445     oldintr = signal(SIGINT,mcancel);
17446     /* Replace with calls to cc_execute() */
17447     setjmp(jcancel);
17448     while ((cp = remglob(argv,proxy)) != NULL) {
17449         if (*cp == '\0') {
17450             mflag = 0;
17451             continue;
17452         }
17453         if (mflag && confirm(argv[0], cp)) {
17454             tp = cp;
17455             if (mcase) {
17456                 while (*tp && !islower(*tp)) {
17457                     tp++;
17458                 }
17459                 if (!*tp) {
17460                     tp = cp;
17461                     tp2 = tmpbuf;
17462                     while ((*tp2 = *tp) != 0) {
17463                         if (isupper(*tp2)) {
17464                             *tp2 = 'a' + *tp2 - 'A';
17465                         }
17466                         tp++;
17467                         tp2++;
17468                     }
17469                 }
17470                 tp = tmpbuf;
17471             }
17472             rc = (recvrequest("RETR", tp, cp, "wb",
17473                                tp != cp || !interactive) == 0,0,NULL,0,0,0);
17474             if (!mflag && fromatty) {
17475                 ointer = interactive;
17476                 interactive = 1;
17477                 if (confirm("Continue with","mget")) {
17478                     mflag++;
17479                 }
17480                 interactive = ointer;
17481             }
17482         }
17483     }
17484     signal(SIGINT,oldintr);
17485     mflag = 0;
17486     return(rc);
17487 }
17488
17489 /* Delete multiple files */
17490
17491 static int
17492 mdelete(argc, argv) int argc; char **argv; {
17493     sig_t oldintr;
17494     int ointer;
17495     char *cp;
17496     sigtype mcancel();
17497
17498     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17499         printf("usage: %s remote-files\n", argv[0]);
17500         ftpcode = -1;
17501         return(-1);
17502     }
17503     mname = argv[0];
17504     mflag = 1;
17505     oldintr = signal(SIGINT, mcancel);
17506     /* Replace with calls to cc_execute() */
17507     setjmp(jcancel);
17508     while ((cp = remglob(argv,0)) != NULL) {
17509         if (*cp == '\0') {
17510             mflag = 0;
17511             continue;
17512         }
17513         if (mflag && confirm(argv[0], cp)) {
17514             rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
17515             if (!mflag && fromatty) {
17516                 ointer = interactive;
17517                 interactive = 1;
17518                 if (confirm("Continue with", "mdelete")) {
17519                     mflag++;
17520                 }
17521                 interactive = ointer;
17522             }
17523         }
17524     }
17525     signal(SIGINT, oldintr);
17526     mflag = 0;
17527     return(rc);
17528 }
17529
17530 /* Get a directory listing of multiple remote files */
17531
17532 static int
17533 mls(argc, argv) int argc; char **argv; {
17534     sig_t oldintr;
17535     int ointer, i;
17536     char *cmd, mode[1], *dest;
17537     sigtype mcancel();
17538     int rc = -1;
17539
17540     if (argc < 2 && !another(&argc, &argv, "remote-files"))
17541       goto usage;
17542     if (argc < 3 && !another(&argc, &argv, "local-file")) {
17543       usage:
17544         printf("usage: %s remote-files local-file\n", argv[0]);
17545         ftpcode = -1;
17546         return(-1);
17547     }
17548     dest = argv[argc - 1];
17549     argv[argc - 1] = NULL;
17550     if (strcmp(dest, "-") && *dest != '|')
17551       if (!globulize(&dest) ||
17552           !confirm("output to local-file:", dest)) {
17553           ftpcode = -1;
17554           return(-1);
17555       }
17556     cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
17557     mname = argv[0];
17558     mflag = 1;
17559     oldintr = signal(SIGINT, mcancel);
17560     /* Replace with calls to cc_execute() */
17561     setjmp(jcancel);
17562     for (i = 1; mflag && i < argc-1; ++i) {
17563         *mode = (i == 1) ? 'w' : 'a';
17564         rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
17565         if (!mflag && fromatty) {
17566             ointer = interactive;
17567             interactive = 1;
17568             if (confirm("Continue with", argv[0])) {
17569                 mflag ++;
17570             }
17571             interactive = ointer;
17572         }
17573     }
17574     signal(SIGINT, oldintr);
17575     mflag = 0;
17576     return(rc);
17577 }
17578
17579 static char *
17580 remglob(argv,doswitch) char *argv[]; int doswitch; {
17581     char temp[16];
17582     static char buf[CKMAXPATH];
17583     static FILE *ftemp = NULL;
17584     static char **args;
17585     int oldhash;
17586     char *cp, *mode;
17587
17588     if (!mflag) {
17589         if (!doglob) {
17590             args = NULL;
17591         } else {
17592             if (ftemp) {
17593                 (void) fclose(ftemp);
17594                 ftemp = NULL;
17595             }
17596         }
17597         return(NULL);
17598     }
17599     if (!doglob) {
17600         if (args == NULL)
17601           args = argv;
17602         if ((cp = *++args) == NULL)
17603           args = NULL;
17604         return(cp);
17605     }
17606     if (ftemp == NULL) {
17607         (void) strcpy(temp, _PATH_TMP);
17608 #ifdef MKTEMP
17609 #ifndef MKSTEMP
17610         (void) mktemp(temp);
17611 #endif /* MKSTEMP */
17612 #endif /* MKTEMP */
17613         verbose = 0;
17614         oldhash = hash, hash = 0;
17615 #ifdef FTP_PROXY
17616         if (doswitch) {
17617             pswitch(!proxy);
17618         }
17619 #endif /* FTP_PROXY */
17620         for (mode = "wb"; *++argv != NULL; mode = "ab")
17621           recvrequest ("NLST", temp, *argv, mode, 0);
17622 #ifdef FTP_PROXY
17623         if (doswitch) {
17624             pswitch(!proxy);
17625         }
17626 #endif /* FTP_PROXY */
17627         hash = oldhash;
17628         ftemp = fopen(temp, "r");
17629         unlink(temp);
17630         if (ftemp == NULL && (!dpyactive || ftp_deb)) {
17631             printf("Can't find list of remote files, oops\n");
17632             return(NULL);
17633         }
17634     }
17635     if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
17636         fclose(ftemp), ftemp = NULL;
17637         return(NULL);
17638     }
17639     if ((cp = ckstrchr(buf,'\n')) != NULL)
17640       *cp = '\0';
17641     return(buf);
17642 }
17643 #endif /* NOT_USED */
17644 #endif /* TCPSOCKET (top of file) */
17645 #endif /* SYSFTP (top of file) */
17646 #endif /* NOFTP (top of file) */