Standards-Version: 3.9.6 (no changes)
[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.263, 5 Feb 2015";
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, 2014,
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
187 #ifndef HPUXPRE65
188 #include <errno.h>                      /* Error number symbols */
189 #else
190 #ifndef ERRNO_INCLUDED
191 #include <errno.h>                      /* Error number symbols */
192 #endif  /* ERRNO_INCLUDED */
193 #endif  /* HPUXPRE65 */
194
195 #ifndef NOTIMEH
196 #include <time.h>
197 #endif /* NOTIMEH */
198 #ifndef EPIPE
199 #define EPIPE 32                        /* Broken pipe error */
200 #endif /* EPIPE */
201
202 /* Kermit includes */
203
204 #include "ckcasc.h"
205 #include "ckcker.h"
206 #include "ckucmd.h"
207 #include "ckuusr.h"
208 #include "ckcnet.h"                     /* Includes ckctel.h */
209 #include "ckctel.h"                     /* (then why include it again?) */
210 #include "ckcxla.h"
211
212 #ifdef CK_SSL
213 #include "ckuath.h"                     /* SMS 2007/02/15 */
214 #endif /* def CK_SSL */
215
216 /*
217   How to get the struct timeval definition so we can call select().  The
218   xxTIMEH symbols are defined in ckcdeb.h, overridden in various makefile
219   targets.  The problem is: maybe we have already included some header file
220   that defined struct timeval, and maybe we didn't.  If we did, we don't want
221   to include another header file that defines it again or the compilation will
222   fail.  If we didn't, we have to include the header file where it's defined.
223   But in some cases even that won't work because of strict POSIX constraints
224   or somesuch, or because this introduces other conflicts (e.g. struct tm
225   multiply defined), in which case we have to define it ourselves, but this
226   can work only if we didn't already encounter a definition.
227 */
228 #ifndef DCLTIMEVAL
229 #ifdef SV68R3V6
230 #define DCLTIMEVAL
231 #else
232 #ifdef SCO234
233 #define DCLTIMEVAL
234 #endif /* SCO234 */
235 #endif /* SV68R3V6 */
236 #endif /* DCLTIMEVAL */
237
238 #ifdef DCLTIMEVAL
239 /* Also maybe in some places the elements must be unsigned... */
240 struct timeval {
241     long tv_sec;
242     long tv_usec;
243 };
244 #ifdef COMMENT
245 /* Currently we don't use this... */
246 struct timezone {
247     int tz_minuteswest;
248     int tz_dsttime;
249 };
250 #endif /* COMMENT */
251 #else  /* !DCLTIMEVAL */
252 #ifndef NOSYSTIMEH
253 #ifdef SYSTIMEH
254 #include <sys/time.h>
255 #endif /* SYSTIMEH */
256 #endif /* NOSYSTIMEH */
257 #ifndef NOSYSTIMEBH
258 #ifdef SYSTIMEBH
259 #include <sys/timeb.h>
260 #endif /* SYSTIMEBH */
261 #endif /* NOSYSTIMEBH */
262 #endif /* DCLTIMEVAL */
263
264 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
265 #ifdef VMS
266 #include <types.h>
267 #else /* def VMS */
268 #include <sys/types.h>
269 #endif /* def VMS [else] */
270 #include <stdio.h>
271 #include <string.h>
272 #ifdef HAVE_STDLIB_H
273 #include <stdlib.h>
274 #endif /* HAVE_STDLIB_H */
275
276 #ifndef NOSETTIME
277 #ifdef COMMENT
278 /* This section moved to ckcdeb.h */
279 #ifdef POSIX
280 #define UTIMEH
281 #else
282 #ifdef HPUX9
283 #define UTIMEH
284 #else
285 #ifdef OS2
286 #define SYSUTIMEH
287 #endif /* OS2 */
288 #endif /* HPUX9 */
289 #endif /* POSIX */
290 #endif /* COMMENT */
291
292 #ifdef VMS                              /* SMS 2007/02/15 */
293 #include "ckvrtl.h"                     /* for utime() */
294 #else  /* def VMS */
295 #ifdef SYSUTIMEH
296 #include <sys/utime.h>
297 #else
298 #ifdef UTIMEH
299 #include <utime.h>
300 #define SYSUTIMEH
301 #endif /* UTIMEH */
302 #endif /* SYSUTIMEH */
303 #endif /* def VMS */
304 #endif /* NOSETTIME */
305
306 #ifndef SCO_OSR504
307 #ifdef SELECT_H
308 #include <sys/select.h>
309 #endif /* SELECT_H */
310 #endif /* SCO_OSR504 */
311
312 #ifndef INADDR_NONE                     /* 2010-03-29 */
313 #define INADDR_NONE -1
314 #endif  /* INADDR_NONE */
315
316 /* select() dialects... */
317
318 #ifdef UNIX
319 #define BSDSELECT                       /* BSD select() syntax/semantics */
320 #ifndef FD_SETSIZE
321 #define FD_SETSIZE 128
322 #endif  /* FD_SETSIZE */
323 #ifdef HPUX6                            /* For HP-UX 6.5 circa 1989 */
324 typedef long fd_mask;
325 #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
326 #ifndef howmany
327 #define howmany(x, y)   (((x)+((y)-1))/(y))
328 #endif  /* howmany */
329 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
330 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
331 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
332 #define FD_COPY(f, t)   bcopy(f, t, sizeof(*(f)))
333 #define FD_ZERO(p)      bzero(p, sizeof(*(p)))
334 #endif  /* HPUX6 */
335
336 #else
337 #ifdef OS2                              /* OS/2 or Win32 */
338 #ifdef NT
339 #define BSDSELECT
340 #else /* NT */
341 #define IBMSELECT
342 #endif /* NT */
343 #endif /* OS2 */
344 #endif /* UNIX */
345
346 #ifdef VMS
347 #define BSDSELECT                       /* SMS 2007/02/15 */
348 #endif /* def VMS */
349
350 /* Other select() peculiarities */
351
352 #ifdef HPUX
353 #ifndef HPUX10                          /* HP-UX 9.xx and earlier */
354 #ifndef HPUX1100
355 /* The three interior args to select() are (int *) rather than (fd_set *) */
356 #ifndef INTSELECT
357 #define INTSELECT
358 #endif /* INTSELECT */
359 #endif /* HPUX1100 */
360 #endif /* HPUX10 */
361 #endif /* HPUX */
362
363 #ifdef CK_SOCKS                         /* SOCKS Internet relay package */
364 #ifdef CK_SOCKS5                        /* SOCKS 5 */
365 #define accept  SOCKSaccept
366 #define bind    SOCKSbind
367 #define connect SOCKSconnect
368 #define getsockname SOCKSgetsockname
369 #define listen SOCKSlisten
370 #else  /* Not SOCKS 5 */
371 #define accept  Raccept
372 #define bind    Rbind
373 #define connect Rconnect
374 #define getsockname Rgetsockname
375 #define listen Rlisten
376 #endif /* CK_SOCKS5 */
377 #endif /* CK_SOCKS */
378
379 #ifndef NOHTTP
380 extern char * tcp_http_proxy;           /* Name[:port] of http proxy server */
381 extern int    tcp_http_proxy_errno;
382 extern char * tcp_http_proxy_user;
383 extern char * tcp_http_proxy_pwd;
384 extern char * tcp_http_proxy_agent;
385 #define HTTPCPYL 1024
386 static char proxyhost[HTTPCPYL];
387 #endif /* NOHTTP */
388 int ssl_ftp_proxy = 0;                  /* FTP over SSL/TLS Proxy Server */
389
390 /* Feature selection */
391
392 #ifndef USE_SHUTDOWN
393 /*
394   We don't use shutdown() because (a) we always call it just before close()
395   so it's redundant and unnecessary, and (b) it introduces a long pause on
396   some platforms like SV/68 R3.
397 */
398 /* #define USE_SHUTDOWN */
399 #endif /* USE_SHUTDOWN */
400
401 #ifndef NORESEND
402 #ifndef NORESTART                       /* Restart / recover */
403 #ifndef FTP_RESTART
404 #define FTP_RESTART
405 #endif /* FTP_RESTART */
406 #endif /* NORESTART */
407 #endif /* NORESEND */
408
409 #ifndef NOUPDATE                        /* Update mode */
410 #ifndef DOUPDATE
411 #define DOUPDATE
412 #endif /* DOUPDATE */
413 #endif /* NOUPDATE */
414
415 #ifndef UNICODE                         /* Unicode required */
416 #ifndef NOCSETS                         /* for charset translation */
417 #define NOCSETS
418 #endif /* NOCSETS */
419 #endif /* UNICODE */
420
421 #ifndef OS2
422 #ifndef HAVE_MSECS                      /* Millisecond timer */
423 #ifdef UNIX
424 #ifdef GFTIMER
425 #define HAVE_MSECS
426 #endif /* GFTIMER */
427 #endif /* UNIX */
428 #endif /* HAVE_MSECS */
429 #endif /* OS2 */
430
431 #ifdef PIPESEND                         /* PUT from pipe */
432 #ifndef PUTPIPE
433 #define PUTPIPE
434 #endif /* PUTPIPE */
435 #endif /* PIPESEND */
436
437 #ifndef NOSPL                           /* PUT from array */
438 #ifndef PUTARRAY
439 #define PUTARRAY
440 #endif /* PUTARRAY */
441 #endif /* NOSPL */
442
443 /* Security... */
444
445 #ifdef CK_SRP
446 #define FTP_SRP
447 #endif /* CK_SRP */
448
449 #ifdef CK_KERBEROS
450 #ifdef KRB4
451 /*
452   There is a conflict between the Key Schedule formats used internally
453   within the standalone MIT KRB4 library and that used by Eric Young
454   in OpenSSL and his standalone DES library.  Therefore, KRB4 FTP AUTH
455   cannot be supported when either of those two packages are used.
456 */
457 #ifdef KRB524
458 #define FTP_KRB4
459 #else /* KRB524 */
460 #ifndef CK_SSL
461 #ifndef LIBDES
462 #define FTP_KRB4
463 #endif /* LIBDES */
464 #endif /* CK_SSL */
465 #endif /* KRB524 */
466 #endif /* KRB4 */
467 #ifdef KRB5
468 #ifndef HEIMDAL
469 #ifndef NOFTP_GSSAPI                    /* 299 */
470 #define FTP_GSSAPI
471 #endif  /* NOFTP_GSSAPI */
472 #endif /* HEIMDAL */
473 #endif /* KRB5 */
474 #endif /* CK_KERBEROS */
475
476 /* FTP_SECURITY is defined if any of the above is selected */
477 #ifndef FTP_SECURITY
478 #ifdef FTP_GSSAPI
479 #define FTP_SECURITY
480 #else
481 #ifdef FTP_KRB4
482 #define FTP_SECURITY
483 #else
484 #ifdef FTP_SRP
485 #define FTP_SECURITY
486 #else
487 #ifdef CK_SSL
488 #define FTP_SECURITY
489 #endif /* CK_SSL */
490 #endif /* FTP_SRP */
491 #endif /* FTP_KRB4 */
492 #endif /* FTP_GSSAPI */
493 #endif /* FTP_SECURITY */
494
495 #ifdef CK_DES
496 #ifdef CK_SSL
497 #ifndef LIBDES
498 #define LIBDES
499 #endif /* LIBDES */
500 #endif /* CK_SSL */
501 #endif /* CK_DES */
502
503 #ifdef CRYPT_DLL
504 #ifndef LIBDES
505 #define LIBDES
506 #endif /* LIBDES */
507 #endif /* CRYPT_DLL */
508
509 #ifdef FTP_KRB4
510 #define des_cblock Block
511 #define des_key_schedule Schedule
512 #ifdef KRB524
513 #ifdef NT
514 #define _WINDOWS
515 #endif /* NT */
516 #include "kerberosIV/krb.h"
517 #else /* KRB524 */
518 #ifdef SOLARIS
519 #ifndef sun
520 /* For some reason lost in history the Makefile Solaris targets have -Usun */
521 #define sun
522 #endif /* sun */
523 #endif /* SOLARIS */
524 #include "krb.h"
525 #define krb_get_err_text_entry krb_get_err_text
526 #endif /* KRB524 */
527 #endif /* FTP_KRB4 */
528
529 #ifdef CK_SSL
530 #ifdef FTP_KRB4
531 #ifndef HEADER_DES_H
532 #define HEADER_DES_H
533 #endif /* HEADER_DES_H */
534 #endif /* FTP_KRB4 */
535 #include "ck_ssl.h"
536 #endif /* CK_SSL */
537
538 #ifdef FTP_SRP
539 #ifdef HAVE_PWD_H
540 #include "pwd.h"
541 #endif /* HAVE_PWD_H */
542 #include "t_pwd.h"
543 #include "t_client.h"
544 #include "krypto.h"
545 #endif /* FTP_SRP */
546
547 #ifdef FTP_GSSAPI
548 #include <gssapi/gssapi.h>
549 /*
550   Need to include the krb5 file, because we're doing manual fallback
551   from the v2 mech to the v1 mech.  Once there's real negotiation,
552   we can be generic again.
553 */
554 #include <gssapi/gssapi_generic.h>
555 #include <gssapi/gssapi_krb5.h>
556 static gss_ctx_id_t gcontext;
557
558 #ifdef MACOSX
559 /** exported constants defined in gssapi_krb5{,_nx}.h **/
560
561 /* these are bogus, but will compile */
562
563 /*
564  * The OID of the draft krb5 mechanism, assigned by IETF, is:
565  *      iso(1) org(3) dod(5) internet(1) security(5)
566  *      kerberosv5(2) = 1.3.5.1.5.2
567  * The OID of the krb5_name type is:
568  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
569  *      krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1
570  * The OID of the krb5_principal type is:
571  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
572  *      krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2
573  * The OID of the proposed standard krb5 mechanism is:
574  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
575  *      krb5(2) = 1.2.840.113554.1.2.2
576  * The OID of the proposed standard krb5 v2 mechanism is:
577  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
578  *      krb5v2(3) = 1.2.840.113554.1.2.3
579  *
580  */
581
582 /*
583  * Encoding rules: The first two values are encoded in one byte as 40
584  * * value1 + value2.  Subsequent values are encoded base 128, most
585  * significant digit first, with the high bit (\200) set on all octets
586  * except the last in each value's encoding.
587  */
588
589 static CONST gss_OID_desc
590 ck_krb5_gss_oid_array[] = {
591    /* this is the official, rfc-specified OID */
592    {9, "\052\206\110\206\367\022\001\002\002"},
593    /* this is the unofficial, wrong OID */
594    {5, "\053\005\001\005\002"},
595    /* this is the v2 assigned OID */
596    {9, "\052\206\110\206\367\022\001\002\003"},
597    /* these two are name type OID's */
598    {10, "\052\206\110\206\367\022\001\002\002\001"},
599    {10, "\052\206\110\206\367\022\001\002\002\002"},
600    { 0, 0 }
601 };
602
603 static
604 CONST gss_OID_desc * CONST gss_mech_krb5_v2 = ck_krb5_gss_oid_array+2;
605
606 #ifdef MACOSX103
607 static
608 CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0;
609 #endif /* MACOSX103 */
610
611 #ifndef MACOSX
612 static
613 CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0;
614 static
615 CONST gss_OID_desc * CONST gss_mech_krb5_old = ck_krb5_gss_oid_array+1;
616 static
617 CONST gss_OID_desc * CONST gss_nt_krb5_name = ck_krb5_gss_oid_array+3;
618 static
619 CONST gss_OID_desc * CONST gss_nt_krb5_principal = ck_krb5_gss_oid_array+4;
620 #endif  /* MACOSX */
621
622 /*
623  * See krb5/gssapi_krb5.c for a description of the algorithm for
624  * encoding an object identifier.
625  */
626
627 /*
628  * The OID of user_name is:
629  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
630  *      generic(1) user_name(1) = 1.2.840.113554.1.2.1.1
631  * machine_uid_name:
632  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
633  *      generic(1) machine_uid_name(2) = 1.2.840.113554.1.2.1.2
634  * string_uid_name:
635  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
636  *      generic(1) string_uid_name(3) = 1.2.840.113554.1.2.1.3
637  * service_name:
638  *      iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2)
639  *      generic(1) service_name(4) = 1.2.840.113554.1.2.1.4
640  * exported_name:
641  *      1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes),
642  *          4(gss-api-exported-name)
643  * host_based_service_name (v2):
644  *      iso (1) org (3), dod (6), internet (1), security (5), nametypes(6),
645  *      gss-host-based-services(2)
646  */
647
648 static gss_OID_desc ck_oids[] = {
649    {10, "\052\206\110\206\367\022\001\002\001\001"},
650    {10, "\052\206\110\206\367\022\001\002\001\002"},
651    {10, "\052\206\110\206\367\022\001\002\001\003"},
652    {10, "\052\206\110\206\367\022\001\002\001\004"},
653    { 6, "\053\006\001\005\006\004"},
654    { 6, "\053\006\001\005\006\002"},
655 };
656
657 static gss_OID ck_gss_nt_user_name = ck_oids+0;
658 static gss_OID ck_gss_nt_machine_uid_name = ck_oids+1;
659 static gss_OID ck_gss_nt_string_uid_name = ck_oids+2;
660 static gss_OID ck_gss_nt_service_name = ck_oids+3;
661 static gss_OID ck_gss_nt_exported_name = ck_oids+4;
662 static gss_OID ck_gss_nt_service_name_v2 = ck_oids+5;
663 #endif /* MACOSX */
664 #endif /* FTP_GSSAPI */
665
666 #ifdef OS2
667 #ifdef FTP_SRP
668 #define MAP_KRYPTO
669 #ifdef SRPDLL
670 #define MAP_SRP
671 #endif /* SRPDLL */
672 #endif /* FTP_SRP */
673 #ifdef FTP_KRB4
674 #define MAP_KRB4
675 #ifdef CK_ENCRYPTION
676 #define MAP_DES
677 #endif /* CK_ENCRYPTION */
678 #endif /* FTP_KRB4 */
679 #ifdef FTP_GSSAPI
680 #define MAP_GSSAPI
681 #define GSS_OIDS
682 #endif /* FTP_GSSAPI */
683 #include "ckoath.h"
684
685 extern int k95stdout, wherex[], wherey[];
686 extern unsigned char colorcmd;
687 #endif /* OS2 */
688
689 #ifdef FTP_KRB4
690 static char ftp_realm[REALM_SZ + 1];
691 static KTEXT_ST ftp_tkt;
692 #ifdef OS2
693 static LEASH_CREDENTIALS ftp_cred;
694 #else /* OS2 */
695 static CREDENTIALS ftp_cred;
696 #endif /* OS2 */
697 static MSG_DAT ftp_msg_data;
698 static des_key_schedule ftp_sched;
699 static int foo[4] = {99,99,99,99};
700 #endif /* FTP_KRB4 */
701
702 /* getreply() function codes */
703
704 #define GRF_AUTH 1                      /* Reply to AUTH command */
705 #define GRF_FEAT 2                      /* Reply to FEAT command */
706
707 /* Operational definitions */
708
709 #define DEF_VBM 0                       /* Default verbose mode */
710 /* #define SETVBM */                    /* (see getreply) */
711
712 #define URL_ONEFILE                     /* GET, not MGET, for FTP URL */
713
714 #define FTP_BUFSIZ 10240                /* Max size for FTP cmds & replies */
715 #define SRVNAMLEN 32                    /* Max length for server type name */
716 #define PWDSIZ 256
717 #define PASSBUFSIZ 256
718 #define PROMPTSIZ 256
719
720 #ifndef MGETMAX                         /* Max operands for MGET command */
721 #define MGETMAX 1000
722 #endif /* MGETMAX */
723
724 #ifdef FTP_SRP
725 #define FUDGE_FACTOR 100
726 #endif /* FTP_SRP */
727
728 /*
729   Amount of growth from cleartext to ciphertext.  krb_mk_priv adds this
730   number bytes.  Must be defined for each auth type.
731   GSSAPI appears to add 52 bytes, but I'm not sure it is a constant--hartmans
732   3DES requires 56 bytes.  Lets use 96 just to be sure.
733 */
734 #ifdef FTP_GSSAPI
735 #ifndef FUDGE_FACTOR
736 #define FUDGE_FACTOR 96
737 #endif /* FUDGE_FACTOR */
738 #endif /* FTP_GSSAPI */
739
740 #ifdef FTP_KRB4
741 #ifndef FUDGE_FACTOR
742 #define FUDGE_FACTOR 32
743 #endif /* FUDGE_FACTOR */
744 #endif /* FTP_KRB4 */
745
746 #ifndef FUDGE_FACTOR                    /* In case no auth types define it */
747 #define FUDGE_FACTOR 0
748 #endif /* FUDGE_FACTOR */
749
750 #ifndef MAXHOSTNAMELEN
751 #define MAXHOSTNAMELEN 64
752 #endif /* MAXHOSTNAMELEN */
753 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
754
755 /* Fascist compiler toadying */
756
757 #ifndef SENDARG2TYPE
758 #ifdef COMMENT                          /* Might be needed here and there */
759 #define SENDARG2TYPE const char *
760 #else
761 #define SENDARG2TYPE char *
762 #endif /* COMMENT */
763 #endif /* SENDARG2TYPE */
764
765 /* Common text messages */
766
767 static char *nocx = "?No FTP control connection\n";
768
769 static char *fncnam[] = {
770   "rename", "overwrite", "backup", "append", "discard", "ask", "update",
771   "dates-differ", ""
772 };
773
774 /* Macro definitions */
775
776 /* Used to speed up text-mode PUTs */
777 #define zzout(fd,c) \
778 ((fd<0)?(-1):((nout>=ucbufsiz)?(zzsend(fd,c)):(ucbuf[nout++]=c)))
779
780 #define CHECKCONN() if(!connected){printf(nocx);return(-9);}
781
782 /* Externals */
783
784 #ifdef CK_URL
785 extern struct urldata g_url;
786 #endif /* CK_URL */
787
788 #ifdef DYNAMIC
789 extern char *zinbuffer, *zoutbuffer;    /* Regular Kermit file i/o */
790 #else
791 extern char zinbuffer[], zoutbuffer[];
792 #endif /* DYNAMIC */
793 extern char *zinptr, *zoutptr;
794 extern int zincnt, zoutcnt, zobufsize, fncact;
795
796 #ifdef CK_TMPDIR
797 extern int f_tmpdir;                    /* Directory changed temporarily */
798 extern char savdir[];                   /* For saving current directory */
799 extern char * dldir;
800 #endif /* CK_TMPDIR */
801
802 extern char * rfspec, * sfspec, * srfspec, * rrfspec; /* For WHERE command */
803
804 extern xx_strp xxstring;
805 extern struct keytab onoff[], txtbin[], rpathtab[];
806 extern int nrpathtab, xfiletype, patterns, gnferror, moving, what, pktnum;
807 extern int success, nfils, sndsrc, quiet, nopush, recursive, inserver, binary;
808 extern int filepeek, nscanfile, fsecs, xferstat, xfermode, lastxfer, tsecs;
809 extern int backgrd, spackets, rpackets, spktl, rpktl, xaskmore, cmd_rows;
810 extern int nolinks, msgflg, keep;
811 extern CK_OFF_T fsize, ffc, tfc, sendstart, sndsmaller, sndlarger, rs_len;
812 extern long filcnt, xfsecs, tfcps, cps, oldcps;
813
814 #ifdef FTP_TIMEOUT
815 int ftp_timed_out = 0;
816 long ftp_timeout = 0;
817 #endif  /* FTP_TIMEOUT */
818
819 #ifdef GFTIMER
820 extern CKFLOAT fptsecs, fpfsecs, fpxfsecs;
821 #else
822 extern long xfsecs;
823 #endif /* GFTIMER */
824
825 extern char filnam[], * filefile, myhost[];
826 extern char * snd_move, * rcv_move, * snd_rename, * rcv_rename;
827 extern int g_skipbup, skipbup, sendmode;
828 extern int g_displa, fdispla, displa;
829
830 #ifdef LOCUS
831 extern int locus, autolocus;
832 #endif /* LOCUS */
833
834 #ifndef NOCSETS
835 extern int nfilc, dcset7, dcset8, fileorder;
836 extern struct csinfo fcsinfo[];
837 extern struct keytab fcstab[];
838 extern int fcharset;
839 #endif /* NOCSETS */
840
841 extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */
842 extern char sndnbefore[], sndnafter[], *rcvexcept[];
843 extern CHAR feol;
844
845 extern char * remdest;
846 extern int remfile, remappd, rempipe;
847
848 #ifndef NOSPL
849 extern int cmd_quoting;
850 #ifdef PUTARRAY
851 extern int sndxlo, sndxhi, sndxin;
852 extern char sndxnam[];
853 extern char **a_ptr[];                  /* Array pointers */
854 extern int a_dim[];                     /* Array dimensions */
855 #endif /* PUTARRAY */
856 #endif /* NOSPL */
857
858 #ifndef NOMSEND                         /* MPUT and ADD SEND-LIST lists */
859 extern char *msfiles[];
860 extern int filesinlist;
861 extern struct filelist * filehead;
862 extern struct filelist * filetail;
863 extern struct filelist * filenext;
864 extern int addlist;
865 extern char fspec[];                    /* Most recent filespec */
866 extern int fspeclen;                    /* Length of fspec[] buffer */
867 #endif /* NOMSEND */
868
869 extern int pipesend;
870 #ifdef PIPESEND
871 extern char * sndfilter, * rcvfilter;
872 #endif /* PIPESEND */
873
874 #ifdef CKROOT
875 extern int ckrooterr;
876 #endif /* CKROOT */
877
878 #ifdef KRB4
879 extern int krb4_autoget;
880 _PROTOTYP(char * ck_krb4_realmofhost,(char *));
881 #endif /* KRB4 */
882
883 #ifdef KRB5
884 extern int krb5_autoget;
885 extern int krb5_d_no_addresses;
886 _PROTOTYP(char * ck_krb5_realmofhost,(char *));
887 #endif /* KRB5 */
888
889 #ifdef DCMDBUF
890 extern char *atmbuf;                    /* Atom buffer (malloc'd) */
891 extern char *cmdbuf;                    /* Command buffer (malloc'd) */
892 extern char *line;                      /* Big string buffer #1 */
893 extern char *tmpbuf;                    /* Big string buffer #2 */
894 #else
895 extern char atmbuf[];                   /* The same, but static */
896 extern char cmdbuf[];
897 extern char line[];
898 extern char tmpbuf[];
899 #endif /* DCMDBUF */
900
901 extern char * cmarg, * cmarg2, ** cmlist; /* For setting up file lists */
902
903 /* Public variables declared here */
904
905 #ifdef NOXFER
906 int ftpget  =  1;                       /* GET/PUT/REMOTE orientation FTP */
907 #else
908 int ftpget  =  2;                       /* GET/PUT/REMOTE orientation AUTO */
909 #endif /* NOXFER */
910 int ftpcode = -1;                       /* Last FTP response code */
911 int ftp_cmdlin = 0;                     /* FTP invoked from command line */
912 int ftp_fai = 0;                        /* FTP failure count */
913 int ftp_deb = 0;                        /* FTP debugging */
914 int ftp_dis = -1;                       /* FTP display style */
915 int ftp_log = 1;                        /* FTP Auto-login */
916 int sav_log = -1;
917 int ftp_action = 0;                     /* FTP action from command line */
918 int ftp_dates = 1;                      /* Set file dates from server */
919 int ftp_xfermode = XMODE_A;             /* FTP-specific transfer mode */
920
921 char ftp_reply_str[FTP_BUFSIZ] = "";    /* Last line of previous reply */
922 char ftp_srvtyp[SRVNAMLEN] = { NUL, NUL }; /* Server's system type */
923 char ftp_user_host[MAX_DNS_NAMELEN]= ""; /* FTP hostname specified by user */
924 char * ftp_host = NULL;                 /* FTP hostname */
925 char * ftp_logname = NULL;              /* FTP username */
926 char * ftp_rdir = NULL;                 /* Remote directory from cmdline */
927 char * ftp_apw = NULL;                  /* Anonymous password */
928
929 /* Definitions and typedefs needed for prototypes */
930
931 #define sig_t my_sig_t
932 #define sigtype SIGTYP
933 typedef sigtype (*sig_t)();
934
935 /* Static global variables */
936
937 static char ftpsndbuf[FTP_BUFSIZ+64];
938
939 static char * fts_sto = NULL;
940
941 static int ftpsndret = 0;
942 static struct _ftpsnd {
943     sig_t oldintr, oldintp;
944     int            reply;
945     int            incs,
946                    outcs;
947     char *         cmd, * local, * remote;
948     int            bytes;
949     int            restart;
950     int            xlate;
951     char *         lmode;
952 } ftpsnd;
953
954 /*
955   This is just a first stab -- these strings should match how the
956   corresponding FTP servers identify themselves.
957 */
958 #ifdef UNIX
959 static char * myostype = "UNIX";
960 #else
961 #ifdef VMS
962 /* not yet... */
963 static char * myostype = "VMS";
964 #else
965 #ifdef OS2
966 #ifdef NT
967 static char * myostype = "WIN32";
968 #else
969 static char * myostype = "OS/2";
970 #endif /* NT */
971 #else
972 static char * myostype = "UNSUPPORTED";
973 #endif /* OS2  */
974 #endif /* VMS */
975 #endif /* UNIX */
976
977 static int noinit = 0;                  /* Don't send REST, STRU, MODE */
978 static int alike = 0;                   /* Client/server like platforms */
979 static int local = 1;                   /* Shadows Kermit global 'local' */
980 static int dout = -1;                   /* Data connection file descriptor */
981 static int dpyactive = 0;               /* Data transfer is active */
982 static int globaldin = -1;              /* Data connection f.d. */
983 static int out2screen = 0;              /* GET output is to screen */
984 static int forcetype = 0;               /* Force text or binary mode */
985 static int cancelfile = 0;              /* File canceled */
986 static int cancelgroup = 0;             /* Group canceled */
987 static int anonymous = 0;               /* Logging in as anonymous */
988 static int loggedin = 0;                /* Logged in (or not) */
989 static int puterror = 0;                /* What to do on PUT error */
990 static int geterror = 0;                /* What to do on GET error */
991 static int rfrc = 0;                    /* remote_files() return code */
992 static int okrestart = 0;               /* Server understands REST */
993 static int printlines = 0;              /* getreply()should print data lines */
994 static int haveurl = 0;                 /* Invoked by command-line FTP URL */
995 static int mdtmok = 1;                  /* Server supports MDTM */
996 static int sizeok = 1;
997 static int featok = 1;
998 static int mlstok = 1;
999 static int stouarg = 1;
1000 static int typesent = 0;
1001 static int havesigint = 0;
1002 static long havetype =  0;
1003 static CK_OFF_T havesize = (CK_OFF_T)-1;
1004 static char * havemdtm = NULL;
1005 static int mgetmethod = 0;              /* NLST or MLSD */
1006 static int mgetforced = 0;
1007
1008 static int i, /* j, k, */ x, y, z;      /* Volatile temporaries */
1009 static int c0, c1;                      /* Temp variables for characters */
1010
1011 static char putpath[CKMAXPATH+1] = { NUL, NUL };
1012 static char asnambuf[CKMAXPATH+1] = { NUL, NUL };
1013
1014 #define RFNBUFSIZ 4096                  /* Remote filename buffer size */
1015
1016 static unsigned int maxbuf = 0, actualbuf = 0;
1017 static CHAR *ucbuf = NULL;
1018 static int ucbufsiz = 0;
1019 static unsigned int nout = 0;           /* Number of chars in ucbuf */
1020
1021 static jmp_buf recvcancel;
1022 static jmp_buf sendcancel;
1023 static jmp_buf ptcancel;
1024 static jmp_buf jcancel;
1025 static int ptabflg = 0;
1026
1027 /* Protection level symbols */
1028
1029 #define FPL_CLR 1                       /* Clear */
1030 #define FPL_SAF 2                       /* Safe */
1031 #define FPL_PRV 3                       /* Private */
1032 #define FPL_CON 4                       /* Confidential */
1033
1034 /* Symbols for file types returned by MLST/MLSD */
1035
1036 #define FTYP_FILE 1                     /* Regular file */
1037 #define FTYP_DIR  2                     /* Directory */
1038 #define FTYP_CDIR 3                     /* Current directory */
1039 #define FTYP_PDIR 4                     /* Parent directory */
1040
1041 /* File type symbols keyed to the file-type symbols from ckcker.h */
1042
1043 #define FTT_ASC XYFT_T                  /* ASCII (text) */
1044 #define FTT_BIN XYFT_B                  /* Binary (image) */
1045 #define FTT_TEN XYFT_X                  /* TENEX (TOPS-20) */
1046
1047 /* Server feature table - sfttab[0] > 0 means server supports FEAT and OPTS */
1048
1049 static int sfttab[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
1050
1051 #define SFT_AUTH  1                     /* FTP server feature codes */
1052 #define SFT_LANG  2
1053 #define SFT_MDTM  3
1054 #define SFT_MLST  4
1055 #define SFT_PBSZ  5
1056 #define SFT_PROT  6
1057 #define SFT_REST  7
1058 #define SFT_SIZE  8
1059 #define SFT_TVFS  9
1060 #define SFT_UTF8 10
1061
1062 #define CNV_AUTO  2                     /* FTP filename conversion */
1063 #define CNV_CNV   1
1064 #define CNV_LIT   0
1065
1066 /* SET FTP values */
1067
1068 static int                              /* SET FTP values... */
1069   ftp_aut = 1,                          /* Auto-authentication */
1070 #ifdef FTP_SECURITY
1071   ftp_cry = 1,                          /* Auto-encryption */
1072   ftp_cfw = 0,                          /* Credential forwarding */
1073 #endif /* FTP_SECURITY */
1074   ftp_cpl = FPL_CLR,                    /* Command protection level */
1075   ftp_dpl = FPL_CLR,                    /* Data protection level */
1076 #ifdef FTP_PROXY
1077   ftp_prx = 0,                          /* Use proxy */
1078 #endif /* FTP_PROXY */
1079   sav_psv = -1,                         /* For saving passive mode */
1080   ftp_psv = 1,                          /* Passive mode */
1081   ftp_spc = 1,                          /* Send port commands */
1082   ftp_typ = FTT_ASC,                    /* Type */
1083   get_auto = 1,                         /* Automatic type switching for GET */
1084   tenex = 0,                            /* Type is Tenex */
1085   ftp_usn = 0,                          /* Unique server names */
1086   ftp_prm = 0,                          /* Permissions */
1087   ftp_cnv = CNV_AUTO,                   /* Filename conversion (2 = auto) */
1088   ftp_vbm = DEF_VBM,                    /* Verbose mode */
1089   ftp_vbx = DEF_VBM,                    /* Sticky version of same */
1090   ftp_err = 0,                          /* Error action */
1091   ftp_fnc = -1;                         /* Filename collision action */
1092
1093 #ifdef CK_SSL
1094 static int ftp_bug_use_ssl_v2 = 0;      /* use SSLv2 for AUTH SSL */
1095 static int ftp_bug_use_ssl_v3 = 0;      /* use SSLv3 for AUTH SSL */
1096 #endif /* CK_SSL */
1097
1098 static int
1099 #ifdef NOCSETS
1100   ftp_csr = -1,                         /* Remote (server) character set */
1101 #else
1102   ftp_csr = FC_UTF8,
1103 #endif /* NOCSETS */
1104   ftp_xla = 0;                          /* Character-set translation on/off */
1105 int
1106   ftp_csx = -1,                         /* Remote charset currently in use */
1107   ftp_csl = -1;                         /* Local charset currently in use */
1108
1109 static int g_ftp_typ = FTT_ASC;         /* For saving and restoring ftp_typ */
1110
1111 char * ftp_nml = NULL;                  /* /NAMELIST */
1112 char * ftp_tmp = NULL;                  /* Temporary string */
1113 static char * ftp_acc = NULL;           /* Account string */
1114 static char * auth_type = NULL;         /* Authentication type */
1115 static char * srv_renam = NULL;         /* Server-rename string */
1116 FILE * fp_nml = NULL;                   /* Namelist file pointer */
1117
1118 static int csocket = -1;                /* Control socket */
1119 static int connected = 0;               /* Connected to FTP server */
1120 /* static unsigned short ftp_port = 0; */ /* FTP port */ 
1121 /* static int ftp_port = 0; */          /* SMS 2007/02/15 */
1122 static int ftp_port = 0;                /* fdc 2007/08/30 */
1123 #ifdef FTPHOST
1124 static int hostcmd = 0;                 /* Has HOST command been sent */
1125 #endif /* FTPHOST */
1126 static int form, mode, stru, bytesize, curtype = FTT_ASC;
1127 static char bytename[8];
1128
1129 /* For parsing replies to FTP server command */
1130 static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
1131
1132 #ifdef FTP_PROXY
1133 static int proxy, unix_proxy
1134 #endif /* FTP_PROXY */
1135
1136 static char pasv[64];                   /* Passive-mode port */
1137 static int passivemode = 0;
1138 static int sendport = 0;
1139 static int servertype = 0;              /* FTP server's OS type */
1140
1141 static int testing = 0;
1142 static char ftpcmdbuf[FTP_BUFSIZ];
1143
1144 /* Macro definitions */
1145
1146 #define UC(b) ckitoa(((int)b)&0xff)
1147 #define nz(x) ((x) == 0 ? 1 : (x))
1148
1149 /* Command tables and definitions */
1150
1151 #define FTP_ACC  1                      /* FTP command keyword codes */
1152 #define FTP_APP  2
1153 #define FTP_CWD  3
1154 #define FTP_CHM  4
1155 #define FTP_CLS  5
1156 #define FTP_DEL  6
1157 #define FTP_DIR  7
1158 #define FTP_GET  8
1159 #define FTP_IDL  9
1160 #define FTP_MDE 10
1161 #define FTP_MDI 11
1162 #define FTP_MGE 12
1163 #define FTP_MKD 13
1164 #define FTP_MOD 14
1165 #define FTP_MPU 15
1166 #define FTP_OPN 16
1167 #define FTP_PUT 17
1168 #define FTP_PWD 18
1169 #define FTP_RGE 19
1170 #define FTP_REN 20
1171 #define FTP_RES 21
1172 #define FTP_HLP 22
1173 #define FTP_RMD 23
1174 #define FTP_STA 24
1175 #define FTP_SIT 25
1176 #define FTP_SIZ 26
1177 #define FTP_SYS 27
1178 #define FTP_UMA 28
1179 #define FTP_GUP 29
1180 #define FTP_USR 30
1181 #define FTP_QUO 31
1182 #define FTP_TYP 32
1183 #define FTP_FEA 33
1184 #define FTP_OPT 34
1185 #define FTP_CHK 35
1186 #define FTP_VDI 36
1187 #define FTP_ENA 37
1188 #define FTP_DIS 38
1189 #define FTP_REP 39
1190
1191 struct keytab gprtab[] = {              /* GET-PUT-REMOTE keywords */
1192     { "auto",    2, 0 },
1193     { "ftp",     1, 0 },
1194     { "kermit",  0, 0  }
1195 };
1196
1197 static struct keytab qorp[] = {         /* QUIT or PROCEED keywords */
1198     { "proceed", 0, 0 },                /* 0 = proceed */
1199     { "quit",    1, 0 }                 /* 1 = quit */
1200 };
1201
1202 static struct keytab ftpcmdtab[] = {    /* FTP command table */
1203     { "account",   FTP_ACC, 0 },
1204     { "append",    FTP_APP, 0 },
1205     { "bye",       FTP_CLS, 0 },
1206     { "cd",        FTP_CWD, 0 },
1207     { "cdup",      FTP_GUP, 0 },
1208     { "check",     FTP_CHK, 0 },
1209     { "chmod",     FTP_CHM, 0 },
1210     { "close",     FTP_CLS, 0 },
1211     { "cwd",       FTP_CWD, CM_INV },
1212     { "delete",    FTP_MDE, 0 },
1213     { "directory", FTP_DIR, 0 },
1214     { "disable",   FTP_DIS, 0 },
1215     { "enable",    FTP_ENA, 0 },
1216     { "features",  FTP_FEA, 0 },
1217     { "get",       FTP_GET, 0 },
1218     { "help",      FTP_HLP, 0 },
1219     { "idle",      FTP_IDL, 0 },
1220     { "login",     FTP_USR, CM_INV },
1221     { "mdelete",   FTP_MDE, CM_INV },
1222     { "mget",      FTP_MGE, 0 },
1223     { "mkdir",     FTP_MKD, 0 },
1224     { "modtime",   FTP_MOD, 0 },
1225     { "mput",      FTP_MPU, 0 },
1226     { "open",      FTP_OPN, 0 },
1227     { "opt",       FTP_OPT, CM_INV|CM_ABR },
1228     { "opts",      FTP_OPT, CM_INV },
1229     { "options",   FTP_OPT, 0 },
1230     { "put",       FTP_PUT, 0 },
1231     { "pwd",       FTP_PWD, 0 },
1232     { "quit",      FTP_CLS, CM_INV },
1233     { "quote",     FTP_QUO, 0 },
1234     { "reget",     FTP_RGE, 0 },
1235     { "rename",    FTP_REN, 0 },
1236     { "reput",     FTP_REP, 0 },
1237     { "resend",    FTP_REP, CM_INV },
1238     { "reset",     FTP_RES, 0 },
1239     { "rmdir",     FTP_RMD, 0 },
1240     { "send",      FTP_PUT, CM_INV },
1241     { "site",      FTP_SIT, 0 },
1242     { "size",      FTP_SIZ, 0 },
1243     { "status",    FTP_STA, 0 },
1244     { "system",    FTP_SYS, 0 },
1245     { "type",      FTP_TYP, 0 },
1246     { "umask",     FTP_UMA, 0 },
1247     { "up",        FTP_GUP, CM_INV },
1248     { "user",      FTP_USR, 0 },
1249     { "vdirectory",FTP_VDI, 0 },
1250     { "", 0, 0 }
1251 };
1252 static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1;
1253
1254 #define OPN_ANO 1                       /* FTP OPEN switch codes */
1255 #define OPN_PSW 2
1256 #define OPN_USR 3
1257 #define OPN_ACC 4
1258 #define OPN_ACT 5
1259 #define OPN_PSV 6
1260 #define OPN_TLS 7
1261 #define OPN_NIN 8
1262 #define OPN_NOL 9
1263
1264 #ifdef FTP_SECURITY
1265 #ifdef CK_SSL
1266 #define USETLSTAB
1267 static struct keytab tlstab[] = {       /* FTP SSL/TLS switches */
1268     { "/ssl",       OPN_TLS, 0    },
1269     { "/tls",       OPN_TLS, 0    },
1270     { "", 0, 0 }
1271 };
1272 static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1;
1273 #endif /* CK_SSL */
1274 #endif /* FTP_SECURITY */
1275
1276 static struct keytab ftpswitab[] = {    /* FTP command switches */
1277     { "/account",   OPN_ACC, CM_ARG },
1278     { "/active",    OPN_ACT, 0      },
1279     { "/anonymous", OPN_ANO, 0      },
1280     { "/noinit",    OPN_NIN, 0      },
1281     { "/nologin",   OPN_NOL, 0      },
1282     { "/passive",   OPN_PSV, 0      },
1283     { "/password",  OPN_PSW, CM_ARG },
1284     { "/user",      OPN_USR, CM_ARG },
1285     { "", 0, 0 }
1286 };
1287 static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1;
1288
1289 /* FTP { ENABLE, DISABLE } items */
1290
1291 #define ENA_FEAT 1
1292 #define ENA_MDTM 2
1293 #define ENA_MLST 3
1294 #define ENA_SIZE 4
1295 #define ENA_AUTH 5
1296
1297 static struct keytab ftpenatab[] = {
1298     { "AUTH",  ENA_AUTH, 0 },
1299     { "FEAT",  ENA_FEAT, 0 },
1300     { "MDTM",  ENA_MDTM, 0 },
1301     { "ML",    ENA_MLST, CM_INV|CM_ABR },
1302     { "MLS",   ENA_MLST, CM_INV|CM_ABR },
1303     { "MLSD",  ENA_MLST, CM_INV },
1304     { "MLST",  ENA_MLST, 0 },
1305     { "SIZE",  ENA_SIZE, 0 },
1306     { "", 0, 0 }
1307 };
1308 static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1;
1309
1310 /* SET FTP command keyword indices */
1311
1312 #define FTS_AUT  1                      /* Autoauthentication */
1313 #define FTS_CRY  2                      /* Encryption */
1314 #define FTS_LOG  3                      /* Autologin */
1315 #define FTS_CPL  4                      /* Command protection level */
1316 #define FTS_CFW  5                      /* Credentials forwarding */
1317 #define FTS_DPL  6                      /* Data protection level */
1318 #define FTS_DBG  7                      /* Debugging */
1319 #define FTS_PSV  8                      /* Passive mode */
1320 #define FTS_SPC  9                      /* Send port commands */
1321 #define FTS_TYP 10                      /* (file) Type */
1322 #define FTS_USN 11                      /* Unique server names (for files) */
1323 #define FTS_VBM 12                      /* Verbose mode */
1324 #define FTS_ATP 13                      /* Authentication type */
1325 #define FTS_CNV 14                      /* Filename conversion */
1326 #define FTS_TST 15                      /* Test (progress) messages */
1327 #define FTS_PRM 16                      /* (file) Permissions */
1328 #define FTS_XLA 17                      /* Charset translation */
1329 #define FTS_CSR 18                      /* Server charset */
1330 #define FTS_ERR 19                      /* Error action */
1331 #define FTS_FNC 20                      /* Collision */
1332 #define FTS_SRP 21                      /* SRP options */
1333 #define FTS_GFT 22                      /* GET automatic file-type switching */
1334 #define FTS_DAT 23                      /* Set file dates */
1335 #define FTS_STO 24                      /* Server time offset */
1336 #define FTS_APW 25                      /* Anonymous password */
1337 #define FTS_DIS 26                      /* File-transfer display style */
1338 #define FTS_BUG 27                      /* Bug(s) */
1339 #define FTS_TMO 28                      /* Timeout */
1340
1341 /* FTP BUGS */
1342
1343 #define FTB_SV2  1                      /* use SSLv2 */
1344 #define FTB_SV3  2                      /* use SSLv3 */
1345
1346 static struct keytab ftpbugtab[] = {
1347     { "use-ssl-v2",     FTB_SV2,        0 },
1348     { "use-ssl-v3",     FTB_SV3,        0 }
1349
1350 };
1351 static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab));
1352
1353 /* FTP PUT options (mutually exclusive, not a bitmask) */
1354
1355 #define PUT_UPD 1                       /* Update */
1356 #define PUT_RES 2                       /* Restart */
1357 #define PUT_SIM 4                       /* Simulation */
1358 #define PUT_DIF 8                       /* Dates Differ */
1359
1360 static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */
1361 #ifndef MAC
1362     { "append",    XYFX_A, 0 },         /* append to old file */
1363 #endif /* MAC */
1364 #ifdef COMMENT
1365     { "ask",       XYFX_Q, 0 },         /* ask what to do (not implemented) */
1366 #endif
1367     { "backup",    XYFX_B, 0 },         /* rename old file */
1368 #ifndef MAC
1369     { "dates-differ", XYFX_M, 0 },      /* accept if dates differ */
1370     { "discard",   XYFX_D, 0 },         /* don't accept new file */
1371     { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
1372 #endif /* MAC */
1373     { "overwrite", XYFX_X, 0 },         /* overwrite the old file */
1374     { "rename",    XYFX_R, 0 },         /* rename the incoming file */
1375 #ifndef MAC                             /* This crashes Mac Kermit. */
1376     { "update",    XYFX_U, 0 },         /* replace if newer */
1377 #endif /* MAC */
1378     { "", 0, 0 }
1379 };
1380 static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1;
1381
1382
1383 #ifdef FTP_SECURITY
1384 /* FTP authentication options */
1385
1386 #define FTA_AUTO 0                      /* Auto */
1387 #define FTA_SRP  1                      /* SRP */
1388 #define FTA_GK5  2                      /* Kerberos 5 */
1389 #define FTA_K4   3                      /* Kerberos 4 */
1390 #define FTA_SSL  4                      /* SSL */
1391 #define FTA_TLS  5                      /* TLS */
1392
1393 /* FTP authentication types */
1394
1395 #define FTPATYPS 8
1396 static int ftp_auth_type[FTPATYPS] = {
1397 #ifdef FTP_GSSAPI
1398     FTA_GK5,                            /* GSSAPI Kerberos 5 */
1399 #endif /* FTP_GK5 */
1400 #ifdef FTP_SRP
1401     FTA_SRP,                            /* SRP */
1402 #endif /* FTP_SRP */
1403 #ifdef FTP_KRB4
1404     FTA_K4,                             /* Kerberos 4 */
1405 #endif /* FTP_KRB4 */
1406 #ifdef CK_SSL
1407     FTA_TLS,                            /* TLS */
1408     FTA_SSL,                            /* SSL */
1409 #endif /* CK_SSL */
1410     0
1411 };
1412
1413 static struct keytab ftpauth[] = {      /* SET FTP AUTHTYPE cmd table */
1414     { "automatic", FTA_AUTO,  CM_INV },
1415 #ifdef FTP_GSSAPI
1416     { "gssapi-krb5", FTA_GK5, 0 },
1417 #endif /* FTP_GSSAPI */
1418 #ifdef FTP_KRB4
1419     { "k4",       FTA_K4,     CM_INV },
1420 #endif /* FTP_KRB4 */
1421 #ifdef FTP_GSSAPI
1422     { "k5",        FTA_GK5,   CM_INV },
1423 #endif /* FTP_GSSAPI */
1424 #ifdef FTP_KRB4
1425     { "kerberos4", FTA_K4,    0 },
1426 #endif /* FTP_KRB4 */
1427 #ifdef FTP_GSSAPI
1428     { "kerberos5", FTA_GK5,   CM_INV },
1429 #endif /* FTP_GSSAPI */
1430 #ifdef FTP_KRB4
1431     { "kerberos_iv",FTA_K4,   CM_INV },
1432 #endif /* FTP_KRB4 */
1433 #ifdef FTP_GSSAPI
1434     { "kerberos_v", FTA_GK5,  CM_INV },
1435 #endif /* FTP_GSSAPI */
1436 #ifdef FTP_KRB4
1437     { "krb4",     FTA_K4,     CM_INV },
1438 #endif /* FTP_KRB4 */
1439 #ifdef FTP_GSSAPI
1440     { "krb5",     FTA_GK5,    CM_INV },
1441 #endif /* FTP_GSSAPI */
1442 #ifdef FTP_SRP
1443     { "srp",      FTA_SRP,     0 },
1444 #endif /* FTP_SRP */
1445 #ifdef CK_SSL
1446     { "ssl",      FTA_SSL,     0 },
1447     { "tls",      FTA_TLS,     0 },
1448 #endif /* CK_SSL */
1449     { "", 0, 0 }
1450 };
1451 static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1;
1452
1453 #ifdef FTP_SRP
1454 #define SRP_CIPHER 1
1455 #define SRP_HASH   2
1456 static struct keytab ftpsrp[] = {      /* SET FTP SRP command table */
1457     { "cipher",   SRP_CIPHER,     0 },
1458     { "hash",     SRP_HASH,       0 },
1459     { "", 0, 0 }
1460 };
1461 static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1;
1462 #endif /* FTP_SRP */
1463 #endif /* FTP_SECURITY */
1464
1465 static struct keytab ftpset[] = {       /* SET FTP commmand table */
1466     { "anonymous-password",       FTS_APW, 0 },
1467 #ifdef FTP_SECURITY
1468     { "authtype",                 FTS_ATP, 0 },
1469     { "autoauthentication",       FTS_AUT, 0 },
1470     { "autoencryption",           FTS_CRY, 0 },
1471 #endif /* FTP_SECURITY */
1472     { "autologin",                FTS_LOG, 0 },
1473     { "bug",                      FTS_BUG, 0 },
1474 #ifndef NOCSETS
1475     { "character-set-translation",FTS_XLA, 0 },
1476 #endif /* NOCSETS */
1477     { "collision",                FTS_FNC, 0 },
1478 #ifdef FTP_SECURITY
1479     { "command-protection-level", FTS_CPL, 0 },
1480     { "cpl",                      FTS_CPL, CM_INV },
1481     { "credential-forwarding",    FTS_CFW, 0 },
1482     { "da",                       FTS_DAT, CM_INV|CM_ABR },
1483     { "data-protection-level",    FTS_DPL, 0 },
1484 #endif /* FTP_SECURITY */
1485     { "dates",                    FTS_DAT, 0 },
1486     { "debug",                    FTS_DBG, 0 },
1487     { "display",                  FTS_DIS, 0 },
1488 #ifdef FTP_SECURITY
1489     { "dpl",                      FTS_DPL, CM_INV },
1490 #endif /* FTP_SECURITY */
1491     { "error-action",             FTS_ERR, 0 },
1492     { "filenames",                FTS_CNV, 0 },
1493     { "get-filetype-switching",   FTS_GFT, 0 },
1494     { "passive-mode",             FTS_PSV, 0 },
1495     { "pasv",                     FTS_PSV, CM_INV },
1496     { "permissions",              FTS_PRM, 0 },
1497     { "progress-messages",        FTS_TST, 0 },
1498     { "send-port-commands",       FTS_SPC, 0 },
1499 #ifndef NOCSETS
1500     { "server-character-set",     FTS_CSR, 0 },
1501 #endif /* NOCSETS */
1502     { "server-time-offset",       FTS_STO, 0 },
1503 #ifdef FTP_SRP
1504     { "srp",                      FTS_SRP, 0 },
1505 #else
1506     { "srp",                      FTS_SRP, CM_INV },
1507 #endif /* FTP_SRP */
1508 #ifdef FTP_TIMEOUT
1509     { "timeout",                  FTS_TMO, 0 },
1510 #endif  /* FTP_TIMEOUT */
1511     { "type",                     FTS_TYP, 0 },
1512     { "unique-server-names",      FTS_USN, 0 },
1513     { "verbose-mode",             FTS_VBM, 0 },
1514     { "", 0, 0 }
1515 };
1516 static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1;
1517
1518 /*
1519   GET and PUT switches are approximately the same as Kermit GET and SEND,
1520   and use the same SND_xxx definitions, but hijack a couple for FTP use.
1521   Don't just make up new ones, since the number of SND_xxx options must be
1522   known in advance for the switch-parsing arrays.
1523 */
1524 #define SND_USN SND_PRO                 /* /UNIQUE instead of /PROTOCOL */
1525 #define SND_PRM SND_PIP                 /* /PERMISSIONS instead of /PIPES */
1526 #define SND_TEN SND_CAL                 /* /TENEX instead of /CALIBRATE */
1527
1528 static struct keytab putswi[] = {       /* FTP PUT switch table */
1529     { "/after",                SND_AFT, CM_ARG },
1530 #ifdef PUTARRAY
1531     { "/array",                SND_ARR, CM_ARG },
1532 #endif /* PUTARRAY */
1533     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1534     { "/as-name",              SND_ASN, CM_ARG },
1535     { "/ascii",                SND_TXT, CM_INV },
1536     { "/b",                    SND_BIN, CM_INV|CM_ABR },
1537     { "/before",               SND_BEF, CM_ARG },
1538     { "/binary",               SND_BIN, 0 },
1539 #ifdef PUTPIPE
1540     { "/command",              SND_CMD, CM_PSH },
1541 #endif /* PUTPIPE */
1542 #ifdef COMMENT
1543 /* This works but it's dangerous */
1544 #ifdef DOUPDATE
1545     { "/dates-differ",         SND_DIF, CM_INV },
1546 #endif /* DOUPDATE */
1547 #endif /* COMMENT */
1548     { "/delete",               SND_DEL, 0 },
1549 #ifdef UNIXOROSK
1550     { "/dotfiles",             SND_DOT, 0 },
1551 #endif /* UNIXOROSK */
1552     { "/error-action",         SND_ERR, CM_ARG },
1553     { "/except",               SND_EXC, CM_ARG },
1554     { "/filenames",            SND_NAM, CM_ARG },
1555 #ifdef PIPESEND
1556 #ifndef NOSPL
1557     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1558 #endif /* NOSPL */
1559 #endif /* PIPESEND */
1560 #ifdef CKSYMLINK
1561     { "/followlinks",          SND_LNK, 0 },
1562 #endif /* CKSYMLINK */
1563 #ifdef VMS
1564     { "/image",                SND_IMG, 0 },
1565 #else
1566     { "/image",                SND_BIN, CM_INV },
1567 #endif /* VMS */
1568     { "/larger-than",          SND_LAR, CM_ARG },
1569     { "/listfile",             SND_FIL, CM_ARG },
1570 #ifndef NOCSETS
1571     { "/local-character-set",  SND_CSL, CM_ARG },
1572 #endif /* NOCSETS */
1573 #ifdef CK_TMPDIR
1574     { "/move-to",              SND_MOV, CM_ARG },
1575 #endif /* CK_TMPDIR */
1576     { "/nobackupfiles",        SND_NOB, 0 },
1577 #ifdef UNIXOROSK
1578     { "/nodotfiles",           SND_NOD, 0 },
1579 #endif /* UNIXOROSK */
1580 #ifdef CKSYMLINK
1581     { "/nofollowlinks",        SND_NLK, 0 },
1582 #endif /* CKSYMLINK */
1583
1584     { "/not-after",            SND_NAF, CM_ARG },
1585     { "/not-before",           SND_NBE, CM_ARG },
1586 #ifdef UNIX
1587     { "/permissions",          SND_PRM, CM_ARG },
1588 #else
1589     { "/permissions",          SND_PRM, CM_ARG|CM_INV },
1590 #endif /* UNIX */
1591     { "/quiet",                SND_SHH, 0 },
1592 #ifdef FTP_RESTART
1593     { "/recover",              SND_RES, 0 },
1594 #endif /* FTP_RESTART */
1595 #ifdef RECURSIVE
1596     { "/recursive",            SND_REC, 0 },
1597 #endif /* RECURSIVE */
1598     { "/rename-to",            SND_REN, CM_ARG },
1599 #ifdef FTP_RESTART
1600     { "/restart",              SND_RES, CM_INV },
1601 #endif /* FTP_RESTART */
1602 #ifndef NOCSETS
1603     { "/server-character-set", SND_CSR, CM_ARG },
1604 #endif /* NOCSETS */
1605     { "/server-rename-to",     SND_SRN, CM_ARG },
1606     { "/simulate",             SND_SIM, 0 },
1607     { "/since",                SND_AFT, CM_INV|CM_ARG },
1608     { "/smaller-than",         SND_SMA, CM_ARG },
1609 #ifdef COMMENT
1610     { "/starting-at",          SND_STA, CM_ARG },
1611 #endif /* COMMENT */
1612 #ifdef RECURSIVE
1613     { "/subdirectories",       SND_REC, CM_INV },
1614 #endif /* RECURSIVE */
1615     { "/tenex",                SND_TEN, 0 },
1616     { "/text",                 SND_TXT, 0 },
1617 #ifndef NOCSETS
1618     { "/transparent",          SND_XPA, 0 },
1619 #endif /* NOCSETS */
1620     { "/type",                 SND_TYP, CM_ARG },
1621 #ifdef DOUPDATE
1622     { "/update",               SND_UPD, 0 },
1623 #endif /* DOUPDATE */
1624     { "/unique-server-names",  SND_USN, 0 },
1625     { "", 0, 0 }
1626 };
1627 static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1;
1628
1629 static struct keytab getswi[] = {       /* FTP [M]GET switch table */
1630     { "/after",                SND_AFT, CM_INV },
1631     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1632     { "/as-name",              SND_ASN, CM_ARG },
1633     { "/ascii",                SND_TXT, CM_INV },
1634     { "/before",               SND_BEF, CM_INV },
1635     { "/binary",               SND_BIN, 0 },
1636     { "/collision",            SND_COL, CM_ARG },
1637 #ifdef PUTPIPE
1638     { "/command",              SND_CMD, CM_PSH },
1639 #endif /* PUTPIPE */
1640     { "/delete",               SND_DEL, 0 },
1641     { "/error-action",         SND_ERR, CM_ARG },
1642     { "/except",               SND_EXC, CM_ARG },
1643     { "/filenames",            SND_NAM, CM_ARG },
1644 #ifdef PIPESEND
1645 #ifndef NOSPL
1646     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1647 #endif /* NOSPL */
1648 #endif /* PIPESEND */
1649 #ifdef VMS
1650     { "/image",                SND_IMG, 0 },
1651 #else
1652     { "/image",                SND_BIN, CM_INV },
1653 #endif /* VMS */
1654     { "/larger-than",          SND_LAR, CM_ARG },
1655     { "/listfile",             SND_FIL, CM_ARG },
1656 #ifndef NOCSETS
1657     { "/local-character-set",  SND_CSL, CM_ARG },
1658 #endif /* NOCSETS */
1659     { "/match",                SND_PAT, CM_ARG },
1660     { "/ml",                   SND_MLS, CM_INV|CM_ABR },
1661     { "/mls",                  SND_MLS, CM_INV|CM_ABR },
1662     { "/mlsd",                 SND_MLS, 0 },
1663     { "/mlst",                 SND_MLS, CM_INV },
1664 #ifdef CK_TMPDIR
1665     { "/move-to",              SND_MOV, CM_ARG },
1666 #endif /* CK_TMPDIR */
1667     { "/namelist",             SND_NML, CM_ARG },
1668     { "/nlst",                 SND_NLS, 0 },
1669     { "/nobackupfiles",        SND_NOB, 0 },
1670     { "/nodotfiles",           SND_NOD, 0 },
1671 #ifdef DOUPDATE
1672     { "/dates-differ",         SND_DIF, CM_INV },
1673 #endif /* DOUPDATE */
1674     { "/not-after",            SND_NAF, CM_INV },
1675     { "/not-before",           SND_NBE, CM_INV },
1676     { "/permissions",          SND_PRM, CM_INV },
1677     { "/quiet",                SND_SHH, 0 },
1678 #ifdef FTP_RESTART
1679     { "/recover",              SND_RES, 0 },
1680 #endif /* FTP_RESTART */
1681 #ifdef RECURSIVE
1682     { "/recursive",            SND_REC, 0 },
1683 #endif /* RECURSIVE */
1684     { "/rename-to",            SND_REN, CM_ARG },
1685 #ifdef FTP_RESTART
1686     { "/restart",              SND_RES, CM_INV },
1687 #endif /* FTP_RESTART */
1688 #ifndef NOCSETS
1689     { "/server-character-set", SND_CSR, CM_ARG },
1690 #endif /* NOCSETS */
1691     { "/server-rename-to",     SND_SRN, CM_ARG },
1692     { "/smaller-than",         SND_SMA, CM_ARG },
1693 #ifdef RECURSIVE
1694     { "/subdirectories",       SND_REC, CM_INV },
1695 #endif /* RECURSIVE */
1696     { "/text",                 SND_TXT, 0 },
1697     { "/tenex",                SND_TEN, 0 },
1698 #ifndef NOCSETS
1699     { "/transparent",          SND_XPA, 0 },
1700 #endif /* NOCSETS */
1701     { "/to-screen",            SND_MAI, 0 },
1702 #ifdef DOUPDATE
1703     { "/update",               SND_UPD, CM_INV },
1704 #endif /* DOUPDATE */
1705     { "", 0, 0 }
1706 };
1707 static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1;
1708
1709 static struct keytab delswi[] = {       /* FTP [M]DELETE switch table */
1710     { "/error-action",         SND_ERR, CM_ARG },
1711     { "/except",               SND_EXC, CM_ARG },
1712     { "/filenames",            SND_NAM, CM_ARG },
1713     { "/larger-than",          SND_LAR, CM_ARG },
1714     { "/nobackupfiles",        SND_NOB, 0 },
1715 #ifdef UNIXOROSK
1716     { "/nodotfiles",           SND_NOD, 0 },
1717 #endif /* UNIXOROSK */
1718     { "/quiet",                SND_SHH, 0 },
1719 #ifdef RECURSIVE
1720     { "/recursive",            SND_REC, 0 },
1721 #endif /* RECURSIVE */
1722     { "/smaller-than",         SND_SMA, CM_ARG },
1723 #ifdef RECURSIVE
1724     { "/subdirectories",       SND_REC, CM_INV },
1725 #endif /* RECURSIVE */
1726     { "", 0, 0 }
1727 };
1728 static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1;
1729
1730 static struct keytab fntab[] = {        /* Filename conversion keyword table */
1731     { "automatic",    2, CNV_AUTO },
1732     { "converted",    1, CNV_CNV  },
1733     { "literal",      0, CNV_LIT  }
1734 };
1735 static int nfntab = (sizeof(fntab) / sizeof(struct keytab));
1736
1737 static struct keytab ftptyp[] = {       /* SET FTP TYPE table */
1738     { "ascii",        FTT_ASC, 0 },
1739     { "binary",       FTT_BIN, 0 },
1740     { "tenex",        FTT_TEN, 0 },
1741     { "text",         FTT_ASC, CM_INV },
1742     { "", 0, 0 }
1743 };
1744 static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1;
1745
1746 #ifdef FTP_SECURITY
1747 static struct keytab ftppro[] = {       /* SET FTP PROTECTION-LEVEL table */
1748     { "clear",        FPL_CLR, 0 },
1749     { "confidential", FPL_CON, 0 },
1750     { "private",      FPL_PRV, 0 },
1751     { "safe",         FPL_SAF, 0 },
1752     { "", 0, 0 }
1753 };
1754 static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1;
1755 #endif /* FTP_SECURITY */
1756
1757 /* Definitions for FTP from RFC765. */
1758
1759 /* Reply codes */
1760
1761 #define REPLY_PRELIM    1               /* Positive preliminary */
1762 #define REPLY_COMPLETE  2               /* Positive completion */
1763 #define REPLY_CONTINUE  3               /* Positive intermediate */
1764 #define REPLY_TRANSIENT 4               /* Transient negative completion */
1765 #define REPLY_ERROR     5               /* Permanent negative completion */
1766 #define REPLY_SECURE    6               /* Security encoded message */
1767
1768 /* Form codes and names */
1769
1770 #define FORM_N 1                        /* Non-print */
1771 #define FORM_T 2                        /* Telnet format effectors */
1772 #define FORM_C 3                        /* Carriage control (ASA) */
1773
1774 /* Structure codes and names */
1775
1776 #define STRU_F 1                        /* File (no record structure) */
1777 #define STRU_R 2                        /* Record structure */
1778 #define STRU_P 3                        /* Page structure */
1779
1780 /* Mode types and names */
1781
1782 #define MODE_S 1                        /* Stream */
1783 #define MODE_B 2                        /* Block */
1784 #define MODE_C 3                        /* Compressed */
1785
1786 /* Protection levels and names */
1787
1788 #define PROT_C 1                        /* Clear */
1789 #define PROT_S 2                        /* Safe */
1790 #define PROT_P 3                        /* Private */
1791 #define PROT_E 4                        /* Confidential */
1792
1793 #ifdef COMMENT                          /* Not used */
1794 #ifdef FTP_NAMES
1795 char *strunames[]  =  {"0", "File",     "Record", "Page" };
1796 char *formnames[]  =  {"0", "Nonprint", "Telnet", "Carriage-control" };
1797 char *modenames[]  =  {"0", "Stream",   "Block",  "Compressed" };
1798 char *levelnames[] =  {"0", "Clear",    "Safe",   "Private",  "Confidential" };
1799 #endif /* FTP_NAMES */
1800
1801 /* Record Tokens */
1802
1803 #define REC_ESC '\377'                  /* Record-mode Escape */
1804 #define REC_EOR '\001'                  /* Record-mode End-of-Record */
1805 #define REC_EOF '\002'                  /* Record-mode End-of-File */
1806
1807 /* Block Header */
1808
1809 #define BLK_EOR           0x80          /* Block is End-of-Record */
1810 #define BLK_EOF           0x40          /* Block is End-of-File */
1811 #define BLK_REPLY_ERRORS  0x20          /* Block might have errors */
1812 #define BLK_RESTART       0x10          /* Block is Restart Marker */
1813 #define BLK_BYTECOUNT 2                 /* Bytes in this block */
1814 #endif /* COMMENT */
1815
1816 #define RADIX_ENCODE 0                  /* radix_encode() function codes */
1817 #define RADIX_DECODE 1
1818
1819 /*
1820   The default setpbsz() value in the Unix FTP client is 1<<20 (1MB).  This
1821   results in a serious performance degradation due to the increased number
1822   of page faults and the inability to overlap encrypt/decrypt, file i/o, and
1823   network i/o.  So instead we set the value to 1<<13 (8K), about half the size
1824   of the typical TCP window.  Maybe we should add a command to allow the value
1825   to be changed.
1826 */
1827 #define DEFAULT_PBSZ 1<<13
1828
1829 /* Prototypes */
1830
1831 _PROTOTYP(int remtxt, (char **) );
1832 _PROTOTYP(char * gskreason, (int) );
1833 _PROTOTYP(static int ftpclose,(void));
1834 _PROTOTYP(static int zzsend, (int, CHAR));
1835 _PROTOTYP(static int getreply,(int,int,int,int,int));
1836 _PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int));
1837 _PROTOTYP(static int setpbsz,(unsigned int));
1838 _PROTOTYP(static int recvrequest,(char *,char *,char *,char *,
1839   int,int,char *,int,int,int));
1840 _PROTOTYP(static int ftpcmd,(char *,char *,int,int,int));
1841 _PROTOTYP(static int fts_cpl,(int));
1842 _PROTOTYP(static int fts_dpl,(int));
1843 #ifdef FTP_SECURITY
1844 _PROTOTYP(static int ftp_auth, (void));
1845 #endif /* FTP_SECURITY */
1846 _PROTOTYP(static int ftp_user, (char *, char *, char *));
1847 _PROTOTYP(static int ftp_login, (char *));
1848 _PROTOTYP(static int ftp_reset, (void));
1849 _PROTOTYP(static int ftp_rename, (char *, char *));
1850 _PROTOTYP(static int ftp_umask, (char *));
1851 _PROTOTYP(static int secure_flush, (int));
1852 #ifdef COMMENT
1853 _PROTOTYP(static int secure_putc, (char, int));
1854 #endif /* COMMENT */
1855 _PROTOTYP(static int secure_write, (int, CHAR *, unsigned int));
1856 _PROTOTYP(static int scommand, (char *));
1857 _PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int));
1858 _PROTOTYP(static int secure_getc, (int, int));
1859 _PROTOTYP(static int secure_getbyte, (int, int));
1860 _PROTOTYP(static int secure_read, (int, char *, int));
1861 _PROTOTYP(static int initconn, (void));
1862 _PROTOTYP(static int dataconn, (char *));
1863 _PROTOTYP(static int setprotbuf,(unsigned int));
1864 _PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int));
1865
1866 _PROTOTYP(static char * radix_error,(int));
1867 _PROTOTYP(static char * ftp_hookup,(char *, int, int));
1868 _PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int));
1869
1870 _PROTOTYP(static VOID mlsreset, (void));
1871 _PROTOTYP(static VOID secure_error, (char *fmt, ...));
1872 _PROTOTYP(static VOID lostpeer, (void));
1873 _PROTOTYP(static VOID cancel_remote, (int));
1874 _PROTOTYP(static VOID changetype, (int, int));
1875
1876 _PROTOTYP(static sigtype cmdcancel, (int));
1877
1878 #ifdef FTP_SRP
1879 _PROTOTYP(static int srp_reset, ());
1880 _PROTOTYP(static int srp_ftp_auth, (char *,char *,char *));
1881 _PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *));
1882 _PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *));
1883 _PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int));
1884 _PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int));
1885 _PROTOTYP(static int srp_selcipher, (char *));
1886 _PROTOTYP(static int srp_selhash, (char *));
1887 #endif /* FTP_SRP */
1888
1889 #ifdef FTP_GSSAPI
1890 _PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *));
1891 #endif /* FTP_GSSAPI */
1892
1893 /*  D O F T P A R G  --  Do an FTP command-line argument.  */
1894
1895 #ifdef FTP_SECURITY
1896 #ifndef NOICP
1897 #define FT_NOGSS   1
1898 #define FT_NOK4    2
1899 #define FT_NOSRP   3
1900 #define FT_NOSSL   4
1901 #define FT_NOTLS   5
1902 #define FT_CERTFI  6
1903 #define FT_OKCERT  7
1904 #define FT_DEBUG   8
1905 #define FT_KEY     9
1906 #define FT_SECURE 10
1907 #define FT_VERIFY 11
1908
1909 static struct keytab ftpztab[] = {
1910     { "!gss",    FT_NOGSS,  0 },
1911     { "!krb4",   FT_NOK4,   0 },
1912     { "!srp",    FT_NOSRP,  0 },
1913     { "!ssl",    FT_NOSSL,  0 },
1914     { "!tls",    FT_NOTLS,  0 },
1915     { "cert",    FT_CERTFI, CM_ARG },
1916     { "certsok", FT_OKCERT, 0 },
1917     { "debug",   FT_DEBUG,  0 },
1918     { "key",     FT_KEY,    CM_ARG },
1919     { "nogss",   FT_NOGSS,  0 },
1920     { "nokrb4",  FT_NOK4,   0 },
1921     { "nosrp",   FT_NOSRP,  0 },
1922     { "nossl",   FT_NOSSL,  0 },
1923     { "notls",   FT_NOTLS,  0 },
1924 #ifdef COMMENT
1925     { "secure",  FT_SECURE, 0 },
1926 #endif /* COMMENT */
1927     { "verify",  FT_VERIFY, CM_ARG },
1928     { "", 0, 0 }
1929 };
1930 static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1;
1931
1932 /*
1933   The following cipher and hash tables should be replaced with
1934   dynamicly created versions based upon the linked library.
1935 */
1936 #define SRP_BLOWFISH_ECB    1
1937 #define SRP_BLOWFISH_CBC    2
1938 #define SRP_BLOWFISH_CFB64  3
1939 #define SRP_BLOWFISH_OFB64  4
1940 #define SRP_CAST5_ECB       5
1941 #define SRP_CAST5_CBC       6
1942 #define SRP_CAST5_CFB64     7
1943 #define SRP_CAST5_OFB64     8
1944 #define SRP_DES_ECB         9
1945 #define SRP_DES_CBC        10
1946 #define SRP_DES_CFB64      11
1947 #define SRP_DES_OFB64      12
1948 #define SRP_DES3_ECB       13
1949 #define SRP_DES3_CBC       14
1950 #define SRP_DES3_CFB64     15
1951 #define SRP_DES3_OFB64     16
1952
1953 static struct keytab ciphertab[] = {
1954     { "blowfish_ecb",   SRP_BLOWFISH_ECB,   0 },
1955     { "blowfish_cbc",   SRP_BLOWFISH_CBC,   0 },
1956     { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 },
1957     { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 },
1958     { "cast5_ecb",      SRP_CAST5_ECB,      0 },
1959     { "cast5_cbc",      SRP_CAST5_CBC,      0 },
1960     { "cast5_cfb64",    SRP_CAST5_CFB64,    0 },
1961     { "cast5_ofb64",    SRP_CAST5_OFB64,    0 },
1962     { "des_ecb",        SRP_DES_ECB,        0 },
1963     { "des_cbc",        SRP_DES_CBC,        0 },
1964     { "des_cfb64",      SRP_DES_CFB64,      0 },
1965     { "des_ofb64",      SRP_DES_OFB64,      0 },
1966     { "des3_ecb",       SRP_DES3_ECB,       0 },
1967     { "des3_cbc",       SRP_DES3_CBC,       0 },
1968     { "des3_cfb64",     SRP_DES3_CFB64,     0 },
1969     { "des3_ofb64",     SRP_DES3_OFB64,     0 },
1970     { "none",           0, 0 },
1971     { "", 0, 0 }
1972 };
1973 static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1;
1974
1975 #define SRP_MD5  1
1976 #define SRP_SHA  2
1977 static struct keytab hashtab[] = {
1978     { "md5",              SRP_MD5,        0 },
1979     { "none",             0,              0 },
1980     { "sha",              SRP_SHA,        0 },
1981     { "", 0, 0 }
1982 };
1983 static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1;
1984 #endif /* NOICP */
1985 #endif /* FTP_SECURITY */
1986
1987 static char *
1988 strval(s1,s2) char * s1, * s2; {
1989     if (!s1) s1 = "";
1990     if (!s2) s2 = "";
1991     return(*s1 ? s1 : (*s2 ? s2 : "(none)"));
1992 }
1993
1994 #ifndef NOCSETS
1995 static char * rfnptr = NULL;
1996 static int rfnlen = 0;
1997 static char rfnbuf[RFNBUFSIZ];          /* Remote filename translate buffer */
1998 static char * xgnbp = NULL;
1999
2000 static int
2001 strgetc() {                             /* Helper function for xgnbyte() */
2002     int c;
2003     if (!xgnbp)
2004       return(-1);
2005     if (!*xgnbp)
2006       return(-1);
2007     c = (unsigned) *xgnbp++;
2008     return(((unsigned) c) & 0xff);
2009 }
2010
2011 static int                              /* Helper function for xpnbyte() */
2012 #ifdef CK_ANSIC
2013 strputc(char c)
2014 #else
2015 strputc(c) char c;
2016 #endif /* CK_ANSIC */
2017 {
2018     rfnlen = rfnptr - rfnbuf;
2019     if (rfnlen >= (RFNBUFSIZ - 1))
2020       return(-1);
2021     *rfnptr++ = c;
2022     *rfnptr = NUL;
2023     return(0);
2024 }
2025
2026 static int
2027 #ifdef CK_ANSIC
2028 xprintc(char c)
2029 #else
2030 xprintc(c) char c;
2031 #endif /* CK_ANSIC */
2032 {
2033     printf("%c",c);
2034     return(0);
2035 }
2036
2037 static VOID
2038 bytswap(c0,c1) int * c0, * c1; {
2039     int t;
2040     t = *c0;
2041     *c0 = *c1;
2042     *c1 = t;
2043 }
2044 #endif /* NOCSETS */
2045
2046 #ifdef CKLOGDIAL
2047 char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */
2048 int ftplogactive = 0;
2049 long ftplogprev = 0L;
2050
2051 VOID
2052 ftplogend() {
2053     extern int dialog;
2054     extern char diafil[];
2055     long d1, d2, t1, t2;
2056     char buf[32], * p;
2057
2058     debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive);
2059     debug(F110,"ftp cx log buf",ftplogbuf,0);
2060
2061     if (!ftplogactive || !ftplogbuf[0]) /* No active record */
2062       return;
2063
2064     ftplogactive = 0;                   /* Record is not active */
2065
2066     d1 = mjd((char *)ftplogbuf);        /* Get start date of this session */
2067     ckstrncpy(buf,ckdate(),31);         /* Get current date */
2068     d2 = mjd(buf);                      /* Convert them to mjds */
2069     p = ftplogbuf;                      /* Get start time */
2070     p[11] = NUL;
2071     p[14] = NUL;                        /* Convert to seconds */
2072     t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2073     p[11] = ':';
2074     p[14] = ':';
2075     p = buf;                            /* Get end time */
2076     p[11] = NUL;
2077     p[14] = NUL;
2078     t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2079     t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */
2080     if (t2 > -1L) {
2081         ftplogprev = t2;
2082         p = hhmmss(t2);
2083         ckstrncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */
2084         ckstrncat(ftplogbuf,p,CXLOGBUFL);
2085     } else
2086       ftplogprev = 0L;
2087     debug(F101,"ftp cx log dialog","",dialog);
2088     if (dialog) {                       /* If logging */
2089         int x;
2090         x = diaopn(diafil,1,1);         /* Open log in append mode */
2091         if (x > 0) {
2092             debug(F101,"ftp cx log open","",x);
2093             x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */
2094             debug(F101,"ftp cx log write","",x);
2095             x = zclose(ZDIFIL);         /* Close the log */
2096             debug(F101,"ftp cx log close","",x);
2097         }
2098     }
2099 }
2100
2101 VOID
2102 dologftp() {
2103     ftplogend();                        /* Previous session not closed out? */
2104     ftplogprev = 0L;
2105     ftplogactive = 1;                   /* Record is active */
2106
2107     ckmakxmsg(ftplogbuf,CXLOGBUFL,
2108               ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(),
2109               " T=FTP N=", strval(ftp_host,NULL)," H=",myhost,
2110               " P=", ckitoa(ftp_port)," "); /* SMS 2007/02/15 */
2111     debug(F110,"ftp cx log begin",ftplogbuf,0);
2112 }
2113 #endif /* CKLOGDIAL */
2114
2115 static char * dummy[2] = { NULL, NULL };
2116
2117 static struct keytab modetab[] = {
2118     { "active",  0, 0 },
2119     { "passive", 1, 0 }
2120 };
2121
2122 #ifndef NOCMDL
2123 int                                     /* Called from ckuusy.c */
2124 #ifdef CK_ANSIC
2125 doftparg(char c)
2126 #else
2127 doftparg(c) char c;
2128 #endif /* CK_ANSIC */
2129 /* doftparg */ {
2130     int x, z;
2131     char *xp;
2132     extern char **xargv, *xarg0;
2133     extern int xargc, stayflg, haveftpuid;
2134     extern char uidbuf[];
2135
2136     xp = *xargv+1;                      /* Pointer for bundled args */
2137     while (c) {
2138         if (ckstrchr("MuDPkcHzm",c)) {  /* Options that take arguments */
2139             if (*(xp+1)) {
2140                 fatal("?Invalid argument bundling");
2141             }
2142             xargv++, xargc--;
2143             if ((xargc < 1) || (**xargv == '-')) {
2144                 fatal("?Required argument missing");
2145             }
2146         }
2147         switch (c) {                    /* Big switch on arg */
2148           case 'h':                     /* help */
2149            printf("C-Kermit's FTP client command-line personality.  Usage:\n");
2150             printf("  %s [ options ] host [ port ] [-pg files ]\n\n",xarg0);
2151             printf("Options:\n");
2152             printf("  -h           = help (this message)\n");
2153             printf("  -m mode      = \"passive\" (default) or \"active\"\n");
2154             printf("  -u name      = username for autologin (or -M)\n");
2155             printf("  -P password  = password for autologin (RISKY)\n");
2156             printf("  -A           = autologin anonymously\n");
2157             printf("  -D directory = cd after autologin\n");
2158             printf("  -b           = force binary mode\n");
2159             printf("  -a           = force text (\"ascii\") mode (or -T)\n");
2160             printf("  -d           = debug (double to add timestamps)\n");
2161             printf("  -n           = no autologin\n");
2162             printf("  -v           = verbose (default)\n");
2163             printf("  -q           = quiet\n");
2164             printf("  -S           = Stay (issue command prompt when done)\n");
2165             printf("  -Y           = do not execute Kermit init file\n");
2166             printf("  -p files     = files to put after autologin (or -s)\n");
2167             printf("  -g files     = files to get after autologin\n");
2168             printf("  -R           = recursive (for use with -p)\n");
2169
2170 #ifdef FTP_SECURITY
2171             printf("\nSecurity options:\n");
2172             printf("  -k realm     = Kerberos 4 realm\n");
2173             printf("  -f           = Kerboros 5 credentials forwarding\n");
2174             printf("  -x           = autoencryption mode\n");
2175             printf("  -c cipher    = SRP cipher type\n");
2176             printf("  -H hash      = SRP encryption hash\n");
2177             printf("  -z option    = Security options\n");
2178 #endif /* FTP_SECURITY */
2179
2180             printf("\n-p or -g, if given, should be last.  Example:\n");
2181             printf("  ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n");
2182
2183             doexit(GOOD_EXIT,-1);
2184             break;
2185
2186           case 'R':                     /* Recursive */
2187             recursive = 1;
2188             break;
2189
2190           case 'd':                     /* Debug */
2191 #ifdef DEBUG
2192             if (deblog) {
2193                 extern int debtim;
2194                 debtim = 1;
2195             } else {
2196                 deblog = debopn("debug.log",0);
2197                 debok = 1;
2198             }
2199 #endif /* DEBUG */
2200             /* fall thru on purpose */
2201
2202           case 't':                     /* Trace */
2203             ftp_deb++;
2204             break;
2205
2206           case 'n':                     /* No autologin */
2207             ftp_log = 0;
2208             break;
2209
2210           case 'i':                     /* No prompt */
2211           case 'v':                     /* Verbose */
2212             break;                      /* (ignored) */
2213
2214           case 'q':                     /* Quiet */
2215             quiet = 1;
2216             break;
2217
2218           case 'S':                     /* Stay */
2219             stayflg = 1;
2220             break;
2221
2222           case 'M':
2223           case 'u':                     /* My User Name */
2224             if ((int)strlen(*xargv) > 63) {
2225                 fatal("username too long");
2226             }
2227             ckstrncpy(uidbuf,*xargv,UIDBUFLEN);
2228             haveftpuid = 1;
2229             break;
2230
2231           case 'A':
2232             ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2233             haveftpuid = 1;
2234             break;
2235
2236           case 'T':                     /* Text */
2237           case 'a':                     /* "ascii" */
2238           case 'b':                     /* Binary */
2239             binary = (c == 'b') ? FTT_BIN : FTT_ASC;
2240             ftp_xfermode = XMODE_M;
2241             filepeek = 0;
2242             patterns = 0;
2243             break;
2244
2245           case 'g':                     /* Get */
2246           case 'p':                     /* Put */
2247           case 's': {                   /* Send (= Put) */
2248               int havefiles, rc;
2249               if (ftp_action) {
2250                   fatal("Only one FTP action at a time please");
2251               }
2252               if (*(xp+1)) {
2253                   fatal("invalid argument bundling after -s");
2254               }
2255               nfils = 0;                /* Initialize file counter */
2256               havefiles = 0;            /* Assume nothing to send  */
2257               cmlist = xargv + 1;       /* Remember this pointer */
2258
2259               while (++xargv, --xargc > 0) { /* Traverse the list */
2260                   if (c == 'g') {
2261                       havefiles++;
2262                       nfils++;
2263                       continue;
2264                   }
2265 #ifdef RECURSIVE
2266                   if (!strcmp(*xargv,".")) {
2267                       havefiles = 1;
2268                       nfils++;
2269                       recursive = 1;
2270                   } else
2271 #endif /* RECURSIVE */
2272                     if ((rc = zchki(*xargv)) > -1 || (rc == -2)) {
2273                         if  (rc != -2)
2274                           havefiles = 1;
2275                         nfils++;
2276                     } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) {
2277                         havefiles = 1;
2278                         nfils++;
2279                     }
2280               }
2281               xargc++, xargv--;         /* Adjust argv/argc */
2282               if (!havefiles) {
2283                   if (c == 'g') {
2284                       fatal("No files to put");
2285                   } else {
2286                       fatal("No files to get");
2287                   }
2288               }
2289               ftp_action = c;
2290               break;
2291           }
2292           case 'D':                     /* Directory */
2293             makestr(&ftp_rdir,*xargv);
2294             break;
2295
2296           case 'm':                     /* Mode (Active/Passive */
2297             ftp_psv = lookup(modetab,*xargv,2,NULL);
2298             if (ftp_psv < 0) fatal("Invalid mode");
2299             break;
2300
2301           case 'P':
2302             makestr(&ftp_tmp,*xargv);   /* You-Know-What */
2303             break;
2304
2305           case 'Y':                     /* No initialization file */
2306             break;                      /* (already done in prescan) */
2307
2308 #ifdef CK_URL
2309           case 'U': {                   /* URL */
2310               /* These are set by urlparse() - any not set are NULL */
2311               if (g_url.hos) {
2312 /*
2313   Kermit has accepted host:port notation since many years before URLs were
2314   invented.  Unfortunately, URLs conflict with this notation.  Thus "ftp
2315   host:449" looks like a URL and results in service = host and host = 449.
2316   Here we try to catch this situation transparently to the user.
2317 */
2318                   if (ckstrcmp(g_url.svc,"ftp",-1,0)
2319 #ifdef CK_SSL
2320                        && ckstrcmp(g_url.svc,"ftps",-1,0)
2321 #endif /* CK_SSL */
2322                        ) {
2323                       if (!g_url.usr &&
2324                           !g_url.psw &&
2325                           !g_url.por &&
2326                           !g_url.pth) {
2327                           g_url.por = g_url.hos;
2328                           g_url.hos = g_url.svc;
2329                           g_url.svc = "ftp";
2330                       } else {
2331                           ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=",
2332                                    g_url.svc," host=",g_url.hos);
2333                           fatal(tmpbuf);
2334                       }
2335                   }
2336                   makestr(&ftp_host,g_url.hos);
2337                   if (g_url.usr) {
2338                       haveftpuid = 1;
2339                       ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
2340                       makestr(&ftp_logname,uidbuf);
2341                   }
2342                   if (g_url.psw) {
2343                       makestr(&ftp_tmp,g_url.psw);
2344                   }
2345                   if (g_url.pth) {
2346                       if (!g_url.usr) {
2347                           haveftpuid = 1;
2348                           ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2349                           makestr(&ftp_logname,uidbuf);
2350                       }
2351                       if (ftp_action) {
2352                           fatal("Only one FTP action at a time please");
2353                       }
2354                       if (!stayflg)
2355                         quiet = 1;
2356                       nfils = 1;
2357                       dummy[0] = g_url.pth;
2358                       cmlist = dummy;
2359                       ftp_action = 'g';
2360                   }
2361                   xp = NULL;
2362                   haveurl = 1;
2363               }
2364               break;
2365           }
2366 #endif /* CK_URL */
2367
2368 #ifdef FTP_SECURITY
2369           case 'k': {                   /* K4 Realm */
2370 #ifdef FTP_KRB4
2371               ckstrncpy(ftp_realm,*xargv, REALM_SZ);
2372 #endif /* FTP_KRB4 */
2373               if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv);
2374               break;
2375           }
2376           case 'f': {
2377 #ifdef FTP_GSSAPI
2378               ftp_cfw = 1;
2379               if (ftp_deb) printf("K5 Credentials Forwarding\n");
2380 #else /* FTP_GSSAPI */
2381               printf("K5 Credentials Forwarding not supported\n");
2382 #endif /* FTP_GSSAPI */
2383               break;
2384           }
2385           case 'x': {
2386               ftp_cry = 1;
2387               if (ftp_deb) printf("Autoencryption\n");
2388               break;
2389           }
2390           case 'c': {                   /* Cipher */
2391 #ifdef FTP_SRP
2392               if (!srp_selcipher(*xargv)) {
2393                   if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv);
2394               } else
2395                 printf("?Invalid SRP cipher type: \"%s\"\n",*xargv);
2396 #else /* FTP_SRP */
2397               printf("?SRP not supported\n");
2398 #endif /* FTP_SRP */
2399               break;
2400           }
2401           case 'H': {
2402 #ifdef FTP_SRP
2403               if (!srp_selhash(*xargv)) {
2404                   if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv);
2405               } else
2406                 printf("?Invalid SRP hash type: \"%s\"\n",*xargv);
2407 #else /* FTP_SRP */
2408               printf("?SRP not supported\n");
2409 #endif /* FTP_SRP */
2410               break;
2411           }
2412           case 'z': {
2413               /* *xargv contains a value of the form tag=value */
2414               /* we need to lookup the tag and save the value  */
2415               char * p = NULL, * q = NULL;
2416               makestr(&p,*xargv);
2417               y = ckindex("=",p,0,0,1);
2418               if (y > 0)
2419                 p[y-1] = '\0';
2420               x = lookup(ftpztab,p,nftpztab,&z);
2421               if (x < 0) {
2422                   printf("?Invalid security option: \"%s\"\n",p);
2423               } else {
2424                   if (ftp_deb)
2425                     printf("Security option: \"%s",p);
2426                   if (ftpztab[z].flgs & CM_ARG) {
2427                       if (y <= 0)
2428                         fatal("?Missing required value");
2429                       q = &p[y];
2430                       if (!*q)
2431                         fatal("?Missing required value");
2432                       if (ftp_deb)
2433                         printf("=%s\"",q);
2434                   }
2435                   switch (ftpztab[z].kwval) { /* -z options w/args */
2436                     case FT_NOGSS:
2437 #ifdef FTP_GSSAPI
2438                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2439                           if (ftp_auth_type[z] == FTA_GK5) {
2440                               for (y = z;
2441                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2442                                    y++
2443                                    )
2444                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2445                               ftp_auth_type[FTPATYPS-1] = 0;
2446                               break;
2447                           }
2448                       }
2449 #endif /* FTP_GSSAPI */
2450                       break;
2451                     case FT_NOK4:
2452 #ifdef FTP_KRB4
2453                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2454                           if (ftp_auth_type[z] == FTA_K4) {
2455                               for (y = z;
2456                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2457                                    y++
2458                                    )
2459                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2460                               ftp_auth_type[FTPATYPS-1] = 0;
2461                               break;
2462                           }
2463                       }
2464 #endif /* FTP_KRB4 */
2465                       break;
2466                     case FT_NOSRP:
2467 #ifdef FTP_SRP
2468                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2469                           if (ftp_auth_type[z] == FTA_SRP) {
2470                               for (y = z;
2471                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2472                                    y++
2473                                    )
2474                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2475                               ftp_auth_type[FTPATYPS-1] = 0;
2476                               break;
2477                           }
2478                       }
2479 #endif /* FTP_SRP */
2480                       break;
2481                     case FT_NOSSL:
2482 #ifdef CK_SSL
2483                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2484                           if (ftp_auth_type[z] == FTA_SSL) {
2485                               for (y = z;
2486                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2487                                    y++
2488                                    )
2489                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2490                               ftp_auth_type[FTPATYPS-1] = 0;
2491                               break;
2492                           }
2493                       }
2494 #endif /* CK_SSL */
2495                       break;
2496                     case FT_NOTLS:
2497 #ifdef CK_SSL
2498                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2499                           if (ftp_auth_type[z] == FTA_TLS) {
2500                               for (y = z;
2501                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2502                                    y++
2503                                    )
2504                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2505                               ftp_auth_type[FTPATYPS-1] = 0;
2506                               break;
2507                           }
2508                       }
2509 #endif /* CK_SSL */
2510                       break;
2511                     case FT_CERTFI:
2512 #ifdef CK_SSL
2513                       makestr(&ssl_rsa_cert_file,q);
2514 #endif /* CK_SSL */
2515                       break;
2516                     case FT_OKCERT:
2517 #ifdef CK_SSL
2518                       ssl_certsok_flag = 1;
2519 #endif /* CK_SSL */
2520                       break;
2521                     case FT_DEBUG:
2522 #ifdef DEBUG
2523                       if (deblog) {
2524                           extern int debtim;
2525                           debtim = 1;
2526                       } else {
2527                           deblog = debopn("debug.log",0);
2528                       }
2529 #endif /* DEBUG */
2530                       break;
2531                     case FT_KEY:
2532 #ifdef CK_SSL
2533                       makestr(&ssl_rsa_key_file,q);
2534 #endif /* CK_SSL */
2535                       break;
2536                     case FT_SECURE:
2537                       /* no equivalent */
2538                       break;
2539                     case FT_VERIFY:
2540 #ifdef CK_SSL
2541                       if (!rdigits(q))
2542                         printf("?Bad number: %s\n",q);
2543                       ssl_verify_flag = atoi(q);
2544 #endif /* CK_SSL */
2545                       break;
2546                   }
2547               }
2548               if (ftp_deb) printf("\"\n");
2549               free(p);
2550               break;
2551           }
2552 #endif /* FTP_SECURITY */
2553
2554           default:
2555             fatal2(*xargv,
2556                    "unknown command-line option, type \"ftp -h\" for help"
2557                    );
2558         }
2559         if (!xp) break;
2560         c = *++xp;                      /* See if options are bundled */
2561     }
2562     return(0);
2563 }
2564 #endif /* NOCMDL */
2565
2566 int
2567 ftpisconnected() {
2568     return(connected);
2569 }
2570
2571 int
2572 ftpisloggedin() {
2573     return(connected ? loggedin : 0);
2574 }
2575
2576 int
2577 ftpissecure() {
2578     return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1);
2579 }
2580
2581 static VOID
2582 ftscreen(n, c, z, s) int n; char c; CK_OFF_T z; char * s; {
2583     if (displa && fdispla && !backgrd && !quiet && !out2screen) {
2584         if (!dpyactive) {
2585             ckscreen(SCR_PT,'S',(CK_OFF_T)0,"");
2586             dpyactive = 1;
2587         }
2588         ckscreen(n,c,z,s);
2589     }
2590 }
2591
2592 #ifndef OS2
2593 /*  g m s t i m e r  --  Millisecond timer */
2594
2595 long
2596 gmstimer() {
2597 #ifdef HAVE_MSECS
2598     /* For those versions of ztime() that also set global ztmsec. */
2599     char *p = NULL;
2600     long z;
2601     ztime(&p);
2602     if (!p) return(0L);
2603     if (!*p) return(0L);
2604     z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
2605     return(z * 1000 + ztmsec);
2606 #else
2607     return((long)time(NULL) * 1000L);
2608 #endif /* HAVE_MSECS */
2609 }
2610 #endif /* OS2 */
2611
2612 /*  d o s e t f t p  --  The SET FTP command  */
2613
2614 int
2615 dosetftp() {
2616     int cx;
2617     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */
2618       return(cx);
2619     switch (cx) {
2620
2621       case FTS_FNC:                     /* Filename collision action */
2622         if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
2623           return(x);
2624         if ((y = cmcfm()) < 0)
2625           return(y);
2626         ftp_fnc = x;
2627         return(1);
2628
2629       case FTS_CNV:                     /* Filename conversion */
2630         if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
2631           return(x);
2632         if ((y = cmcfm()) < 0)
2633           return(y);
2634         ftp_cnv = x;
2635         return(1);
2636
2637       case FTS_DBG:                     /* Debug messages */
2638         return(seton(&ftp_deb));
2639
2640       case FTS_LOG:                     /* Auto-login */
2641         return(seton(&ftp_log));
2642
2643       case FTS_PSV:                     /* Passive mode */
2644         return(dosetftppsv());
2645
2646       case FTS_SPC:                     /* Send port commands */
2647         x = seton(&ftp_spc);
2648         if (x > 0) sendport = ftp_spc;
2649         return(x);
2650
2651       case FTS_TYP:                     /* Type */
2652         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
2653           return(x);
2654         if ((y = cmcfm()) < 0) return(y);
2655         ftp_typ = x;
2656         g_ftp_typ = x;
2657         tenex = (ftp_typ == FTT_TEN);
2658         return(1);
2659
2660       case FTS_USN:                     /* Unique server names */
2661         return(seton(&ftp_usn));
2662
2663       case FTS_VBM:                     /* Verbose mode */
2664         if ((x = seton(&ftp_vbm)) < 0)  /* Per-command copy */
2665           return(x);
2666         ftp_vbx = ftp_vbm;              /* Global sticky copy */
2667         return(x);
2668
2669       case FTS_TST:                     /* "if (testing)" messages */
2670         return(seton(&testing));
2671
2672       case FTS_PRM:                     /* Send permissions */
2673         return(setonaut(&ftp_prm));
2674
2675       case FTS_AUT:                     /* Auto-authentication */
2676         return(seton(&ftp_aut));
2677
2678       case FTS_ERR:                     /* Error action */
2679         if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
2680           return(x);
2681         if ((y = cmcfm()) < 0)
2682           return(y);
2683         ftp_err = x;
2684         return(success = 1);
2685
2686 #ifndef NOCSETS
2687       case FTS_XLA:                     /* Translation */
2688         return(seton(&ftp_xla));
2689
2690       case FTS_CSR:                     /* Server charset */
2691         if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0)
2692           return(x);
2693         if ((y = cmcfm()) < 0)
2694           return(y);
2695         ftp_csr = x;
2696         ftp_xla = 1;                    /* Also enable translation */
2697         return(success = 1);
2698 #endif /* NOCSETS */
2699
2700       case FTS_GFT:
2701         return(seton(&get_auto));       /* GET-filetype-switching */
2702
2703       case FTS_DAT:
2704         return(seton(&ftp_dates));      /* Set file dates */
2705
2706 #ifdef FTP_TIMEOUT
2707       case FTS_TMO:                     /* Timeout */
2708         if ((x = cmnum("Number of seconds","0",10,&z,xxstring)) < 0)
2709           return(x);
2710         if ((y = cmcfm()) < 0)
2711           return(y);
2712         ftp_timeout = z;
2713         return(success = 1);
2714 #endif  /* FTP_TIMEOUT */
2715
2716       case FTS_STO: {                   /* Server time offset */
2717           char * s, * p = NULL;
2718           long k;
2719           if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0)
2720             return(x);
2721           if (!strcmp(s,"+0")) {
2722               s = NULL;
2723           } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */
2724               printf("?Invalid time offset\n");
2725               return(-9);
2726           }
2727           makestr(&p,s);                /* Make a safe copy the string */
2728           if ((x = cmcfm()) < 0) {      /* Get confirmation */
2729               if (p)
2730                 makestr(&p,NULL);
2731               return(x);
2732           }
2733           fts_sto = p;                  /* Confirmed - set the string. */
2734           return(success = 1);
2735       }
2736       case FTS_APW: {
2737           char * s;
2738           if ((x = cmtxt("Text", "", &s, xxstring)) < 0)
2739             return(x);
2740           makestr(&ftp_apw, *s ? s : NULL);
2741           return(success = 1);
2742       }
2743
2744       case FTS_BUG: {
2745           if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0) 
2746             return(x);
2747           switch (x) {
2748 #ifdef CK_SSL
2749           case FTB_SV2:
2750             return seton(&ftp_bug_use_ssl_v2);
2751           case FTB_SV3:
2752             return seton(&ftp_bug_use_ssl_v3);
2753 #endif /* CK_SSL */
2754           default:
2755             return(-2);
2756           }
2757       }
2758
2759 #ifdef FTP_SECURITY
2760       case FTS_CRY:                     /* Auto-encryption */
2761         return(seton(&ftp_cry));
2762
2763       case FTS_CFW:                     /* Credential-forwarding */
2764         return(seton(&ftp_cfw));
2765
2766       case FTS_CPL:                     /* Command protection level */
2767         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2768         if ((y = cmcfm()) < 0) return(y);
2769         success = fts_cpl(x);
2770         return(success);
2771
2772       case FTS_DPL:                     /* Data protection level */
2773         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2774         if ((y = cmcfm()) < 0) return(y);
2775           success = fts_dpl(x);
2776           return(success);
2777
2778       case FTS_ATP: {                   /* FTP Auth Type */
2779           int i, j, atypes[8];
2780
2781           for (i = 0; i < 8; i++) {
2782               if ((y = cmkey(ftpauth,nftpauth,"",
2783                              (i == 0) ? "automatic" : "",
2784                              xxstring)) < 0) {
2785                   if (y == -3)
2786                     break;
2787                   return(y);
2788               }
2789               if (i > 0 && (y == FTA_AUTO)) {
2790                   printf("?Choice may only be used in first position.\r\n");
2791                   return(-9);
2792               }
2793               for (j = 0; j < i; j++) {
2794                   if (atypes[j] == y) {
2795                       printf("\r\n?Choice has already been used.\r\n");
2796                       return(-9);
2797                   }
2798               }
2799               atypes[i] = y;
2800               if (y == FTA_AUTO) {
2801                   i++;
2802                   break;
2803               }
2804           }
2805           if (i < 8)
2806             atypes[i] = 0;
2807           if ((z = cmcfm()) < 0)
2808             return(z);
2809           if (atypes[0] == FTA_AUTO) {
2810               i = 0;
2811 #ifdef FTP_GSSAPI
2812               ftp_auth_type[i++] = FTA_GK5;
2813 #endif /* FTP_GSSAPI */
2814 #ifdef FTP_SRP
2815               ftp_auth_type[i++] = FTA_SRP;
2816 #endif /* FTP_SRP */
2817 #ifdef FTP_KRB4
2818               ftp_auth_type[i++] = FTA_K4;
2819 #endif /* FTP_KRB4 */
2820 #ifdef CK_SSL
2821               ftp_auth_type[i++] = FTA_TLS;
2822               ftp_auth_type[i++] = FTA_SSL;
2823 #endif /* CK_SSL */
2824               ftp_auth_type[i] = 0;
2825           } else {
2826               for (i = 0; i < 8; i++)
2827                 ftp_auth_type[i] = atypes[i];
2828           }
2829           return(success = 1);
2830       }
2831
2832       case FTS_SRP:
2833 #ifdef FTP_SRP
2834         if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0)
2835           return(x);
2836         switch (x) {
2837           case SRP_CIPHER:
2838             if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0)
2839               return(x);
2840             if ((z = cmcfm()) < 0)
2841               return(z);
2842             success = !srp_selcipher(ciphertab[x].kwd);
2843             return(success);
2844           case SRP_HASH:
2845             if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0)
2846               return(x);
2847             if ((z = cmcfm()) < 0)
2848               return(z);
2849             success = !srp_selhash(hashtab[x].kwd);
2850             return(success = 1);
2851           default:
2852             if ((z = cmcfm()) < 0)
2853               return(z);
2854             return(-2);
2855         }
2856 #else /* FTP_SRP */
2857         if ((z = cmcfm()) < 0)
2858           return(z);
2859         return(-2);
2860 #endif /* FTP_SRP */
2861 #endif /* FTP_SECURITY */
2862
2863       case FTS_DIS:
2864         doxdis(2);                      /* 2 == ftp */
2865         return(success = 1);
2866
2867       default:
2868         return(-2);
2869     }
2870 }
2871
2872 int
2873 ftpbye() {
2874     int x;
2875     if (!connected)
2876       return(1);
2877     if (testing)
2878       printf(" ftp closing %s...\n",ftp_host);
2879     x = ftpclose();
2880     return((x > -1) ? 1 : 0);
2881 }
2882
2883 /*  o p e n f t p  --  Parse FTP hostname & port and open */
2884
2885 static int
2886 openftp(s,opn_tls) char * s; int opn_tls; {
2887     char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL;
2888     int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0;
2889     int haveuser = 0;
2890     struct FDB sw, fl, cm;
2891     extern int nnetdir;                 /* Network services directory */
2892     extern int nhcount;                 /* Lookup result */
2893     extern char *nh_p[];                /* Network directory entry pointers */
2894     extern char *nh_p2[];               /* Network directory entry nettype */
2895
2896     if (!s) return(-2);
2897     if (!*s) return(-2);
2898
2899     makestr(&hostname,s);
2900     hostsave = hostname;
2901     makestr(&ftp_logname,NULL);
2902     anonymous = 0;
2903     noinit = 0;
2904
2905     debug(F110,"ftp open",hostname,0);
2906
2907     if (sav_psv > -1) {                 /* Restore prevailing active/passive */
2908         ftp_psv = sav_psv;              /* selection in case it was */
2909         sav_psv = -1;                   /* temporarily overriden by a switch */
2910     }
2911     if (sav_log > -1) {                 /* Ditto for autologin */
2912         ftp_log = sav_log;
2913         sav_log = -1;
2914     }
2915     cmfdbi(&sw,                         /* Switches */
2916            _CMKEY,
2917            "Service name or port;\n or switch",
2918            "",                          /* default */
2919            "",                          /* addtl string data */
2920            nftpswi,                     /* addtl numeric data 1: tbl size */
2921            4,                           /* addtl numeric data 2: none */
2922            xxstring,                    /* Processing function */
2923            ftpswitab,                   /* Keyword table */
2924            &fl                          /* Pointer to next FDB */
2925            );
2926     cmfdbi(&fl,                         /* A host name or address */
2927            _CMFLD,                      /* fcode */
2928            "",                          /* help */
2929            "xYzBoo",                    /* default */
2930            "",                          /* addtl string data */
2931            0,                           /* addtl numeric data 1 */
2932            0,                           /* addtl numeric data 2 */
2933            xxstring,
2934            NULL,
2935            &cm
2936            );
2937     cmfdbi(&cm,                         /* Command confirmation */
2938            _CMCFM,
2939            "",
2940            "",
2941            "",
2942            0,
2943            0,
2944            NULL,
2945            NULL,
2946            NULL
2947            );
2948
2949     for (n = 0;; n++) {
2950         rc = cmfdb(&sw);                /* Parse a service name or a switch */
2951         if (rc < 0)
2952           goto xopenftp;
2953
2954         if (cmresult.fcode == _CMCFM) { /* Done? */
2955             break;
2956         } else if (cmresult.fcode == _CMFLD) {  /* Port */
2957             if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1))
2958               makestr(&service,cmresult.sresult);
2959             else
2960               makestr(&service,opn_tls?"ftps":"ftp");
2961         } else if (cmresult.fcode == _CMKEY) { /* Have a switch */
2962             c = cmgbrk();               /* get break character */
2963             getval = (c == ':' || c == '=');
2964             rc = -9;
2965             if (getval && !(cmresult.kflags & CM_ARG)) {
2966                 printf("?This switch does not take arguments\n");
2967                 goto xopenftp;
2968             }
2969             if (!getval && (cmresult.kflags & CM_ARG)) {
2970                 printf("?This switch requires an argument\n");
2971                 goto xopenftp;
2972             }
2973             switch (cmresult.nresult) { /* Switch */
2974               case OPN_ANO:             /* /ANONYMOUS */
2975                 anonymous++;
2976                 nologin = 0;
2977                 break;
2978               case OPN_NIN:             /* /NOINIT */
2979                 noinit++;
2980                 break;
2981               case OPN_NOL:             /* /NOLOGIN */
2982                 nologin++;
2983                 anonymous = 0;
2984                 makestr(&ftp_logname,NULL);
2985                 break;
2986               case OPN_PSW:             /* /PASSWORD */
2987                 if (!anonymous)         /* Don't log real passwords */
2988                   debok = 0;
2989                 rc = cmfld("Password for FTP server","",&p,xxstring);
2990                 if (rc == -3) {
2991                     makestr(&ftp_tmp,NULL);
2992                 } else if (rc < 0) {
2993                     goto xopenftp;
2994                 } else {
2995                     makestr(&ftp_tmp,brstrip(p));
2996                     nologin = 0;
2997                 }
2998                 break;
2999               case OPN_USR:             /* /USER */
3000                 rc = cmfld("Username for FTP server","",&p,xxstring);
3001                 if (rc == -3) {
3002                     makestr(&ftp_logname,NULL);
3003                 } else if (rc < 0) {
3004                     goto xopenftp;
3005                 } else {
3006                     nologin = 0;
3007                     anonymous = 0;
3008                     haveuser = 1;
3009                     makestr(&ftp_logname,brstrip(p));
3010                 }
3011                 break;
3012               case OPN_ACC:
3013                 rc = cmfld("Account for FTP server","",&p,xxstring);
3014                 if (rc == -3) {
3015                     makestr(&ftp_acc,NULL);
3016                 } else if (rc < 0) {
3017                     goto xopenftp;
3018                 } else {
3019                     makestr(&ftp_acc,brstrip(p));
3020                 }
3021                 break;
3022               case OPN_ACT:
3023                 opn_psv = 0;
3024                 break;
3025               case OPN_PSV:
3026                 opn_psv = 1;
3027                 break;
3028               case OPN_TLS:
3029                 opn_tls = 1;
3030                 break;
3031               default:
3032                 break;
3033             }
3034         }
3035         if (n == 0) {                   /* After first time through */
3036             cmfdbi(&sw,                 /* accept only switches */
3037                    _CMKEY,
3038                    "\nCarriage return to confirm to command, or switch",
3039                    "",
3040                    "",
3041                    nftpswi,
3042                    4,
3043                    xxstring,
3044                    ftpswitab,
3045                    &cm
3046                    );
3047         }
3048     }
3049 #ifdef COMMENT
3050     debug(F100,"ftp openftp while exit","",0);
3051     rc = cmcfm();
3052     debug(F101,"ftp openftp cmcfm rc","",rc);
3053     if (rc < 0)
3054       goto xopenftp;
3055 #endif /* COMMENT */
3056
3057     if (opn_psv > -1) {                 /* /PASSIVE or /ACTIVE switch given */
3058         sav_psv = ftp_psv;
3059         ftp_psv = opn_psv;
3060     }
3061     if (nologin || haveuser) {          /* /NOLOGIN or /USER switch given */
3062         sav_log = ftp_log;
3063         ftp_log = haveuser ? 1 : 0;
3064     }
3065     if (*hostname == '=') {             /* Bypass directory lookup */
3066         hostname++;                     /* if hostname starts with '=' */
3067         havehost++;
3068     } else if (isdigit(*hostname)) {    /* or if it starts with a digit */
3069         havehost++;
3070     }
3071     if (!service)
3072       makestr(&service,opn_tls?"ftps":"ftp");
3073
3074 #ifndef NODIAL
3075     if (!havehost && nnetdir > 0) {     /* If there is a networks directory */
3076         lunet(hostname);                /* Look up the name */
3077         debug(F111,"ftp openftp lunet",hostname,nhcount);
3078         if (nhcount == 0) {
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 A ftpopen success","",success);
3083             rc = success;
3084         } else {
3085             int found = 0;
3086             for (i = 0; i < nhcount; i++) {
3087                 if (nh_p2[i])           /* If network type specified */
3088                   if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0))
3089                     continue;
3090                 found++;
3091                 makestr(&hostname,nh_p[i]);
3092                 debug(F111,"ftpopen lunet substitution",hostname,i);
3093                 if (testing)
3094                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3095                 success = ftpopen(hostname,service,opn_tls);
3096                 debug(F101,"ftp openftp B ftpopen success","",success);
3097                 rc = success;
3098                 if (success)
3099                   break;
3100             }
3101             if (!found) {               /* E.g. if no network types match */
3102                 if (testing)
3103                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3104                 success = ftpopen(hostname,service,opn_tls);
3105                 debug(F101,"ftp openftp C ftpopen success","",success);
3106                 rc = success;
3107             }
3108         }
3109     } else {
3110 #endif /* NODIAL */
3111         if (testing)
3112           printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3113         success = ftpopen(hostname,service,opn_tls);
3114         debug(F111,"ftp openftp D ftpopen success",hostname,success);
3115         debug(F111,"ftp openftp D ftpopen connected",hostname,connected);
3116         rc = success;
3117 #ifndef NODIAL
3118     }
3119 #endif /* NODIAL */
3120
3121   xopenftp:
3122     debug(F101,"ftp openftp xopenftp rc","",rc);
3123     if (hostsave) free(hostsave);
3124     if (service) free(service);
3125     if (rc < 0 && ftp_logname) {
3126         free(ftp_logname);
3127         ftp_logname = NULL;
3128     }
3129     if (ftp_tmp) {
3130         free(ftp_tmp);
3131         ftp_tmp = NULL;
3132     }
3133     return(rc);
3134 }
3135
3136 VOID                                    /* 12 Aug 2007 */
3137 doftpglobaltype(x) int x; {
3138     ftp_xfermode = XMODE_M;             /* Set manual FTP transfer mode */
3139     ftp_typ = x;                        /* Used by top-level BINARY and */
3140     g_ftp_typ = x;                      /* ASCII commands. */
3141     get_auto = 0;
3142     forcetype = 1;
3143 }
3144
3145 int
3146 doftpacct() {
3147     int x;
3148     char * s;
3149     if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
3150       return(x);
3151     CHECKCONN();
3152     makestr(&ftp_acc,brstrip(s));
3153     if (testing)
3154       printf(" ftp account: \"%s\"\n",ftp_acc);
3155     success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
3156     return(success);
3157 }
3158
3159 int
3160 doftpusr() {                            /* Log in as USER */
3161     extern char uidbuf[];
3162     extern char pwbuf[];
3163     extern int  pwflg, pwcrypt;
3164     int x;
3165     char *s, * acct = "";
3166
3167     debok = 0;                          /* Don't log */
3168
3169     if ((x = cmfld("Remote username or ID",uidbuf,&s,xxstring)) < 0)
3170       return(x);
3171     ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */
3172     if ((x = cmfld("Remote password","",&s,xxstring)) < 0) {
3173         if (x == -3) { /* no input */
3174             if ( pwbuf[0] && pwflg ) {
3175                 ckstrncpy(tmpbuf,(char *)pwbuf,TMPBUFSIZ);
3176 #ifdef OS2
3177                 if ( pwcrypt )
3178                     ck_encrypt((char *)tmpbuf);
3179 #endif /* OS2 */
3180             }
3181         } else {
3182             return(x);
3183         }
3184     } else {
3185         ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
3186     }
3187     if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command",
3188                    "", &s, xxstring)) < 0)
3189       return(x);
3190     CHECKCONN();
3191     if (*s) {
3192         x = strlen(tmpbuf);
3193         if (x > 0) {
3194             acct = &tmpbuf[x+2];
3195             ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2);
3196         }
3197     }
3198     if (testing)
3199       printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf);
3200     success = ftp_user(line,tmpbuf,acct);
3201 #ifdef CKLOGDIAL
3202     dologftp();
3203 #endif /* CKLOGDIAL */
3204     return(success);
3205 }
3206
3207 /* DO (various FTP commands)... */
3208
3209 int
3210 doftptyp(type) int type; {              /* TYPE */
3211     CHECKCONN();
3212     ftp_typ = type;
3213     changetype(ftp_typ,ftp_vbm);
3214     debug(F101,"doftptyp changed type","",type);
3215     return(1);
3216 }
3217
3218 static int
3219 doftpxmkd(s,vbm) char * s; int vbm; {   /* MKDIR action */
3220     int lcs = -1, rcs = -1;
3221 #ifndef NOCSETS
3222     if (ftp_xla) {
3223         lcs = ftp_csl;
3224         if (lcs < 0) lcs = fcharset;
3225         rcs = ftp_csx;
3226         if (rcs < 0) rcs = ftp_csr;
3227     }
3228 #endif /* NOCSETS */
3229     debug(F110,"ftp doftpmkd",s,0);
3230     if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3231       return(success = 1);
3232     if (ftpcode == 500 || ftpcode == 502) {
3233         if (!quiet)
3234           printf("MKD command not recognized, trying XMKD\n");
3235         if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3236           return(success = 1);
3237     }
3238     return(success = 0);
3239 }
3240
3241 static int
3242 doftpmkd() {                            /* MKDIR parse */
3243     int x;
3244     char * s;
3245     if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0)
3246       return(x);
3247     CHECKCONN();
3248     ckstrncpy(line,s,LINBUFSIZ);
3249     if (testing)
3250       printf(" ftp mkdir \"%s\"...\n",line);
3251     return(success = doftpxmkd(line,-1));
3252 }
3253
3254 static int
3255 doftprmd() {                            /* RMDIR */
3256     int x, lcs = -1, rcs = -1;
3257     char * s;
3258     if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
3259       return(x);
3260     CHECKCONN();
3261     ckstrncpy(line,s,LINBUFSIZ);
3262     if (testing)
3263       printf(" ftp rmdir \"%s\"...\n",line);
3264 #ifndef NOCSETS
3265     if (ftp_xla) {
3266         lcs = ftp_csl;
3267         if (lcs < 0) lcs = fcharset;
3268         rcs = ftp_csx;
3269         if (rcs < 0) rcs = ftp_csr;
3270     }
3271 #endif /* NOCSETS */
3272     if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE)
3273       return(success = 1);
3274     if (ftpcode == 500 || ftpcode == 502) {
3275         if (!quiet)
3276           printf("RMD command not recognized, trying XMKD\n");
3277         success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
3278     } else
3279       success = 0;
3280     return(success);
3281 }
3282
3283 static int
3284 doftpren() {                            /* RENAME */
3285     int x;
3286     char * s;
3287     if ((x = cmfld("Remote filename","",&s,xxstring)) < 0)
3288       return(x);
3289     ckstrncpy(line,s,LINBUFSIZ);
3290     if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0)
3291       return(x);
3292     ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3293     if ((x = cmcfm()) < 0)
3294       return(x);
3295     CHECKCONN();
3296     if (testing)
3297       printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf);
3298     success = ftp_rename(line,tmpbuf);
3299     return(success);
3300 }
3301
3302 int
3303 doftpres() {                            /* RESET (log out without close) */
3304     int x;
3305     if ((x = cmcfm()) < 0)
3306       return(x);
3307     CHECKCONN();
3308     if (testing)
3309       printf(" ftp reset...\n");
3310     return(success = ftp_reset());
3311 }
3312
3313 static int
3314 doftpxhlp() {                           /* HELP */
3315     int x;
3316     char * s;
3317     if ((x = cmtxt("Command name", "", &s, xxstring)) < 0)
3318       return(x);
3319     CHECKCONN();
3320     ckstrncpy(line,s,LINBUFSIZ);
3321     if (testing)
3322       printf(" ftp help \"%s\"...\n",line);
3323     /* No need to translate -- all FTP commands are ASCII */
3324     return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE));
3325 }
3326
3327 static int
3328 doftpdir(cx) int cx; {                  /* [V]DIRECTORY */
3329     int x, lcs = 0, rcs = 0, xlate = 0;
3330     char * p, * s, * m = "";
3331     if (cx == FTP_VDI) {
3332         switch (servertype) {
3333           case SYS_VMS:
3334           case SYS_DOS:
3335           case SYS_TOPS10:
3336           case SYS_TOPS20:
3337             m = "*.*";
3338             break;
3339           default:
3340             m = "*";
3341         }
3342     }
3343     if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0)
3344       return(x);
3345     if ((x = remtxt(&s)) < 0)
3346       return(x);
3347 #ifdef NOCSETS
3348     xlate = 0;
3349 #else
3350     xlate = ftp_xla;
3351 #endif /* NOCSETS */
3352     line[0] = NUL;
3353     ckstrncpy(line,s,LINBUFSIZ);
3354     s = line;
3355     CHECKCONN();
3356
3357 #ifndef NOCSETS
3358     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
3359         lcs = ftp_csl;                  /* Local charset */
3360         if (lcs < 0) lcs = fcharset;
3361         if (lcs < 0) xlate = 0;
3362     }
3363     if (xlate) {                        /* Still ON? */
3364         rcs = ftp_csx;                  /* Remote (Server) charset */
3365         if (rcs < 0) rcs = ftp_csr;
3366         if (rcs < 0) xlate = 0;
3367     }
3368 #endif /* NOCSETS */
3369
3370     if (testing) {
3371         p = s;
3372         if (!p) p = "";
3373         if (*p)
3374           printf("Directory of files %s at %s:\n", line, ftp_host);
3375         else
3376           printf("Directory of files at %s:\n", ftp_host);
3377     }
3378     debug(F111,"doftpdir",s,cx);
3379
3380     if (cx == FTP_DIR) {
3381         /* Translation of line[] is done inside recvrequest() */
3382         /* when it calls ftpcmd(). */
3383         return(success =
3384           (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0));
3385     }
3386     success = 1;                        /* VDIR - one file at a time... */
3387     p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */
3388     cancelgroup = 0;
3389     if (!ftp_vbm && !quiet)
3390       printlines = 1;
3391     while (p && !cancelfile && !cancelgroup) { /* STAT one file */
3392         if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) {
3393             success = 0;
3394             break;
3395         }
3396         p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */
3397         debug(F110,"ftp vdir file",s,0);
3398     }
3399     return(success);
3400 }
3401
3402 static int
3403 doftppwd() {                            /* PWD */
3404     int x, lcs = -1, rcs = -1;
3405 #ifndef NOCSETS
3406     if (ftp_xla) {
3407         lcs = ftp_csl;
3408         if (lcs < 0) lcs = fcharset;
3409         rcs = ftp_csx;
3410         if (rcs < 0) rcs = ftp_csr;
3411     }
3412 #endif /* NOCSETS */
3413     if ((x = cmcfm()) < 0)
3414       return(x);
3415     CHECKCONN();
3416     if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) {
3417         success = 1;
3418     } else if (ftpcode == 500 || ftpcode == 502) {
3419         if (ftp_deb)
3420           printf("PWD command not recognized, trying XPWD\n");
3421         success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE);
3422     }
3423     return(success);
3424 }
3425
3426 static int
3427 doftpcwd(s,vbm) char * s; int vbm; {    /* CD (CWD) */
3428     int lcs = -1, rcs = -1;
3429 #ifndef NOCSETS
3430     if (ftp_xla) {
3431         lcs = ftp_csl;
3432         if (lcs < 0) lcs = fcharset;
3433         rcs = ftp_csx;
3434         if (rcs < 0) rcs = ftp_csr;
3435     }
3436 #endif /* NOCSETS */
3437
3438     debug(F110,"ftp doftpcwd",s,0);
3439     if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3440       return(success = 1);
3441     if (ftpcode == 500 || ftpcode == 502) {
3442         if (!quiet)
3443           printf("CWD command not recognized, trying XCWD\n");
3444         if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3445           return(success = 1);
3446     }
3447     return(success = 0);
3448 }
3449
3450 static int
3451 doftpcdup() {                           /* CDUP */
3452     debug(F100,"ftp doftpcdup","",0);
3453     if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE)
3454       return(success = 1);
3455     if (ftpcode == 500 || ftpcode == 502) {
3456         if (!quiet)
3457           printf("CDUP command not recognized, trying XCUP\n");
3458         if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE)
3459           return(success = 1);
3460     }
3461     return(success = 0);
3462 }
3463
3464 /* s y n c d i r  --  Synchronizes client & server directories */
3465
3466 /*
3467   Call with:
3468     local = pointer to pathname of local file to be sent.
3469     sim   = 1 for simulation, 0 for real uploading.
3470   Returns 0 on failure, 1 on success.
3471
3472   The 'local' argument is relative to the initial directory of the MPUT,
3473   i.e. the root of the tree being uploaded.  If the directory of the
3474   argument file is different from the directory of the previous file
3475   (which is stored in global putpath[]), this routine does the appropriate
3476   CWDs, CDUPs, and/or MKDIRs to position the FTP server in the same place.
3477 */
3478 static int cdlevel = 0, cdsimlvl = 0;   /* Tree-level trackers */
3479
3480 static int
3481 syncdir(local,sim) char * local; int sim; {
3482     char buf[CKMAXPATH+1];
3483     char tmp[CKMAXPATH+1];
3484     char msgbuf[CKMAXPATH+64];
3485     char c, * p = local, * s = buf, * q = buf, * psep, * ssep;
3486     int i, k = 0, done = 0, itsadir = 0, saveq;
3487
3488     debug(F110,"ftp syncdir local (new)",local,0);
3489     debug(F110,"ftp syncdir putpath (old)",putpath,0);
3490
3491     itsadir = isdir(local);             /* Is the local file a directory? */
3492     saveq = quiet;
3493
3494     while ((*s = *p)) {                 /* Copy the argument filename */
3495         if (++k == CKMAXPATH)           /* so we can poke it. */
3496           return(-1);
3497         if (*s == '/')                  /* Pointer to rightmost dirsep */
3498           q = s;
3499         s++;
3500         p++;
3501     }
3502     if (!itsadir)                       /* If it's a regular file */
3503       *q = NUL;                         /* keep just the path part */
3504
3505     debug(F110,"ftp syncdir buf",buf,0);
3506     if (!strcmp(buf,putpath)) {         /* Same path as previous file? */
3507         if (itsadir) {                  /* This file is a directory? */
3508             if (doftpcwd(local,0)) {    /* Try to CD to it */
3509                 doftpcdup();            /* Worked - CD back up */
3510             } else if (sim) {           /* Simulating... */
3511                 if (fdispla == XYFD_B) {
3512                     printf("WOULD CREATE DIRECTORY %s\n",local);
3513                 } else if (fdispla) {
3514                     ckmakmsg(msgbuf,CKMAXPATH,
3515                              "WOULD CREATE DIRECTORY",local,NULL,NULL);
3516                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3517                 }
3518                 /* See note above */
3519                 return(0);
3520             } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */
3521                 return(0);
3522             } else {                    /* Remote directory created OK */
3523                 if (fdispla == XYFD_B) {
3524                     printf("CREATED DIRECTORY %s\n",local);
3525                 } else if (fdispla) {
3526                     ckmakmsg(msgbuf,CKMAXPATH+64,
3527                              "CREATED DIRECTORY ",local,NULL,NULL);
3528                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3529                 }
3530             }
3531         }
3532         debug(F110,"ftp syncdir no change",buf,0);
3533         return(1);                      /* Yes, done. */
3534     }
3535     ckstrncpy(tmp,buf,CKMAXPATH+1);     /* Make a safe (pre-poked) copy */
3536     debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */
3537
3538     p = buf;                            /* New */
3539     s = putpath;                        /* Old */
3540
3541     debug(F110,"ftp syncdir A (old) s",s,0); /* Previous */
3542     debug(F110,"ftp syncdir A (new) p",p,0); /* New */
3543
3544     psep = buf;
3545     ssep = putpath;
3546     while (*p != NUL && *s != NUL && *p == *s) {
3547         if (*p == '/') { psep = p+1; ssep = s+1; }
3548         p++,s++;
3549     }
3550     /*
3551       psep and ssep point to the first path segment that differs.
3552       We have to do as many CDUPs as there are path segments in ssep.
3553       then we have to do as many MKDs and CWDs as there are segments in psep.
3554     */
3555     s = ssep;
3556     p = psep;
3557
3558     debug(F110,"ftp syncdir B (old) s",s,0); /* Previous */
3559     debug(F110,"ftp syncdir B (new) p",p,0); /* New */
3560
3561     /* p and s now point to the leftmost spot where the paths differ */
3562
3563     if (*s) {                           /* We have to back up */
3564         k = 1;                          /* How many levels counting this one */
3565         while ((c = *s++)) {            /* Count dirseps remaining in prev */
3566             if (c == '/' && *s)
3567               k++;
3568         }
3569         debug(F101,"ftp syncdir levels up","",k);
3570
3571         for (i = 1; i <= k; i++) {       /* Do that many CDUPs */
3572             debug(F111,"ftp syncdir CDUP A",p,i);
3573             if (fdispla == XYFD_B)
3574               printf(" CDUP\n");
3575             if (sim && cdsimlvl) {
3576                 cdsimlvl--;
3577             } else {
3578                 if (!doftpcdup()) {
3579                     quiet = saveq;
3580                     return(0);
3581                 }
3582             }
3583             cdlevel--;
3584         }
3585         if (!*p)                        /* If we don't have to go down */
3586           goto xcwd;                    /* we're done. */
3587     }
3588 #ifdef COMMENT
3589     while (p > buf && *p && *p != '/')  /* If in middle of segment */
3590       p--;                              /* back up to beginning */
3591     if (*p == '/')                      /* and terminate there */
3592       p++;
3593 #endif  /* COMMENT */
3594
3595     debug(F110,"ftp syncdir NEW PATH",p,0);
3596
3597     s = p;                              /* Point to start of new down path. */
3598     while (1) {                         /* Loop through characters. */
3599         if (*s == '/' || !*s) {         /* Have a segment. */
3600             if (!*s)                    /* If end of string, */
3601               done++;                   /* after this segment we're done. */
3602             else
3603               *s = NUL;                 /* NUL out the separator. */
3604             if (*p) {                   /* If segment is not empty */
3605                 debug(F110,"ftp syncdir down segment",p,0);
3606                 if (!doftpcwd(p,0)) {   /* Try to CD to it */
3607                     if (sim) {
3608                         if (fdispla == XYFD_B) {
3609                             printf(" WOULD CREATE DIRECTORY %s\n",local);
3610                         } else if (fdispla) {
3611                             ckmakmsg(msgbuf,CKMAXPATH,
3612                                      "WOULD CREATE DIRECTORY",
3613                                      local,NULL,NULL);
3614                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3615                         }
3616                         cdsimlvl++;
3617                     } else {
3618                         if (!doftpxmkd(p,0)) { /* Can't CD - try to create */
3619                             debug(F110,"ftp syncdir mkdir failed",p,0); 
3620 /*
3621   Suppose we are executing SEND /RECURSIVE.  Locally we have a directory
3622   FOO but the remote has a regular file with the same name.  We can't CD
3623   to it, can't MKDIR it either.  There's no way out but to fail and let
3624   the user handle the problem.
3625 */
3626                             quiet = saveq;
3627                             return(0);
3628                         }
3629                         debug(F110,"ftp syncdir mkdir OK",p,0); 
3630                         if (fdispla == XYFD_B) {
3631                             printf(" CREATED DIRECTORY %s\n",p);
3632                         } else if (fdispla) {
3633                             ckmakmsg(msgbuf,CKMAXPATH,
3634                                      "CREATED DIRECTORY ",p,NULL,NULL);
3635                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3636                         }
3637                         if (!doftpcwd(p,0)) { /* Try again to CD */
3638                             debug(F110,"ftp syncdir CD failed",p,0); 
3639                             quiet = saveq;
3640                             return(0);
3641                         }
3642                         if (fdispla == XYFD_B) printf(" CWD %s\n",p);
3643                         debug(F110,"ftp syncdir CD OK",p,0); 
3644                     }
3645                 }
3646                 cdlevel++;
3647             }
3648             if (done)                   /* Quit if no next segment */
3649               break;
3650             p = s+1;                    /* Point to next segment */
3651         }
3652         s++;                            /* Point to next source char */
3653     }
3654
3655   xcwd:
3656     ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */
3657     quiet = saveq;
3658     return(1);
3659 }
3660
3661 #ifdef DOUPDATE
3662 #ifdef DEBUG
3663 static VOID
3664 dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */
3665     if (deblog) {
3666         debug(F111,"ftp year ",s,xx->tm_year);
3667         debug(F111,"ftp month",s,xx->tm_mon);
3668         debug(F111,"ftp day  ",s,xx->tm_mday);
3669         debug(F111,"ftp hour ",s,xx->tm_hour);
3670         debug(F111,"ftp min  ",s,xx->tm_min);
3671         debug(F111,"ftp sec  ",s,xx->tm_sec);
3672     }
3673 }
3674 #endif /* DEBUG */
3675
3676 /*  t m c o m p a r e  --  Compare two struct tm's */
3677
3678 /*  Like strcmp() but for struct tm's  */
3679 /*  Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */
3680
3681 static int
3682 tmcompare(xx,yy) struct tm * xx, * yy; {
3683
3684     if (xx->tm_year < yy->tm_year)      /* First year less than second */
3685       return(-1);
3686     if (xx->tm_year > yy->tm_year)      /* First year greater than second */
3687       return(1);
3688
3689     /* Years are equal so compare months */
3690
3691     if (xx->tm_mon  < yy->tm_mon)       /* And so on... */
3692       return(-1);
3693     if (xx->tm_mon  > yy->tm_mon)
3694       return(1);
3695
3696     if (xx->tm_mday < yy->tm_mday)
3697       return(-1);
3698     if (xx->tm_mday > yy->tm_mday)
3699       return(1);
3700
3701     if (xx->tm_hour < yy->tm_hour)
3702       return(-1);
3703     if (xx->tm_hour > yy->tm_hour)
3704       return(1);
3705
3706     if (xx->tm_min  < yy->tm_min)
3707       return(-1);
3708     if (xx->tm_min  > yy->tm_min)
3709       return(1);
3710
3711     if (xx->tm_sec  < yy->tm_sec)
3712       return(-1);
3713     if (xx->tm_sec  > yy->tm_sec)
3714       return(1);
3715
3716     return(0);
3717 }
3718 #endif /* DOUPDATE */
3719
3720 #ifndef HAVE_TIMEGM             /* For platforms that do not have timegm() */
3721 static CONST int MONTHDAYS[] = { /* Number of days in each month. */
3722     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3723 };
3724
3725 /* Macro for whether a given year is a leap year. */
3726 #define ISLEAP(year) \
3727 (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
3728 #endif /* HAVE_TIMEGM */
3729
3730 /*  m k u t i m e  --  Like mktime() but argument is already UTC */
3731
3732 static time_t
3733 #ifdef CK_ANSIC
3734 mkutime(struct tm * tm)
3735 #else
3736 mkutime(tm) struct tm * tm;
3737 #endif /* CK_ANSIC */
3738 /* mkutime */ {
3739 #ifdef HAVE_TIMEGM
3740     return(timegm(tm));                 /* Have system service, use it. */
3741 #else
3742 /*
3743   Contributed by Russ Allbery (rra@stanford.edu), used by permission.
3744   Given a struct tm representing a calendar time in UTC, convert it to
3745   seconds since epoch.  Returns (time_t) -1 if the time is not
3746   convertable.  Note that this function does not canonicalize the provided
3747   struct tm, nor does it allow out-of-range values or years before 1970.
3748   Result should be identical with timegm().
3749 */
3750     time_t result = 0;
3751     int i;
3752     /*
3753       We do allow some ill-formed dates, but we don't do anything special
3754       with them and our callers really shouldn't pass them to us.  Do
3755       explicitly disallow the ones that would cause invalid array accesses
3756       or other algorithm problems.
3757     */
3758 #ifdef DEBUG
3759     if (deblog) {
3760         debug(F101,"mkutime tm_mon","",tm->tm_mon);
3761         debug(F101,"mkutime tm_year","",tm->tm_year);
3762     }
3763 #endif /* DEBUG */
3764     if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70)
3765       return((time_t) -1);
3766
3767     /* Convert to time_t. */
3768     for (i = 1970; i < tm->tm_year + 1900; i++)
3769       result += 365 + ISLEAP(i);
3770     for (i = 0; i < tm->tm_mon; i++)
3771       result += MONTHDAYS[i];
3772     if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900))
3773       result++;
3774     result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
3775     result = 60 * result + tm->tm_min;
3776     result = 60 * result + tm->tm_sec;
3777     debug(F101,"mkutime result","",result);
3778     return(result);
3779 #endif /* HAVE_TIMEGM */
3780 }
3781
3782
3783 /*
3784   s e t m o d t i m e  --  Set file modification time.
3785
3786   f = char * filename;
3787   t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local)
3788
3789   UNIX-specific; isolates mainline code from hideous #ifdefs.
3790   Returns:
3791     0 on success,
3792    -1 on error.
3793
3794 */
3795 static int
3796 #ifdef CK_ANSIC
3797 setmodtime(char * f, time_t t)
3798 #else
3799 setmodtime(f,t) char * f; time_t t;
3800 #endif /* CK_ANSIC */
3801 /* setmodtime */ {
3802 #ifdef NT
3803     struct _stat sb;
3804 #else /* NT */
3805     struct stat sb;
3806 #endif /* NT */
3807     int x, rc = 0;
3808 #ifdef BSD44
3809     struct timeval tp[2];
3810 #else  /* def BSD44 */
3811 #ifdef V7
3812     struct utimbuf {
3813         time_t timep[2];
3814     } tp;
3815 #else  /* def V7 */
3816 #ifdef SYSUTIMEH
3817 #ifdef NT
3818     struct _utimbuf tp;
3819 #else /* NT */
3820     struct utimbuf tp;
3821 #endif /* NT */
3822 #else /* def SYSUTIMEH */
3823 #ifdef VMS
3824     struct utimbuf tp;
3825 #define SYSUTIMEH               /* Our utimbuf matches this one. */
3826 #else /* def VMS */
3827     struct utimbuf {
3828         time_t atime;
3829         time_t mtime;
3830     } tp;
3831 #endif /* def VMS [else] */
3832 #endif /* def SYSUTIMEH [else] */
3833 #endif /* def V7 [else] */
3834 #endif /* def BSD44 [else] */
3835
3836     if (stat(f,&sb) < 0) {
3837         debug(F111,"setmodtime stat failure",f,errno);
3838         return(-1);
3839     }
3840 #ifdef BSD44
3841     tp[0].tv_sec = sb.st_atime;         /* Access time first */
3842     tp[1].tv_sec = t;                   /* Update time second */
3843     debug(F111,"setmodtime BSD44",f,t);
3844 #else
3845 #ifdef V7
3846     tp.timep[0] = t;                    /* Set modif. time to creation date */
3847     tp.timep[1] = sb.st_atime;          /* Don't change the access time */
3848     debug(F111,"setmodtime V7",f,t);
3849 #else
3850 #ifdef SYSUTIMEH
3851     tp.modtime = t;                     /* Set modif. time to creation date */
3852     tp.actime = sb.st_atime;            /* Don't change the access time */
3853     debug(F111,"setmodtime SYSUTIMEH",f,t);
3854 #else
3855     tp.mtime = t;                       /* Set modif. time to creation date */
3856     tp.atime = sb.st_atime;             /* Don't change the access time */
3857     debug(F111,"setmodtime (other)",f,t);
3858 #endif /* SYSUTIMEH */
3859 #endif /* V7 */
3860 #endif /* BSD44 */
3861
3862     /* Try to set the file date */
3863
3864 #ifdef BSD44
3865     x = utimes(f,tp);
3866     debug(F111,"setmodtime utimes()","BSD44",x);
3867 #else
3868 #ifdef IRIX65
3869     {
3870       /*
3871         The following produces the nonsensical warning:
3872         Argument  of type "const struct utimbuf *" is incompatible with
3873         parameter of type "const struct utimbuf *".  If you can make it
3874         go away, be my guest.
3875       */
3876         const struct utimbuf * t2 = &tp;
3877         x = utime(f,t2);
3878     }
3879 #else
3880     x = utime(f,&tp);
3881     debug(F111,"setmodtime utime()","other",x);
3882 #endif /* IRIX65 */
3883 #endif /* BSD44 */
3884     if (x)
3885       rc = -1;
3886
3887     debug(F101,"setmodtime result","",rc);
3888     return(rc);
3889 }
3890
3891
3892 /*
3893   c h k m o d t i m e  --  Check/Set file modification time.
3894
3895   fc = function code:
3896     0 = Check; returns:
3897       -1 on error,
3898        0 if local older than remote,
3899        1 if modtimes are equal,
3900        2 if local newer than remote.
3901     1 = Set (local file's modtime from remote's); returns:
3902       -1 on error,
3903        0 on success.
3904 */
3905 static int
3906 chkmodtime(local,remote,fc) char * local, * remote; int fc; {
3907 #ifdef NT
3908     struct _stat statbuf;
3909 #else /* NT */
3910     struct stat statbuf;
3911 #endif /* NT */
3912     struct tm * tmlocal = NULL;
3913     struct tm tmremote;
3914     int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0;
3915     char * s, timebuf[64];
3916
3917     debug(F111,"chkmodtime",local,mdtmok);
3918     if (!mdtmok)                        /* Server supports MDTM? */
3919       return(-1);                       /* No don't bother. */
3920
3921 #ifndef NOCSETS
3922     if (ftp_xla) {
3923         lcs = ftp_csl;
3924         if (lcs < 0) lcs = fcharset;
3925         rcs = ftp_csx;
3926         if (rcs < 0) rcs = ftp_csr;
3927     }
3928 #endif /* NOCSETS */
3929
3930     if (fc == 0) {
3931         rc = stat(local,&statbuf);
3932         if (rc == 0) {                  /* Get local file's mod time */
3933             /* Convert to struct tm */
3934             tmlocal = gmtime((time_t *)&statbuf.st_mtime);
3935 #ifdef DEBUG
3936             if (tmlocal) {
3937                 dbtime(local,tmlocal);
3938             }
3939 #endif /* DEBUG */
3940         }
3941     }
3942     /* Get remote file's mod time as yyyymmddhhmmss */
3943
3944     if (havemdtm) {                     /* Already got it from MLSD? */
3945         s = havemdtm;
3946         flag++;
3947     } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) {
3948         char c;
3949         bzero((char *)&tmremote, sizeof(struct tm));
3950         s = ftp_reply_str;
3951         while ((c = *s++)) {            /* Skip past response code */
3952             if (c == SP) {
3953                 flag++;
3954                 break;
3955             }
3956         }
3957     }
3958     if (flag) {
3959         debug(F111,"ftp chkmodtime string",s,flag);
3960         if (fts_sto) {                  /* User gave server time offset? */
3961             char * p;
3962             debug(F110,"ftp chkmodtime offset",fts_sto,0);
3963             ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */
3964             if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */
3965                 ckstrncpy(timebuf,p,64);      /* Convert to MDTM format */
3966                 timebuf[8]  = timebuf[9];  /* h */
3967                 timebuf[9]  = timebuf[10]; /* h */
3968                 timebuf[10] = timebuf[12]; /* m */
3969                 timebuf[11] = timebuf[13]; /* m */
3970                 timebuf[12] = timebuf[12]; /* s */
3971                 timebuf[13] = timebuf[13]; /* s */
3972                 timebuf[14] = NUL;
3973                 s = timebuf;
3974                 debug(F110,"ftp chkmodtime adjust",s,0);
3975             }
3976         }
3977         if (flag) {                     /* Convert to struct tm */
3978             char * pat;
3979             int y2kbug = 0;             /* Seen in Kerberos 4 FTP servers */
3980             if (!ckstrcmp(s,"191",3,0)) {
3981                 pat = "%05d%02d%02d%02d%02d%02d";
3982                 y2kbug++;
3983                 debug(F110,"ftp chkmodtime Y2K BUG detected",s,0);
3984             } else {
3985                 pat = "%04d%02d%02d%02d%02d%02d";
3986             }
3987             if (sscanf(s,               /* Parse into struct tm */
3988                        pat,
3989                        &(tmremote.tm_year),
3990                        &(tmremote.tm_mon),
3991                        &(tmremote.tm_mday),
3992                        &(tmremote.tm_hour),
3993                        &(tmremote.tm_min),
3994                        &(tmremote.tm_sec)
3995                        ) == 6) {
3996                 tmremote.tm_year -= (y2kbug ? 19000 : 1900);
3997                 debug(F101,"ftp chkmodtime year","",tmremote.tm_year);
3998                 tmremote.tm_mon--;
3999
4000 #ifdef DEBUG
4001                 debug(F100,"SERVER TIME FOLLOWS:","",0);
4002                 dbtime(remote,&tmremote);
4003 #endif /* DEBUG */
4004
4005                 if (havedate > -1)
4006                   havedate = 1;
4007             }
4008         }
4009     } else {                            /* Failed */
4010         debug(F101,"ftp chkmodtime ftpcode","",ftpcode);
4011         if (ftpcode == 500 ||           /* Command unrecognized */
4012             ftpcode == 502 ||           /* Command not implemented */
4013             ftpcode == 202)             /* Command superfluous */
4014           mdtmok = 0;                   /* Don't ask this server again */
4015         return(-1);
4016     }
4017     if (fc == 0) {                      /* Compare */
4018         if (havedate == 1) {            /* Only if we have both file dates */
4019             /*
4020               Compare with local file's time.  We don't use
4021               clock time (time_t) here in case of signed/unsigned
4022               confusion, etc.
4023             */
4024             int xx;
4025 #ifdef COMMENT
4026 #ifdef DEBUG        
4027             if (deblog) {
4028                 dbtime("LOCAL",tmlocal);
4029                 dbtime("REMOT",&tmremote);
4030             }
4031 #endif /* DEBUG */
4032 #endif /* COMMENT */
4033             xx = tmcompare(tmlocal,&tmremote);
4034             debug(F101,"chkmodtime tmcompare","",xx);
4035             return(xx + 1);
4036         }
4037     } else if (ftp_dates) {             /* Set */
4038         /*
4039           Here we must convert struct tm to time_t
4040           without applying timezone conversion, for which
4041           there is no portable API.  The method is hidden
4042           in mkutime(), defined above.
4043         */
4044         time_t utc;
4045         utc = mkutime(&tmremote);
4046         debug(F111,"ftp chkmodtime mkutime",remote,utc);
4047         if (utc != (time_t)-1)
4048           return(setmodtime(local,utc));
4049     }
4050     return(-1);
4051 }
4052
4053 /* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */
4054
4055 static int
4056 getfile(remote,local,recover,append,pipename,xlate,fcs,rcs)
4057     char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs;
4058 /* getfile */ {
4059     int rc = -1;
4060     ULONG t0, t1;
4061
4062 #ifdef GFTIMER
4063     CKFLOAT sec;
4064 #else
4065     int sec = 0;
4066 #endif /* GFTIMER */
4067     char fullname[CKMAXPATH+1];
4068
4069     debug(F110,"ftp getfile remote A",remote,0);
4070     debug(F110,"ftp getfile local A",local,0);
4071     debug(F110,"ftp getfile pipename",pipename,0);
4072     if (!remote) remote = "";
4073
4074 #ifdef PATTERNS
4075     /* Automatic type switching? */
4076     if (ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype) {
4077         int x;
4078         x = matchname(remote,0,servertype);
4079         debug(F111,"ftp getfile matchname",remote,x);
4080         switch (x) {
4081           case 0: ftp_typ = FTT_ASC; break;
4082           case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break;
4083           default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ;
4084         }
4085         changetype(ftp_typ,ftp_vbm);
4086         binary = ftp_typ;               /* For file-transfer display */
4087     }
4088 #endif /* PATTERNS */
4089
4090 #ifndef NOCSETS
4091     ftp_csx = -1;                       /* For file-transfer display */
4092     ftp_csl = -1;                       /* ... */
4093
4094     if (rcs > -1)                       /* -1 means no translation */
4095       if (ftp_typ == FTT_ASC)           /* File type is "ascii"? */
4096         if (fcs < 0)                    /* File charset not forced? */
4097           fcs = fcharset;               /* use prevailing FILE CHARACTER-SET */
4098     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4099         debug(F110,"ftp getfile","initxlate",0);
4100         initxlate(rcs,fcs);             /* NB: opposite order of PUT */
4101         ftp_csx = rcs;
4102         ftp_csl = fcs;
4103     } else
4104       xlate = 0;
4105 #endif /* NOCSETS */
4106
4107     if (!local) local = "";
4108     if (!pipename && !*local)
4109       local = remote;
4110
4111     out2screen = !strcmp(local,"-");
4112
4113     fullname[0] = NUL;
4114     if (pipename) {
4115         ckstrncpy(fullname,pipename,CKMAXPATH+1);
4116     } else {
4117         zfnqfp(local,CKMAXPATH,fullname);
4118         if (!fullname[0])
4119           ckstrncpy(fullname,local,CKMAXPATH+1);
4120     }
4121     if (!out2screen && displa && fdispla) { /* Screen */
4122         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,remote);
4123         ftscreen(SCR_AN,0,(CK_OFF_T)0,fullname);
4124         ftscreen(SCR_FS,0,fsize,"");
4125     }
4126     tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0);
4127     tlog(F110," as",fullname,0);
4128     debug(F111,"ftp getfile size",remote,fsize);
4129     debug(F111,"ftp getfile local",local,out2screen);
4130
4131     ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH);
4132
4133     t0 = gmstimer();                    /* Start time */
4134     debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */
4135     rc = recvrequest("RETR",
4136                      local,
4137                      remote,
4138                      append ? "ab" : "wb",
4139                      0,
4140                      recover,
4141                      pipename,
4142                      xlate,
4143                      fcs,
4144                      rcs
4145                      );
4146     t1 = gmstimer();                    /* End time */
4147     debug(F111,"ftp getfile t1",remote,t1);
4148     debug(F111,"ftp getfile sec",remote,(t1-t0)/1000);
4149 #ifdef GFTIMER
4150     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4151     fpxfsecs = sec;                     /* (for doxlog()) */
4152 #else
4153     sec = (t1 - t0) / 1000;
4154     xfsecs = (int)sec;
4155 #endif /* GFTIMER */
4156
4157 #ifdef FTP_TIMEOUT
4158     if (ftp_timed_out)
4159       rc = -4;
4160 #endif  /* FTP_TIMEOUT */
4161
4162     debug(F111,"ftp recvrequest rc",remote,rc);
4163     if (cancelfile || cancelgroup) {
4164         debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup);
4165         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4166     } else if (rc > 0) {
4167         debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup);
4168         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,cmarg);
4169     } else if (rc < 0) {
4170         switch (ftpcode) {
4171           case -4:                      /* Network error */
4172           case -2:                      /* File error */
4173             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,ck_errstr());
4174             break;
4175           case -3:
4176             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,
4177                      "Failure to make data connection");
4178             break;
4179           case -1:                      /* (should be covered above) */
4180             ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4181             break;
4182           default:
4183             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4184         }
4185     } else {                            /* Tudo bem */
4186         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4187         if (rc == 0) {
4188             ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,""); /* For screen */
4189             makestr(&rrfspec,remote);     /* For WHERE command */
4190             makestr(&rfspec,fullname);
4191         }
4192     }
4193     if (rc > -1) {
4194         if (ftp_dates)                  /* If FTP DATES ON... */
4195           if (!pipename && !out2screen) /* and it's a real file */
4196             if (rc < 1 && rc != -3)     /* and it wasn't skipped */
4197               if (connected)            /* and we still have a connection */
4198                 if (zchki(local) > -1) { /* and the file wasn't discarded */
4199                     chkmodtime(local,remote,1); /* set local file date */
4200                     debug(F110,"ftp get set date",local,0);
4201                 }
4202         filcnt++;                       /* Used by \v(filenum) */
4203     }
4204 #ifdef TLOG
4205     if (tralog) {
4206         if (rc > 0) {
4207             tlog(F100," recovery skipped","",0);
4208         } else if (rc == 0) {
4209             tlog(F101," complete, size", "", fsize);
4210         } else if (cancelfile) {
4211             tlog(F100," canceled by user","",0);
4212 #ifdef FTP_TIMEOUT
4213         } else if (ftp_timed_out) {
4214             tlog(F100," timed out","",0);
4215 #endif  /* FTP_TIMEOUT */
4216         } else {
4217             tlog(F110," failed:",ftp_reply_str,0);
4218         }
4219         if (!tlogfmt)
4220           doxlog(what,local,fsize,ftp_typ,rc,"");
4221     }
4222 #endif /* TLOG */
4223     return(rc);
4224 }
4225
4226 /* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */
4227 /* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */
4228
4229 static int
4230 putfile(cx,
4231     local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg)
4232     char * local, * remote, * mvto, *rnto, *srvrn;
4233     int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg, prm;
4234
4235 /* putfile */ {
4236
4237     char asname[CKMAXPATH+1];
4238     char fullname[CKMAXPATH+1];
4239     int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0;
4240     int xlate = 0, restart = 0, mt = -1;
4241     char * s = NULL, * cmd = NULL;
4242     ULONG t0 = 0, t1 = 0;               /* Times for stats */
4243     int ofcs = 0, orcs = 0;
4244
4245 #ifdef GFTIMER
4246     CKFLOAT sec = 0.0;
4247 #else
4248     int sec = 0;
4249 #endif /* GFTIMER */
4250     debug(F111,"ftp putfile flg",local,flg);
4251     debug(F110,"ftp putfile srv_renam",srvrn,0);
4252     debug(F101,"ftp putfile fcs","",fcs);
4253     debug(F101,"ftp putfile rcs","",rcs);
4254
4255     ofcs = fcs;                         /* Save charset args */
4256     orcs = rcs;
4257
4258     sendstart = (CK_OFF_T)0;
4259     restart = flg & PUT_RES;
4260     if (!remote)
4261       remote = "";
4262
4263     /* FTP protocol command to send to server */
4264     cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR");
4265
4266     if (x_cnv == SET_AUTO) {            /* Name conversion is auto */
4267         if (alike) {                    /* If server & client are alike */
4268             nc = 0;                     /* no conversion */
4269         } else {                        /* If they are different */
4270             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
4271               nc = -1;                  /* only minimal conversions needed */
4272             else                        /* otherwise */
4273               nc = 1;                   /* full conversion */
4274         }
4275     } else                              /* Not auto - do what user said */
4276       nc = x_cnv;
4277
4278     /* If Transfer Mode is Automatic, determine file type */
4279     if (ftp_xfermode == XMODE_A && filepeek && !pipesend) {
4280         if (isdir(local)) {             /* If it's a directory */
4281             k = FT_BIN;                 /* skip the file scan */
4282         } else {
4283             debug(F110,"FTP PUT calling scanfile",local,0);
4284             k = scanfile(local,&o,nscanfile); /* Scan the file */
4285         }
4286         debug(F111,"FTP PUT scanfile",local,k);
4287         if (k > -1 && !forcetype) {
4288             ftp_typ = (k == FT_BIN) ? 1 : 0;
4289             if (xft > -1 && ftp_typ != xft) {
4290                 if (flg & PUT_SIM)
4291                   tlog(F110,"ftp put SKIP (Type):", local, 0);
4292                 return(SKP_TYP);
4293             }
4294             if (ftp_typ == 1 && tenex)  /* User said TENEX? */
4295               ftp_typ = FTT_TEN;
4296         }
4297     }
4298 #ifndef NOCSETS
4299     ftp_csx = -1;                       /* For file-transfer display */
4300     ftp_csl = -1;                       /* ... */
4301
4302     if (rcs > -1) {                     /* -1 means no translation */
4303         if (ftp_typ == 0) {             /* File type is "ascii"? */
4304             if (fcs < 0) {              /* File charset not forced? */
4305                 if (k < 0) {            /* If we didn't scan */
4306                     fcs = fcharset;     /* use prevailing FILE CHARACTER-SET */
4307                 } else {                /* If we did scan, use scan result */
4308                     switch (k) {
4309                       case FT_TEXT:     /* Unknown text */
4310                         fcs = fcharset;
4311                         break;
4312                       case FT_7BIT:     /* 7-bit text */
4313                         fcs = dcset7;
4314                         break;
4315                       case FT_8BIT:     /* 8-bit text */
4316                         fcs = dcset8;
4317                         break;
4318                       case FT_UTF8:     /* UTF-8 */
4319                         fcs = FC_UTF8;
4320                         break;
4321                       case FT_UCS2:     /* UCS-2 */
4322                         fcs = FC_UCS2;
4323                         if (o > -1)     /* Input file byte order */
4324                           fileorder = o;
4325                         break;
4326                       default:
4327                         rcs = -1;
4328                     }
4329                 }
4330             }
4331         }
4332     }
4333     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4334         debug(F110,"ftp putfile","initxlate",0);
4335         initxlate(fcs,rcs);
4336         debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs);
4337         xlate = 1;
4338         ftp_csx = rcs;
4339         ftp_csl = fcs;
4340     }
4341 #endif /* NOCSETS */
4342
4343     binary = ftp_typ;                   /* For file-transfer display */
4344     asname[0] = NUL;
4345
4346     if (recursive) {                    /* If sending recursively, */
4347         if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */
4348           return(-1);                   /* Don't PUT if it fails. */
4349         else if (isdir(local))          /* It's a directory */
4350           return(0);                    /* Don't send it! */
4351     }
4352     if (*remote) {                      /* If an as-name template was given */
4353 #ifndef NOSPL
4354         if (cmd_quoting) {              /* and COMMAND QUOTING is ON */
4355             y = CKMAXPATH;              /* evaluate it for this file */
4356             s = asname;
4357             zzstring(remote,&s,&y);
4358         } else
4359 #endif /* NOSPL */
4360           ckstrncpy(asname,remote,CKMAXPATH);   /* (or take it literally) */
4361     } else {                                    /* No as-name */
4362         nzltor(local,asname,nc,0,CKMAXPATH);    /* use local name strip path */
4363         debug(F110,"FTP PUT nzltor",asname,0);
4364     }
4365     /* Preliminary messages and log entries */
4366
4367     fullname[0] = NUL;
4368     zfnqfp(local,CKMAXPATH,fullname);
4369     if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1);
4370     fullname[CKMAXPATH] = NUL;
4371
4372     if (displa && fdispla) {            /* Screen */
4373         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,local);
4374         ftscreen(SCR_AN,0,(CK_OFF_T)0,asname);
4375         ftscreen(SCR_FS,0,fsize,"");
4376     }
4377 #ifdef DOUPDATE
4378     if (flg & (PUT_UPD|PUT_DIF)) {      /* Date-checking modes... */
4379         mt = chkmodtime(fullname,asname,0);
4380         debug(F111,"ftp putfile chkmodtime",asname,mt);
4381         if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */
4382             tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0);
4383             /* Skip this one */
4384             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_DAT,fullname);
4385             filcnt++;
4386             return(SKP_DAT);
4387         } else if (mt == 1) {           /* Times are equal */
4388             tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0);
4389             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_EQU,fullname); /* Skip it */
4390             filcnt++;
4391             return(SKP_DAT);
4392         }
4393         /* Local file is newer */
4394         tlog(F110,ftp_typ ? "ftp put /update BINARY:" :
4395              "ftp put /update TEXT:", fullname, 0);
4396     } else if (flg & PUT_RES) {
4397         tlog(F110,ftp_typ ? "ftp put /recover BINARY:" :
4398              "ftp put /recover TEXT:", fullname, 0);
4399     } else {
4400         tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4401     }
4402 #else
4403     tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4404 #endif /* DOUPDATE */
4405     tlog(F110," as",asname,0);
4406
4407 #ifndef NOCSETS
4408     if (xlate) {
4409         debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs);
4410         tlog(F110," file character set:",fcsinfo[fcs].keyword,0);
4411         tlog(F110," server character set:",fcsinfo[rcs].keyword,0);
4412     } else if (!ftp_typ) {
4413         tlog(F110," character sets:","no conversion",0);
4414         fcs = ofcs;                     /* Binary file but we still must */
4415         rcs = orcs;                     /* translate its name */
4416     }
4417 #endif /* NOCSETS */
4418
4419     /* PUT THE FILE */
4420
4421     t0 = gmstimer();                    /* Start time */
4422     if (flg & PUT_SIM) {                /* rc > 0 is a skip reason code */
4423         if (flg & (PUT_UPD|PUT_DIF)) {  /* (see SKP_xxx in ckcker.h) */
4424             rc = (mt < 0) ?             /* Update mode... */
4425               SKP_XNX :                 /* Remote file doesn't exist */
4426                 SKP_XUP;                /* Remote file is older */
4427         } else {
4428             rc = SKP_SIM;               /* "Would be sent", period. */
4429         }
4430     } else {
4431         rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart);
4432     }
4433     t1 = gmstimer();                    /* End time */
4434     filcnt++;                           /* File number */
4435
4436 #ifdef GFTIMER
4437     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4438     fpxfsecs = sec;                     /* (for doxlog()) */
4439 #else
4440     sec = (t1 - t0) / 1000;
4441     xfsecs = (int)sec;
4442 #endif /* GFTIMER */
4443
4444     debug(F111,"ftp sendrequest rc",local,rc);
4445
4446     if (cancelfile || cancelgroup) {
4447         debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup);
4448         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4449     } else if (rc > 0) {
4450         debug(F101,"ftp put skipped",local,rc);
4451         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)rc,fullname);
4452     } else if (rc < 0) {
4453         debug(F111,"ftp put error",local,ftpcode);
4454         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4455     } else {
4456         debug(F111,"ftp put not canceled",ckitoa(displa),fdispla);
4457         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4458         debug(F111,"ftp put ST_OK",local,rc);
4459         ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,"");
4460         debug(F110,"ftp put old sfspec",sfspec,0);
4461         makestr(&sfspec,fullname);      /* For WHERE command */
4462         debug(F110,"ftp put new sfspec",sfspec,0);
4463         debug(F110,"ftp put old srfspec",srfspec,0);
4464         makestr(&srfspec,asname);
4465         debug(F110,"ftp put new srfspec",srfspec,0);
4466     }
4467
4468     /* Final log entries */
4469
4470 #ifdef TLOG
4471     if (tralog) {
4472         if (rc > 0) {
4473             if (rc == SKP_XNX)
4474               tlog(F100," /simulate: WOULD BE SENT:","no remote file",0);
4475             else if (rc == SKP_XUP)
4476               tlog(F100," /simulate: WOULD BE SENT:","remote file older",0);
4477             else if (rc == SKP_SIM)
4478               tlog(F100," /simulate: WOULD BE SENT","",0);
4479             else
4480               tlog(F110," skipped:",gskreason(rc),0);
4481         } else if (rc == 0) {
4482             tlog(F101," complete, size", "", fsize);
4483         } else if (cancelfile) {
4484             tlog(F100," canceled by user","",0);
4485         } else {
4486             tlog(F110," failed:",ftp_reply_str,0);
4487         }
4488         if (!tlogfmt)
4489           doxlog(what,local,fsize,ftp_typ,rc,"");
4490     }
4491 #endif /* TLOG */
4492
4493     if (rc < 0)                         /* PUT did not succeed */
4494       return(-1);                       /* so done. */
4495
4496     if (flg & PUT_SIM)                  /* Simulating, skip the rest. */
4497       return(SKP_SIM);
4498
4499 #ifdef UNIX
4500     /* Set permissions too? */
4501
4502     if (prm) {                          /* Change permissions? */
4503         s = zgperm(local);              /* Get perms of local file */
4504         if (!s) s = "";
4505         x = strlen(s);
4506         if (x > 3) s += (x - 3);
4507         if (rdigits(s)) {
4508             ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL);
4509             x =
4510               ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE;
4511             tlog(F110, x ? " chmod" : " chmod failed",
4512                  s,
4513                  0
4514                  );
4515             if (!x)
4516               return(-1);
4517         }
4518     }
4519 #endif /* UNIX */
4520
4521     /* Disposition of source file */
4522
4523     if (moving) {
4524         x = zdelet(local);
4525         tlog(F110, (x > -1) ?
4526              " deleted" : " failed to delete",
4527              local,
4528              0
4529              );
4530         if (x < 0)
4531           return(-1);
4532     } else if (mvto) {
4533         x = zrename(local,mvto);
4534         tlog(F110, (x > -1) ?
4535              " moved source to" : " failed to move source to",
4536              mvto,
4537              0
4538              );
4539         if (x < 0)
4540           return(-1);
4541         /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,mvto); */
4542
4543     } else if (rnto) {
4544         char * s = rnto;
4545 #ifndef NOSPL
4546         int y;                          /* Pass it thru the evaluator */
4547         extern int cmd_quoting;         /* for \v(filename) */
4548         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4549             y = CKMAXPATH;
4550             s = (char *)asname;
4551             zzstring(rnto,&s,&y);
4552             s = (char *)asname;
4553         }
4554 #endif /* NOSPL */
4555         if (s) if (*s) {
4556             int x;
4557             x = zrename(local,s);
4558             tlog(F110, (x > -1) ?
4559                  " renamed source file to" :
4560                  " failed to rename source file to",
4561                  s,
4562                  0
4563                  );
4564             if (x < 0)
4565               return(-1);
4566             /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,s); */
4567         }
4568     }
4569
4570     /* Disposition of destination file */
4571
4572     if (srvrn) {                        /* /SERVER-RENAME: */
4573         char * s = srvrn;
4574 #ifndef NOSPL
4575         int y;                          /* Pass it thru the evaluator */
4576         extern int cmd_quoting; /* for \v(filename) */
4577         debug(F111,"ftp putfile srvrn",s,1);
4578
4579         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4580             y = CKMAXPATH;
4581             s = (char *)fullname;       /* We can recycle this buffer now */
4582             zzstring(srvrn,&s,&y);
4583             s = (char *)fullname;
4584         }
4585 #endif /* NOSPL */
4586         debug(F111,"ftp putfile srvrn",s,2);
4587         if (s) if (*s) {
4588             int x;
4589             x = ftp_rename(asname,s);
4590             debug(F111,"ftp putfile ftp_rename",asname,x);
4591             tlog(F110, (x > 0) ?
4592                  " renamed destination file to" :
4593                  " failed to rename destination file to",
4594                  s,
4595                  0
4596                  );
4597             if (x < 1)
4598               return(-1);
4599         }
4600     }
4601     return(0);
4602 }
4603
4604 /* xxout must only be used for ASCII transfers */
4605 static int
4606 #ifdef CK_ANSIC
4607 xxout(char c)
4608 #else
4609 xxout(c) char c;
4610 #endif /* CK_ANSIC */
4611 {
4612 #ifndef OS2
4613 #ifndef VMS
4614 #ifndef MAC
4615 #ifndef OSK
4616     /* For Unix, DG, Stratus, Amiga, Gemdos, other */
4617     if (c == '\012') {
4618         if (zzout(dout,(CHAR)'\015') < 0)
4619           return(-1);
4620         ftpsnd.bytes++;
4621     }
4622 #else /* OSK */
4623     if (c == '\015') {
4624         c = '\012';
4625         if (zzout(dout,(CHAR)'\015') < 0)
4626           return(-1);
4627         ftpsnd.bytes++;
4628     }
4629 #endif /* OSK */
4630 #else /* MAC */
4631     if (c == '\015') {
4632         c = '\012';
4633         if (zzout(dout,(CHAR)'\015') < 0)
4634           return(-1);
4635         ftpsnd.bytes++;
4636     }
4637 #endif /* MAC */
4638 #endif /* VMS */
4639 #endif /* OS2 */
4640     if (zzout(dout,(CHAR)c) < 0)
4641       return(-1);
4642     ftpsnd.bytes++;
4643     return(0);
4644 }
4645
4646 static int
4647 #ifdef CK_ANSIC
4648 scrnout(char c)
4649 #else
4650 scrnout(c) char c;
4651 #endif /* CK_ANSIC */
4652 {
4653     return(putchar(c));
4654 }
4655
4656 static int
4657 #ifdef CK_ANSIC
4658 pipeout(char c)
4659 #else
4660 pipeout(c) char c;
4661 #endif /* CK_ANSIC */
4662 {
4663     return(zmchout(c));
4664 }
4665
4666 static int
4667 ispathsep(c) int c; {
4668     switch (servertype) {
4669       case SYS_VMS:
4670       case SYS_TOPS10:
4671       case SYS_TOPS20:
4672         return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0);
4673       case SYS_OS2:
4674       case SYS_WIN32:
4675       case SYS_DOS:
4676         return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0);
4677       case SYS_VOS:
4678         return((c == '>') ? 1 : 0);
4679       default:
4680         return((c == '/') ? 1 : 0);
4681     }
4682 }
4683
4684 static int
4685 iscanceled() {
4686 #ifdef CK_CURSES
4687     extern int ck_repaint();
4688 #endif /* CK_CURSES */
4689     int x, rc = 0;
4690     char c = 0;
4691     if (cancelfile)
4692       return(1);
4693     x = conchk();                       /* Any chars waiting at console? */
4694     if (x-- > 0) {                      /* Yes...  */
4695         c = coninc(5);                  /* Get one */
4696         switch (c) {
4697           case 032:                     /* Ctrl-X or X */
4698           case 'z':
4699           case 'Z': cancelgroup++;      /* fall thru on purpose */
4700           case 030:                     /* Ctrl-Z or Z */
4701           case 'x':
4702           case 'X': cancelfile++; rc++; break;
4703 #ifdef CK_CURSES
4704           case 'L':
4705           case 'l':
4706           case 014:                     /* Ctrl-L or L or Ctrl-W */
4707           case 027:
4708             ck_repaint();               /* Refresh screen */
4709 #endif /* CK_CURSES */
4710         }
4711     }
4712     while (x-- > 0)                     /* Soak up any rest */
4713       c = coninc(1);
4714     return(rc);
4715 }
4716
4717 #ifdef FTP_TIMEOUT
4718 /* fc = 0 for read; 1 for write */
4719 static int
4720 check_data_connection(fd,fc) int fd, fc; {
4721     int x;
4722     struct timeval tv;
4723     fd_set in, out, err;
4724
4725     if (ftp_timeout < 1L)
4726       return(0);
4727
4728     FD_ZERO(&in);
4729     FD_ZERO(&out);
4730     FD_ZERO(&err);
4731     FD_SET(fd,fc ? &out : &in);
4732     tv.tv_sec = ftp_timeout;            /* Time limit */
4733     tv.tv_usec = 0L;
4734
4735 #ifdef INTSELECT
4736     x = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err,&tv);
4737 #else
4738     x = select(FD_SETSIZE,&in,&out,&err,&tv);
4739 #endif /* INTSELECT */
4740
4741     if (x == 0) {
4742 #ifdef EWOULDBLOCK
4743         errno = EWOULDBLOCK;
4744 #else
4745 #ifdef EAGAIN
4746         errno = EAGAIN;
4747 #else
4748         errno = 11;
4749 #endif  /* EAGAIN */
4750 #endif  /* EWOULDBLOCK */
4751         debug(F100,"ftp check_data_connection TIMOUT","",0);
4752         return(-3);
4753     }
4754     return(0);
4755 }
4756 #endif  /* FTP_TIMEOUT */
4757
4758 /* zzsend - used by buffered output macros. */
4759
4760 static int
4761 #ifdef CK_ANSIC
4762 zzsend(int fd, CHAR c)
4763 #else
4764 zzsend(fd,c) int fd; CHAR c;
4765 #endif /* CK_ANSIC */
4766 {
4767     int rc;
4768
4769     debug(F101,"zzsend ucbufsiz","",ucbufsiz);
4770     debug(F101,"zzsend nout","",nout);
4771     debug(F111,"zzsend","secure?",ftpissecure());
4772
4773     if (iscanceled())                   /* Check for cancellation */
4774       return(-9);
4775
4776 #ifdef FTP_TIMEOUT    
4777     ftp_timed_out = 0;
4778     if (check_data_connection(fd,1) < 0) {
4779         ftp_timed_out = 1;
4780         return(-3);
4781     }
4782 #endif  /* FTP_TIMEOUT */
4783
4784     rc = (!ftpissecure()) ?
4785       send(fd, (SENDARG2TYPE)ucbuf, nout, 0) :
4786         secure_putbuf(fd, ucbuf, nout);
4787     ucbuf[nout] = NUL;
4788     nout = 0;
4789     ucbuf[nout++] = c;
4790     spackets++;
4791     pktnum++;
4792     if (rc > -1 && fdispla != XYFD_B) {
4793         spktl = nout;
4794         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
4795     }
4796     return(rc);
4797 }
4798
4799 /* c m d l i n p u t  --  Command-line PUT */
4800
4801 int
4802 cmdlinput(stay) int stay; {
4803     int x, rc = 0, done = 0, good = 0, status = 0;
4804     ULONG t0, t1;                       /* Times for stats */
4805 #ifdef GFTIMER
4806     CKFLOAT sec;
4807 #else
4808     int sec = 0;
4809 #endif /* GFTIMER */
4810
4811     if (quiet) {                        /* -q really means quiet */
4812         displa = 0;
4813         fdispla = 0;
4814     } else {
4815         displa = 1;
4816         fdispla = XYFD_B;
4817     }
4818     testing = 0;
4819     out2screen = 0;
4820     dpyactive = 0;
4821     what = W_FTP|W_SEND;
4822
4823 #ifndef NOSPL
4824     cmd_quoting = 0;
4825 #endif /* NOSPL */
4826     sndsrc = nfils;
4827
4828     t0 = gmstimer();                    /* Record starting time */
4829
4830     while (!done && !cancelgroup) {     /* Loop for all files */
4831
4832         cancelfile = 0;
4833         x = gnfile();                   /* Get next file from list(s) */
4834         if (x == 0)                     /* (see gnfile() comments...) */
4835           x = gnferror;
4836
4837         switch (x) {
4838           case 1:                       /* File to send */
4839             rc = putfile(FTP_PUT,       /* Function (PUT, APPEND) */
4840                          filnam,        /* Local file to send */
4841                          filnam,        /* Remote name for file */
4842                          forcetype,     /* Text/binary mode forced */
4843                          0,             /* Not moving */
4844                          NULL,          /* No move-to */
4845                          NULL,          /* No rename-to */
4846                          NULL,          /* No server-rename */
4847                          ftp_cnv,       /* Filename conversion */
4848                          0,             /* Unique-server-names */
4849                          -1,            /* All file types */
4850                          0,             /* No permissions */
4851                          -1,            /* No character sets */
4852                          -1,            /* No character sets */
4853                          0              /* No update or restart */
4854                          );
4855             if (rc > -1) {
4856                 good++;
4857                 status = 1;
4858             }
4859             if (cancelfile) {
4860                 continue;               /* Or break? */
4861             }
4862             if (rc < 0) {
4863                 ftp_fai++;
4864             }
4865             continue;                   /* Or break? */
4866
4867           case 0:                       /* No more files, done */
4868             done++;
4869             continue;
4870
4871           case -2:
4872           case -1:
4873             printf("?%s: file not found - \"%s\"\n",
4874                    puterror ? "Fatal" : "Warning",
4875                    filnam
4876                    );
4877             continue;                   /* or break? */
4878           case -3:
4879             printf("?Warning access denied - \"%s\"\n", filnam);
4880             continue;                   /* or break? */
4881           case -5:
4882             printf("?Too many files match\n");
4883             done++;
4884             break;
4885           case -6:
4886             if (good < 1)
4887               printf("?No files selected\n");
4888             done++;
4889             break;
4890           default:
4891             printf("?getnextfile() - unknown failure\n");
4892             done++;
4893         }
4894     }
4895     if (status > 0) {
4896         if (cancelgroup)
4897           status = 0;
4898         else if (cancelfile && good < 1)
4899           status = 0;
4900     }
4901     success = status;
4902     x = success;
4903     if (x > -1) {
4904         lastxfer = W_FTP|W_SEND;
4905         xferstat = success;
4906     }
4907     t1 = gmstimer();                    /* End time */
4908 #ifdef GFTIMER
4909     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4910     if (!sec) sec = 0.001;
4911     fptsecs = sec;
4912 #else
4913     sec = (t1 - t0) / 1000;
4914     if (!sec) sec = 1;
4915 #endif /* GFTIMER */
4916     tfcps = (long) (tfc / sec);
4917     tsecs = (int)sec;
4918     lastxfer = W_FTP|W_SEND;
4919     xferstat = success;
4920     if (dpyactive)
4921       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
4922     if (!stay)
4923       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
4924     return(success);
4925 }
4926
4927
4928 /*  d o f t p p u t  --  Parse and execute PUT, MPUT, and APPEND  */
4929
4930 int
4931 #ifdef CK_ANSIC
4932 doftpput(int cx, int who)               /* who == 1 for ftp, 0 for kermit */
4933 #else
4934 doftpput(cx,who) int cx, who;
4935 #endif /* CK_ANSIC */
4936 {
4937     struct FDB sf, fl, sw, cm;
4938     int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0;
4939     int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0;
4940     char * s, * s2;
4941
4942     int x_csl, x_csr = -1;              /* Local and remote charsets */
4943     int x_xla = 0;
4944     int x_recurse = 0;
4945     char c, * p;                        /* Workers */
4946 #ifdef PUTARRAY
4947     int range[2];                       /* Array range */
4948     char ** ap = NULL;                  /* Array pointer */
4949     int arrayx = -1;                    /* Array index */
4950 #endif /* PUTARRAY */
4951     ULONG t0 = 0L, t1 = 0L;             /* Times for stats */
4952 #ifdef GFTIMER
4953     CKFLOAT sec;
4954 #else
4955     int sec = 0;
4956 #endif /* GFTIMER */
4957
4958     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
4959     success = 0;                        /* Assume failure */
4960     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
4961     out2screen = 0;                     /* Not outputting file to screen */
4962     putflags = 0;                       /* PUT options */
4963     x_cnv = ftp_cnv;                    /* Filename conversion */
4964     x_usn = ftp_usn;                    /* Unique server names */
4965     x_prm = ftp_prm;                    /* Permissions */
4966     if (x_prm == SET_AUTO)              /* Permissions AUTO */
4967       x_prm = alike;
4968
4969 #ifndef NOCSETS
4970     x_csr = ftp_csr;                    /* Inherit global server charset */
4971     x_csl = ftp_csl;
4972     if (x_csl < 0)
4973       x_csl = fcharset;
4974     x_xla = ftp_xla;
4975 #endif /* NOCSETS */
4976
4977     makestr(&filefile,NULL);            /* No filename list file yet. */
4978     makestr(&srv_renam,NULL);           /* Clear /SERVER-RENAME: */
4979     makestr(&snd_rename,NULL);          /*  PUT /RENAME */
4980     makestr(&snd_move,NULL);            /*  PUT /MOVE */
4981     putpath[0] = NUL;                   /* Initialize for syncdir(). */
4982     puterror = ftp_err;                 /* Inherit global error action. */
4983     what = W_SEND|W_FTP;                /* What we're doing (sending w/FTP) */
4984     asnambuf[0] = NUL;                  /* Clear as-name buffer */
4985
4986     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
4987         ftp_typ = g_ftp_typ;
4988         /* g_ftp_typ = -1; */
4989     }
4990     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
4991         pv[i].sval = NULL;              /* to null pointers */
4992         pv[i].ival = -1;                /* and -1 int values */
4993         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
4994     }
4995     if (who == 0) {                     /* Called with unprefixed command */
4996         switch (cx) {
4997           case XXRSEN:  pv[SND_RES].ival = 1; break;
4998           case XXCSEN:  pv[SND_CMD].ival = 1; break;
4999           case XXMOVE:  pv[SND_DEL].ival = 1; break;
5000           case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */
5001           case XXMSE:   mput++; break;
5002         }
5003     } else {
5004         if (cx == FTP_REP)
5005           pv[SND_RES].ival = 1;
5006         if (cx == FTP_MPU)
5007           mput++;
5008     }
5009     cmfdbi(&sw,                         /* First FDB - command switches */
5010            _CMKEY,                      /* fcode */
5011            "Filename, or switch",       /* hlpmsg */
5012            "",                          /* default */
5013            "",                          /* addtl string data */
5014            nputswi,                     /* addtl numeric data 1: tbl size */
5015            4,                           /* addtl numeric data 2: 4 = cmswi */
5016            xxstring,                    /* Processing function */
5017            putswi,                      /* Keyword table */
5018            &sf                          /* Pointer to next FDB */
5019            );
5020     cmfdbi(&fl,                         /* 3rd FDB - local filespec */
5021            _CMFLD,                      /* fcode */
5022            "",                          /* hlpmsg */
5023            "",                          /* default */
5024            "",                          /* addtl string data */
5025            0,                           /* addtl numeric data 1 */
5026            0,                           /* addtl numeric data 2 */
5027            xxstring,
5028            NULL,
5029            &cm
5030            );
5031     cmfdbi(&cm,                         /* 4th FDB - Confirmation */
5032            _CMCFM,                      /* fcode */
5033            "",                          /* hlpmsg */
5034            "",                          /* default */
5035            "",                          /* addtl string data */
5036            0,                           /* addtl numeric data 1 */
5037            0,                           /* addtl numeric data 2 */
5038            NULL,
5039            NULL,
5040            NULL
5041            );
5042
5043   again:
5044     cmfdbi(&sf,                         /* 2nd FDB - file to send */
5045            _CMIFI,                      /* fcode */
5046            "",                          /* hlpmsg */
5047            "",                          /* default */
5048            "",                          /* addtl string data */
5049            /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */
5050            nolinks | x_recurse,         /* addtl numeric data 1 */
5051            0,                           /* dirflg 0 means "not dirs only" */
5052            xxstring,
5053            NULL,
5054 #ifdef COMMENT
5055            mput ? &cm : &fl
5056 #else
5057            &fl
5058 #endif /* COMMENT */
5059            );
5060
5061     while (1) {                         /* Parse zero or more switches */
5062         x = cmfdb(&sw);                 /* Parse something */
5063         debug(F101,"ftp put cmfdb A","",x);
5064         debug(F101,"ftp put fcode A","",cmresult.fcode);
5065         if (x < 0)                      /* Error */
5066           goto xputx;                   /* or reparse needed */
5067         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
5068           break;
5069         c = cmgbrk();                   /* Get break character */
5070         getval = (c == ':' || c == '='); /* to see how they ended the switch */
5071         if (getval && !(cmresult.kflags & CM_ARG)) {
5072             printf("?This switch does not take arguments\n");
5073             x = -9;
5074             goto xputx;
5075         }
5076         if (!getval && (cmgkwflgs() & CM_ARG)) {
5077             printf("?This switch requires an argument\n");
5078             x = -9;
5079             goto xputx;
5080         }
5081         n = cmresult.nresult;           /* Numeric result = switch value */
5082         debug(F101,"ftp put switch","",n);
5083
5084         switch (n) {                    /* Process the switch */
5085           case SND_AFT:                 /* Send /AFTER:date-time */
5086           case SND_BEF:                 /* Send /BEFORE:date-time */
5087           case SND_NAF:                 /* Send /NOT-AFTER:date-time */
5088           case SND_NBE:                 /* Send /NOT-BEFORE:date-time */
5089             if (!getval) break;
5090             if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) {
5091                 if (x == -3) {
5092                     printf("?Date-time required\n");
5093                     x = -9;
5094                 }
5095                 goto xputx;
5096             }
5097             pv[n].ival = 1;
5098             makestr(&(pv[n].sval),s);
5099             break;
5100
5101           case SND_ASN:                 /* /AS-NAME: */
5102             debug(F101,"ftp put /as-name getval","",getval);
5103             if (!getval) break;
5104             if ((x = cmfld("Name to send under","",&s,NULL)) < 0) {
5105                 if (x == -3) {
5106                     printf("?name required\n");
5107                     x = -9;
5108                 }
5109                 goto xputx;
5110             }
5111             makestr(&(pv[n].sval),brstrip(s));
5112             debug(F110,"ftp put /as-name 1",pv[n].sval,0);
5113             if (pv[n].sval) pv[n].ival = 1;
5114             break;
5115
5116 #ifdef PUTARRAY
5117           case SND_ARR:                 /* /ARRAY */
5118             if (!getval) break;
5119             ap = NULL;
5120             if ((x = cmfld("Array name (a single letter will do)",
5121                            "",
5122                            &s,
5123                            NULL
5124                            )) < 0) {
5125                 if (x == -3)
5126                   break;
5127                 else
5128                   return(x);
5129             }
5130             if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) {
5131                 printf("?Bad array: %s\n",s);
5132                 return(-9);
5133             }
5134             if (!(ap = a_ptr[x])) {
5135                 printf("?No such array: %s\n",s);
5136                 return(-9);
5137             }
5138             pv[n].ival = 1;
5139             pv[SND_CMD].ival = 0;       /* Undo any conflicting ones... */
5140             pv[SND_RES].ival = 0;
5141             pv[SND_FIL].ival = 0;
5142             arrayx = x;
5143             break;
5144 #endif /* PUTARRAY */
5145
5146           case SND_BIN:                 /* /BINARY */
5147           case SND_TXT:                 /* /TEXT or /ASCII */
5148           case SND_TEN:                 /* /TENEX */
5149             pv[SND_BIN].ival = 0;
5150             pv[SND_TXT].ival = 0;
5151             pv[SND_TEN].ival = 0;
5152             pv[n].ival = 1;
5153             break;
5154
5155 #ifdef PUTPIPE
5156           case SND_CMD:                 /* These take no args */
5157             if (nopush) {
5158                 printf("?Sorry, system command access is disabled\n");
5159                 x = -9;
5160                 goto xputx;
5161             }
5162 #ifdef PIPESEND
5163             else if (sndfilter) {
5164                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
5165                 x = -9;
5166                 goto xputx;
5167             }
5168 #endif /* PIPESEND */
5169             sw.hlpmsg = "Command, or switch"; /* Change help message */
5170             pv[n].ival = 1;             /* Just set the flag */
5171             pv[SND_ARR].ival = 0;
5172             break;
5173 #endif /* PUTPIPE */
5174
5175 #ifdef CKSYMLINK
5176           case SND_LNK:
5177             nolinks = 0;
5178             goto again;                 /* Because CMIFI params changed... */
5179           case SND_NLK:
5180             nolinks = 2;
5181             goto again;
5182 #endif /* CKSYMLINK */
5183
5184 #ifdef FTP_RESTART
5185           case SND_RES:                 /* /RECOVER (resend) */
5186             pv[SND_ARR].ival = 0;       /* fall thru on purpose... */
5187 #endif /* FTP_RESTART */
5188
5189           case SND_NOB:
5190           case SND_DEL:                 /* /DELETE */
5191           case SND_SHH:                 /* /QUIET */
5192           case SND_UPD:                 /* /UPDATE */
5193           case SND_SIM:                 /* /UPDATE */
5194           case SND_USN:                 /* /UNIQUE */
5195             pv[n].ival = 1;             /* Just set the flag */
5196             break;
5197
5198           case SND_REC:                 /* /RECURSIVE */
5199             recursive = 2;              /* Must be set before cmifi() */
5200             x_recurse = 1;
5201             goto again;                 /* Because CMIFI params changed... */
5202             break;
5203
5204 #ifdef UNIXOROSK
5205           case SND_DOT:                 /* /DOTFILES */
5206             matchdot = 1;
5207             break;
5208           case SND_NOD:                 /* /NODOTFILES */
5209             matchdot = 0;
5210             break;
5211 #endif /* UNIXOROSK */
5212
5213           case SND_ERR:                 /* /ERROR-ACTION */
5214             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
5215               goto xputx;
5216             pv[n].ival = x;
5217             break;
5218
5219           case SND_EXC:                 /* Excludes */
5220             if (!getval) break;
5221             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
5222                 if (x == -3) {
5223                     printf("?Pattern required\n");
5224                     x = -9;
5225                 }
5226                 goto xputx;
5227             }
5228             if (s) if (!*s) s = NULL;
5229             makestr(&(pv[n].sval),s);
5230             if (pv[n].sval)
5231               pv[n].ival = 1;
5232             break;
5233
5234           case SND_PRM:                 /* /PERMISSIONS */
5235             if (!getval)
5236               x = 1;
5237             else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
5238               goto xputx;
5239             pv[SND_PRM].ival = x;
5240             break;
5241
5242 #ifdef PIPESEND
5243           case SND_FLT:                 /* /FILTER */
5244             debug(F101,"ftp put /filter getval","",getval);
5245             if (!getval) break;
5246             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
5247                 if (x == -3)
5248                   s = "";
5249                 else
5250                   goto xputx;
5251             }
5252             if (*s) s = brstrip(s);
5253             y = strlen(s);
5254             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
5255                 if (s[x] != '\\') continue;
5256                 if (s[x+1] == 'v') break;
5257             }
5258             if (x == y) {
5259                 printf(
5260                 "?Filter must contain a replacement variable for filename.\n"
5261                        );
5262                 x = -9;
5263                 goto xputx;
5264             }
5265             if (s) if (!*s) s = NULL;
5266             makestr(&(pv[n].sval),s);
5267             if (pv[n].sval)
5268               pv[n].ival = 1;
5269             break;
5270 #endif /* PIPESEND */
5271
5272           case SND_NAM:                 /* /FILENAMES */
5273             if (!getval) break;
5274             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
5275               goto xputx;
5276             debug(F101,"ftp put /filenames","",x);
5277             pv[n].ival = x;
5278             break;
5279
5280           case SND_SMA:                 /* Smaller / larger than */
5281           case SND_LAR: {
5282               CK_OFF_T y;
5283               if (!getval) break;
5284               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
5285                 goto xputx;
5286               pv[n].wval = y;
5287               break;
5288           }
5289           case SND_FIL:                 /* Name of file containing filenames */
5290             if (!getval) break;
5291             if ((x = cmifi("Name of file containing list of filenames",
5292                                "",&s,&y,xxstring)) < 0) {
5293                 if (x == -3) {
5294                     printf("?Filename required\n");
5295                     x = -9;
5296                 }
5297                 goto xputx;
5298             } else if (y && iswild(s)) {
5299                 printf("?Wildcards not allowed\n");
5300                 x = -9;
5301                 goto xputx;
5302             }
5303             if (s) if (!*s) s = NULL;
5304             makestr(&(pv[n].sval),s);
5305             if (pv[n].sval) {
5306                 pv[n].ival = 1;
5307                 pv[SND_ARR].ival = 0;
5308             } else {
5309                 pv[n].ival = 0;
5310             }
5311             mput = 0;
5312             break;
5313
5314           case SND_MOV:                 /* MOVE after */
5315           case SND_REN:                 /* RENAME after */
5316           case SND_SRN: {               /* SERVER-RENAME after */
5317               char * m = "";
5318               switch (n) {
5319                 case SND_MOV:
5320                   m = "device and/or directory for source file after sending";
5321                   break;
5322                 case SND_REN:
5323                   m = "new name for source file after sending";
5324                   break;
5325                 case SND_SRN:
5326                   m = "new name for destination file after sending";
5327                   break;
5328               }
5329               if (!getval) break;
5330               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
5331                   if (x == -3) {
5332                       printf("%s\n", n == SND_MOV ?
5333                              "?Destination required" :
5334                              "?New name required"
5335                              );
5336                       x = -9;
5337                   }
5338                   goto xputx;
5339               }
5340               if (s) if (!*s) s = NULL;
5341               makestr(&(pv[n].sval),s ? brstrip(s) : NULL);
5342               pv[n].ival = (pv[n].sval) ? 1 : 0;
5343               break;
5344           }
5345           case SND_STA:                 /* Starting position (= PSEND) */
5346             if (!getval) break;
5347             if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0)
5348               goto xputx;
5349             pv[n].ival = y;
5350             break;
5351
5352           case SND_TYP:                 /* /TYPE */
5353             if (!getval) break;
5354             if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0)
5355               goto xputx;
5356             pv[n].ival = (x == 2) ? -1 : x;
5357             break;
5358
5359 #ifndef NOCSETS
5360           case SND_CSL:                 /* Local character set */
5361           case SND_CSR:                 /* Remote (server) charset */
5362             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) {
5363                 return((x == -3) ? -2 : x);
5364             }
5365             if (n == SND_CSL)
5366               x_csl = x;
5367             else
5368               x_csr = x;
5369             x_xla = 1;                  /* Overrides global OFF setting */
5370             break;
5371
5372           case SND_XPA:                 /* Transparent */
5373             x_xla = 0;
5374             x_csr = -1;
5375             x_csl = -1;
5376             break;
5377 #endif /* NOCSETS */
5378         }
5379     }
5380 #ifdef PIPESEND
5381     if (pv[SND_RES].ival > 0) { /* /RECOVER */
5382         if (sndfilter || pv[SND_FLT].ival > 0) {
5383             printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n");
5384             x = -9;
5385             goto xputx;
5386         }
5387         if (sfttab[0] > 0 && sfttab[SFT_REST] == 0)
5388           printf("WARNING: Server says it doesn't support REST.\n");
5389     }
5390 #endif /* PIPESEND */
5391
5392     cmarg = "";
5393     cmarg2 = asnambuf;
5394     line[0] = NUL;
5395     s = line;
5396     wild = 0;
5397
5398     switch (cmresult.fcode) {           /* How did we get out of switch loop */
5399       case _CMIFI:                      /* Input filename */
5400         if (pv[SND_FIL].ival > 0) {
5401             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5402             x = -9;
5403             goto xputx;
5404         }
5405         ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */
5406         if (pv[SND_ARR].ival > 0)
5407           ckstrncpy(asnambuf,line,CKMAXPATH);
5408         else
5409           wild = cmresult.nresult;      /* Wild flag */
5410         debug(F111,"ftp put wild",line,wild);
5411         if (!wild && !recursive && !mput)
5412           nolinks = 0;
5413         break;
5414       case _CMFLD:                      /* Field */
5415         /* Only allowed with /COMMAND and /ARRAY */
5416         if (pv[SND_FIL].ival > 0) {
5417             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5418             x = -9;
5419             goto xputx;
5420         }
5421         /* For MPUT it's OK to have filespecs that don't match any files */
5422         if (mput)
5423           break;
5424         if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) {
5425 #ifdef CKROOT
5426             if (ckrooterr)
5427               printf("?Off limits: %s\n",cmresult.sresult);
5428             else
5429 #endif /* CKROOT */
5430               printf("?%s - \"%s\"\n",
5431                    iswild(cmresult.sresult) ?
5432                    "No files match" : "File not found",
5433                    cmresult.sresult
5434                    );
5435             x = -9;
5436             goto xputx;
5437         }
5438         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
5439         if (pv[SND_ARR].ival > 0)
5440           ckstrncpy(asnambuf,line,CKMAXPATH);
5441         break;
5442       case _CMCFM:                      /* Confirmation */
5443         confirmed = 1;
5444         break;
5445       default:
5446         printf("?Unexpected function code: %d\n",cmresult.fcode);
5447         x = -9;
5448         goto xputx;
5449     }
5450     debug(F110,"ftp put string",s,0);
5451     debug(F101,"ftp put confirmed","",confirmed);
5452
5453     /* Save and change protocol and transfer mode */
5454     /* Global values are restored in main parse loop */
5455
5456     g_displa = fdispla;
5457     if (ftp_dis > -1)
5458       fdispla = ftp_dis;
5459     g_skipbup = skipbup;
5460
5461     if (pv[SND_NOB].ival > -1) {        /* /NOBACKUP (skip backup file) */
5462         g_skipbup = skipbup;
5463         skipbup = 1;
5464     }
5465     if (pv[SND_TYP].ival > -1) {        /* /TYPE */
5466         xfiletype = pv[SND_TYP].ival;
5467         if (xfiletype == 2)
5468           xfiletype = -1;
5469     }
5470     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
5471         forcetype = 1;                  /* So skip file scan */
5472         ftp_typ = FTT_BIN;              /* Set binary */
5473     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
5474         forcetype = 1;
5475         ftp_typ = FTT_ASC;
5476     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
5477         forcetype = 1;
5478         ftp_typ = FTT_TEN;
5479     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
5480         forcetype = 1;
5481         ftp_typ = binary;
5482         g_ftp_typ = binary;
5483     }
5484
5485 #ifdef PIPESEND
5486     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
5487         debug(F110,"PUT /COMMAND before stripping",s,0);
5488         s = brstrip(s);
5489         debug(F110,"PUT /COMMAND after stripping",s,0);
5490         if (!*s) {
5491             printf("?Sorry, a command to send from is required\n");
5492             x = -9;
5493             goto xputx;
5494         }
5495         cmarg = s;
5496     }
5497 #endif /* PIPESEND */
5498
5499 /* Set up /MOVE and /RENAME */
5500
5501     if (pv[SND_DEL].ival > 0 &&
5502         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
5503         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
5504         x = -9;
5505         goto xputx;
5506     }
5507 #ifdef CK_TMPDIR
5508     if (pv[SND_MOV].ival > 0) {
5509         int len;
5510         char * p = pv[SND_MOV].sval;
5511         len = strlen(p);
5512         if (!isdir(p)) {                /* Check directory */
5513 #ifdef CK_MKDIR
5514             char * s = NULL;
5515             s = (char *)malloc(len + 4);
5516             if (s) {
5517                 strcpy(s,p);            /* safe */
5518 #ifdef datageneral
5519                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
5520 #else
5521                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
5522 #endif /* datageneral */
5523                 s[len++] = 'X';
5524                 s[len] = NUL;
5525 #ifdef NOMKDIR
5526                 x = -1;
5527 #else
5528                 x = zmkdir(s);
5529 #endif /* NOMKDIR */
5530                 free(s);
5531                 if (x < 0) {
5532                     printf("?Can't create \"%s\"\n",p);
5533                     x = -9;
5534                     goto xputx;
5535                 }
5536             }
5537 #else
5538             printf("?Directory \"%s\" not found\n",p);
5539             x = -9;
5540             goto xputx;
5541 #endif /* CK_MKDIR */
5542         }
5543         makestr(&snd_move,p);
5544     }
5545 #endif /* CK_TMPDIR */
5546
5547     if (pv[SND_REN].ival > 0) {         /* /RENAME */
5548         char * p = pv[SND_REN].sval;
5549         if (!p) p = "";
5550         if (!*p) {
5551             printf("?New name required for /RENAME\n");
5552             x = -9;
5553             goto xputx;
5554         }
5555         p = brstrip(p);
5556 #ifndef NOSPL
5557     /* If name given is wild, rename string must contain variables */
5558         if (wild) {
5559             char * s = tmpbuf;
5560             x = TMPBUFSIZ;
5561             zzstring(p,&s,&x);
5562             if (!strcmp(tmpbuf,p)) {
5563                 printf(
5564     "?/RENAME for file group must contain variables such as \\v(filename)\n"
5565                        );
5566                 x = -9;
5567                 goto xputx;
5568             }
5569         }
5570 #endif /* NOSPL */
5571         makestr(&snd_rename,p);
5572         debug(F110,"FTP snd_rename",snd_rename,0);
5573     }
5574     if (pv[SND_SRN].ival > 0) {         /* /SERVER-RENAME */
5575         char * p = pv[SND_SRN].sval;
5576         if (!p) p = "";
5577         if (!*p) {
5578             printf("?New name required for /SERVER-RENAME\n");
5579             x = -9;
5580             goto xputx;
5581         }
5582         p = brstrip(p);
5583 #ifndef NOSPL
5584         if (wild) {
5585             char * s = tmpbuf;
5586             x = TMPBUFSIZ;
5587             zzstring(p,&s,&x);
5588             if (!strcmp(tmpbuf,p)) {
5589                 printf(
5590 "?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n"
5591                        );
5592                 x = -9;
5593                 goto xputx;
5594             }
5595         }
5596 #endif /* NOSPL */
5597         makestr(&srv_renam,p);
5598         debug(F110,"ftp put srv_renam",srv_renam,0);
5599     }
5600     if (!confirmed) {                   /* CR not typed yet, get more fields */
5601         char * lp;
5602         if (mput) {                     /* MPUT or MMOVE */
5603             nfils = 0;                  /* We already have the first one */
5604 #ifndef NOMSEND
5605             if (cmresult.fcode == _CMIFI) {
5606                 /* First filespec is valid */
5607                 msfiles[nfils++] = line;    /* Store pointer */
5608                 lp = line + (int)strlen(line) + 1; /* Point past it */
5609                 debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1);
5610             } else {
5611                 /* First filespec matches no files */
5612                 debug(F110,"ftp put mput skipping first filespec",
5613                       cmresult.sresult,
5614                       0
5615                       );
5616                 lp = line;
5617             }
5618             /* Parse a filespec, a "field", or confirmation */
5619
5620             cmfdbi(&sf,                 /* 1st FDB - file to send */
5621                    _CMIFI,              /* fcode */
5622                    "",                  /* hlpmsg */
5623                    "",                  /* default */
5624                    "",                  /* addtl string data */
5625                    nolinks | x_recurse, /* addtl numeric data 1 */
5626                    0,                   /* dirflg 0 means "not dirs only" */
5627                    xxstring,
5628                    NULL,
5629                    &fl
5630                    );
5631             cmfdbi(&fl,                 /* 2nd FDB - local filespec */
5632                    _CMFLD,              /* fcode */
5633                    "",                  /* hlpmsg */
5634                    "",                  /* default */
5635                    "",                  /* addtl string data */
5636                    0,                   /* addtl numeric data 1 */
5637                    0,                   /* addtl numeric data 2 */
5638                    xxstring,
5639                    NULL,
5640                    &cm
5641                    );
5642             cmfdbi(&cm,                 /* 3rd FDB - Confirmation */
5643                    _CMCFM,              /* fcode */
5644                    "",
5645                    "",
5646                    "",
5647                    0,
5648                    0,
5649                    NULL,
5650                    NULL,
5651                    NULL
5652                    );
5653
5654             while (!confirmed) {        /* Get more filenames */
5655                 x = cmfdb(&sf);         /* Parse something */
5656                 debug(F101,"ftp put cmfdb B","",x);
5657                 debug(F101,"ftp put fcode B","",cmresult.fcode);
5658                 if (x < 0)              /* Error */
5659                   goto xputx;           /* or reparse needed */
5660                 switch (cmresult.fcode) {
5661                   case _CMCFM:          /* End of command */
5662                     confirmed++;
5663                     if (nfils < 1) {
5664                         debug(F100,"ftp put mput no files match","",0);
5665                         printf("?No files match MPUT list\n");
5666                         x = -9;
5667                         goto xputx;
5668                     }
5669                     break;
5670                   case _CMFLD:          /* No match */
5671                     debug(F110,"ftp put mput skipping",cmresult.sresult,0);
5672                     continue;
5673                   case _CMIFI:          /* Good match */
5674                     s = cmresult.sresult;
5675                     msfiles[nfils++] = lp; /* Got one, count, point to it, */
5676                     p = lp;                /* remember pointer, */
5677                     while ((*lp++ = *s++)) /* and copy it into buffer */
5678                       if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */
5679                           printf("?MPUT list too long\n");
5680                           line[0] = NUL;
5681                           x = -9;
5682                           goto xputx;
5683                       }
5684                     debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1);
5685                     if (nfils == 1)     /* Take care of \v(filespec) */
5686                       fspec[0] = NUL;
5687 #ifdef ZFNQFP
5688                     zfnqfp(p,TMPBUFSIZ,tmpbuf);
5689                     p = tmpbuf;
5690 #endif /* ZFNQFP */
5691                     if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) {
5692                         strcat(fspec,p);    /* safe */
5693                         strcat(fspec," ");  /* safe */
5694                     } else {
5695 #ifdef COMMENT
5696                         printf("WARNING - \\v(filespec) buffer overflow\n");
5697 #else
5698                         debug(F101,"doxput filespec buffer overflow","",0);
5699 #endif /* COMMENT */
5700                     }
5701                 }
5702             }
5703 #endif /* NOMSEND */
5704         } else {                        /* Regular PUT */
5705             nfils = -1;
5706             if ((x = cmtxt(wild ?
5707 "\nOptional as-name template containing replacement variables \
5708 like \\v(filename)" :
5709                            "Optional name to send it with",
5710                            "",&p,NULL)) < 0)
5711               goto xputx;
5712
5713             if (p) if (!*p) p = NULL;
5714             p = brstrip(p);
5715
5716             if (p && *p) {
5717                 makestr(&(pv[SND_ASN].sval),p);
5718                 if (pv[SND_ASN].sval)
5719                   pv[SND_ASN].ival = 1;
5720                 debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0);
5721             }
5722         }
5723     }
5724     /* Set cmarg2 from as-name, however we got it. */
5725
5726     CHECKCONN();
5727     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
5728         char * p;
5729         p = brstrip(pv[SND_ASN].sval);
5730         ckstrncpy(asnambuf,p,CKMAXPATH+1);
5731     }
5732     debug(F110,"ftp put asnambuf",asnambuf,0);
5733
5734     if (pv[SND_FIL].ival > 0) {
5735         if (confirmed) {
5736             if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
5737                 debug(F110,"ftp put can't open",pv[SND_FIL].sval,0);
5738                 printf("?Failure to open %s\n",pv[SND_FIL].sval);
5739                 x = -9;
5740                 goto xputx;
5741             }
5742             makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */
5743             debug(F110,"ftp PUT /LISTFILE opened",filefile,0);
5744             wild = 1;
5745         }
5746     }
5747     if (confirmed && !line[0] && !filefile) {
5748 #ifndef NOMSEND
5749         if (filehead) {                 /* OK if we have a SEND-LIST */
5750             nfils = filesinlist;
5751             sndsrc = nfils;             /* Like MSEND */
5752             addlist = 1;                /* But using a different list... */
5753             filenext = filehead;
5754             goto doput;
5755         }
5756 #endif /* NOMSEND */
5757         printf("?Filename required but not given\n");
5758         x = -9;
5759         goto xputx;
5760     }
5761 #ifndef NOMSEND
5762     addlist = 0;                        /* Don't use SEND-LIST. */
5763 #endif /* NOMSEND */
5764
5765     if (mput) {                         /* MPUT (rather than PUT) */
5766 #ifndef NOMSEND
5767         cmlist = msfiles;               /* List of filespecs */
5768         sndsrc = nfils;                 /* rather filespec and as-name */
5769 #endif /* NOMSEND */
5770         pipesend = 0;
5771     } else if (filefile) {              /* File contains list of filenames */
5772         s = "";
5773         cmarg = "";
5774         line[0] = NUL;
5775         nfils = 1;
5776         sndsrc = 1;
5777
5778     } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) {
5779
5780         /* Not MSEND, MMOVE, /LIST, or /ARRAY */
5781         nfils = sndsrc = -1;
5782         if (!wild) {
5783             if (zchki(s) < 0) {
5784                 printf("?Read access denied - \"%s\"\n", s);
5785                 x = -9;
5786                 goto xputx;
5787             }
5788         }
5789         if (s != line)                  /* We might already have done this. */
5790           ckstrncpy(line,s,LINBUFSIZ);  /* Copy of string just parsed. */
5791 #ifdef DEBUG
5792         else
5793           debug(F110,"doxput line=s",line,0);
5794 #endif /* DEBUG */
5795         cmarg = line;                   /* File to send */
5796     }
5797 #ifndef NOMSEND
5798     zfnqfp(cmarg,fspeclen,fspec);       /* Get full name */
5799 #endif /* NOMSEND */
5800
5801     if (!mput) {                        /* For all but MPUT... */
5802 #ifdef PIPESEND
5803         if (pv[SND_CMD].ival > 0)       /* /COMMAND sets pipesend flag */
5804           pipesend = 1;
5805         debug(F101,"ftp put /COMMAND pipesend","",pipesend);
5806         if (pipesend && filefile) {
5807             printf("?Invalid switch combination\n");
5808             x = -9;
5809             goto xputx;
5810         }
5811 #endif /* PIPESEND */
5812
5813 #ifndef NOSPL
5814     /* If as-name given and filespec is wild, as-name must contain variables */
5815         if ((wild || mput) && asnambuf[0]) {
5816             char * s = tmpbuf;
5817             x = TMPBUFSIZ;
5818             zzstring(asnambuf,&s,&x);
5819             if (!strcmp(tmpbuf,asnambuf)) {
5820                 printf(
5821     "?As-name for file group must contain variables such as \\v(filename)\n"
5822                        );
5823                 x = -9;
5824                 goto xputx;
5825             }
5826         }
5827 #endif /* NOSPL */
5828     }
5829
5830   doput:
5831
5832     if (pv[SND_SHH].ival > 0) {         /* SEND /QUIET... */
5833         fdispla = 0;
5834         debug(F101,"ftp put display","",fdispla);
5835     } else {
5836         displa = 1;
5837         if (ftp_deb)
5838           fdispla = XYFD_B;
5839     }
5840
5841 #ifdef PUTARRAY                         /* SEND /ARRAY... */
5842     if (pv[SND_ARR].ival > 0) {
5843         if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */
5844         if (range[0] == -1)             /* If low end of range not specified */
5845           range[0] = 1;                 /* default to 1 */
5846         if (range[1] == -1)             /* If high not specified */
5847           range[1] = a_dim[arrayx];     /* default to size of array */
5848         if ((range[0] < 0) ||           /* Check range */
5849             (range[0] > a_dim[arrayx]) ||
5850             (range[1] < range[0]) ||
5851             (range[1] > a_dim[arrayx])) {
5852             printf("?Bad array range - [%d:%d]\n",range[0],range[1]);
5853             x = -9;
5854             goto xputx;
5855         }
5856         sndarray = ap;                  /* Array pointer */
5857         sndxin = arrayx;                /* Array index */
5858         sndxlo = range[0];              /* Array range */
5859         sndxhi = range[1];
5860         sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE);
5861         if (!asnambuf[0])
5862           ckstrncpy(asnambuf,sndxnam,CKMAXPATH);
5863         cmarg = "";
5864     }
5865 #endif /* PUTARRAY */
5866
5867     moving = 0;
5868
5869     if (pv[SND_ARR].ival < 1) {         /* File selection & disposition... */
5870         if (pv[SND_DEL].ival > 0)       /* /DELETE was specified */
5871           moving = 1;
5872         if (pv[SND_AFT].ival > 0)       /* Copy SEND criteria */
5873           ckstrncpy(sndafter,pv[SND_AFT].sval,19);
5874         if (pv[SND_BEF].ival > 0)
5875           ckstrncpy(sndbefore,pv[SND_BEF].sval,19);
5876         if (pv[SND_NAF].ival > 0)
5877           ckstrncpy(sndnafter,pv[SND_NAF].sval,19);
5878         if (pv[SND_NBE].ival > 0)
5879           ckstrncpy(sndnbefore,pv[SND_NBE].sval,19);
5880         if (pv[SND_EXC].ival > 0)
5881           makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT);
5882         if (pv[SND_SMA].ival > -1)
5883           sndsmaller = pv[SND_SMA].wval;
5884         if (pv[SND_LAR].ival > -1)
5885           sndlarger = pv[SND_LAR].wval;
5886         if (pv[SND_NAM].ival > -1)
5887           x_cnv = pv[SND_NAM].ival;
5888         if (pv[SND_USN].ival > -1)
5889           x_usn = pv[SND_USN].ival;
5890         if (pv[SND_ERR].ival > -1)
5891           puterror = pv[SND_ERR].ival;
5892
5893 #ifdef DOUPDATE
5894         if (pv[SND_UPD].ival > 0) {
5895             if (x_usn) {
5896                 printf("?Conflicting switches: /UPDATE /UNIQUE\n");
5897                 x = -9;
5898                 goto xputx;
5899             }
5900             putflags |= PUT_UPD;
5901             ftp_dates |= 2;
5902         }
5903 #ifdef COMMENT
5904         /* This works but it's useless, maybe dangerous */
5905         if (pv[SND_DIF].ival > 0) {
5906             if (x_usn) {
5907                 printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n");
5908                 x = -9;
5909                 goto xputx;
5910             }
5911             putflags |= PUT_DIF;
5912             ftp_dates |= 2;
5913         }
5914 #endif /* COMMENT */
5915 #endif /* DOUPDATE */
5916
5917         if (pv[SND_SIM].ival > 0)
5918           putflags |= PUT_SIM;
5919
5920         if (pv[SND_PRM].ival > -1) {
5921 #ifdef UNIX
5922             if (x_usn) {
5923                 printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n");
5924                 x = -9;
5925                 goto xputx;
5926             }
5927             x_prm = pv[SND_PRM].ival;
5928 #else /* UNIX */
5929             printf("?/PERMISSIONS switch is not supported\n");
5930 #endif /* UNIX */
5931         }
5932 #ifdef FTP_RESTART
5933         if (pv[SND_RES].ival > 0) {
5934             if (!sizeok) {
5935                 printf("?PUT /RESTART can't be used because SIZE disabled.\n");
5936                 x = -9;
5937                 goto xputx;
5938             }
5939             if (x_usn || putflags) {
5940                 printf("?Conflicting switches: /RECOVER %s\n",
5941                        x_usn && putflags ? "/UNIQUE /UPDATE" :
5942                        (x_usn ? "/UNIQUE" : "/UPDATE")
5943                        );
5944                 x = -9;
5945                 goto xputx;
5946             }
5947 #ifndef NOCSETS
5948             if (x_xla &&
5949                 (x_csl == FC_UCS2 ||
5950                  x_csl == FC_UTF8 ||
5951                  x_csr == FC_UCS2 ||
5952                  x_csr == FC_UTF8)) {
5953                 printf("?/RECOVER can not be used with Unicode translation\n");
5954                 x = -9;
5955                 goto xputx;
5956             }
5957 #endif /* NOCSETS */
5958             putflags = PUT_RES;
5959         }
5960 #endif /* FTP_RESTART */
5961     }
5962     debug(F101,"ftp PUT restart","",putflags & PUT_RES);
5963     debug(F101,"ftp PUT update","",putflags & PUT_UPD);
5964
5965 #ifdef PIPESEND
5966     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
5967         if (!pv[SND_FLT].sval) {
5968             sndfilter = NULL;
5969         } else {
5970             sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1);
5971             if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */
5972         }
5973         debug(F110,"ftp put /FILTER", sndfilter, 0);
5974     }
5975     if (sndfilter || pipesend)          /* No /UPDATE or /RESTART */
5976       if (putflags)                     /* with pipes or filters */
5977         putflags = 0;
5978 #endif /* PIPESEND */
5979
5980     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
5981     filcnt = 0;
5982     pktnum = 0;
5983     spackets = 0L;
5984
5985     if (wild)                           /* (is this necessary?) */
5986       cx = FTP_MPU;
5987
5988     t0 = gmstimer();                    /* Record starting time */
5989
5990     done = 0;                           /* Loop control */
5991     cancelgroup = 0;
5992
5993     cdlevel = 0;
5994     cdsimlvl = 0;
5995     while (!done && !cancelgroup) {     /* Loop for all files */
5996                                         /* or until canceled. */
5997 #ifdef FTP_PROXY
5998         /*
5999            If we are using a proxy, we don't use the local file list;
6000            instead we use the list on the remote machine which we want
6001            sent to someone else, and we use remglob() to get the names.
6002            But in that case we shouldn't even be executing this routine;
6003            see ftp_mput().
6004         */
6005 #endif /* FTP_PROXY */
6006
6007         cancelfile = 0;
6008         x = gnfile();                   /* Get next file from list(s) */
6009
6010         if (x == 0)                     /* (see gnfile() comments...) */
6011           x = gnferror;
6012         debug(F111,"FTP PUT gnfile",filnam,x);
6013         debug(F111,"FTP PUT binary",filnam,binary);
6014
6015         switch (x) {
6016           case 1:                       /* File to send */
6017             s2 = asnambuf;
6018 #ifndef NOSPL
6019             if (asnambuf[0]) {          /* As-name */
6020                 int n; char *p;         /* to be evaluated... */
6021                 n = TMPBUFSIZ;
6022                 p = tmpbuf;
6023                 zzstring(asnambuf,&p,&n);
6024                 s2 = tmpbuf;
6025                 debug(F110,"ftp put asname",s2,0);
6026             }
6027 #endif /* NOSPL */
6028             rc = putfile(cx,            /* Function (PUT, APPEND) */
6029                     filnam, s2,         /* Name to send, as-name */
6030                     forcetype, moving,  /* Parameters from switches... */
6031                     snd_move, snd_rename, srv_renam,
6032                     x_cnv, x_usn, xfiletype, x_prm,
6033 #ifndef NOCSETS
6034                     x_csl, (!x_xla ? -1 : x_csr),
6035 #else
6036                     -1, -1,
6037 #endif /* NOCSETS */
6038                     putflags
6039                     );
6040             debug(F111,"ftp put putfile rc",filnam,rc);
6041             debug(F111,"ftp put putfile cancelfile",filnam,cancelfile);
6042             debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup);
6043             if (rc > -1) {
6044                 good++;
6045                 status = 1;
6046             }
6047             if (cancelfile)
6048               continue;
6049             if (rc < 0) {
6050                 ftp_fai++;
6051                 if (puterror) {
6052                     status = 0;
6053                     printf("?Fatal upload error: %s\n",filnam);
6054                     done++;
6055                 }
6056             }
6057             continue;
6058           case 0:                       /* No more files, done */
6059             done++;
6060             continue;
6061           case -1:
6062             printf("?%s: file not found - \"%s\"\n",
6063                    puterror ? "Fatal" : "Warning",
6064                    filnam
6065                    );
6066             if (puterror) {
6067                 status = 0;
6068                 done++;
6069                 break;
6070             }
6071             continue;
6072           case -2:
6073             if (puterror) {
6074                 printf("?Fatal: file not found - \"%s\"\n", filnam);
6075                 status = 0;
6076                 done++;
6077                 break;
6078             }
6079             continue;                   /* Not readable, keep going */
6080           case -3:
6081             if (puterror) {
6082                 printf("?Fatal: Read access denied - \"%s\"\n", filnam);
6083                 status = 0;
6084                 done++;
6085                 break;
6086             }
6087             printf("?Warning access denied - \"%s\"\n", filnam);
6088             continue;
6089 #ifdef COMMENT
6090           case -4:                      /* Canceled */
6091             done++;
6092             break;
6093 #endif /* COMMENT */
6094           case -5:
6095             printf("?Too many files match\n");
6096             done++;
6097             break;
6098           case -6:
6099             if (good < 1)
6100               printf("?No files selected\n");
6101             done++;
6102             break;
6103           default:
6104             printf("?getnextfile() - unknown failure\n");
6105             done++;
6106         }
6107     }
6108     if (cdlevel > 0) {
6109         while (cdlevel--) {
6110             if (cdsimlvl) {
6111                 cdsimlvl--;
6112             } else if (!doftpcdup())
6113               break;
6114         }
6115     }
6116     if (status > 0) {
6117         if (cancelgroup)
6118           status = 0;
6119         else if (cancelfile && good < 1)
6120           status = 0;
6121     }
6122     success = status;
6123     x = success;
6124
6125   xputx:
6126     if (x > -1) {
6127 #ifdef GFTIMER
6128         t1 = gmstimer();                /* End time */
6129         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6130         if (!sec) sec = 0.001;
6131         fptsecs = sec;
6132 #else
6133         sec = (t1 - t0) / 1000;
6134         if (!sec) sec = 1;
6135 #endif /* GFTIMER */
6136         tfcps = (long) (tfc / sec);
6137         tsecs = (int)sec;
6138         lastxfer = W_FTP|W_SEND;
6139         xferstat = success;
6140         if (dpyactive)
6141           ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6142     }
6143     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
6144         if (pv[i].sval)
6145           free(pv[i].sval);
6146     }
6147     ftreset();                          /* Undo switch effects */
6148     dpyactive = 0;
6149     return(x);
6150 }
6151
6152
6153 static char ** mgetlist = NULL;         /* For MGET */
6154 static int mgetn = 0, mgetx = 0;
6155 static char xtmpbuf[4096];
6156
6157 /*
6158   c m d l i n g e t
6159
6160   Get files specified by -g command-line option.
6161   File list is set up in cmlist[] by ckuusy.c; nfils is length of list.
6162 */
6163 int
6164 cmdlinget(stay) int stay; {
6165     int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0;
6166     int lcs = -1, rcs = -1, xlate = 0;
6167     int first = 1;
6168     int mget = 1;
6169     int nc;
6170     char * s, * s2, * s3;
6171     ULONG t0, t1;                       /* Times for stats */
6172 #ifdef GFTIMER
6173     CKFLOAT sec;
6174 #else
6175     int sec = 0;
6176 #endif /* GFTIMER */
6177
6178     if (quiet) {                        /* -q really means quiet */
6179         displa = 0;
6180         fdispla = 0;
6181     } else {
6182         displa = 1;
6183         fdispla = XYFD_B;
6184     }
6185     testing = 0;
6186     dpyactive = 0;
6187     out2screen = 0;
6188     what = W_FTP|W_RECV;
6189     mgetmethod = 0;
6190     mgetforced = 0;
6191
6192     havetype = 0;
6193     havesize = (CK_OFF_T)-1;
6194     makestr(&havemdtm,NULL);
6195
6196     if (ftp_fnc < 0)
6197       ftp_fnc = fncact;
6198
6199 #ifndef NOSPL
6200     cmd_quoting = 0;
6201 #endif /* NOSPL */
6202     debug(F101,"ftp cmdlinget nfils","",nfils);
6203
6204     if (ftp_cnv == CNV_AUTO) {          /* Name conversion is auto */
6205         if (alike) {                    /* If server & client are alike */
6206             nc = 0;                     /* no conversion */
6207         } else {                        /* If they are different */
6208             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6209               nc = -1;                  /* only minimal conversions needed */
6210             else                        /* otherwise */
6211               nc = 1;                   /* full conversion */
6212         }
6213     } else                              /* Not auto - do what user said */
6214       nc = ftp_cnv;
6215
6216     if (nfils < 1)
6217       doexit(BAD_EXIT,-1);
6218
6219     t0 = gmstimer();                    /* Starting time for this batch */
6220
6221 #ifndef NOCSETS
6222     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
6223         lcs = ftp_csl;                  /* Local charset */
6224         if (lcs < 0) lcs = fcharset;
6225         if (lcs < 0) xlate = 0;
6226     }
6227     if (xlate) {                        /* Still ON? */
6228         rcs = ftp_csx;                  /* Remote (Server) charset */
6229         if (rcs < 0) rcs = ftp_csr;
6230         if (rcs < 0) xlate = 0;
6231     }
6232 #endif /* NOCSETS */
6233     /*
6234       If we have only one file and it is a directory, then we ask for a
6235       listing of its contents, rather than retrieving the directory file
6236       itself.  This is what (e.g.) Netscape does.
6237     */
6238     if (nfils == 1) {
6239         if (doftpcwd((char *)cmlist[mgetx],-1)) {
6240             /* If we can CD to it, it must be a directory */
6241             if (recursive) {
6242                 cmlist[mgetx] = "*";
6243             } else {
6244                 status =
6245                   (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0);
6246                 done = 1;
6247             }
6248         }
6249     }
6250 /*
6251   The following is to work around UNIX servers which, when given a command
6252   like "NLST path/blah" (not wild) returns the basename without the path.
6253 */
6254     if (!done && servertype == SYS_UNIX && nfils == 1) {
6255         mget = iswild(cmlist[mgetx]);
6256     }
6257     if (!mget && !done) {               /* Invoked by command-line FTP URL */
6258         if (ftp_deb)
6259           printf("DOING GET...\n");
6260         done++;
6261         cancelfile = 0;                 /* This file not canceled yet */
6262         s = cmlist[mgetx];
6263         rc = 0;                         /* Initial return code */
6264         fsize = (CK_OFF_T)-1;
6265         if (sizeok) {
6266             x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */
6267             if (x == REPLY_COMPLETE)
6268               fsize = ckatofs(&ftp_reply_str[4]);
6269         }
6270         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6271         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6272
6273         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6274         s2 = tmpbuf;
6275
6276         /* If local file already exists, take collision action */
6277
6278         if (zchki(s2) > -1) {
6279             switch (ftp_fnc) {
6280               case XYFX_A:              /* Append */
6281                 append = 1;
6282                 break;
6283               case XYFX_R:              /* Rename */
6284               case XYFX_B: {            /* Backup */
6285                   char * p = NULL;
6286                   int x = -1;
6287                   znewn(s2,&p);         /* Make unique name */
6288                   debug(F110,"ftp cmdlinget znewn",p,0);
6289                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6290                       x = zrename(s2,p);
6291                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6292                   } else {              /* Rename incoming file */
6293                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6294                       s2 = tmpbuf;
6295                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6296                   }
6297                   if (x < 0) {
6298                       printf("?Backup/Rename failed\n");
6299                       return(success = 0);
6300                   }
6301                   break;
6302               }
6303               case XYFX_D:              /* Discard */
6304                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6305                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6306                 tlog(F100," refused: name","",0);
6307                 debug(F110,"ftp cmdlinget skip name",s2,0);
6308                 goto xclget;
6309
6310               case XYFX_X:              /* Overwrite */
6311               case XYFX_U:              /* Update (already handled above) */
6312               case XYFX_M:              /* ditto */
6313                 break;
6314             }
6315         }
6316         rc = getfile(s,                 /* Remote name */
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         debug(F111,"ftp cmdlinget rc",s,rc);
6326         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6327         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6328
6329         if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */
6330             rc = getfile(&s[1],         /* Remote name without leading '/' */
6331                          s2,            /* Local name */
6332                          0,             /* Recover/Restart */
6333                          append,        /* Append */
6334                          NULL,          /* Pipename */
6335                          0,             /* Translate charsets */
6336                          -1,            /* File charset (none) */
6337                          -1             /* Server charset (none) */
6338                          );
6339         if (rc > -1) {
6340             good++;
6341             status = 1;
6342         }
6343         if (cancelfile)
6344           goto xclget;
6345         if (rc < 0) {
6346             ftp_fai++;
6347 #ifdef FTP_TIMEOUT
6348             if (ftp_timed_out)
6349               status = 0;
6350 #endif  /* FTP_TIMEOUT */
6351             if (geterror) {
6352                 status = 0;
6353                 done++;
6354             }
6355         }
6356     }
6357     if (ftp_deb && !done)
6358       printf("DOING MGET...\n");
6359     while (!done && !cancelgroup) {
6360         cancelfile = 0;                 /* This file not canceled yet */
6361         s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6362         if (!s) s = "";
6363         if (!*s) {
6364             first = 1;
6365             mgetx++;
6366             if (mgetx < nfils)
6367               s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6368             else
6369               s = NULL;
6370             debug(F111,"ftp cmdlinget remote_files B",s,0);
6371             if (!s) {
6372                 done = 1;
6373                 break;
6374             }
6375         }
6376         /*
6377           The semantics of NLST are ill-defined.  Suppose we have just sent
6378           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
6379           /path/bar, etc.  But some send back only foo and bar, and subsequent
6380           RETR commands based on the pathless names are not going to work.
6381         */
6382         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
6383             if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) {
6384                 int len, left = 4096;
6385                 char * tmp = xtmpbuf;
6386                 len = s3 - cmlist[mgetx] + 1;
6387                 ckstrncpy(tmp,cmlist[mgetx],left);
6388                 tmp += len;
6389                 left -= len;
6390                 ckstrncpy(tmp,s,left);
6391                 s = xtmpbuf;
6392                 debug(F111,"ftp cmdlinget remote_files X",s,0);
6393             }
6394         }
6395         first = 0;                      /* Not first any more */
6396
6397         debug(F111,"ftp cmdlinget havetype",s,havetype);
6398         if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */
6399             debug(F110,"ftp cmdlinget not-a-file",s,0);
6400             continue;
6401         }
6402         rc = 0;                         /* Initial return code */
6403         if (havesize > (CK_OFF_T)-1) {  /* Already have file size? */
6404             fsize = havesize;
6405         } else {                        /* No - must ask server */
6406             /*
6407               Prior to sending the NLST command we necessarily put the
6408               server into ASCII mode.  We must now put it back into the
6409               the requested mode so the upcoming SIZE command returns
6410               right kind of size; this is especially important for
6411               GET /RECOVER; otherwise the server returns the "ASCII" size
6412               of the file, rather than its true size.
6413             */
6414             changetype(ftp_typ,0);      /* Change to requested type */
6415             fsize = (CK_OFF_T)-1;
6416             if (sizeok) {
6417                 x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm);
6418                 if (x == REPLY_COMPLETE)
6419                   fsize = ckatofs(&ftp_reply_str[4]);
6420             }
6421         }
6422         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6423         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6424
6425         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6426         s2 = tmpbuf;
6427
6428         /* If local file already exists, take collision action */
6429
6430         if (zchki(s2) > -1) {
6431             switch (ftp_fnc) {
6432               case XYFX_A:              /* Append */
6433                 append = 1;
6434                 break;
6435               case XYFX_R:              /* Rename */
6436               case XYFX_B: {            /* Backup */
6437                   char * p = NULL;
6438                   int x = -1;
6439                   znewn(s2,&p);         /* Make unique name */
6440                   debug(F110,"ftp cmdlinget znewn",p,0);
6441                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6442                       x = zrename(s2,p);
6443                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6444                   } else {              /* Rename incoming file */
6445                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6446                       s2 = tmpbuf;
6447                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6448                   }
6449                   if (x < 0) {
6450                       printf("?Backup/Rename failed\n");
6451                       return(success = 0);
6452                   }
6453                   break;
6454               }
6455               case XYFX_D:      /* Discard */
6456                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6457                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6458                 tlog(F100," refused: name","",0);
6459                 debug(F110,"ftp cmdlinget skip name",s2,0);
6460                 continue;
6461               case XYFX_X:              /* Overwrite */
6462               case XYFX_U:              /* Update (already handled above) */
6463               case XYFX_M:              /* ditto */
6464                 break;
6465             }
6466         }
6467                                         /* ^^^ ADD CHARSET STUFF HERE ^^^ */
6468         rc = getfile(s,                 /* Remote name */
6469                      s2,                /* Local name */
6470                      0,                 /* Recover/Restart */
6471                      append,            /* Append */
6472                      NULL,              /* Pipename */
6473                      0,                 /* Translate charsets */
6474                      -1,                /* File charset (none) */
6475                      -1                 /* Server charset (none) */
6476                      );
6477         debug(F111,"ftp cmdlinget rc",s,rc);
6478         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6479         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6480
6481         if (rc > -1) {
6482             good++;
6483             status = 1;
6484         }
6485         if (cancelfile)
6486           continue;
6487         if (rc < 0) {
6488             ftp_fai++;
6489 #ifdef FTP_TIMEOUT
6490             if (ftp_timed_out)
6491               status = 0;
6492 #endif  /* FTP_TIMEOUT */
6493             if (geterror) {
6494                 status = 0;
6495                 done++;
6496             }
6497         }
6498     }
6499
6500   xclget:
6501     if (cancelgroup)
6502       mlsreset();
6503     if (status > 0) {
6504         if (cancelgroup)
6505           status = 0;
6506         else if (cancelfile && good < 1)
6507           status = 0;
6508     }
6509     success = status;
6510
6511 #ifdef GFTIMER
6512     t1 = gmstimer();                    /* End time */
6513     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6514     if (!sec) sec = 0.001;
6515     fptsecs = sec;
6516 #else
6517     sec = (t1 - t0) / 1000;
6518     if (!sec) sec = 1;
6519 #endif /* GFTIMER */
6520
6521     tfcps = (long) (tfc / sec);
6522     tsecs = (int)sec;
6523     lastxfer = W_FTP|W_RECV;
6524     xferstat = success;
6525     if (dpyactive)
6526       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6527     if (!stay)
6528       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
6529     return(success);
6530 }
6531
6532 /*  d o f t p g e t  --  Parse and execute GET, MGET, MDELETE, ...  */
6533
6534 /*
6535   Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use
6536   zstrdat() to convert to UTC-based time_t.  But it doesn't make sense from
6537   the user-interface perspective, since the server's directory listings show
6538   its own local times and since we don't know what timezone it's in, there's
6539   no way to reconcile our local times with the server's.
6540 */
6541 int
6542 doftpget(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
6543     struct FDB fl, sw, cm;
6544     int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0;
6545     int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0;
6546     int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0;
6547     int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0;
6548     int moving = 0, deleting = 0, toscreen = 0, haspath = 0;
6549     int gotsize = 0;
6550     int matchdot = 0;
6551     CK_OFF_T getlarger = (CK_OFF_T)-1;
6552     CK_OFF_T getsmaller = (CK_OFF_T)-1;
6553     char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL;
6554     char * src = "", * local = "";
6555     char * pat = "";
6556
6557     int x_csl = -1, x_csr = -1;         /* Local and remote charsets */
6558     int x_xla = 0;
6559     char c;                             /* Worker char */
6560     ULONG t0 = 0L, t1;                  /* Times for stats */
6561 #ifdef GFTIMER
6562     CKFLOAT sec;
6563 #else
6564     int sec = 0;
6565 #endif /* GFTIMER */
6566
6567     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
6568
6569     success = 0;                        /* Assume failure */
6570     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
6571     restart = 0;                        /* No restart yet */
6572     out2screen = 0;                     /* No TO-SCREEN switch given yet */
6573     mgetmethod = 0;                     /* No NLST or MLSD switch yet */
6574     mgetforced = 0;
6575
6576     g_displa = fdispla;
6577     if (ftp_dis > -1)
6578       fdispla = ftp_dis;
6579
6580     x_cnv = ftp_cnv;                    /* Filename conversion */
6581     if (x_cnv == CNV_AUTO) {            /* Name conversion is auto */
6582         if (alike) {                    /* If server & client are alike */
6583             x_cnv = 0;                  /* no conversion */
6584         } else {                        /* If they are different */
6585             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6586               x_cnv = -1;               /* only minimal conversions needed */
6587             else                        /* otherwise */
6588               x_cnv = 1;                /* full conversion */
6589         }
6590     } else                              /* Not auto - do what user said */
6591       x_cnv = ftp_cnv;
6592
6593     x_prm = ftp_prm;                    /* Permissions */
6594     if (x_prm == SET_AUTO)              /* Permissions AUTO */
6595       x_prm = alike;
6596
6597 #ifndef NOCSETS
6598     x_csr = ftp_csr;                    /* Inherit global server charset */
6599     x_csl = ftp_csl;                    /* Inherit global local charset */
6600     if (x_csl < 0)                      /* If none, use current */
6601       x_csl = fcharset;                 /* file character-set. */
6602     x_xla = ftp_xla;                    /* Translation On/Off */
6603 #endif /* NOCSETS */
6604
6605     geterror = ftp_err;                 /* Inherit global error action. */
6606     asnambuf[0] = NUL;                  /* No as-name yet. */
6607     pipesave = pipesend;
6608     pipesend = 0;
6609
6610     havetype = 0;
6611     havesize = (CK_OFF_T)-1;
6612     makestr(&havemdtm,NULL);
6613
6614     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
6615         ftp_typ = g_ftp_typ;
6616         /* g_ftp_typ = -1; */
6617     }
6618     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
6619         pv[i].sval = NULL;              /* to null pointers */
6620         pv[i].ival = -1;                /* and -1 int values */
6621         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
6622     }
6623     zclose(ZMFILE);                     /* In case it was left open */
6624
6625     x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */
6626
6627     if (fp_nml) {                       /* Reset /NAMELIST */
6628         if (fp_nml != stdout)
6629           fclose(fp_nml);
6630         fp_nml = NULL;
6631     }
6632     makestr(&ftp_nml,NULL);
6633
6634     /* Initialize list of remote filespecs */
6635
6636     if (!mgetlist) {
6637         mgetlist = (char **)malloc(MGETMAX * sizeof(char *));
6638         if (!mgetlist) {
6639             printf("?Memory allocation failure - MGET list\n");
6640             return(-9);
6641         }
6642         for (i = 0; i < MGETMAX; i++)
6643           mgetlist[i] = NULL;
6644     }
6645     mgetn = 0;                          /* Number of mget arguments */
6646     mgetx = 0;                          /* Current arg */
6647
6648     if (who == 0) {                     /* Called with unprefixed command */
6649         if (cx == XXGET || cx == XXREGET || cx == XXRETR)
6650           getone++;
6651         switch (cx) {
6652           case XXREGET: pv[SND_RES].ival = 1; break;
6653           case XXRETR:  pv[SND_DEL].ival = 1; break;
6654           case XXGET:
6655           case XXMGET:  mget++; break;
6656         }
6657     } else {                            /* FTP command */
6658         if (cx == FTP_GET || cx == FTP_RGE)
6659           getone++;
6660         switch (cx) {
6661           case FTP_DEL:                 /* (fall thru on purpose) */
6662           case FTP_MDE: mdel++;         /* (ditto) */
6663           case FTP_GET:                 /* (ditto) */
6664           case FTP_MGE: mget++; break;
6665           case FTP_RGE: pv[SND_RES].ival = 1; break;
6666         }
6667     }
6668     cmfdbi(&sw,                         /* First FDB - command switches */
6669            _CMKEY,                      /* fcode */
6670            "Remote filename;\n or switch", /* hlpmsg */
6671            "",                          /* default */
6672            "",                          /* addtl string data */
6673            mdel ? ndelswi : ngetswi,    /* addtl numeric data 1: tbl size */
6674            4,                           /* addtl numeric data 2: 4 = cmswi */
6675            xxstring,                    /* Processing function */
6676            mdel ? delswi : getswi,      /* Keyword table */
6677            &fl                          /* Pointer to next FDB */
6678            );
6679     cmfdbi(&fl,                         /* 2nd FDB - remote filename */
6680            _CMFLD,                      /* fcode */
6681            "",                          /* hlpmsg */
6682            "",                          /* default */
6683            "",                          /* addtl string data */
6684            0,                           /* addtl numeric data 1 */
6685            0,                           /* addtl numeric data 2 */
6686            xxstring,
6687            NULL,
6688            &cm
6689            );
6690     cmfdbi(&cm,                         /* 3rd FDB - Confirmation */
6691            _CMCFM,                      /* fcode */
6692            "",                          /* hlpmsg */
6693            "",                          /* default */
6694            "",                          /* addtl string data */
6695            0,                           /* addtl numeric data 1 */
6696            0,                           /* addtl numeric data 2 */
6697            NULL,
6698            NULL,
6699            NULL
6700            );
6701
6702     while (1) {                         /* Parse 0 or more switches */
6703         x = cmfdb(&sw);                 /* Parse something */
6704         debug(F101,"ftp get cmfdb","",x);
6705         if (x < 0)                      /* Error */
6706           goto xgetx;                   /* or reparse needed */
6707         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
6708           break;
6709         c = cmgbrk();                   /* Get break character */
6710         getval = (c == ':' || c == '='); /* to see how they ended the switch */
6711         if (getval && !(cmresult.kflags & CM_ARG)) {
6712             printf("?This switch does not take arguments\n");
6713             x = -9;
6714             goto xgetx;
6715         }
6716         n = cmresult.nresult;           /* Numeric result = switch value */
6717         debug(F101,"ftp get switch","",n);
6718
6719         if (!getval && (cmgkwflgs() & CM_ARG)) {
6720             printf("?This switch requires an argument\n");
6721             x = -9;
6722             goto xgetx;
6723         }
6724         switch (n) {                    /* Process the switch */
6725           case SND_ASN:                 /* /AS-NAME: */
6726             debug(F101,"ftp get /as-name getval","",getval);
6727             if (!getval) break;
6728             if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) {
6729                 if (x == -3) {
6730                     printf("?name required\n");
6731                     x = -9;
6732                 }
6733                 goto xgetx;
6734             }
6735             s = brstrip(s);
6736             if (!*s) s = NULL;
6737             makestr(&(pv[n].sval),s);
6738             pv[n].ival = 1;
6739             break;
6740
6741           case SND_BIN:                 /* /BINARY */
6742           case SND_TXT:                 /* /TEXT or /ASCII */
6743           case SND_TEN:                 /* /TENEX */
6744             pv[SND_BIN].ival = 0;
6745             pv[SND_TXT].ival = 0;
6746             pv[SND_TEN].ival = 0;
6747             pv[n].ival = 1;
6748             break;
6749
6750 #ifdef PUTPIPE
6751           case SND_CMD:                 /* These take no args */
6752             if (nopush) {
6753                 printf("?Sorry, system command access is disabled\n");
6754                 x = -9;
6755                 goto xgetx;
6756             }
6757 #ifdef PIPESEND
6758             else if (rcvfilter) {
6759                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
6760                 x = -9;
6761                 goto xgetx;
6762             }
6763 #endif /* PIPESEND */
6764             sw.hlpmsg = "Command, or switch"; /* Change help message */
6765             pv[n].ival = 1;             /* Just set the flag */
6766             pv[SND_ARR].ival = 0;
6767             break;
6768 #endif /* PUTPIPE */
6769
6770           case SND_SHH:                 /* /QUIET */
6771           case SND_RES:                 /* /RECOVER (reget) */
6772           case SND_NOB:                 /* /NOBACKUPFILES */
6773           case SND_DEL:                 /* /DELETE */
6774           case SND_UPD:                 /* /UPDATE */
6775           case SND_USN:                 /* /UNIQUE */
6776           case SND_NOD:                 /* /NODOTFILES */
6777           case SND_REC:                 /* /RECOVER */
6778           case SND_MAI:                 /* /TO-SCREEN */
6779             pv[n].ival = 1;             /* Just set the flag */
6780             break;
6781
6782           case SND_DIF:                 /* /DATES-DIFFER */
6783             pv[SND_COL].ival = XYFX_M;  /* Now it's a collision option */
6784             pv[n].ival = 1;
6785             break;
6786
6787           case SND_COL:                 /* /COLLISION: */
6788             if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
6789               goto xgetx;
6790             if (x == XYFX_M)
6791               pv[SND_DIF].ival = 1;     /* (phase this out) */
6792             pv[n].ival = x;             /* this should be sufficient */
6793             break;
6794
6795           case SND_ERR:                 /* /ERROR-ACTION */
6796             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
6797               goto xgetx;
6798             pv[n].ival = x;
6799             break;
6800
6801           case SND_EXC:                 /* Exception list */
6802             if (!getval) break;
6803             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
6804                 if (x == -3) {
6805                     printf("?Pattern required\n");
6806                     x = -9;
6807                 }
6808                 goto xgetx;
6809             }
6810             if (s) if (!*s) s = NULL;
6811             makestr(&(pv[n].sval),s);
6812             if (pv[n].sval)
6813               pv[n].ival = 1;
6814             break;
6815
6816 #ifdef PIPESEND
6817           case SND_FLT:
6818             debug(F101,"ftp get /filter getval","",getval);
6819             if (!getval) break;
6820             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
6821                 if (x == -3)
6822                   s = "";
6823                 else
6824                   goto xgetx;
6825             }
6826             s = brstrip(s);
6827             if (pv[SND_MAI].ival < 1) {
6828                 y = strlen(s);
6829                 /* Make sure they included "\v(...)" */
6830                 for (x = 0; x < y; x++) {
6831                     if (s[x] != '\\') continue;
6832                     if (s[x+1] == 'v') break;
6833                 }
6834                 if (x == y) {
6835                     printf(
6836                 "?Filter must contain a replacement variable for filename.\n"
6837                            );
6838                     x = -9;
6839                     goto xgetx;
6840                 }
6841             }
6842             if (*s) {
6843                 pv[n].ival = 1;
6844                 makestr(&(pv[n].sval),s);
6845             } else {
6846                 pv[n].ival = 0;
6847                 makestr(&(pv[n].sval),NULL);
6848             }
6849             break;
6850 #endif /* PIPESEND */
6851
6852           case SND_NAM:
6853             if (!getval) break;
6854             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
6855               goto xgetx;
6856             debug(F101,"ftp get /filenames","",x);
6857             pv[n].ival = x;
6858             break;
6859
6860           case SND_SMA:                 /* Smaller / larger than */
6861           case SND_LAR: {
6862               CK_OFF_T y;
6863               if (!getval) break;
6864               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
6865                 goto xgetx;
6866               pv[n].wval = y;
6867               break;
6868           }
6869           case SND_FIL:                 /* Name of file containing filnames */
6870             if (!getval) break;
6871             if ((x = cmifi("Name of file containing list of filenames",
6872                                "",&s,&y,xxstring)) < 0) {
6873                 if (x == -3) {
6874                     printf("?Filename required\n");
6875                     x = -9;
6876                 }
6877                 goto xgetx;
6878             } else if (y && iswild(s)) {
6879                 printf("?Wildcards not allowed BBB\n");
6880                 x = -9;
6881                 goto xgetx;
6882             }
6883             if (s) if (!*s) s = NULL;
6884             makestr(&(pv[n].sval),s);
6885             if (pv[n].sval)
6886               pv[n].ival = 1;
6887             break;
6888
6889           case SND_MOV:                 /* MOVE after */
6890           case SND_REN:                 /* RENAME after */
6891           case SND_SRN: {               /* SERVER-RENAME */
6892               char * m = "";
6893               switch (n) {
6894                 case SND_MOV:
6895                   m =
6896                    "Device and/or directory for incoming file after reception";
6897                   break;
6898                 case SND_REN:
6899                   m = "New name for incoming file after reception";
6900                   break;
6901                 case SND_SRN:
6902                   m = "New name for source file on server after reception";
6903                   break;
6904               }
6905               if (!getval) break;
6906               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
6907                   if (x == -3) {
6908                       printf("%s\n", n == SND_MOV ?
6909                              "?Destination required" :
6910                              "?New name required"
6911                              );
6912                       x = -9;
6913                   }
6914                   goto xgetx;
6915               }
6916               makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6917               pv[n].ival = (pv[n].sval) ? 1 : 0;
6918               break;
6919           }
6920 #ifndef NOCSETS
6921           case SND_CSL:                 /* Local character set */
6922           case SND_CSR:                 /* Remote (server) charset */
6923             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0)
6924               return((x == -3) ? -2 : x);
6925             if (n == SND_CSL)
6926               x_csl = x;
6927             else
6928               x_csr = x;
6929             x_xla = 1;                  /* Overrides global OFF setting */
6930             break;
6931
6932           case SND_XPA:                 /* Transparent */
6933             x_xla =  0;
6934             x_csr = -1;
6935             x_csl = -1;
6936             break;
6937 #endif /* NOCSETS */
6938
6939           case SND_NML:
6940             if ((x = cmofi("Local filename","-",&s,xxstring)) < 0)
6941               goto xgetx;
6942             makestr(&ftp_nml,s);
6943             break;
6944
6945           case SND_PAT:                 /* /PATTERN: */
6946             if (!getval) break;
6947             if ((x = cmfld("Pattern","*", &s, xxstring)) < 0)
6948               goto xgetx;
6949             makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6950             pv[n].ival = (pv[n].sval) ? 1 : 0;
6951             break;
6952
6953           case SND_NLS:                 /* /NLST */
6954             pv[n].ival = 1;             /* Use NLST */
6955             pv[SND_MLS].ival = 0;       /* Don't use MLSD */
6956             break;
6957
6958           case SND_MLS:                 /* /MLSD */
6959             pv[n].ival = 1;             /* Use MLSD */
6960             pv[SND_NLS].ival = 0;       /* Don't use NLST */
6961             break;
6962
6963           default:                      /* /AFTER, /PERMISSIONS, etc... */
6964             printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf);
6965             x = -9;
6966             goto xgetx;
6967         }
6968     }
6969     line[0] = NUL;
6970     cmarg = line;
6971     cmarg2 = asnambuf;
6972     s = line;
6973 /*
6974   For GET, we want to parse an optional as-name, like with PUT.
6975   For MGET, we must parse a list of names, and then send NLST or MLSD
6976   commands for each name separately.
6977 */
6978     switch (cmresult.fcode) {           /* How did we get out of switch loop */
6979       case _CMFLD:                      /* Field */
6980         if (!getone) {
6981             s = brstrip(cmresult.sresult);
6982             makestr(&(mgetlist[mgetn++]),s);
6983             while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) {
6984                 if (x < 0)
6985                   goto xgetx;
6986                 makestr(&(mgetlist[mgetn++]),brstrip(s));
6987                 if (mgetn >= MGETMAX) {
6988                     printf("?Too many items in MGET list\n");
6989                     goto xgetx;
6990                 }
6991             }
6992             if ((x = cmcfm()) < 0)
6993               goto xgetx;
6994         } else {
6995             s = brstrip(cmresult.sresult);
6996             ckstrncpy(line,s,LINBUFSIZ);
6997             if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0)
6998               if (x != -3)
6999                 goto xgetx;
7000             s = brstrip(s);
7001             ckstrncpy(asnambuf,s,CKMAXPATH+1);
7002             if ((x = cmcfm()) < 0)
7003               goto xgetx;
7004         }
7005         break;
7006       case _CMCFM:                      /* Confirmation */
7007         break;
7008       default:
7009         printf("?Unexpected function code: %d\n",cmresult.fcode);
7010         x = -9;
7011         goto xgetx;
7012     }
7013     if (pv[SND_REC].ival > 0)           /* /RECURSIVE */
7014       recursive = 2;
7015
7016     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
7017         forcetype = 1;                  /* So skip the name-pattern match */
7018         ftp_typ = XYFT_B;               /* Set binary */
7019     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
7020         forcetype = 1;
7021         ftp_typ = XYFT_T;
7022     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
7023         forcetype = 1;
7024         ftp_typ = FTT_TEN;
7025     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
7026         forcetype = 1;
7027         ftp_typ = binary;
7028         g_ftp_typ = binary;
7029     }
7030     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
7031         char * p;
7032         p = brstrip(pv[SND_ASN].sval);  /* As-name */
7033         ckstrncpy(asnambuf,p,CKMAXPATH+1);
7034     }
7035     debug(F110,"ftp get asnambuf",asnambuf,0);
7036
7037 #ifdef PIPESEND
7038     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
7039         char * p;
7040         p = asnambuf;
7041         debug(F110,"GET /COMMAND before stripping",p,0);
7042         p = brstrip(p);
7043         debug(F110,"GET /COMMAND after stripping",p,0);
7044         if (!*p) {
7045             printf("?Sorry, a command to write to is required\n");
7046             x = -9;
7047             goto xgetx;
7048         }
7049         pipename = p;
7050         pipesend = 1;
7051     }
7052 #endif /* PIPESEND */
7053
7054 /* Set up /MOVE and /RENAME */
7055
7056 #ifdef COMMENT
7057     /* Conflict exists only for PUT - removed 13 Mar 2006 - fdc */
7058     if (pv[SND_DEL].ival > 0 &&
7059         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
7060         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
7061         x = -9;
7062         goto xgetx;
7063     }
7064 #endif  /* COMMENT */
7065 #ifdef CK_TMPDIR
7066     if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) {
7067         int len;
7068         char * p = pv[SND_MOV].sval;
7069         len = strlen(p);
7070         if (!isdir(p)) {                /* Check directory */
7071 #ifdef CK_MKDIR
7072             char * s = NULL;
7073             s = (char *)malloc(len + 4);
7074             if (s) {
7075                 strcpy(s,p);            /* safe */
7076 #ifdef datageneral
7077                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
7078 #else
7079                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
7080 #endif /* datageneral */
7081                 s[len++] = 'X';
7082                 s[len] = NUL;
7083 #ifdef NOMKDIR
7084                 x = -1;
7085 #else
7086                 x = zmkdir(s);
7087 #endif /* NOMKDIR */
7088                 free(s);
7089                 if (x < 0) {
7090                     printf("?Can't create \"%s\"\n",p);
7091                     x = -9;
7092                     goto xgetx;
7093                 }
7094             }
7095 #else
7096             printf("?Directory \"%s\" not found\n",p);
7097             x = -9;
7098             goto xgetx;
7099 #endif /* CK_MKDIR */
7100         }
7101         makestr(&rcv_move,p);
7102         moving = 1;
7103     }
7104 #endif /* CK_TMPDIR */
7105
7106     if (pv[SND_REN].ival > 0) {         /* /RENAME */
7107         char * p = pv[SND_REN].sval;
7108         if (!p) p = "";
7109         if (!*p) {
7110             printf("?New name required for /RENAME\n");
7111             x = -9;
7112             goto xgetx;
7113         }
7114         p = brstrip(p);
7115 #ifndef NOSPL
7116     /* If name given is wild, rename string must contain variables */
7117         if (mget && !getone) {
7118             char * s = tmpbuf;
7119             x = TMPBUFSIZ;
7120             zzstring(p,&s,&x);
7121             if (!strcmp(tmpbuf,p)) {
7122                 printf(
7123     "?/RENAME for file group must contain variables such as \\v(filename)\n"
7124                        );
7125                 x = -9;
7126                 goto xgetx;
7127             }
7128         }
7129 #endif /* NOSPL */
7130         renaming = 1;
7131         makestr(&rcv_rename,p);
7132         debug(F110,"FTP rcv_rename",rcv_rename,0);
7133     }
7134     if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) {
7135         printf("?Filename required but not given\n");
7136         x = -9;
7137         goto xgetx;
7138     } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) {
7139         printf("?You can't give both /LISTFILE and a remote filename\n");
7140         x = -9;
7141         goto xgetx;
7142     }
7143     CHECKCONN();                        /* Check connection */
7144
7145     if (pv[SND_COL].ival > -1)
7146       x_fnc = pv[SND_COL].ival;
7147
7148 #ifndef NOSPL
7149     /* If as-name given for MGET, as-name must contain variables */
7150     if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) {
7151         char * s = tmpbuf;
7152         x = TMPBUFSIZ;
7153         zzstring(asnambuf,&s,&x);
7154         if (!strcmp(tmpbuf,asnambuf)) {
7155             printf(
7156     "?As-name for MGET must contain variables such as \\v(filename)\n"
7157                    );
7158             x = -9;
7159             goto xgetx;
7160         }
7161     }
7162 #endif /* NOSPL */
7163
7164 /* doget: */
7165
7166     if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */
7167         fdispla = 0;
7168     } else {
7169         displa = 1;
7170         if (mdel || ftp_deb)
7171           fdispla = XYFD_B;
7172     }
7173     deleting = 0;
7174     if (pv[SND_DEL].ival > 0)           /* /DELETE was specified */
7175       deleting = 1;
7176     if (pv[SND_EXC].ival > 0)
7177       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
7178     if (pv[SND_SMA].wval > -1)
7179       getsmaller = pv[SND_SMA].wval;
7180     if (pv[SND_LAR].wval > -1)
7181       getlarger = pv[SND_LAR].wval;
7182     if (pv[SND_NAM].ival > -1)
7183       x_cnv = pv[SND_NAM].ival;
7184     if (pv[SND_ERR].ival > -1)
7185       geterror = pv[SND_ERR].ival;
7186     if (pv[SND_MAI].ival > -1)
7187       toscreen = 1;
7188
7189     if (pv[SND_NLS].ival > 0) {         /* Force NLST or MLSD? */
7190         mgetmethod = SND_NLS;
7191         mgetforced = 1;
7192     } else if (pv[SND_MLS].ival > 0) {
7193         mgetmethod = SND_MLS;
7194         mgetforced = 1;
7195     }
7196
7197 #ifdef FTP_RESTART
7198     if (pv[SND_RES].ival > 0) {
7199         if (!ftp_typ) {
7200             printf("?Sorry, GET /RECOVER requires binary mode\n");
7201             x = -9;
7202             goto xgetx;
7203 #ifdef COMMENT
7204         /* Not true - the fact that the initial REST fails does not mean */
7205         /* it will fail here.  */
7206         } else if (!okrestart) {
7207             printf("WARNING: Server might not support restart...\n");
7208 #endif /* COMMENT */
7209         }
7210         restart = 1;
7211     }
7212 #endif /* FTP_RESTART */
7213
7214 #ifdef PIPESEND
7215     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
7216         if (pipesend) {
7217             printf("?Switch conflict: /FILTER and /COMMAND\n");
7218             x = -9;
7219             goto xgetx;
7220         }
7221         makestr(&rcvfilter,pv[SND_FLT].sval);
7222         debug(F110,"ftp get /FILTER", rcvfilter, 0);
7223     }
7224     if (rcvfilter || pipesend) {        /* /RESTART */
7225 #ifdef FTP_RESTART
7226         if (restart) {                  /* with pipes or filters */
7227             printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n");
7228             x = -9;
7229             goto xgetx;
7230         }
7231 #endif /* FTP_RESTART */
7232         if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) {
7233             printf(
7234                 "?Switch conflict: /FILTER or /COMMAND and Date Checking\n");
7235             x = -9;
7236             goto xgetx;
7237         }
7238     }
7239 #endif /* PIPESEND */
7240
7241     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
7242     filcnt = 0;
7243     pktnum = 0;
7244     rpackets = 0L;
7245
7246     if (pv[SND_FIL].ival > 0) {
7247         if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
7248             debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno);
7249             printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval);
7250             x = -9;
7251             goto xgetx;
7252         }
7253         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */
7254             zclose(ZMFILE);                       /* Failed */
7255             debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0);
7256             printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval);
7257             x = -9;
7258             goto xgetx;
7259         }
7260         listfile = 1;
7261         debug(F110,"ftp get listfile first",tmpbuf,0);
7262         makestr(&(mgetlist[0]),tmpbuf);
7263     }
7264     t0 = gmstimer();                    /* Record starting time */
7265
7266     updating = 0;                       /* Checking dates? */
7267     if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U))
7268       updating = 1;
7269     if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M)
7270       updating = 2;
7271     if (updating)                       /* These switches force FTP DATES ON */
7272       ftp_dates |= 2;
7273
7274     what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */
7275
7276     cancelgroup = 0;                    /* Group not canceled yet */
7277     if (!(ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype))
7278       changetype(ftp_typ,0);            /* Change to requested type */
7279     binary = ftp_typ;                   /* For file-transfer display */
7280     first = 1;                          /* For MGET list */
7281     done = 0;                           /* Loop control */
7282
7283 #ifdef CK_TMPDIR
7284     if (dldir && !f_tmpdir) {           /* If they have a download directory */
7285         if ((s = zgtdir())) {           /* Get current directory */
7286             if (zchdir(dldir)) {        /* Change to download directory */
7287                 ckstrncpy(savdir,s,TMPDIRLEN);
7288                 f_tmpdir = 1;           /* Remember that we did this */
7289             }
7290         }
7291     }
7292 #endif /* CK_TMPDIR */
7293
7294     if (ftp_nml) {                      /* /NAMELIST */
7295         debug(F110,"ftp GET ftp_nml",ftp_nml,0);
7296         if (ftp_nml[0] == '-' && ftp_nml[1] == 0)
7297           fp_nml = stdout;
7298         else
7299           fp_nml = fopen(ftp_nml, "wb");
7300         if (!fp_nml) {
7301             printf("?%s: %s\n",ftp_nml,ck_errstr());
7302             goto xgetx;
7303         }
7304     }
7305     while (!done && !cancelgroup) {     /* Loop for all files */
7306                                         /* or until canceled. */
7307 #ifdef FTP_PROXY
7308         /* do something here if proxy */
7309 #endif /* FTP_PROXY */
7310
7311         rs_len = (CK_OFF_T)0;           /* REGET position */
7312         cancelfile = 0;                 /* This file not canceled yet */
7313         haspath = 0;                    /* Recalculate this each time thru */
7314
7315         if (getone) {                   /* GET */
7316             char * p;
7317             s = line;
7318             src = line;                 /* Server name */
7319             done = 1;
7320             debug(F111,"ftp get file",s,0);
7321         } else if (mget) {              /* MGET */
7322             src = mgetlist[mgetx];
7323             debug(F111,"ftp mget remote_files A",src,first);
7324             s = (char *)remote_files(first,
7325                                      (CHAR *)mgetlist[mgetx],
7326                                      (CHAR *)pv[SND_PAT].sval,
7327                                      0
7328                                      );
7329             debug(F110,"ftp mget remote_files B",s,0);
7330             if (!s) s = "";
7331             if (!*s) {
7332                 first = 1;
7333                 if (listfile) {         /* Names from listfile */
7334                   again:
7335                     tmpbuf[0] = NUL;
7336                     while (!tmpbuf[0]) {
7337                         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) {
7338                             zclose(ZMFILE);
7339                             debug(F110,"ftp get listfile EOF",
7340                                   pv[SND_FIL].sval,0);
7341                             makestr(&(mgetlist[0]),NULL);
7342                             s = NULL;
7343                             done = 1;
7344                             break;
7345                         }
7346                     }
7347                     if (done)
7348                       continue;
7349
7350                     makestr(&(mgetlist[0]),tmpbuf);
7351                     debug(F110,"ftp get listfile next",tmpbuf,0);
7352                     s = (char *)remote_files(first,
7353                                              (CHAR *)mgetlist[0],
7354                                              (CHAR *)pv[SND_PAT].sval,
7355                                              0
7356                                              );
7357                     debug(F110,"ftp mget remote_files C",s,0);
7358                     if (!s) {
7359                         ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7360                         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,"File not found");
7361                         tlog(F110,"ftp get file not found:",s,0);
7362                         goto again;
7363                     }
7364                 } else {                /* Names from command line */
7365                     mgetx++;
7366                     if (mgetx < mgetn)
7367                       s = (char *)remote_files(first,
7368                                                (CHAR *)mgetlist[mgetx],
7369                                                (CHAR *)pv[SND_PAT].sval,
7370                                                0
7371                                                );
7372                     else
7373                       s = NULL;
7374                     if (!s) mgetx++;
7375                     debug(F111,"ftp mget remote_files D",s,mgetx);
7376                 }
7377                 if (!s) {
7378                     if (!first || mgetx >= mgetn) {
7379                         done = 1;
7380                         break;
7381                     } else if (geterror) {
7382                         status = 0;
7383                         done = 1;
7384                         break;
7385                     } else {
7386                         continue;
7387                     }
7388                 }
7389             }
7390         }
7391         debug(F111,"ftp mget remote_files E",s,0);
7392         /*
7393           The semantics of NLST are ill-defined.  Suppose we have just sent
7394           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
7395           /path/bar, etc.  But some send back only foo and bar, and subsequent
7396           RETR commands based on the pathless names are not going to work.
7397         */
7398         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
7399             char * s3;
7400             if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) {
7401                 int len, left = 4096;
7402                 char * tmp = xtmpbuf;
7403                 len = s3 - mgetlist[mgetx] + 1;
7404                 ckstrncpy(tmp,mgetlist[mgetx],left);
7405                 tmp += len;
7406                 left -= len;
7407                 ckstrncpy(tmp,s,left);
7408                 s = xtmpbuf;
7409                 debug(F111,"ftp mget remote_files F",s,0);
7410             }
7411         }
7412         first = 0;
7413         skipthis = 0;                   /* File selection... */
7414         msg = "";
7415         nam = s;                        /* Filename (without path) */
7416         rc = 0;                         /* Initial return code */
7417         s2 = "";
7418
7419         if (!getone && !skipthis) {     /* For MGET and MDELETE... */
7420             char c, * p = s;
7421             int srvpath = 0;
7422             int usrpath = 0;
7423             int i, k = 0;
7424
7425             debug(F111,"ftp mget havetype",s,havetype);
7426             if (havetype > 0 && havetype != FTYP_FILE) {
7427                 /* Server says it's not file... */
7428                 debug(F110,"ftp mget not-a-file",s,0);
7429                 continue;
7430             }
7431 /*
7432   Explanation: Some ftp servers (such as wu-ftpd) return a recursive list.
7433   But if the client did not ask for a recursive list, we have to ignore any
7434   server files that include a pathname that extends beyond any path that
7435   was included in the user's request.
7436
7437   User's filespec is blah or path/blah (or other non-UNIX syntax).  We need to
7438   get the user's path segment.  Then, for each incoming file, if it begins
7439   with the same path segment, we must strip it (point past it).
7440 */
7441             src = mgetlist[mgetx];      /* In case it moved! */
7442             if (src) {
7443                 for (i = 0; src[i]; i++) { /* Find rightmost path separator */
7444                     if (ispathsep(src[i])) /* in user's pathname */
7445                       k = i + 1;
7446                 }
7447             } else {
7448                 src = "";
7449             }
7450             usrpath = k;                /* User path segment length */
7451             debug(F111,"ftp get usrpath",src,usrpath);
7452
7453             p = s;                      /* Server filename */
7454             while ((c = *p++)) {        /* Look for path in server filename */
7455                 if (ispathsep(c)) {
7456                     /* haspath++; */
7457                     nam = p;            /* Pathless name (for ckmatch) */
7458                     srvpath = p - s;    /* Server path segment length */
7459                 }
7460             }
7461             debug(F111,"ftp get srvpath",s,srvpath);
7462
7463             if (usrpath == 0) {
7464 /*
7465   Here we handle the case where the user said "mget foo" where foo is a
7466   directory name, and the server is sending back names like "foo/file1",
7467   "foo/file2", etc.  This is a nasty trick but it's necessary because the
7468   user can't compensate by typing "mget foo/" because then the server is
7469   likely to send back "foo//file1, foo//file2" etc, and we still won't
7470   get a match...
7471 */
7472                 int srclen = 0, srvlen = 0;
7473                 if (src) srclen = strlen(src);
7474                 if (s) srvlen = strlen(s);
7475                 if (src && (srvlen > srclen)) {
7476                     if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) {
7477                         char * tmpsrc = NULL;
7478                         tmpsrc = (char *)malloc(srclen + 2);
7479                         strncpy(tmpsrc,src,srclen);
7480                         tmpsrc[srclen] = s[srclen];
7481                         tmpsrc[srclen+1] = NUL;
7482                         free(mgetlist[mgetx]);
7483                         mgetlist[mgetx] = tmpsrc;
7484                         tmpsrc = NULL;
7485                         src = mgetlist[mgetx];
7486                         usrpath = srclen+1;
7487                     }                         
7488                 }
7489             }
7490 /*
7491   If as-name not given and server filename includes path that matches
7492   the pathname from the user's file specification, we must trim the common
7493   path prefix from the server's name when constructing the local name.
7494 */
7495             if (src &&                  /* Wed Sep 25 17:27:48 2002 */
7496                 !asnambuf[0] &&
7497                 !recursive &&           /* Thu Sep 19 16:11:59 2002 */
7498                 (srvpath > 0) &&
7499                 !strncmp(src,s,usrpath)) {
7500                 s2 = s + usrpath;       /* Local name skips past remote path */
7501             }
7502 #ifdef COMMENT
7503             /* This doesn't work if the path prefix contains wildcards! */
7504             haspath = (srvpath > usrpath);
7505 #else
7506             {                           /* Count path segments instead */
7507                 int x1 = 0, x2 = 0;
7508                 char *p;
7509                 for (p = s; *p; p++)
7510                   if (ispathsep(*p)) x1++;
7511                 for (p = src; *p; p++) {
7512                     if (ispathsep(*p)) x2++;
7513                 }
7514                 haspath = recursive ? x1 || x2 : x1 > x2;
7515                 debug(F111,"ftp get server path segments",s,x1);
7516                 debug(F111,"ftp get user   path segments",src,x2);
7517             }
7518
7519 #endif /* COMMENT */
7520             debug(F111,"ftp get haspath",s+usrpath,haspath);
7521
7522             if (haspath) {              /* Server file has path segments? */
7523                 if (!recursive) {       /* [M]GET /RECURSIVE? */
7524 /*
7525   We did not ask for a recursive listing, but the server is sending us one
7526   anyway (as wu-ftpd is wont to do).  We get here if the current filename
7527   includes a path segment beyond any path segment we asked for in our
7528   non-recursive [M]GET command.  We MUST skip this file.
7529 */
7530                     debug(F111,"ftp get skipping because of path",s,0);
7531                     continue;
7532                 }
7533             }
7534         } else if (getone && !skipthis) { /* GET (not MGET) */
7535             char * p = nam;
7536             while ((c = *p++)) {        /* Handle path in local name */
7537                 if (ispathsep(c)) {
7538                     if (recursive) {    /* If recursive, keep it */
7539                         haspath = 1;
7540                         break;
7541                     } else {            /* Otherwise lose it. */
7542                       nam = p;
7543                     }
7544                 }
7545             }
7546             s2 = nam;
7547         }
7548         if (!*nam)                      /* Name without path */
7549           nam = s;
7550
7551         if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */
7552             if (nam[0] == '.')
7553               continue;
7554         }
7555         if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */
7556             int xx;
7557             for (i = 0; i < NSNDEXCEPT; i++) {
7558                 if (!rcvexcept[i]) {
7559                     break;
7560                 }
7561                 xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1);
7562                 debug(F111,"ftp mget /except match",rcvexcept[i],xx);
7563                 if (xx) {
7564                     tlog(F100," refused: exception list","",0);
7565                     msg = "Refused: Exception List";
7566                     skipthis++;
7567                     break;
7568                 }
7569             }
7570         }
7571         if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */
7572             if (ckmatch(
7573 #ifdef CKREGEX
7574                         "*.~[0-9]*~"
7575 #else
7576                         "*.~*~"
7577 #endif /* CKREGEX */
7578                         ,nam,0,1) > 0)
7579               continue;
7580         }
7581         if (!x_xla) {                   /* If translation is off */
7582             x_csl = -2;                 /* unset the charsets */
7583             x_csr = -2;
7584         }
7585         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
7586         if (!*s2)                       /* Local name */
7587           s2 = asnambuf;                /* As-name */
7588
7589         if (!*s2)                       /* Sat Nov 16 19:19:39 2002 */
7590           s2 = recursive ? s : nam;     /* Fri Jan 10 13:15:19 2003 */
7591
7592         debug(F110,"ftp get filnam  ",s,0);
7593         debug(F110,"ftp get asname A",s2,0);
7594
7595         /* Receiving to real file */
7596         if (!pipesend &&
7597 #ifdef PIPESEND
7598             !rcvfilter &&
7599 #endif /* PIPESEND */
7600             !toscreen) {
7601 #ifndef NOSPL
7602             /* Do this here so we can decide whether to skip */
7603             if (cmd_quoting && !skipthis && asnambuf[0]) {
7604                 int n; char *p;
7605                 n = TMPBUFSIZ;
7606                 p = tmpbuf;
7607                 zzstring(asnambuf,&p,&n);
7608                 s2 = tmpbuf;
7609                 debug(F111,"ftp get asname B",s2,updating);
7610             }
7611 #endif /* NOSPL */
7612
7613             local = *s2 ? s2 : s;
7614
7615             if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */
7616                 CK_OFF_T x;
7617                 x = zchki(local);
7618                 debug(F111,"ftp get DISCARD zchki",local,x);
7619                 if (x > -1) {
7620                     skipthis++;
7621                     debug(F110,"ftp get skip name",local,0);
7622                     tlog(F100," refused: name","",0);
7623                     msg = "Refused: Name";
7624                 }
7625             }
7626
7627 #ifdef DOUPDATE
7628             if (!skipthis && updating) { /* If updating and not yet skipping */
7629                 if (zchki(local) > -1) {
7630                     x = chkmodtime(local,s,0);
7631 #ifdef DEBUG
7632                     if (deblog) {
7633                         if (updating == 2)
7634                           debug(F111,"ftp get /dates-diff chkmodtime",local,x);
7635                         else
7636                           debug(F111,"ftp get /update chkmodtime",local,x);
7637                     }
7638 #endif /* DEBUG */
7639                     if ((updating == 1 && x > 0) ||  /* /UPDATE */
7640                         (updating == 2 && x == 1)) { /* /DATES-DIFFER */
7641                         skipthis++;
7642                         tlog(F100," refused: date","",0);
7643                         msg = "Refused: Date";
7644                         debug(F110,"ftp get skip date",local,0);
7645                     }
7646                 }
7647             }
7648 #endif /* DOUPDATE */
7649         }
7650         /* Initialize file size to -1 in case server doesn't understand */
7651         /* SIZE command, so xxscreen() will know we don't know the size */
7652
7653         fsize = (CK_OFF_T)-1;
7654
7655         /* Ask for size now only if we need it for selection */
7656         /* because if you're going thru a list 100,000 files to select */
7657         /* a small subset, 100,000 SIZE commands can take hours... */
7658
7659         gotsize = 0;
7660         if (!mdel && !skipthis &&        /* Don't need size for DELE... */
7661             (getsmaller >= (CK_OFF_T)0  || getlarger >= (CK_OFF_T)0)) {
7662             if (havesize >= (CK_OFF_T)0) { /* Already have file size? */
7663                 fsize = havesize;
7664                 gotsize = 1;
7665             } else {                    /* No - must ask server */
7666                 /*
7667                   Prior to sending the NLST command we necessarily put the
7668                   server into ASCII mode.  We must now put it back into the
7669                   the requested mode so the upcoming SIZE command returns
7670                   right kind of size; this is especially important for
7671                   GET /RECOVER; otherwise the server returns the "ASCII" size
7672                   of the file, rather than its true size.
7673                 */
7674                 changetype(ftp_typ,0);  /* Change to requested type */
7675                 fsize = (CK_OFF_T)-1;
7676                 if (sizeok) {
7677                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7678                     if (x == REPLY_COMPLETE) {
7679                         fsize = ckatofs(&ftp_reply_str[4]);
7680                         gotsize = 1;
7681                     }
7682                 }
7683             }
7684             if (gotsize) {
7685                 if (getsmaller >= (CK_OFF_T)0 && fsize >= getsmaller)
7686                   skipthis++;
7687                 if (getlarger >= (CK_OFF_T)0 && fsize <= getlarger)
7688                   skipthis++;
7689                 if (skipthis) {
7690                     debug(F111,"ftp get skip size",s,fsize);
7691                     tlog(F100," refused: size","",0);
7692                     msg = "Refused: Size";
7693                 }
7694 #ifdef COMMENT
7695             } else if (getone) {
7696                 /* SIZE can fail for many reasons.  Does the file exist? */
7697                 x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm);
7698                 if (x != REPLY_COMPLETE) {
7699                     printf(">>> FILE NOT FOUND: %s\n",s);
7700                     break;
7701                 }
7702 #endif /* COMMENT */
7703             }
7704         }
7705         if (skipthis) {                 /* Skipping this file? */
7706             ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7707             if (msg)
7708               ftscreen(SCR_ST,ST_ERR,(CK_OFF_T)0,msg);
7709             else
7710               ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,s);
7711             continue;
7712         }
7713         if (fp_nml) {                   /* /NAMELIST only - no transfer */
7714             fprintf(fp_nml,"%s\n",s);
7715             continue;
7716         }
7717         if (recursive && haspath && !pipesend
7718 #ifdef PIPESEND
7719             && !rcvfilter
7720 #endif /* PIPESEND */
7721             ) {
7722             int x;
7723
7724 #ifdef NOMKDIR
7725             x = -1;
7726 #else
7727             x = zmkdir(s);              /* Try to make the directory */
7728 #endif /* NOMKDIR */
7729
7730             if (x < 0) {
7731                 rc = -1;                /* Failure is fatal */
7732                 if (geterror) {
7733                     status = 0;
7734                     ftscreen(SCR_EM,0,(CK_OFF_T)0,
7735                              "Directory creation failure");
7736                     break;
7737                 }
7738             }
7739         }
7740
7741         /* Not skipping */
7742
7743         selected++;                     /* Count this file as selected */
7744         pn = NULL;
7745
7746         if (!gotsize && !mdel) {        /* Didn't get size yet */
7747             if (havesize > (CK_OFF_T)-1) { /* Already have file size? */
7748                 fsize = havesize;
7749                 gotsize = 1;
7750             } else {                    /* No - must ask server */
7751                 fsize = (CK_OFF_T)-1;
7752                 if (sizeok) {
7753                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7754                     if (x == REPLY_COMPLETE) {
7755                         fsize = ckatofs(&ftp_reply_str[4]);
7756                         gotsize = 1;
7757                     }
7758                 }
7759             }
7760         }
7761         if (mdel) {                     /* [M]DELETE */
7762             if (displa && !ftp_vbm)
7763               printf(" %s...",s);
7764             rc =
7765              (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1;
7766             if (rc > -1) {
7767                 tlog(F110,"ftp mdelete",s,0);
7768                 if (displa && !ftp_vbm)
7769                   printf("OK\n");
7770             } else {
7771                 tlog(F110,"ftp mdelete failed:",s,0);
7772                 if (displa)
7773                   printf("Failed\n");
7774             }
7775 #ifndef NOSPL
7776 #ifdef PIPESEND
7777         } else if (rcvfilter) {         /* [M]GET with filter */
7778             int n; char * p;
7779             n = CKMAXPATH;
7780             p = tmpbuf;                 /* Safe - no asname with filter */
7781             zzstring(rcvfilter,&p,&n);
7782             if (n > -1)
7783               pn = tmpbuf;
7784             debug(F111,"ftp get rcvfilter",pn,n);
7785 #endif /* PIPESEND */
7786 #endif /* NOSPL */
7787             if (toscreen) s2 = "-";
7788         } else if (pipesend) {          /* [M]GET /COMMAND */
7789             int n; char * p;
7790             n = CKMAXPATH;
7791             p = tmpbuf;                 /* Safe - no asname with filter */
7792             zzstring(pipename,&p,&n);
7793             if (n > -1)
7794               pn = tmpbuf;
7795             debug(F111,"ftp get pipename",pipename,n);
7796             if (toscreen) s2 = "-";
7797         } else {                        /* [M]GET with no pipes or filters */
7798             debug(F111,"ftp get s2 A",s2,x_cnv);
7799             if (toscreen) {
7800                 s2 = "-";               /* (hokey convention for stdout) */
7801             } else if (!*s2) {          /* No asname? */
7802                 if (x_cnv) {            /* If converting */
7803                     nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */
7804                     s2 = tmpbuf;
7805                     debug(F110,"ftp get nzrtol",s2,0);
7806                 } else                  /* otherwise */
7807                   s2 = s;               /* use incoming file's name */
7808             }
7809             debug(F110,"ftp get s2 B",s2,0);
7810
7811             /* If local file already exists, take collision action */
7812
7813             if (!pipesend &&
7814 #ifdef PIPESEND
7815                 !rcvfilter &&
7816 #endif /* PIPESEND */
7817                 !toscreen) {
7818                 CK_OFF_T x;
7819                 x = zchki(s2);
7820                 debug(F111,"ftp get zchki",s2,x);
7821                 debug(F111,"ftp get x_fnc",s2,x_fnc);
7822
7823                 if (x > (CK_OFF_T)-1 && !restart) {
7824                     int x = -1;
7825                     char * newname = NULL;
7826
7827                     switch (x_fnc) {
7828                       case XYFX_A:      /* Append */
7829                         append = 1;
7830                         break;
7831                       case XYFX_R:      /* Rename */
7832                       case XYFX_B:      /* Backup */
7833                         znewn(s2,&newname); /* Make unique name */
7834                         debug(F110,"ftp get znewn",newname,0);
7835                         if (x_fnc == XYFX_B) { /* Backup existing file */
7836                             x = zrename(s2,newname);
7837                             debug(F111,"ftp get backup zrename",newname,x);
7838                         } else {      /* Rename incoming file */
7839                             x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1);
7840                             s2 = tmpbuf;
7841                             debug(F111,"ftp get rename incoming",newname,x);
7842                         }
7843                         if (x < 0) {
7844                             ftscreen(SCR_EM,0,(CK_OFF_T)0,
7845                                      "Backup/Rename failed");
7846                             x = 0;
7847                             goto xgetx;
7848                         }
7849                         break;
7850                       case XYFX_D:      /* Discard (already handled above) */
7851                       case XYFX_U:      /* Update (ditto) */
7852                       case XYFX_M:      /* Update (ditto) */
7853                       case XYFX_X:      /* Overwrite */
7854                         break;
7855                     }
7856                 }
7857             }
7858         }
7859         if (!mdel) {
7860 #ifdef PIPESEND
7861             debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0);
7862 #endif /* PIPESEND */
7863             if (pipesend && !toscreen)
7864               s2 = NULL;
7865 #ifdef DEBUG
7866             if (deblog) {
7867                 debug(F101,"ftp get x_xla","",x_xla);
7868                 debug(F101,"ftp get x_csl","",x_csl);
7869                 debug(F101,"ftp get x_csr","",x_csr);
7870                 debug(F101,"ftp get append","",append);
7871             }
7872 #endif /* DEBUG */
7873
7874             rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr);
7875
7876 #ifdef DEBUG
7877             if (deblog) {
7878                 debug(F111,"ftp get rc",s,rc);
7879                 debug(F111,"ftp get ftp_timed_out",s,ftp_timed_out);
7880                 debug(F111,"ftp get cancelfile",s,cancelfile);
7881                 debug(F111,"ftp get cancelgroup",s,cancelgroup);
7882                 debug(F111,"ftp get renaming",s,renaming);
7883                 debug(F111,"ftp get moving",s,moving);
7884             }
7885 #endif /* DEBUG */
7886         }
7887         if (rc > -1) {
7888             good++;
7889             status = 1;
7890             if (!cancelfile) {
7891                 if (deleting) {         /* GET /DELETE (source file) */
7892                     rc =
7893                       (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE)
7894                         ? 1 : -1;
7895                     tlog(F110, (rc > -1) ?
7896                          " deleted" : " failed to delete", s, 0);
7897                 }
7898                 if (renaming && rcv_rename && !toscreen) {
7899                     char *p;            /* Rename downloaded file */
7900 #ifndef NOSPL
7901                     char tmpbuf[CKMAXPATH+1];
7902                     int n;
7903                     n = CKMAXPATH;
7904                     p = tmpbuf;
7905                     debug(F111,"ftp get /rename",rcv_rename,0);
7906                     zzstring(rcv_rename,&p,&n);
7907                     debug(F111,"ftp get /rename",rcv_rename,0);
7908                     p = tmpbuf;
7909 #else
7910                     p = rcv_rename;
7911 #endif /* NOSPL */
7912                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7913                     debug(F111,"doftpget /RENAME zrename",p,rc);
7914                     tlog(F110, (rc > -1) ?
7915                          " renamed to" :
7916                          " failed to rename to",
7917                          p,
7918                          0
7919                          );
7920                 } else if (moving && rcv_move && !toscreen) {
7921                     char *p;            /* Move downloaded file */
7922 #ifndef NOSPL
7923                     char tmpbuf[CKMAXPATH+1];
7924                     int n;
7925                     n = TMPBUFSIZ;
7926                     p = tmpbuf;
7927                     debug(F111,"ftp get /move-to",rcv_move,0);
7928                     zzstring(rcv_move,&p,&n);
7929                     p = tmpbuf;
7930 #else
7931                     p = rcv_move;
7932 #endif /* NOSPL */
7933                     debug(F111,"ftp get /move-to",p,0);
7934                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7935                     debug(F111,"doftpget /MOVE zrename",p,rc);
7936                     tlog(F110, (rc > -1) ?
7937                          " moved to" : " failed to move to", p, 0);
7938                 }
7939                 if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) {
7940                     char * s = pv[SND_SRN].sval;
7941                     char * srvrn = pv[SND_SRN].sval;
7942                     char tmpbuf[CKMAXPATH+1];
7943 #ifndef NOSPL
7944                     int y;              /* Pass it thru the evaluator */
7945                     extern int cmd_quoting; /* for \v(filename) */
7946                     debug(F111,"ftp get srv_renam",s,1);
7947
7948                     if (cmd_quoting) {
7949                         y = CKMAXPATH;
7950                         s = (char *)tmpbuf;
7951                         zzstring(srvrn,&s,&y);
7952                         s = (char *)tmpbuf;
7953                     }
7954 #endif /* NOSPL */
7955                     debug(F111,"ftp get srv_renam",s,1);
7956                     if (s) if (*s) {
7957                         int x;
7958                         x = ftp_rename(s2,s);
7959                         debug(F111,"ftp get ftp_rename",s2,x);
7960                         tlog(F110, (x > 0) ?
7961                              " renamed source file to" :
7962                              " failed to rename source file to",
7963                              s,
7964                              0
7965                              );
7966                         if (x < 1)
7967                           return(-1);
7968                     }
7969                 }
7970             }
7971         }
7972         if (cancelfile)
7973           continue;
7974         if (rc < 0) {
7975             ftp_fai++;
7976 #ifdef FTP_TIMEOUT
7977             debug(F101,"ftp get ftp_timed_out","",ftp_timed_out);
7978             if (ftp_timed_out) {
7979                 status = 0;
7980                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"GET timed out");
7981             }
7982 #endif  /* FTP_TIMEOUT */
7983             if (geterror) {
7984                 status = 0;
7985                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"Fatal download error");
7986                 done++;
7987             }
7988         }
7989     }
7990 #ifdef DEBUG
7991     if (deblog) {
7992         debug(F101,"ftp get status","",status);
7993         debug(F101,"ftp get cancelgroup","",cancelgroup);
7994         debug(F101,"ftp get cancelfile","",cancelfile);
7995         debug(F101,"ftp get selected","",selected);
7996         debug(F101,"ftp get good","",good);
7997     }
7998 #endif /* DEBUG */
7999
8000     if (selected == 0) {                /* No files met selection criteria */
8001         status = 1;                     /* which is a kind of success. */
8002     } else if (status > 0) {            /* Some files were selected */
8003         if (cancelgroup)                /* but MGET was canceled */
8004           status = 0;                   /* so MGET failed */
8005         else if (cancelfile && good < 1) /* If file was canceled */
8006           status = 0;                   /* MGET failed if it got no files */
8007     }
8008     success = status;
8009     x = success;
8010     debug(F101,"ftp get success","",success);
8011
8012   xgetx:
8013     pipesend = pipesave;                /* Restore global pipe selection */
8014     if (fp_nml) {                       /* Close /NAMELIST */
8015         if (fp_nml != stdout)
8016           fclose(fp_nml);
8017         fp_nml = NULL;
8018     }
8019     if (
8020 #ifdef COMMENT
8021         x > -1
8022 #else
8023         success
8024 #endif  /* COMMENT */
8025         ) {                             /* Download successful */
8026 #ifdef GFTIMER
8027         t1 = gmstimer();                /* End time */
8028         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
8029         if (!sec) sec = 0.001;
8030         fptsecs = sec;
8031 #else
8032         sec = (t1 - t0) / 1000;
8033         if (!sec) sec = 1;
8034 #endif /* GFTIMER */
8035         tfcps = (long) (tfc / sec);
8036         tsecs = (int)sec;
8037         lastxfer = W_FTP|W_RECV;
8038         xferstat = success;
8039     }
8040     if (dpyactive)
8041       ftscreen(success > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
8042 #ifdef CK_TMPDIR
8043     if (f_tmpdir) {                     /* If we changed to download dir */
8044         zchdir((char *) savdir);        /* Go back where we came from */
8045         f_tmpdir = 0;
8046     }
8047 #endif /* CK_TMPDIR */
8048
8049     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
8050         if (pv[i].sval)
8051           free(pv[i].sval);
8052     }
8053     for (i = 0; i < mgetn; i++)         /* MGET list too */
8054       makestr(&(mgetlist[i]),NULL);
8055
8056     if (cancelgroup)                    /* Clear temp-file stack */
8057       mlsreset();
8058
8059     ftreset();                          /* Undo switch effects */
8060     dpyactive = 0;
8061     return(x);
8062 }
8063
8064 static struct keytab ftprmt[] = {
8065     { "cd",        XZCWD, 0 },
8066     { "cdup",      XZCDU, 0 },
8067     { "cwd",       XZCWD, CM_INV },
8068     { "delete",    XZDEL, 0 },
8069     { "directory", XZDIR, 0 },
8070     { "exit",      XZXIT, 0 },
8071     { "help",      XZHLP, 0 },
8072     { "login",     XZLGI, 0 },
8073     { "logout",    XZLGO, 0 },
8074     { "mkdir",     XZMKD, 0 },
8075     { "pwd",       XZPWD, 0 },
8076     { "rename",    XZREN, 0 },
8077     { "rmdir",     XZRMD, 0 },
8078     { "type",      XZTYP, 0 },
8079     { "", 0, 0 }
8080 };
8081 static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1;
8082
8083 int
8084 doftpsite() {                           /* Send a SITE command */
8085     int reply;
8086     char * s;
8087     int lcs = -1, rcs = -1;
8088     int save_vbm = ftp_vbm;
8089
8090 #ifndef NOCSETS
8091     if (ftp_xla) {
8092         lcs = ftp_csl;
8093         if (lcs < 0) lcs = fcharset;
8094         rcs = ftp_csx;
8095         if (rcs < 0) rcs = ftp_csr;
8096     }
8097 #endif /* NOCSETS */
8098     if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8099       return(x);
8100     CHECKCONN();
8101     ckstrncpy(line,s,LINBUFSIZ);
8102     if (testing) printf(" ftp site \"%s\"...\n",line);
8103     if (!ftp_vbm)
8104         ftp_vbm = !ckstrcmp("HELP",line,4,0);
8105     if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) {
8106         do {
8107             reply = getreply(0,lcs,rcs,ftp_vbm,0);
8108         } while (reply == REPLY_PRELIM);
8109     }
8110     ftp_vbm = save_vbm;
8111     return(success = (reply == REPLY_COMPLETE));
8112 }
8113
8114
8115 int
8116 dosetftppsv() {                         /* Passive mode */
8117     x = seton(&ftp_psv);
8118     if (x > 0) passivemode = ftp_psv;
8119     return(x);
8120 }
8121
8122 /*  d o f t p r m t  --  Parse and execute REMOTE commands  */
8123
8124 int
8125 doftprmt(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
8126     /* cx == 0 means REMOTE */
8127     /* cx != 0 is a XZxxx value */
8128     char * s;
8129
8130     if (who != 0)
8131       return(0);
8132
8133     if (cx == 0) {
8134         if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0)
8135           return(x);
8136         cx = x;
8137     }
8138     switch (cx) {
8139       case XZCDU:                       /* CDUP */
8140         if ((x = cmcfm()) < 0) return(x);
8141         return(doftpcdup());
8142
8143       case XZCWD:                       /* RCD */
8144         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8145           return(x);
8146         ckstrncpy(line,s,LINBUFSIZ);
8147         s = brstrip(line);
8148         return(doftpcwd(s,1));
8149       case XZPWD:                       /* RPWD */
8150         return(doftppwd());
8151       case XZDEL:                       /* RDEL */
8152         return(doftpget(FTP_MDE,1));
8153       case XZDIR:                       /* RDIR */
8154         return(doftpdir(FTP_DIR));
8155       case XZHLP:                       /* RHELP */
8156         return(doftpxhlp());
8157       case XZMKD:                       /* RMKDIR */
8158         return(doftpmkd());
8159       case XZREN:                       /* RRENAME */
8160         return(doftpren());
8161       case XZRMD:                       /* RRMDIR */
8162         return(doftprmd());
8163       case XZLGO:                       /* LOGOUT */
8164         return(doftpres());
8165       case XZXIT:                       /* EXIT */
8166         return(ftpbye());
8167     }
8168     printf("?Not usable with FTP - \"%s\"\n", atmbuf);
8169     return(-9);
8170 }
8171
8172 int
8173 doxftp() {                              /* Command parser for built-in FTP */
8174     int cx, n;
8175     struct FDB kw, fl;
8176     char * s;
8177     int usetls = 0;
8178     int lcs = -1, rcs = -1;
8179
8180 #ifndef NOCSETS
8181     if (ftp_xla) {
8182         lcs = ftp_csl;
8183         if (lcs < 0) lcs = fcharset;
8184         rcs = ftp_csx;
8185         if (rcs < 0) rcs = ftp_csr;
8186     }
8187 #endif /* NOCSETS */
8188
8189     if (inserver)                       /* FTP not allowed in IKSD. */
8190       return(-2);
8191
8192     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8193         ftp_typ = g_ftp_typ;
8194         /* g_ftp_typ = -1; */
8195     }
8196 #ifdef COMMENT
8197 /*
8198   We'll set the collision action locally in doftpget() based on whether
8199   ftp_fnc was ever set to a value.  if not, we'll use the fncact value.
8200 */
8201     if (ftp_fnc < 0)                    /* Inherit global collision action */
8202       ftp_fnc = fncact;                 /* if none specified for FTP */
8203 #endif /* COMMENT */
8204
8205     /* Restore global verbose mode */
8206     if (ftp_deb)
8207       ftp_vbm = 1;
8208     else if (quiet)
8209       ftp_vbm = 0;
8210     else
8211       ftp_vbm = ftp_vbx;
8212
8213     ftp_dates &= 1;                     /* Undo any previous /UPDATE switch */
8214
8215     dpyactive = 0;                      /* Reset global transfer-active flag */
8216     printlines = 0;                     /* Reset printlines */
8217
8218     if (fp_nml) {                       /* Reset /NAMELIST */
8219         if (fp_nml != stdout)
8220           fclose(fp_nml);
8221         fp_nml = NULL;
8222     }
8223     makestr(&ftp_nml,NULL);
8224
8225     cmfdbi(&kw,                         /* First FDB - commands */
8226            _CMKEY,                      /* fcode */
8227            "Hostname; or FTP command",  /* help */
8228            "",                          /* default */
8229            "",                          /* addtl string data */
8230            nftpcmd,                     /* addtl numeric data 1: tbl size */
8231            0,                           /* addtl numeric data 2: none */
8232            xxstring,                    /* Processing function */
8233            ftpcmdtab,                   /* Keyword table */
8234            &fl                          /* Pointer to next FDB */
8235            );
8236     cmfdbi(&fl,                         /* A host name or address */
8237            _CMFLD,                      /* fcode */
8238            "Hostname or address",       /* help */
8239            "",                          /* default */
8240            "",                          /* addtl string data */
8241            0,                           /* addtl numeric data 1 */
8242            0,                           /* addtl numeric data 2 */
8243            xxstring,
8244            NULL,
8245            NULL
8246            );
8247     x = cmfdb(&kw);                     /* Parse a hostname or a keyword */
8248     if (x == -3) {
8249         printf("?ftp what? \"help ftp\" for hints\n");
8250         return(-9);
8251     }
8252     if (x < 0)
8253       return(x);
8254     if (cmresult.fcode == _CMFLD) {     /* If hostname */
8255         return(openftp(cmresult.sresult,0)); /* go open the connection */
8256     } else {
8257         cx = cmresult.nresult;
8258     }
8259     switch (cx) {
8260       case FTP_ACC:                     /* ACCOUNT */
8261         if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
8262           return(x);
8263         CHECKCONN();
8264         makestr(&ftp_acc,s);
8265         if (testing)
8266           printf(" ftp account: \"%s\"\n",ftp_acc);
8267         success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
8268         return(success);
8269
8270       case FTP_GUP:                     /* Go UP */
8271         if ((x = cmcfm()) < 0) return(x);
8272         CHECKCONN();
8273         if (testing) printf(" ftp cd: \"(up)\"\n");
8274         return(success = doftpcdup());
8275
8276       case FTP_CWD:                     /* CD */
8277         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8278           return(x);
8279         CHECKCONN();
8280         ckstrncpy(line,s,LINBUFSIZ);
8281         if (testing)
8282           printf(" ftp cd: \"%s\"\n", line);
8283         return(success = doftpcwd(line,1));
8284
8285       case FTP_CHM:                     /* CHMOD */
8286         if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0)
8287           return(x);
8288         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8289         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8290           return(x);
8291         CHECKCONN();
8292         ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL);
8293         if (testing)
8294           printf(" ftp chmod: %s\n",ftpcmdbuf);
8295         success =
8296           (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8297         return(success);
8298
8299       case FTP_CLS:                     /* CLOSE FTP connection */
8300         if ((y = cmcfm()) < 0)
8301           return(y);
8302         CHECKCONN();
8303         if (testing)
8304           printf(" ftp closing...\n");
8305         ftpclose();
8306         return(success = 1);
8307
8308       case FTP_DIR:                     /* DIRECTORY of remote files */
8309       case FTP_VDI:
8310         return(doftpdir(cx));
8311
8312       case FTP_GET:                     /* GET a remote file */
8313       case FTP_RGE:                     /* REGET */
8314       case FTP_MGE:                     /* MGET */
8315       case FTP_MDE:                     /* MDELETE */
8316         return(doftpget(cx,1));
8317
8318       case FTP_IDL:                     /* IDLE */
8319         if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0)
8320           return(x);
8321         if ((y = cmcfm()) < 0)
8322           return(y);
8323         CHECKCONN();
8324         if (z < 0)  {                   /* Display idle timeout */
8325             if (testing)
8326               printf(" ftp query idle timeout...\n");
8327             success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE);
8328         } else {                        /* Set idle timeout */
8329             if (testing)
8330               printf(" ftp idle timeout set: %d...\n",z);
8331             success =
8332               (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE);
8333         }
8334         return(success);
8335
8336       case FTP_MKD:                     /* MKDIR */
8337         return(doftpmkd());
8338
8339       case FTP_MOD:                     /* MODTIME */
8340         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8341           return(x);
8342         CHECKCONN();
8343         ckstrncpy(line,s,LINBUFSIZ);
8344         if (testing)
8345           printf(" ftp modtime \"%s\"...\n",line);
8346         success = 0;
8347         if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) {
8348             success = 1;
8349             mdtmok = 1;
8350             if (!quiet) {
8351                 int flag = 0;
8352                 char c, * s;
8353                 struct tm tmremote;
8354
8355                 bzero((char *)&tmremote, sizeof(struct tm));
8356                 s = ftp_reply_str;
8357                 while ((c = *s++)) {
8358                     if (c == SP) {
8359                         flag++;
8360                         break;
8361                     }
8362                 }
8363                 if (flag) {
8364                     if (sscanf(s, "%04d%02d%02d%02d%02d%02d",
8365                                &tmremote.tm_year,
8366                                &tmremote.tm_mon,
8367                                &tmremote.tm_mday,
8368                                &tmremote.tm_hour,
8369                                &tmremote.tm_min,
8370                                &tmremote.tm_sec
8371                                ) == 6) {
8372                         printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n",
8373                                line,
8374                                tmremote.tm_year,
8375                                tmremote.tm_mon,
8376                                tmremote.tm_mday,
8377                                tmremote.tm_hour,
8378                                tmremote.tm_min,
8379                                tmremote.tm_sec
8380                                );
8381                     } else {
8382                         success = 0;
8383                     }
8384                 }
8385             }
8386         }
8387         return(success);
8388
8389       case FTP_OPN:                     /* OPEN connection */
8390 #ifdef COMMENT
8391         x = cmfld("IP hostname or address","",&s,xxstring);
8392         if (x < 0) {
8393             success = 0;
8394             return(x);
8395         }
8396         ckstrncpy(line,s,LINBUFSIZ);
8397         s = line;
8398         return(openftp(s,0));
8399 #else
8400         {                               /* OPEN connection */
8401             char name[TTNAMLEN+1], *p;
8402             extern int network;
8403             extern char ttname[];
8404             if (network)                /* If we have a current connection */
8405               ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */
8406             else
8407               *name = '\0';             /* as default host */
8408             for (p = name; *p; p++)     /* Remove ":service" from end. */
8409               if (*p == ':') { *p = '\0'; break; }
8410 #ifndef USETLSTAB
8411             x = cmfld("IP hostname or address",name,&s,xxstring);
8412 #else
8413             cmfdbi(&kw,                 /* First FDB - commands */
8414                    _CMKEY,              /* fcode */
8415                    "Hostname or switch", /* help */
8416                    "",                  /* default */
8417                    "",                  /* addtl string data */
8418                    ntlstab,             /* addtl numeric data 1: tbl size */
8419                    0,                   /* addtl numeric data 2: none */
8420                    xxstring,            /* Processing function */
8421                    tlstab,              /* Keyword table */
8422                    &fl                  /* Pointer to next FDB */
8423                    );
8424             cmfdbi(&fl,                 /* A host name or address */
8425                    _CMFLD,              /* fcode */
8426                    "Hostname or address", /* help */
8427                    "",                  /* default */
8428                    "",                  /* addtl string data */
8429                    0,                   /* addtl numeric data 1 */
8430                    0,                   /* addtl numeric data 2 */
8431                    xxstring,
8432                    NULL,
8433                    NULL
8434                    );
8435
8436             for (n = 0;; n++) {
8437                 x = cmfdb(&kw);         /* Parse a hostname or a keyword */
8438                 if (x == -3) {
8439                   printf("?ftp open what? \"help ftp\" for hints\n");
8440                   return(-9);
8441                 }
8442                 if (x < 0)
8443                   break;
8444                 if (cmresult.fcode == _CMFLD) { /* Hostname */
8445                     s = cmresult.sresult;
8446                     break;
8447                 } else if (cmresult.nresult == OPN_TLS) {
8448                     usetls = 1;
8449                 }
8450             }
8451 #endif /* USETLSTAB */
8452             if (x < 0) {
8453                 success = 0;
8454                 return(x);
8455             }
8456             ckstrncpy(line,s,LINBUFSIZ);
8457             s = line;
8458             return(openftp(s,usetls));
8459         }
8460 #endif /* COMMENT */
8461
8462       case FTP_PUT:                     /* PUT */
8463       case FTP_MPU:                     /* MPUT */
8464       case FTP_APP:                     /* APPEND */
8465       case FTP_REP:                     /* REPUT */
8466         return(doftpput(cx,1));
8467
8468       case FTP_PWD:                     /* PWD */
8469         x = doftppwd();
8470         if (x > -1) success = x;
8471         return(x);
8472
8473       case FTP_REN:                     /* RENAME */
8474         return(doftpren());
8475
8476       case FTP_RES:                     /* RESET */
8477         return(doftpres());
8478
8479       case FTP_HLP:                     /* (remote) HELP */
8480         return(doftpxhlp());
8481
8482       case FTP_RMD:                     /* RMDIR */
8483         return(doftprmd());
8484
8485       case FTP_STA:                     /* STATUS */
8486         if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8487           return(x);
8488         CHECKCONN();
8489         ckstrncpy(line,s,LINBUFSIZ);
8490         if (testing) printf(" ftp status \"%s\"...\n",line);
8491         success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE);
8492         return(success);
8493
8494       case FTP_SIT: {                   /* SITE */
8495           return(doftpsite());
8496       }
8497
8498       case FTP_SIZ:                     /* (ask for) SIZE */
8499         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8500           return(x);
8501         CHECKCONN();
8502         ckstrncpy(line,s,LINBUFSIZ);
8503         if (testing)
8504           printf(" ftp size \"%s\"...\n",line);
8505         success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE);
8506         if (success)
8507           sizeok = 1;
8508         return(success);
8509
8510       case FTP_SYS:                     /* Ask for server's SYSTEM type */
8511         if ((x = cmcfm()) < 0) return(x);
8512         CHECKCONN();
8513         if (testing)
8514           printf(" ftp system...\n");
8515         success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE);
8516         return(success);
8517
8518       case FTP_UMA:                     /* Set/query UMASK */
8519         if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0)
8520           if (x != -3)
8521             return(x);
8522         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8523         if ((x = cmcfm()) < 0) return(x);
8524         CHECKCONN();
8525         if (testing) {
8526             if (tmpbuf[0])
8527               printf(" ftp umask \"%s\"...\n",tmpbuf);
8528             else
8529               printf(" ftp query umask...\n");
8530         }
8531         success = ftp_umask(tmpbuf);
8532         return(success);
8533
8534       case FTP_USR:
8535         return(doftpusr());
8536
8537       case FTP_QUO:
8538         if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0)
8539           return(x);
8540         CHECKCONN();
8541         success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
8542         return(success);
8543
8544       case FTP_TYP:                     /* Type */
8545         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
8546           return(x);
8547         if ((y = cmcfm()) < 0) return(y);
8548         CHECKCONN();
8549         ftp_typ = x;
8550         g_ftp_typ = x;
8551         tenex = (ftp_typ == FTT_TEN);
8552         changetype(ftp_typ,ftp_vbm);
8553         return(1);
8554
8555       case FTP_CHK:                     /* Check if remote file(s) exist(s) */
8556         if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0)
8557           return(x);
8558         CHECKCONN();
8559         success = remote_files(1,(CHAR *)s,(CHAR *)s,0) ? 1 : 0;
8560         return(success);
8561
8562       case FTP_FEA:                     /* RFC2389 */
8563         if ((y = cmcfm()) < 0)
8564           return(y);
8565         CHECKCONN();
8566         success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE);
8567         if (success) {
8568             if (sfttab[0] > 0) {
8569                 ftp_aut = sfttab[SFT_AUTH];
8570                 sizeok  = sfttab[SFT_SIZE];
8571                 mdtmok  = sfttab[SFT_MDTM];
8572                 mlstok  = sfttab[SFT_MLST];
8573             }
8574         }
8575         return(success);
8576
8577       case FTP_OPT:                     /* RFC2389 */
8578         /* Perhaps this should be a keyword list... */
8579         if ((x = cmfld("FTP command","",&s,xxstring)) < 0)
8580           return(x);
8581         CHECKCONN();
8582         ckstrncpy(line,s,LINBUFSIZ);
8583         if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0)
8584           return(x);
8585         success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8586         return(success);
8587
8588       case FTP_ENA:                     /* FTP ENABLE */
8589       case FTP_DIS:                     /* FTP DISABLE */
8590         if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0)
8591           return(x);
8592         if ((y = cmcfm()) < 0) return(y);
8593         switch (x) {
8594           case ENA_AUTH:                /* OK to use autoauthentication */
8595             ftp_aut = (cx == FTP_ENA) ? 1 : 0;
8596             sfttab[SFT_AUTH] = ftp_aut;
8597             break;
8598           case ENA_FEAT:                /* OK to send FEAT command */
8599             featok = (cx == FTP_ENA) ? 1 : 0;
8600             break;
8601           case ENA_MLST:                /* OK to use MLST/MLSD */
8602             mlstok = (cx == FTP_ENA) ? 1 : 0;
8603             sfttab[SFT_MLST] = mlstok;
8604             break;
8605           case ENA_MDTM:                /* OK to use MDTM */
8606             mdtmok = (cx == FTP_ENA) ? 1 : 0;
8607             sfttab[SFT_MDTM] = mdtmok;
8608             break;
8609           case ENA_SIZE:                /* OK to use SIZE */
8610             sizeok = (cx == FTP_ENA) ? 1 : 0;
8611             sfttab[SFT_SIZE] = sizeok;
8612             break;
8613         }
8614         return(success = 1);
8615     }
8616     return(-2);
8617 }
8618
8619 #ifndef NOSHOW
8620 static char *
8621 shopl(x) int x; {
8622     switch (x) {
8623       case FPL_CLR: return("clear");
8624       case FPL_PRV: return("private");
8625       case FPL_SAF: return("safe");
8626       case 0:  return("(not set)");
8627       default: return("(unknown)");
8628     }
8629 }
8630
8631 int
8632 shoftp(brief) int brief; {
8633     char * s = "?";
8634     int n, x;
8635
8636     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8637         ftp_typ = g_ftp_typ;
8638         /* g_ftp_typ = -1; */
8639     }
8640     printf("\n");
8641     printf("FTP connection:                 %s\n",connected ?
8642            ftp_host :
8643            "(none)"
8644            );
8645     n = 2;
8646     if (connected) {
8647         n++;
8648         printf("FTP server type:                %s\n",
8649                ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)");
8650     }
8651     if (loggedin)
8652       printf("Logged in as:                   %s\n",
8653              strval(ftp_logname,"(unknown)"));
8654     else
8655       printf("Not logged in\n");
8656     n++;
8657     if (brief) return(0);
8658
8659     printf("\nSET FTP values:\n\n");
8660     n += 3;
8661
8662     printf(" ftp anonymous-password:        %s\n",
8663            ftp_apw ? ftp_apw : "(default)"
8664            );
8665     printf(" ftp auto-login:                %s\n",showoff(ftp_log));
8666     printf(" ftp auto-authentication:       %s\n",showoff(ftp_aut));
8667     switch (ftp_typ) {
8668       case FTT_ASC: s = "text"; break;
8669       case FTT_BIN: s = "binary"; break;
8670       case FTT_TEN: s = "tenex"; break;
8671     }
8672 #ifdef FTP_TIMEOUT
8673     printf(" ftp timeout:                   %ld\n",ftp_timeout);
8674 #endif  /* FTP_TIMEOUT */
8675     printf(" ftp type:                      %s\n",s);
8676     printf(" ftp get-filetype-switching:    %s\n",showoff(get_auto));
8677     printf(" ftp dates:                     %s\n",showoff(ftp_dates));
8678     printf(" ftp error-action:              %s\n",ftp_err ? "quit":"proceed");
8679     printf(" ftp filenames:                 %s\n",
8680            ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal")
8681            );
8682     printf(" ftp debug                      %s\n",showoff(ftp_deb));
8683
8684     printf(" ftp passive-mode:              %s\n",showoff(ftp_psv));
8685     printf(" ftp permissions:               %s\n",showooa(ftp_prm));
8686     printf(" ftp verbose-mode:              %s\n",showoff(ftp_vbx));
8687     printf(" ftp send-port-commands:        %s\n",showoff(ftp_psv));
8688     printf(" ftp unique-server-names:       %s\n",showoff(ftp_usn));
8689 #ifdef COMMENT
8690     /* See note in doxftp() */
8691     if (ftp_fnc < 0)
8692       ftp_fnc = fncact;
8693 #endif /* COMMENT */
8694     printf(" ftp collision:                 %s\n",
8695            fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]);
8696     printf(" ftp server-time-offset:        %s\n",
8697            fts_sto ? fts_sto : "(none)");
8698     n += 15;
8699
8700 #ifndef NOCSETS
8701     printf(" ftp character-set-translation: %s\n",showoff(ftp_xla));
8702     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8703
8704     printf(" ftp server-character-set:      %s\n",fcsinfo[ftp_csr].keyword);
8705     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8706
8707     printf(" file character-set:            %s\n",fcsinfo[fcharset].keyword);
8708     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8709 #endif /* NOCSETS */
8710
8711     x = ftp_dis;
8712     if (x < 0)
8713       x = fdispla;
8714     switch (x) {
8715       case XYFD_N: s = "none"; break;
8716       case XYFD_R: s = "serial"; break;
8717       case XYFD_C: s = "fullscreen"; break;
8718       case XYFD_S: s = "crt"; break;
8719       case XYFD_B: s = "brief"; break;
8720     }
8721     printf(" ftp display:                   %s\n",s);
8722     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8723
8724     if (mlstok || featok || mdtmok || sizeok || ftp_aut) {
8725         printf(" enabled:                      ");
8726         if (ftp_aut) printf(" AUTH");
8727         if (featok)  printf(" FEAT");
8728         if (mdtmok)  printf(" MDTM");
8729         if (mlstok)  printf(" MLST");
8730         if (sizeok)  printf(" SIZE");
8731         printf("\n");
8732         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8733     }
8734     if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) {
8735         printf(" disabled:                     ");
8736         if (!ftp_aut) printf(" AUTH");
8737         if (!featok)  printf(" FEAT");
8738         if (!mdtmok)  printf(" MDTM");
8739         if (!mlstok)  printf(" MLST");
8740         if (!sizeok)  printf(" SIZE");
8741         printf("\n");
8742         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8743     }
8744     switch (ftpget) {
8745       case 0: s = "kermit"; break;
8746       case 1: s = "ftp"; break;
8747       case 2: s = "auto"; break;
8748       default: s = "?";
8749     }
8750     printf(" get-put-remote:                %s\n",s);
8751     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8752
8753     printf("\n");
8754     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8755
8756 #ifdef FTP_SECURITY
8757     printf("Available security methods:    ");
8758 #ifdef FTP_GSSAPI
8759     printf("GSSAPI ");
8760 #endif /* FTP_GSSAPI */
8761 #ifdef FTP_KRB4
8762     printf("Kerberos4 ");
8763 #endif /* FTP_KRB4 */
8764 #ifdef FTP_SRP
8765     printf("SRP ");
8766 #endif /* FTP_SRP */
8767 #ifdef FTP_SSL
8768     printf("SSL ");
8769 #endif /* FTP_SSL */
8770
8771     n++;
8772     printf("\n\n");
8773     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8774     printf(" ftp authtype:                  %s\n",strval(auth_type,NULL));
8775     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8776     printf(" ftp auto-encryption:           %s\n",showoff(ftp_cry));
8777     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8778     printf(" ftp credential-forwarding:     %s\n",showoff(ftp_cfw));
8779     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8780     printf(" ftp command-protection-level:  %s\n",shopl(ftp_cpl));
8781     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8782     printf(" ftp data-protection-level:     %s\n",shopl(ftp_dpl));
8783     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8784     printf(" ftp secure proxy:              %s\n",shopl(ssl_ftp_proxy));
8785     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8786 #else
8787     printf("Available security methods:     (none)\n");
8788     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8789 #endif /* FTP_SECURITY */
8790
8791     if (n <= cmd_rows - 3)
8792       printf("\n");
8793     return(0);
8794 }
8795 #endif /* NOSHOW */
8796
8797 #ifndef NOHELP
8798 /* FTP HELP text strings */
8799
8800 static char * fhs_ftp[] = {
8801     "Syntax: FTP subcommand [ operands ]",
8802     "  Makes an FTP connection, or sends a command to the FTP server.",
8803     "  To see a list of available FTP subcommands, type \"ftp ?\".",
8804     "  and then use HELP FTP xxx to get help about subcommand xxx.",
8805     "  Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.",
8806     ""
8807 };
8808
8809 static char * fhs_acc[] = {             /* ACCOUNT */
8810     "Syntax: FTP ACCOUNT text",
8811     "  Sends an account designator to an FTP server that needs one.",
8812     "  Most FTP servers do not use accounts; some use them for other",
8813     "  other purposes, such as disk-access passwords.",
8814     ""
8815 };
8816 static char * fhs_app[] = {             /* APPEND */
8817     "Syntax: FTP APPEND filname",
8818     "  Equivalent to [ FTP ] PUT /APPEND.  See HELP FTP PUT.",
8819     ""
8820 };
8821 static char * fhs_cls[] = {             /* BYE, CLOSE */
8822     "Syntax: [ FTP ] BYE",
8823     "  Logs out from the FTP server and closes the FTP connection.",
8824     "  Also see HELP SET GET-PUT-REMOTE.  Synonym: [ FTP ] CLOSE.",
8825     ""
8826 };
8827 static char * fhs_cwd[] = {             /* CD, CWD */
8828     "Syntax: [ FTP ] CD directory",
8829     "  Asks the FTP server to change to the given directory.",
8830     "  Also see HELP SET GET-PUT-REMOTE.  Synonyms: [ FTP ] CWD, RCD, RCWD.",
8831     ""
8832 };
8833 static char * fhs_gup[] = {             /* CDUP, UP */
8834     "Syntax: FTP CDUP",
8835     "  Asks the FTP server to change to the parent directory of its current",
8836     "  directory.  Also see HELP SET GET-PUT-REMOTE.  Synonym: FTP UP.",
8837     ""
8838 };
8839 static char * fhs_chm[] = {             /* CHMOD */
8840     "Syntax: FTP CHMOD filename permissions",
8841     "  Asks the FTP server to change the permissions, protection, or mode of",
8842     "  the given file.  The given permissions must be in the syntax of the",
8843     "  the server's file system, e.g. an octal number for UNIX.  Also see",
8844     "  FTP PUT /PERMISSIONS",
8845     ""
8846 };
8847 static char * fhs_mde[] = {             /* DELETE */
8848     "Syntax: FTP DELETE [ switches ] filespec",
8849     "  Asks the FTP server to delete the given file or files.",
8850     "  Synonym: MDELETE (Kermit makes no distinction between single and",
8851     "  multiple file deletion).  Optional switches:",
8852     " ",
8853     "  /ERROR-ACTION:{PROCEED,QUIT}",
8854     "  /EXCEPT:pattern",
8855     "  /FILENAMES:{AUTO,CONVERTED,LITERAL}",
8856     "  /LARGER-THAN:number",
8857 #ifdef UNIXOROSK
8858     "  /NODOTFILES",
8859 #endif /* UNIXOROSK */
8860     "  /QUIET",
8861 #ifdef RECURSIVE
8862     "  /RECURSIVE (depends on server)",
8863     "  /SUBDIRECTORIES",
8864 #endif /* RECURSIVE */
8865     "  /SMALLER-THAN:number",
8866     ""
8867 };
8868 static char * fhs_dir[] = {             /* DIRECTORY */
8869     "Syntax: FTP DIRECTORY [ filespec ]",
8870     "  Asks the server to send a directory listing of the files that match",
8871     "  the given filespec, or if none is given, all the files in its current",
8872     "  directory.  The filespec, including any wildcards, must be in the",
8873     "  syntax of the server's file system.  Also see HELP SET GET-PUT-REMOTE.",
8874     "  Synonym: RDIRECTORY.",
8875     ""
8876 };
8877 static char * fhs_vdi[] = {             /* VDIRECTORY */
8878     "Syntax: FTP VDIRECTORY [ filespec ]",
8879     "  Asks the server to send a directory listing of the files that match",
8880     "  the given filespec, or if none is given, all the files in its current",
8881     "  directory.  VDIRECTORY is needed for getting verbose directory",
8882     "  listings from certain FTP servers, such as on TOPS-20.  Try it if",
8883     "  FTP DIRECTORY lists only filenames without details.",
8884     ""
8885 };
8886 static char * fhs_fea[] = {             /* FEATURES */
8887     "Syntax: FTP FEATURES",
8888     "  Asks the FTP server to list its special features.  Most FTP servers",
8889     "  do not recognize this command.",
8890     ""
8891 };
8892 static char * fhs_mge[] = {             /* MGET */
8893     "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]",
8894     "  Download a single file or multiple files.  Asks the FTP server to send",
8895     "  the given file or files.  Also see FTP GET.  Optional switches:",
8896     " ",
8897     "  /AS-NAME:text",
8898     "    Name under which to store incoming file.",
8899     "    Pattern required for for multiple files.",
8900     "  /BINARY",                        /* /IMAGE */
8901     "    Force binary mode.  Synonym: /IMAGE.",
8902     "  /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}",
8903    "    What to do if an incoming file has the same name as an existing file.",
8904
8905 #ifdef PUTPIPE
8906     "  /COMMAND",
8907     "    Specifies that the as-name is a command to which the incoming file",
8908     "    is to be piped as standard input.",
8909 #endif /* PUTPIPE */
8910
8911 #ifdef DOUPDATE
8912     "  /DATES-DIFFER",
8913     "    Download only those files whose modification date-times differ from",
8914     "    those of the corresponding local files, or that do not already",
8915     "    exist on the local computer.",
8916 #endif /* DOUPDATE */
8917
8918     "  /DELETE",
8919     "    Specifies that each file is to be deleted from the server after,",
8920     "    and only if, it is successfully downloaded.",
8921     "  /ERROR-ACTION:{PROCEED,QUIT}",
8922     "    When downloading a group of files, what to do upon failure to",
8923     "    transfer a file: quit or proceed to the next one.",
8924     "  /EXCEPT:pattern",
8925     "    Exception list: don't download any files that match this pattern.",
8926     "    See HELP WILDCARD for pattern syntax.",
8927     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
8928     "    Whether to convert incoming filenames to local syntax.",
8929 #ifdef PIPESEND
8930 #ifndef NOSPL
8931     "  /FILTER:command",
8932     "    Pass incoming files through the given command.",
8933 #endif /* NOSPL */
8934 #endif /* PIPESEND */
8935     "  /LARGER-THAN:number",
8936     "    Only download files that are larger than the given number of bytes.",
8937     "  /LISTFILE:filename",
8938     "    Obtain the list of files to download from the given file.",
8939 #ifndef NOCSETS
8940     "  /LOCAL-CHARACTER-SET:name",
8941     "    When downloading in text mode and character-set conversion is",
8942     "    desired, this specifies the target set.",
8943 #endif /* NOCSETS */
8944     "  /MATCH:pattern",
8945     "    Specifies a pattern to be used to select filenames locally from the",
8946     "    server's list.",
8947     "  /MLSD",
8948     "    Forces sending of MLSD (rather than NLST) to get the file list.",
8949 #ifdef CK_TMPDIR
8950     "  /MOVE-TO:directory",
8951     "    Each file that is downloaded is to be moved to the given local",
8952     "    directory immediately after, and only if, it has been received",
8953     "    successfully.",
8954 #endif /* CK_TMPDIR */
8955     "  /NAMELIST:filename",
8956     "    Instead of downloading the files, stores the list of files that",
8957     "    would be downloaded in the given local file, one filename per line.",
8958     "  /NLST",
8959     "    Forces sending of NLST (rather than MLSD) to get the file list.",
8960     "  /NOBACKUPFILES",
8961     "    Don't download any files whose names end with .~<number>~.",
8962     "  /NODOTFILES",
8963     "    Don't download any files whose names begin with period (.).",
8964     "  /QUIET",
8965     "    Suppress the file-transfer display.",
8966 #ifdef FTP_RESTART
8967     "  /RECOVER",                       /* /RESTART */
8968     "    Resume a download that was previously interrupted from the point of",
8969     "    failure.  Works only in binary mode.  Not supported by all servers.",
8970     "    Synonym: /RESTART.",
8971 #endif /* FTP_RESTART */
8972 #ifdef RECURSIVE
8973     "  /RECURSIVE",                     /* /SUBDIRECTORIES */
8974     "    Create subdirectories automatically if the server sends files",
8975     "    recursively and includes pathnames (most don't).",
8976 #endif /* RECURSIVE */
8977     "  /RENAME-TO:text",
8978     "    Each file that is downloaded is to be renamed as indicated just,",
8979     "    after, and only if, it has arrived successfully.",
8980 #ifndef NOCSETS
8981     "  /SERVER-CHARACTER-SET:name",
8982     "    When downloading in text mode and character-set conversion is desired"
8983 ,   "    this specifies the original file's character set on the server.",
8984 #endif /* NOCSETS */
8985     "  /SERVER-RENAME:text",
8986     "    Each server source file is to be renamed on the server as indicated",
8987     "    immediately after, but only if, it has arrived successfully.",
8988     "  /SMALLER-THAN:number",
8989     "    Download only those files smaller than the given number of bytes.",
8990     "  /TEXT",                          /* /ASCII */
8991     "    Force text mode.  Synonym: /ASCII.",
8992     "  /TENEX",
8993     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
8994 #ifndef NOCSETS
8995     "  /TRANSPARENT",
8996     "    When downloading in text mode, do not convert chracter-sets.",
8997 #endif /* NOCSETS */
8998     "  /TO-SCREEN",
8999     "    The downloaded file is to be displayed on the screen.",
9000 #ifdef DOUPDATE
9001     "  /UPDATE",
9002     "    Equivalent to /COLLISION:UPDATE.  Download only those files that are",
9003     "    newer than than their local counterparts, or that do not exist on",
9004     "    the local computer.",
9005 #endif /* DOUPDATE */
9006     ""
9007 };
9008 static char * fhs_hlp[] = {             /* HELP */
9009     "Syntax: FTP HELP [ command [ subcommand... ] ]",
9010     "  Asks the FTP server for help about the given command.  First use",
9011     "  FTP HELP by itself to get a list of commands, then use HELP FTP xxx",
9012     "  to get help for command \"xxx\".  Synonyms: REMOTE HELP, RHELP.",
9013     ""
9014 };
9015 static char * fhs_idl[] = {             /* IDLE */
9016     "Syntax: FTP IDLE [ number ]",
9017     "  If given without a number, this asks the FTP server to tell its",
9018     "  current idle-time limit.  If given with a number, it asks the server",
9019     "  to change its idle-time limit to the given number of seconds.",
9020     ""
9021 };
9022 static char * fhs_usr[] = {             /* USER, LOGIN */
9023     "Syntax: FTP USER username [ password [ account ] ]",
9024     "  Log in to the FTP server.  To be used when connected but not yet",
9025     "  logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.",
9026     "  If you omit the password, and one is required by the server, you are",
9027     "  prompted for it.  If you omit the account, no account is sent.",
9028     "  Synonym: FTP LOGIN.",
9029     ""
9030 };
9031 static char * fhs_get[] = {             /* GET */
9032     "Syntax: [ FTP ] GET [ options ] filename [ as-name ]",
9033     "  Download a single file.  Asks the FTP server to send the given file.",
9034     "  The optional as-name is the name to store it under when it arrives;",
9035     "  if omitted, the file is stored with the name it arrived with, as",
9036     "  modified according to the FTP FILENAMES setting or /FILENAMES: switch",
9037     "  value.  Aside from the file list and as-name, syntax and options are",
9038     "  the same as for FTP MGET, which is used for downloading multiple files."
9039 ,   ""
9040 };
9041 static char * fhs_mkd[] = {             /* MKDIR */
9042     "Syntax: FTP MKDIR directory",
9043     "  Asks the FTP server to create a directory with the given name,",
9044     "  which must be in the syntax of the server's file system.  Synonyms:",
9045     "  REMOTE MKDIR, RMKDIR.",
9046     ""
9047 };
9048 static char * fhs_mod[] = {             /* MODTIME */
9049     "Syntax: FTP MODTIME filename",
9050     "  Asks the FTP server to send the modification time of the given file,",
9051     "  to be displayed on the screen.  The date-time format is all numeric:",
9052     "  yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating",
9053     "  fractions of seconds).",
9054     ""
9055 };
9056 static char * fhs_mpu[] = {             /* MPUT */
9057     "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]",
9058     "  Uploads files.  Sends the given file or files to the FTP server.",
9059     "  Also see FTP PUT.  Optional switches are:",
9060     " ",
9061     "  /AFTER:date-time",
9062     "    Uploads only those files newer than the given date-time.",
9063     "    HELP DATE for info about date-time formats.  Synonym: /SINCE.",
9064 #ifdef PUTARRAY
9065     "  /ARRAY:array-designator",
9066     "    Tells Kermit to upload the contents of the given array, rather than",
9067     "    a file.",
9068 #endif /* PUTARRAY */
9069     "  /AS-NAME:text",
9070     "    Name under which to send files.",
9071     "    Pattern required for for multiple files.",
9072     "  /BEFORE:date-time",
9073     "    Upload only those files older than the given date-time.",
9074     "  /BINARY",
9075     "    Force binary mode.  Synonym: /IMAGE.",
9076 #ifdef PUTPIPE
9077     "  /COMMAND",
9078     "    Specifies that the filespec is a command whose standard output is",
9079     "    to be sent.",
9080 #endif /* PUTPIPE */
9081
9082 #ifdef COMMENT
9083 #ifdef DOUPDATE
9084     "  /DATES-DIFFER",
9085     "    Upload only those files whose modification date-times differ from",
9086     "    those on the server, or that don't exist on the server at all.",
9087 #endif /* DOUPDATE */
9088 #endif /* COMMENT */
9089
9090     "  /DELETE",
9091     "    Specifies that each source file is to be deleted after, and only if,",
9092     "    it is successfully uploaded.",
9093     "  /DOTFILES",
9094     "    Include files whose names begin with period (.).",
9095     "  /ERROR-ACTION:{PROCEED,QUIT}",
9096     "    When uploading a group of files, what to do upon failure to",
9097     "    transfer a file: quit or proceed to the next one.",
9098     "  /EXCEPT:pattern",
9099     "    Exception list: don't upload any files that match this pattern.",
9100     "    See HELP WILDCARD for pattern syntax.",
9101     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
9102     "    Whether to convert outbound filenames to common syntax.",
9103 #ifdef PIPESEND
9104 #ifndef NOSPL
9105     "  /FILTER:command",
9106     "    Pass outbound files through the given command.",
9107 #endif /* NOSPL */
9108 #endif /* PIPESEND */
9109 #ifdef CKSYMLINK
9110     "  /FOLLOWINKS",
9111     "    Send files that are pointed to by symbolic links.",
9112     "  /NOFOLLOWINKS",
9113     "    Skip over symbolic links (default).",
9114 #endif /* CKSYMLINK */
9115     "  /LARGER-THAN:number",
9116     "    Only upload files that are larger than the given number of bytes.",
9117     "  /LISTFILE:filename",
9118     "    Obtain the list of files to upload from the given file.",
9119 #ifndef NOCSETS
9120     "  /LOCAL-CHARACTER-SET:name",
9121     "    When uploading in text mode and character-set conversion is",
9122     "    desired, this specifies the source-file character set.",
9123 #endif /* NOCSETS */
9124 #ifdef CK_TMPDIR
9125     "  /MOVE-TO:directory",
9126     "    Each source file that is uploaded is to be moved to the given local",
9127     "    directory when, and only if, the transfer is successful.",
9128 #endif /* CK_TMPDIR */
9129     "  /NOBACKUPFILES",
9130     "    Don't upload any files whose names end with .~<number>~.",
9131 #ifdef UNIXOROSK
9132     "  /NODOTFILES",
9133     "    Don't upload any files whose names begin with period (.).",
9134 #endif /* UNIXOROSK */
9135     "  /NOT-AFTER:date-time",
9136     "    Upload only files that are not newer than the given date-time",
9137     "  /NOT-BEFORE:date-time",
9138     "    Upload only files that are not older than the given date-time",
9139 #ifdef UNIX
9140     "  /PERMISSIONS",
9141     "    Ask the server to set the permissions of each file it receives",
9142     "    according to the source file's permissions.",
9143 #endif /* UNIX */
9144     "  /QUIET",
9145     "    Suppress the file-transfer display.",
9146 #ifdef FTP_RESTART
9147     "  /RECOVER",
9148     "    Resume an upload that was previously interrupted from the point of",
9149     "    failure.  Synonym: /RESTART.",
9150 #endif /* FTP_RESTART */
9151 #ifdef RECURSIVE
9152     "  /RECURSIVE",
9153     "    Send files from the given directory and all the directories beneath",
9154     "    it.  Synonym: /SUBDIRECTORIES.",
9155 #endif /* RECURSIVE */
9156     "  /RENAME-TO:text",
9157     "    Each source file that is uploaded is to be renamed on the local",
9158     "    local computer as indicated when and only if, the transfer completes",
9159     "    successfully.",
9160 #ifndef NOCSETS
9161     "  /SERVER-CHARACTER-SET:name",
9162     "    When uploading in text mode and character-set conversion is desired,",
9163     "    this specifies the character set to which the file should be",
9164     "    converted for storage on the server.",
9165 #endif /* NOCSETS */
9166     "  /SERVER-RENAME:text",
9167     "    Each file that is uploaded is to be renamed as indicated on the",
9168     "    server after, and only if, if arrives successfully.",
9169     "  /SIMULATE",
9170     "    Show which files would be sent without actually sending them.",
9171     "  /SMALLER-THAN:number",
9172     "    Upload only those files smaller than the given number of bytes.",
9173     "  /TEXT",
9174     "    Force text mode.  Synonym: /ASCII.",
9175     "  /TENEX",
9176     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
9177 #ifndef NOCSETS
9178     "  /TRANSPARENT",
9179     "    When uploading in text mode, do not convert chracter-sets.",
9180 #endif /* NOCSETS */
9181     "  /TYPE:{TEXT,BINARY}",
9182     "    Upload only files of the given type.",
9183 #ifdef DOUPDATE
9184     "  /UPDATE",
9185     "    If a file of the same name exists on the server, upload only if",
9186     "    the local file is newer.",
9187 #endif /* DOUPDATE */
9188     "  /UNIQUE-SERVER-NAMES",
9189     "    Ask the server to compute new names for any incoming file that has",
9190     "    the same name as an existing file.",
9191     ""
9192 };
9193 static char * fhs_opn[] = {             /* OPEN */
9194 #ifdef CK_SSL
9195     "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]",
9196     "  Opens a connection to the FTP server on the given host.  The default",
9197     "  TCP port is 21 (990 if SSL/TLS is used), but a different port number",
9198     "  can be supplied if necessary.  Optional switches are:",
9199 #else /* CK_SSL */
9200     "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]",
9201     "  Opens a connection to the FTP server on the given host.  The default",
9202     "  TCP port is 21, but a different port number can be supplied if",
9203     "  necessary.  Optional switches are:",
9204 #endif /* CK_SSL */
9205     " ",
9206     "  /ANONYMOUS",
9207     "    Logs you in anonymously.",
9208     "  /USER:text",
9209     "    Supplies the given text as your username.",
9210     "  /PASSWORD:text",
9211     "    Supplies the given text as your password.  If you include a username",
9212     "    but omit this switch and the server requires a password, you are",
9213     "    prompted for it.",
9214     "  /ACCOUNT:text",
9215     "    Supplies the given text as your account, if required by the server.",
9216     "  /ACTIVE",
9217     "    Forces an active (rather than passive) connection.",
9218     "  /PASSIVE",
9219     "    Forces a passive (rather than active) connection.",
9220     "  /NOINIT",
9221     "    Inhibits sending initial REST, STRU, and MODE commands, which are",
9222     "    well-known standard commands, but to which some servers react badly.",
9223     "  /NOLOGIN",
9224     "    Inhibits autologin for this connection only.",
9225     ""
9226 };
9227 static char * fhs_opt[] = {             /* OPTS, OPTIONS */
9228     "Syntax: FTP OPTIONS",
9229     "  Asks the FTP server to list its current options.  Advanced, new,",
9230     "  not supported by most FTP servers.",
9231     ""
9232 };
9233 static char * fhs_put[] = {             /* PUT, SEND */
9234     "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]",
9235     "  Like FTP MPUT, but only one filespec is allowed, and if it is followed",
9236     "  by an additional field, this is interpreted as the name under which",
9237     "  to send the file or files.  See HELP FTP MPUT.",
9238     ""
9239 };
9240 static char * fhs_reput[] = {           /* REPUT, RESEND */
9241     "Syntax: [ FTP ] REPUT [ switches ] filespec [ as-name ]",
9242     "  Synonym for FTP PUT /RECOVER.  Recovers an interrupted binary-mode",
9243     "  upload from the point of failure if the FTP server supports recovery.",
9244     "  Synonym: [ FTP ] RESEND.  For details see HELP FTP MPUT.",
9245     ""
9246 };
9247 static char * fhs_pwd[] = {             /* PWD */
9248     "Syntax: FTP PWD",
9249     "  Asks the FTP server to reveal its current working directory.",
9250     "  Synonyms: REMOTE PWD, RPWD.",
9251     ""
9252 };
9253 static char * fhs_quo[] = {             /* QUOTE */
9254     "Syntax: FTP QUOTE text",
9255     "  Sends an FTP protocol command to the FTP server.  Use this command",
9256     "  for sending commands that Kermit might not support.",
9257     ""
9258 };
9259 static char * fhs_rge[] = {             /* REGET */
9260     "Syntax: FTP REGET",
9261     "  Synonym for FTP GET /RECOVER.",
9262     ""
9263 };
9264 static char * fhs_ren[] = {             /* RENAME */
9265     "Syntax: FTP RENAME name1 name1",
9266     "  Asks the FTP server to change the name of the file whose name is name1",
9267     "  and which resides in the FTP server's file system, to name2.  Works",
9268     "  only for single files; wildcards are not accepted.",
9269     ""
9270 };
9271 static char * fhs_res[] = {             /* RESET */
9272     "Syntax: FTP RESET",
9273     "  Asks the server to log out your session, terminating your access",
9274     "  rights, without closing the connection.",
9275     ""
9276 };
9277 static char * fhs_rmd[] = {             /* RMDIR */
9278     "Syntax: FTP RMDIR directory",
9279     "  Asks the FTP server to remove the directory whose name is given.",
9280     "  This usually requires the directory to be empty.  Synonyms: REMOTE",
9281     "  RMDIR, RRMDIR.",
9282     ""
9283 };
9284 static char * fhs_sit[] = {             /* SITE */
9285     "Syntax: FTP SITE text",
9286     "  Sends a site-specific command to the FTP server.",
9287     ""
9288 };
9289 static char * fhs_siz[] = {             /* SIZE */
9290     "Syntax: FTP SIZE filename",
9291     "  Asks the FTP server to send a numeric string representing the size",
9292     "  of the given file.",
9293     ""
9294 };
9295 static char * fhs_sta[] = {             /* STATUS */
9296     "Syntax: FTP STATUS [ filename ]",
9297     "  Asks the FTP server to report its status.  If a filename is given,",
9298     "  the FTP server should report details about the file.",
9299     ""
9300 };
9301 static char * fhs_sys[] = {             /* SYSTEM */
9302     "Syntax: FTP SYSTEM",
9303     "  Asks the FTP server to report its operating system type.",
9304     ""
9305 };
9306 static char * fhs_typ[] = {             /* TYPE */
9307     "Syntax: FTP TYPE { TEXT, BINARY, TENEX }",
9308     "  Puts the client and server in the indicated transfer mode.",
9309     "  ASCII is a synonym for TEXT.  TENEX is used only for uploading 8-bit",
9310     "  binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or",
9311     "  downloading files from TENEX or TOPS-20 that have been uploaded in",
9312     "  TENEX mode.",
9313     ""
9314 };
9315 static char * fhs_uma[] = {             /* UMASK */
9316     "Syntax: FTP UMASK number",
9317     "  Asks the FTP server to set its file creation mode mask.  Applies",
9318     "  only (or mainly) to UNIX-based FTP servers.",
9319     ""
9320 };
9321 static char * fhs_chk[] = {             /* CHECK */
9322     "Syntax: FTP CHECK remote-filespec",
9323     "  Asks the FTP server if the given file or files exist.  If the",
9324     "  remote-filespec contains wildcards, this command fails if no server",
9325     "  files match, and succeeds if at least one file matches.  If the",
9326     "  remote-filespec does not contain wildcards, this command succeeds if",
9327     "  the given file exists and fails if it does not.",
9328     ""
9329 };
9330 static char * fhs_ena[] = {             /* ENABLE */
9331     "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9332     "  Enables the use of the given FTP protocol command in case it has been",
9333     "  disabled (but this is no guarantee that the FTP server understands it)."
9334 ,
9335     "  Use SHOW FTP to see which of these commands is enabled and disabled.",
9336     "  Also see FTP DISABLE.",
9337     ""
9338 };
9339 static char * fhs_dis[] = {             /* DISABLE */
9340     "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9341     "  Disables the use of the given FTP protocol command.",
9342     "  Also see FTP ENABLE.",
9343     ""
9344 };
9345
9346 #endif /* NOHELP */
9347
9348 int
9349 doftphlp() {
9350     int cx;
9351     if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0)
9352       if (cx != -3)
9353         return(cx);
9354     if ((x = cmcfm()) < 0)
9355       return(x);
9356
9357 #ifdef NOHELP
9358     printf("Sorry, no help available\n");
9359 #else
9360     switch (cx) {
9361       case -3:
9362         return(hmsga(fhs_ftp));
9363       case FTP_ACC:                     /* ACCOUNT */
9364         return(hmsga(fhs_acc));
9365       case FTP_APP:                     /* APPEND */
9366         return(hmsga(fhs_app));
9367       case FTP_CLS:                     /* BYE, CLOSE */
9368         return(hmsga(fhs_cls));
9369       case FTP_CWD:                     /* CD, CWD */
9370         return(hmsga(fhs_cwd));
9371       case FTP_GUP:                     /* CDUP, UP */
9372         return(hmsga(fhs_gup));
9373       case FTP_CHM:                     /* CHMOD */
9374         return(hmsga(fhs_chm));
9375       case FTP_MDE:                     /* DELETE, MDELETE */
9376         return(hmsga(fhs_mde));
9377       case FTP_DIR:                     /* DIRECTORY */
9378         return(hmsga(fhs_dir));
9379       case FTP_VDI:                     /* VDIRECTORY */
9380         return(hmsga(fhs_vdi));
9381       case FTP_FEA:                     /* FEATURES */
9382         return(hmsga(fhs_fea));
9383       case FTP_GET:                     /* GET */
9384         return(hmsga(fhs_get));
9385       case FTP_HLP:                     /* HELP */
9386         return(hmsga(fhs_hlp));
9387       case FTP_IDL:                     /* IDLE */
9388         return(hmsga(fhs_idl));
9389       case FTP_USR:                     /* USER, LOGIN */
9390         return(hmsga(fhs_usr));
9391       case FTP_MGE:                     /* MGET */
9392         return(hmsga(fhs_mge));
9393       case FTP_MKD:                     /* MKDIR */
9394         return(hmsga(fhs_mkd));
9395       case FTP_MOD:                     /* MODTIME */
9396         return(hmsga(fhs_mod));
9397       case FTP_MPU:                     /* MPUT */
9398         return(hmsga(fhs_mpu));
9399       case FTP_OPN:                     /* OPEN */
9400         return(hmsga(fhs_opn));
9401       case FTP_OPT:                     /* OPTS, OPTIONS */
9402         return(hmsga(fhs_opt));
9403       case FTP_PUT:                     /* PUT, SEND */
9404         return(hmsga(fhs_put));
9405       case FTP_REP:                     /* REPUT, RESEND */
9406         return(hmsga(fhs_reput));
9407       case FTP_PWD:                     /* PWD */
9408         return(hmsga(fhs_pwd));
9409       case FTP_QUO:                     /* QUOTE */
9410         return(hmsga(fhs_quo));
9411       case FTP_RGE:                     /* REGET */
9412         return(hmsga(fhs_rge));
9413       case FTP_REN:                     /* RENAME */
9414         return(hmsga(fhs_ren));
9415       case FTP_RES:                     /* RESET */
9416         return(hmsga(fhs_res));
9417       case FTP_RMD:                     /* RMDIR */
9418         return(hmsga(fhs_rmd));
9419       case FTP_SIT:                     /* SITE */
9420         return(hmsga(fhs_sit));
9421       case FTP_SIZ:                     /* SIZE */
9422         return(hmsga(fhs_siz));
9423       case FTP_STA:                     /* STATUS */
9424         return(hmsga(fhs_sta));
9425       case FTP_SYS:                     /* SYSTEM */
9426         return(hmsga(fhs_sys));
9427       case FTP_TYP:                     /* TYPE */
9428         return(hmsga(fhs_typ));
9429       case FTP_UMA:                     /* UMASK */
9430         return(hmsga(fhs_uma));
9431       case FTP_CHK:                     /* CHECK */
9432         return(hmsga(fhs_chk));
9433       case FTP_ENA:
9434         return(hmsga(fhs_ena));
9435       case FTP_DIS:
9436         return(hmsga(fhs_dis));
9437       default:
9438         printf("Sorry, help available for this command.\n");
9439         break;
9440     }
9441 #endif /* NOHELP */
9442     return(success = 0);
9443 }
9444
9445 int
9446 dosetftphlp() {
9447     int cx;
9448     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0)
9449       if (cx != -3)
9450         return(cx);
9451     if (cx != -3)
9452       ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ);
9453     if ((x = cmcfm()) < 0)
9454       return(x);
9455
9456 #ifdef NOHELP
9457     printf("Sorry, no help available\n");
9458 #else
9459     switch (cx) {
9460       case -3:
9461         printf("\nSyntax: SET FTP parameter value\n");
9462         printf("  Type \"help set ftp ?\" for a list of parameters.\n");
9463         printf("  Type \"help set ftp xxx\" for information about setting\n");
9464         printf("  parameter xxx.  Type \"show ftp\" for current values.\n\n");
9465         return(0);
9466
9467       case FTS_BUG:
9468         printf("\nSyntax: SET FTP BUG <name> {ON, OFF}\n");
9469         printf(
9470             "  Activates a workaround for the named bug in the FTP server.\n");
9471         printf("  Type SET FTP BUG ? for a list of names.\n");
9472         printf("  For each bug, the default is OFF\n\n");
9473         return(0);
9474
9475 #ifdef FTP_SECURITY
9476       case FTS_ATP:                     /* "authtype" */
9477         printf("\nSyntax: SET FTP AUTHTYPE list\n");
9478         printf("  Specifies an ordered list of authentication methods to be\n"
9479                );
9480         printf("  when FTP AUTOAUTHENTICATION is ON.  The default list is:\n");
9481         printf("  GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n");
9482         return(0);
9483
9484       case FTS_AUT:                     /* "autoauthentication" */
9485         printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n");
9486         printf("  Tells whether authentication should be negotiated by the\n");
9487         printf("  FTP OPEN command.  Default is ON.\n\n");
9488         break;
9489
9490       case FTS_CRY:                     /* "autoencryption" */
9491         printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n");
9492         printf("  Tells whether encryption (privacy) should be negotiated\n");
9493         printf("  by the FTP OPEN command.  Default is ON.\n\n");
9494         break;
9495 #endif /* FTP_SECURITY */
9496
9497       case FTS_LOG:                     /* "autologin" */
9498         printf("\nSET FTP AUTOLOGIN { ON, OFF }\n");
9499         printf("  Tells Kermit whether to try to log you in automatically\n");
9500         printf("  as part of the connection process.\n\n");
9501         break;
9502
9503       case FTS_DIS:
9504         printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n");
9505         printf("  Chooses the file-transfer display style for FTP.\n");
9506         printf("  Like SET TRANSFER DISPLAY but applies only to FTP.\n\n");
9507         break;
9508
9509 #ifndef NOCSETS
9510       case FTS_XLA:                     /* "character-set-translation" */
9511         printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n");
9512         printf("  Whether to translate character sets when transferring\n");
9513         printf("  text files with FTP.  OFF by default.\n\n");
9514         break;
9515
9516 #endif /* NOCSETS */
9517       case FTS_FNC:                     /* "collision" */
9518         printf("\n");
9519         printf(
9520 "Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n"
9521                );
9522         printf("  Tells what do when an incoming file has the same name as\n");
9523         printf("  an existing file when downloading with FTP.\n\n");
9524         break;
9525
9526 #ifdef FTP_SECURITY
9527       case FTS_CPL:                     /* "command-protection-level" */
9528         printf("\n");
9529         printf(
9530 "Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9531                );
9532         printf("\n");
9533         printf(
9534 "  Tells what level of protection is applied to the FTP command channel.\n\n");
9535         break;
9536       case FTS_CFW:                     /* "credential-forwarding" */
9537         printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n");
9538         printf("  Tells whether end-user credentials are to be forwarded\n");
9539         printf("  to the server if supported by the authentication method\n");
9540         printf("  (GSSAPI-KRB5 only).\n\n");
9541         break;
9542       case FTS_DPL:                     /* "data-protection-level" */
9543         printf("\n");
9544         printf(
9545 "Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9546                );
9547         printf("\n");
9548         printf(
9549 "  Tells what level of protection is applied to the FTP data channel.\n\n");
9550         break;
9551 #endif /* FTP_SECURITY */
9552
9553       case FTS_DBG:                     /* "debug" */
9554         printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n");
9555         printf("  Whether to print FTP protocol messages.\n\n");
9556         return(0);
9557
9558       case FTS_ERR:                     /* "error-action" */
9559         printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n");
9560         printf("  What to do when an error occurs when transferring a group\n")
9561           ;
9562         printf("  of files: quit and fail, or proceed to the next file.\n\n");
9563         return(0);
9564
9565       case FTS_CNV:                     /* "filenames" */
9566         printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n");
9567         printf("  What to do with filenames: convert them, take and use them\n"
9568                );
9569         printf("  literally; or choose what to do automatically based on the\n"
9570                );
9571         printf("  OS type of the server.  The default is AUTO.\n\n");
9572         return(0);
9573
9574       case FTS_PSV:                     /* "passive-mode" */
9575         printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n");
9576         printf("  Whether to use passive mode, which helps to get through\n");
9577         printf("  firewalls.  ON by default.\n\n");
9578         return(0);
9579
9580       case FTS_PRM:                     /* "permissions" */
9581         printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n");
9582         printf("  Whether to try to send file permissions when uploading.\n");
9583         printf("  OFF by default.  AUTO means only if client and server\n");
9584         printf("  have the same OS type.\n\n");
9585         return(0);
9586
9587       case FTS_TST:                     /* "progress-messages" */
9588         printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n");
9589         printf("  Whether Kermit should print locally-generated feedback\n");
9590         printf("  messages for each non-file-transfer command.");
9591         printf("  ON by default.\n\n");
9592         return(0);
9593
9594       case FTS_SPC:                     /* "send-port-commands" */
9595         printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n");
9596         printf("  Whether Kermit should send a new PORT command for each");
9597         printf("  task.\n\n");
9598         return(0);
9599
9600 #ifndef NOCSETS
9601       case FTS_CSR:                     /* "server-character-set" */
9602         printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n");
9603         printf("  The name of the character set used for text files on the\n");
9604         printf("  server.  Enter a name of '?' for a menu.\n\n");
9605         return(0);
9606 #endif /* NOCSETS */
9607
9608       case FTS_STO:                     /* "server-time-offset */
9609         printf(
9610 "\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n");
9611         printf(
9612 "  Specifies an offset to apply to the server's file timestamps.\n");
9613         printf(
9614 "  Use this to correct for misconfigured server time or timezone.\n");
9615         printf(
9616 "  Format: must begin with + or - sign.  Hours must be given; minutes\n");
9617         printf(
9618 "  and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n");
9619         return(0);
9620
9621       case FTS_TYP:                     /* "type" */
9622         printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n");
9623         printf("  Establishes the default transfer mode.\n");
9624         printf("  TENEX is used for uploading 8-bit binary files to 36-bit\n");
9625         printf("  platforms such as TENEX and TOPS-20 and for downloading\n");
9626         printf("  them again.  ASCII is a synonym for TEXT.  Normally each\n");
9627         printf("  file's type is determined automatically from its contents\n"
9628                );
9629         printf("  or its name; SET FTP TYPE does not prevent that, it only\n");
9630         printf("  tells which mode to use when the type can't be determined\n"
9631                );
9632         printf("  automatically.  To completely disable automatic transfer-\n"
9633                );
9634         printf("  mode switching and force either text or binary mode, give\n"
9635                );
9636         printf("  the top-level command ASCII or BINARY, as in traditional\n");
9637         printf("  FTP clients.\n\n");
9638         return(0);
9639
9640 #ifdef FTP_TIMEOUT
9641       case FTS_TMO:
9642        printf("\nSyntax: SET FTP TIMEOUT number-of-seconds\n");
9643        printf("  Establishes a timeout for FTP transfers.\n");
9644        printf("  The timeout applies per network read or write on the data\n");
9645        printf("  connection, not to the whole transfer.  By default the\n");
9646        printf("  timeout value is 0, meaning no timeout.  Use a positive\n");
9647        printf("  number to escape gracefully from hung data connections or\n");
9648        printf("  directory listings.\n\n");
9649         return(0);
9650 #endif  /* FTP_TIMEOUT */
9651
9652 #ifdef PATTERNS
9653       case FTS_GFT:
9654         printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n");
9655         printf("  Tells whether GET and MGET should automatically switch\n");
9656         printf("  the appropriate file type, TEXT, BINARY, or TENEX, by\n");
9657         printf("  matching the name of each incoming file with its list of\n");
9658         printf("  FILE TEXT-PATTERNS and FILE BINARY-PATTERNS.  ON by\n");
9659         printf("  default.  SHOW PATTERNS displays the current pattern\n");
9660         printf("  list.  HELP SET FILE to see how to change it.\n");
9661         break;
9662 #endif /* PATTERNS */
9663
9664       case FTS_USN:                     /* "unique-server-names" */
9665         printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n");
9666         printf("  Tells whether to ask the server to create unique names\n");
9667         printf("  for any uploaded file that has the same name as an\n");
9668         printf("  existing file.  Default is OFF.\n\n");
9669         return(0);
9670
9671       case FTS_VBM:                     /* "verbose-mode" */
9672         printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n");
9673         printf("  Whether to display all responses from the FTP server.\n");
9674         printf("  OFF by default.\n\n");
9675         return(0);
9676
9677       case FTS_DAT:
9678         printf("\nSyntax: SET FTP DATES { ON, OFF }\n");
9679         printf("  Whether to set date of incoming files from the file date\n");
9680         printf("  on the server.  ON by default.  Note: there is no way to\n")
9681           ;
9682         printf("  set the date on files uploaded to the server.  Also note\n");
9683         printf("  that not all servers support this feature.\n\n");
9684         return(0);
9685
9686       case FTS_APW:
9687         printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n");
9688         printf("  Password to supply automatically on anonymous FTP\n");
9689         printf("  connections instead of the default user@host.\n");
9690         printf("  Omit optional text to restore default.\n\n");
9691         return(0);
9692
9693       default:
9694         printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf);
9695     }
9696 #endif /* NOHELP */
9697     return(0);
9698 }
9699
9700 #ifndef L_SET
9701 #define L_SET 0
9702 #endif /* L_SET */
9703 #ifndef L_INCR
9704 #define L_INCR 1
9705 #endif /* L_INCR */
9706
9707 #ifdef FTP_SRP
9708 char srp_user[BUFSIZ];                  /* where is BUFSIZ defined? */
9709 char *srp_pass;
9710 char *srp_acct;
9711 #endif /* FTP_SRP */
9712
9713 static int kerror;                      /* Needed for all auth types */
9714
9715 static struct   sockaddr_in hisctladdr;
9716 static struct   sockaddr_in hisdataaddr;
9717 static struct   sockaddr_in data_addr;
9718 static int      data = -1;
9719 static int      ptflag = 0;
9720 static struct   sockaddr_in myctladdr;
9721
9722 #ifdef COMMENT
9723 #ifndef OS2
9724 UID_T getuid();
9725 #endif /* OS2 */
9726 #endif /* COMMENT */
9727
9728
9729 static int cpend = 0;                   /* No pending replies */
9730
9731 #ifdef CK_SSL
9732 extern SSL *ssl_ftp_con;
9733 extern SSL_CTX *ssl_ftp_ctx;
9734 extern SSL *ssl_ftp_data_con;
9735 extern int ssl_ftp_active_flag;
9736 extern int ssl_ftp_data_active_flag;
9737 #endif /* CK_SSL */
9738
9739 /*  f t p c m d  --  Send a command to the FTP server  */
9740 /*
9741   Call with:
9742     char * cmd: The command to send.
9743     char * arg: The argument (e.g. a filename).
9744     int lcs: The local character set index.
9745     int rcs: The remote (server) character set index.
9746     int vbm: Verbose mode:
9747       0 = force verbosity off
9748      >0 = force verbosity on
9749
9750   If arg is given (not NULL or empty) and lcs != rcs and both are > -1,
9751   and neither lcs or rcs is UCS-2, the arg is translated from the local
9752   character set to the remote one before sending the result to the server.
9753
9754    Returns:
9755     0 on failure with ftpcode = -1
9756     >= 0 on success (getreply() result) with ftpcode = 0.
9757 */
9758 static char xcmdbuf[RFNBUFSIZ];
9759
9760 static int
9761 ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; {
9762     char * s = NULL;
9763     int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1;
9764     sig_t oldintr;
9765
9766     if (ftp_deb)                        /* DEBUG */
9767       vbm = 1;
9768     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
9769       vbm = 0;
9770     else if (vbm < 0)                   /* VERBOSE */
9771       vbm = ftp_vbm;
9772
9773     cancelfile = 0;
9774     if (!cmd) cmd = "";
9775     if (!arg) arg = "";
9776     cmdlen = (int)strlen(cmd);
9777     len = cmdlen + (int)strlen(arg) + 1;
9778
9779     if (ftp_deb /* && !dpyactive */ ) {
9780 #ifdef FTP_PROXY
9781         if (ftp_prx) printf("%s ", ftp_host);
9782 #endif /* FTP_PROXY */
9783         printf("---> ");
9784         if (!anonymous && strcmp("PASS",cmd) == 0)
9785           printf("PASS XXXX");
9786         else
9787           printf("%s %s",cmd,arg);
9788         printf("\n");
9789     }
9790     /* bzero(xcmdbuf,RFNBUFSIZ); */
9791     ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL);
9792
9793 #ifdef DEBUG
9794     if (deblog) {
9795         debug(F110,"ftpcmd cmd",cmd,0);
9796         debug(F110,"ftpcmd arg",arg,0);
9797         debug(F101,"ftpcmd lcs","",lcs);
9798         debug(F101,"ftpcmd rcs","",rcs);
9799     }
9800 #endif /* DEBUG */
9801
9802     if (csocket == -1) {
9803         perror("No control connection for command");
9804         ftpcode = -1;
9805         return(0);
9806     }
9807     havesigint = 0;
9808     oldintr = signal(SIGINT, cmdcancel);
9809
9810 #ifndef NOCSETS
9811     if (*arg &&                         /* If an arg was given */
9812         lcs > -1 &&                     /* and a local charset */
9813         rcs > -1 &&                     /* and a remote charset */
9814         lcs != rcs &&                   /* and the two are not the same */
9815         lcs != FC_UCS2 &&               /* and neither one is UCS-2 */
9816         rcs != FC_UCS2                  /* ... */
9817         ) {
9818         initxlate(lcs,rcs);             /* Translate arg from lcs to rcs */
9819         xgnbp = arg;                    /* Global pointer to input string */
9820         rfnptr = rfnbuf;                /* Global pointer to output buffer */
9821
9822         while (1) {
9823             if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break;
9824             if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break;
9825         }
9826         /*
9827           We have to copy here instead of translating directly into
9828           xcmdbuf[] so strputc() can check length.  Alternatively we could
9829           write yet another xpnbyte() output function.
9830         */
9831         if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) {
9832             printf("?FTP command too long: %s + arg\n",cmd);
9833             ftpcode = -1;
9834             return(0);
9835         }
9836         x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1));
9837     }
9838 #endif /* NOCSETS */
9839
9840     s = xcmdbuf;                        /* Command to send to server */
9841
9842 #ifdef DEBUG
9843     if (deblog) {                       /* Log it */
9844         if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) {
9845             /* But don't log passwords */
9846             debug(F110,"FTP SENT ","PASS XXXX",0);
9847         } else {
9848             debug(F110,"FTP SENT ",s,0);
9849         }
9850     }
9851 #endif /* DEBUG */
9852
9853 #ifdef CK_ENCRYPTION
9854   again:
9855 #endif /* CK_ENCRYPTION */
9856     if (scommand(s) == 0) {              /* Send it. */
9857       signal(SIGINT, oldintr);
9858       return(0);
9859     }
9860     cpend = 1;
9861     x = !strcmp(cmd,"QUIT");            /* Is it the QUIT command? */
9862     if (x)                              /* In case we're interrupted */
9863       connected = 0;                    /* while waiting for the reply... */
9864
9865     fc = 0;                             /* Function code for getreply() */
9866     if (!strncmp(cmd,"AUTH ",5)         /* Must parse AUTH reply */
9867 #ifdef FTPHOST
9868         && strncmp(cmd, "HOST ",5)
9869 #endif /* FTPHOST */
9870         ) {
9871         fc = GRF_AUTH;
9872     } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */
9873         fc = GRF_FEAT;                  /* But FEAT not widely understood */
9874         if (!ftp_deb)                   /* So suppress error messages */
9875           vbm = 9;
9876     }
9877     r = getreply(x,                     /* Expect connection to close */
9878                  lcs,rcs,               /* Charsets */
9879                  vbm,                   /* Verbosity */
9880                  fc                     /* Function code */
9881                  );
9882     if (q > -1)
9883       quiet = q;
9884
9885 #ifdef CK_ENCRYPTION
9886     if (ftpcode == 533 && ftp_cpl == FPL_PRV) {
9887         fprintf(stderr,
9888                "ENC command not supported at server; retrying under MIC...\n");
9889         ftp_cpl = FPL_SAF;
9890         goto again;
9891     }
9892 #endif /* CK_ENCRYPTION */
9893 #ifdef COMMENT
9894     if (cancelfile && oldintr != SIG_IGN)
9895       (*oldintr)(SIGINT);
9896 #endif /* COMMENT */
9897     signal(SIGINT, oldintr);
9898     return(r);
9899 }
9900
9901 static VOID
9902 lostpeer() {
9903     debug(F100,"lostpeer","",0);
9904     if (connected) {
9905         if (csocket != -1) {
9906 #ifdef CK_SSL
9907             if (ssl_ftp_active_flag) {
9908                 SSL_shutdown(ssl_ftp_con);
9909                 SSL_free(ssl_ftp_con);
9910                 ssl_ftp_proxy = 0;
9911                 ssl_ftp_active_flag = 0;
9912                 ssl_ftp_con = NULL;
9913             }
9914 #endif /* CK_SSL */
9915 #ifdef TCPIPLIB
9916             socket_close(csocket);
9917 #else /* TCPIPLIB */
9918 #ifdef USE_SHUTDOWN
9919             shutdown(csocket, 1+1);
9920 #endif /* USE_SHUTDOWN */
9921             close(csocket);
9922 #endif /* TCPIPLIB */
9923             csocket = -1;
9924         }
9925         if (data != -1) {
9926 #ifdef CK_SSL
9927             if (ssl_ftp_data_active_flag) {
9928                 SSL_shutdown(ssl_ftp_data_con);
9929                 SSL_free(ssl_ftp_data_con);
9930                 ssl_ftp_data_active_flag = 0;
9931                 ssl_ftp_data_con = NULL;
9932             }
9933 #endif /* CK_SSL */
9934 #ifdef TCPIPLIB
9935             socket_close(data);
9936 #else /* TCPIPLIB */
9937 #ifdef USE_SHUTDOWN
9938             shutdown(data, 1+1);
9939 #endif /* USE_SHUTDOWN */
9940             close(data);
9941 #endif /* TCPIPLIB */
9942             data = -1;
9943             globaldin = -1;
9944         }
9945         connected = 0;
9946         anonymous = 0;
9947         loggedin = 0;
9948         auth_type = NULL;
9949         ftp_cpl = ftp_dpl = FPL_CLR;
9950 #ifdef CKLOGDIAL
9951         ftplogend();
9952 #endif /* CKLOGDIAL */
9953
9954 #ifdef LOCUS
9955         if (autolocus)                  /* Auotomatic locus switching... */
9956           setlocus(1,1);                /* Switch locus to local. */
9957 #endif /* LOCUS */
9958 #ifdef OS2
9959         DialerSend(OPT_KERMIT_HANGUP, 0);
9960 #endif /* OS2 */
9961     }
9962 #ifdef FTP_PROXY
9963     pswitch(1);
9964     if (connected) {
9965         if (csocket != -1) {
9966 #ifdef TCPIPLIB
9967             socket_close(csocket);
9968 #else /* TCPIPLIB */
9969 #ifdef USE_SHUTDOWN
9970             shutdown(csocket, 1+1);
9971 #endif /* USE_SHUTDOWN */
9972             close(csocket);
9973 #endif /* TCPIPLIB */
9974             csocket = -1;
9975         }
9976         connected = 0;
9977         anonymous = 0;
9978         loggedin = 0;
9979         auth_type = NULL;
9980         ftp_cpl = ftp_dpl = FPL_CLR;
9981     }
9982     proxflag = 0;
9983     pswitch(0);
9984 #endif /* FTP_PROXY */
9985 }
9986
9987 int
9988 ftpisopen() {
9989     return(connected);
9990 }
9991
9992 static int
9993 ftpclose() {
9994     extern int quitting;
9995     if (!connected)
9996       return(0);
9997     ftp_xfermode = xfermode;
9998     if (!ftp_vbm && !quiet)
9999       printlines = 1;
10000     ftpcmd("QUIT",NULL,0,0,ftp_vbm);
10001     if (csocket) {
10002 #ifdef CK_SSL
10003         if (ssl_ftp_active_flag) {
10004             SSL_shutdown(ssl_ftp_con);
10005             SSL_free(ssl_ftp_con);
10006             ssl_ftp_proxy = 0;
10007             ssl_ftp_active_flag = 0;
10008             ssl_ftp_con = NULL;
10009         }
10010 #endif /* CK_SSL */
10011 #ifdef TCPIPLIB
10012         socket_close(csocket);
10013 #else /* TCPIPLIB */
10014 #ifdef USE_SHUTDOWN
10015         shutdown(csocket, 1+1);
10016 #endif /* USE_SHUTDOWN */
10017         close(csocket);
10018 #endif /* TCPIPLIB */
10019     }
10020     csocket = -1;
10021     connected = 0;
10022     anonymous = 0;
10023     loggedin = 0;
10024     mdtmok = 1;
10025     sizeok = 1;
10026     featok = 1;
10027     stouarg = 1;
10028     typesent = 0;
10029     data = -1;
10030     globaldin = -1;
10031 #ifdef FTP_PROXY
10032     if (!proxy)
10033       macnum = 0;
10034 #endif /* FTP_PROXY */
10035     auth_type = NULL;
10036     ftp_dpl = FPL_CLR;
10037 #ifdef CKLOGDIAL
10038     ftplogend();
10039 #endif /* CKLOGDIAL */
10040 #ifdef LOCUS
10041     /* Unprefixed file management commands are executed locally */
10042     if (autolocus && !ftp_cmdlin && !quitting) {
10043         setlocus(1,1);
10044     }
10045 #endif /* LOCUS */
10046 #ifdef OS2
10047     DialerSend(OPT_KERMIT_HANGUP, 0);
10048 #endif /* OS2 */
10049     return(0);
10050 }
10051
10052 int
10053 ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; {
10054     char * host;
10055
10056     if (connected) {
10057         printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host);
10058         ftpcode = -1;
10059         return(0);
10060     }
10061 #ifdef FTPHOST
10062     hostcmd = 0;
10063 #endif /* FTPHOST */
10064     alike = 0;
10065     ftp_srvtyp[0] = NUL;
10066     if (!service) service = "";
10067     if (!*service) service = use_tls ? "ftps" : "ftp";
10068
10069     if (!isdigit(service[0])) {
10070         struct servent *destsp;
10071         destsp = getservbyname(service, "tcp");
10072         if (!destsp) {
10073             if (!ckstrcmp(service,"ftp",-1,0)) {
10074                 ftp_port = 21;
10075             } else if (!ckstrcmp(service,"ftps",-1,0)) {
10076                 ftp_port = 990;
10077             } else {
10078                 printf("?Bad port name - \"%s\"\n", service);
10079                 ftpcode = -1;
10080                 return(0);
10081             }
10082         } else {
10083             ftp_port = destsp->s_port;
10084             ftp_port = ntohs((unsigned short)ftp_port); /* SMS 2007/02/15 */
10085         }
10086     } else
10087         ftp_port = atoi(service);
10088     if (ftp_port <= 0) {
10089         printf("?Bad port name - \"%s\"\n", service);
10090         ftpcode = -1;
10091         return(0);
10092     }
10093     host = ftp_hookup(remote, ftp_port, use_tls);
10094     if (host) {
10095         ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN);
10096         connected = 1;                  /* Set FTP defaults */
10097         ftp_cpl = ftp_dpl = FPL_CLR;
10098         curtype = FTT_ASC;              /* Server uses ASCII mode */
10099         form = FORM_N;
10100         mode = MODE_S;
10101         stru = STRU_F;
10102         strcpy(bytename, "8");
10103         bytesize = 8;
10104
10105 #ifdef FTP_SECURITY
10106         if (ftp_aut) {
10107             if (ftp_auth()) {
10108                 if (ftp_cry 
10109 #ifdef OS2
10110                      && ck_crypt_is_installed()
10111 #endif /* OS2 */
10112                      ) {
10113                     if (!quiet)
10114                       printf("FTP Command channel is Private (encrypted)\n");
10115                     ftp_cpl = FPL_PRV;
10116                     if (setpbsz(DEFAULT_PBSZ) < 0) {
10117                         /* a failure here is most likely caused by a mixup */
10118                         /* in the session key used by client and server    */
10119                         printf("?Protection buffer size negotiation failed\n");
10120                         return(0);
10121                     }
10122                     if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) {
10123                         if (!quiet)
10124                           printf("FTP Data channel is Private (encrypted)\n");
10125                         ftp_dpl = FPL_PRV;
10126                     } else
10127                       printf("?Unable to enable encryption on data channel\n");
10128                 } else {
10129                     ftp_cpl = FPL_SAF;
10130                 }
10131             }
10132             if (!connected)
10133               goto fail;
10134         }
10135 #endif /* FTP_SECURITY */
10136         if (ftp_log)                    /* ^^^ */
10137           ftp_login(remote);
10138
10139         if (!connected)
10140           goto fail;
10141
10142         ftp_xfermode = xfermode;
10143
10144 #ifdef CKLOGDIAL
10145         dologftp();
10146 #endif /* CKLOGDIAL */
10147 #ifdef OS2
10148         DialerSend(OPT_KERMIT_CONNECT, 0);
10149 #endif /* OS2 */
10150         passivemode = ftp_psv;
10151         sendport = ftp_spc;
10152         mdtmok = 1;
10153         sizeok = 1;
10154         stouarg = 1;
10155         typesent = 0;
10156
10157         if (ucbuf == NULL) {
10158             actualbuf = DEFAULT_PBSZ;
10159             while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL)
10160               actualbuf >>= 2;
10161         }
10162         if (!maxbuf)
10163           ucbufsiz = actualbuf - FUDGE_FACTOR;
10164         debug(F101,"ftpopen ucbufsiz","",ucbufsiz);
10165         return(1);
10166     }
10167   fail:
10168     printf("?Can't FTP connect to %s:%s\n",remote,service);
10169     ftpcode = -1;
10170     return(0);
10171 }
10172
10173 #ifdef CK_SSL
10174 int
10175 ssl_auth() {
10176     int i;
10177     char* p;
10178     CONST SSL_METHOD *client_method;
10179
10180     if (ssl_debug_flag) {
10181         fprintf(stderr,"SSL DEBUG ACTIVE\n");
10182         fflush(stderr);
10183         /* for the moment I want the output on screen */
10184     }
10185     if (ssl_ftp_data_con != NULL) {
10186         SSL_free(ssl_ftp_data_con);
10187         ssl_ftp_data_con = NULL;
10188     }
10189     if (ssl_ftp_con != NULL) {
10190         SSL_free(ssl_ftp_con);
10191         ssl_ftp_con=NULL;
10192     }
10193     if (ssl_ftp_ctx != NULL) {
10194         SSL_CTX_free(ssl_ftp_ctx);
10195         ssl_ftp_ctx = NULL;
10196     }
10197
10198     /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 
10199      * was added to OpenSSL 0.9.6e and 0.9.7.  It does not exist in previous
10200      * versions
10201      */
10202 #ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
10203 #define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L
10204 #endif
10205 /*
10206   Pick allowed SSL/TLS versions according to enabled bugs.
10207   Modified 5 Feb 2015 to default to TLS 1.0 if no bugs are enabled,
10208   instead of to SSL 3.0, which has the POODLE vulnerability.
10209 */
10210     if (ftp_bug_use_ssl_v2) {
10211         /* allow SSL 2.0 or later */
10212         client_method = SSLv23_client_method();
10213 #ifndef DISABLE_SSLV3
10214     } else if (ftp_bug_use_ssl_v3) {
10215         /* allow SSL 3.0 ONLY - previous default */
10216         client_method = SSLv3_client_method();
10217 #endif /* DISABLE_SSLV3 */
10218     } else {
10219         /* default - allow TLS 1.0 or later */
10220         client_method = TLSv1_client_method();
10221     }
10222     if (auth_type && !strcmp(auth_type,"TLS")) {
10223         ssl_ftp_ctx=SSL_CTX_new(client_method);
10224         if (!ssl_ftp_ctx)
10225           return(0);
10226         SSL_CTX_set_options(ssl_ftp_ctx,
10227                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10228 #ifdef DISABLE_SSLV3
10229                             |SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3
10230 #endif
10231                             );
10232     } else {
10233         ssl_ftp_ctx = SSL_CTX_new(client_method);
10234         if (!ssl_ftp_ctx)
10235           return(0);
10236         SSL_CTX_set_options(ssl_ftp_ctx,
10237                             (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
10238                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10239 #ifdef DISABLE_SSLV3
10240                             |SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3
10241 #endif
10242                             );
10243     }
10244     SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
10245                                   (pem_password_cb *)ssl_passwd_callback);
10246     SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
10247     SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
10248
10249 #ifdef OS2
10250 #ifdef NT
10251     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10252     {
10253         char path[CKMAXPATH];
10254         extern char exedir[];
10255
10256         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10257         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10258             debug(F110,"ftp ssl_auth unable to load path",path,0);
10259             if (ssl_debug_flag)
10260                 printf("?Unable to load verify-dir: %s\r\n",path);
10261         }
10262
10263         ckmakmsg(path,CKMAXPATH,
10264                  (char *)GetAppData(1),"kermit 95/certs",NULL,NULL);
10265         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10266             debug(F110,"ftp ssl_auth unable to load path",path,0);
10267             if (ssl_debug_flag)
10268                 printf("?Unable to load verify-dir: %s\r\n",path);
10269         }
10270
10271         ckmakmsg(path,CKMAXPATH,
10272                  (char *)GetAppData(0),"kermit 95/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
10279         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10280         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10281             debug(F110,"ftp ssl_auth unable to load path",path,0);
10282             if (ssl_debug_flag)
10283                 printf("?Unable to load verify-file: %s\r\n",path);
10284         }
10285
10286         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10287                  "kermit 95/ca_certs.pem",NULL,NULL);
10288         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10289             debug(F110,"ftp ssl_auth unable to load path",path,0);
10290             if (ssl_debug_flag)
10291                 printf("?Unable to load verify-file: %s\r\n",path);
10292         }
10293
10294         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10295                  "kermit 95/ca_certs.pem",NULL,NULL);
10296         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10297             debug(F110,"ftp ssl_auth unable to load path",path,0);
10298             if (ssl_debug_flag)
10299                 printf("?Unable to load verify-file: %s\r\n",path);
10300         }
10301     }
10302 #else /* NT */
10303     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10304     {
10305
10306         char path[CKMAXPATH];
10307         extern char exedir[];
10308
10309         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10310         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10311             debug(F110,"ftp ssl_auth unable to load path",path,0);
10312             if (ssl_debug_flag)
10313                 printf("?Unable to load verify-dir: %s\r\n",path);
10314         }
10315         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10316         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10317             debug(F110,"ftp ssl_auth unable to load path",path,0);
10318             if (ssl_debug_flag)
10319                 printf("?Unable to load verify-file: %s\r\n",path);
10320         }
10321     }
10322 #endif /* NT */
10323 #else /* OS2 */
10324     SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
10325 #endif /* OS2 */
10326
10327     if (ssl_verify_file &&
10328         SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
10329         debug(F110,
10330               "ftp ssl auth unable to load ssl_verify_file",
10331               ssl_verify_file,
10332               0
10333               );
10334         if (ssl_debug_flag)
10335           printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
10336     }
10337     if (ssl_verify_dir &&
10338         SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
10339         debug(F110,
10340               "ftp ssl auth unable to load ssl_verify_dir",
10341               ssl_verify_dir,
10342               0
10343               );
10344         if (ssl_debug_flag)
10345           printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
10346     }
10347
10348     /* set up the new CRL Store */
10349     crl_store = (X509_STORE *)X509_STORE_new();
10350     if (crl_store) {
10351 #ifdef OS2
10352         char path[CKMAXPATH];
10353         extern char exedir[];
10354
10355         ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
10356         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10357             debug(F110,"ftp ssl auth unable to load dir",path,0);
10358             if (ssl_debug_flag)
10359                 printf("?Unable to load crl-dir: %s\r\n",path);
10360         }
10361 #ifdef NT
10362         ckmakmsg(path,CKMAXPATH,
10363                  (char *)GetAppData(1),"kermit 95/crls",NULL,NULL);
10364         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10365             debug(F110,"ftp ssl auth unable to load dir",path,0);
10366             if (ssl_debug_flag)
10367                 printf("?Unable to load crl-dir: %s\r\n",path);
10368         }
10369         ckmakmsg(path,CKMAXPATH,
10370                  (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
10371         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10372             debug(F110,"ftp ssl auth unable to load dir",path,0);
10373             if (ssl_debug_flag)
10374                 printf("?Unable to load crl-dir: %s\r\n",path);
10375         }
10376 #endif /* NT */
10377         
10378         ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
10379         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10380             debug(F110,"ftp ssl auth unable to load file",path,0);
10381             if (ssl_debug_flag)
10382                 printf("?Unable to load crl-file: %s\r\n",path);
10383         }
10384 #ifdef NT
10385         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10386                  "kermit 95/ca_crls.pem",NULL,NULL);
10387         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10388             debug(F110,"ftp ssl auth unable to load file",path,0);
10389             if (ssl_debug_flag)
10390                 printf("?Unable to load crl-file: %s\r\n",path);
10391         }
10392         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10393                  "kermit 95/ca_crls.pem",NULL,NULL);
10394         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10395             debug(F110,"ftp ssl auth unable to load file",path,0);
10396             if (ssl_debug_flag)
10397                 printf("?Unable to load crl-file: %s\r\n",path);
10398         }
10399 #endif /* NT */
10400 #endif /* OS2 */
10401
10402         if (ssl_crl_file || ssl_crl_dir) {
10403             if (ssl_crl_file &&
10404                 X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
10405                 debug(F110,
10406                       "ftp ssl auth unable to load ssl_crl_file",
10407                       ssl_crl_file,
10408                       0
10409                       );
10410                 if (ssl_debug_flag)
10411                   printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
10412             }
10413             if (ssl_crl_dir &&
10414                 X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
10415                 debug(F110,
10416                       "ftp ssl auth unable to load ssl_crl_dir",
10417                       ssl_crl_dir,
10418                       0
10419                       );
10420                 if (ssl_debug_flag)
10421                   printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
10422             }
10423         } else {
10424             X509_STORE_set_default_paths(crl_store);
10425         }
10426     }
10427     SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
10428                        ssl_client_verify_callback);
10429     ssl_verify_depth = -1;
10430     ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
10431     tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
10432     SSL_set_fd(ssl_ftp_con,csocket);
10433     SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
10434     if (ssl_cipher_list) {
10435         SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
10436     } else {
10437         char * p;
10438         if (p = getenv("SSL_CIPHER")) {
10439             SSL_set_cipher_list(ssl_ftp_con,p);
10440         } else {
10441             SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
10442         }
10443     }
10444     if (ssl_debug_flag) {
10445         fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
10446         fflush(stderr);
10447     }
10448     if (SSL_connect(ssl_ftp_con) <= 0) {
10449         static char errbuf[1024];
10450         ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
10451                  ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
10452         fprintf(stderr,"%s\n", errbuf);
10453         fflush(stderr);
10454         ssl_ftp_active_flag=0;
10455         SSL_free(ssl_ftp_con);
10456         ssl_ftp_con = NULL;
10457     } else {
10458         ssl_ftp_active_flag = 1;
10459
10460         if (!ssl_certsok_flag &&
10461             (ssl_verify_flag & SSL_VERIFY_PEER) && /* JEA 2013-12-10 */
10462             !tls_is_krb5(1)) {
10463             char *subject = ssl_get_subject_name(ssl_ftp_con);
10464
10465             if (!subject) {
10466                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
10467                     debug(F110,"ssl_auth","[SSL - FAILED]",0);
10468                     return(ssl_ftp_active_flag = 0);
10469                 } else {
10470                     if (uq_ok("Warning: Server didn't provide a certificate\n",
10471                                "Continue? (Y/N)",3,NULL,0) <= 0) {
10472                         debug(F110, "ssl_auth","[SSL - FAILED]",0);
10473                         return(ssl_ftp_active_flag = 0);
10474                     }
10475                 }
10476             } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
10477                 debug(F110,"ssl_auth","[SSL - FAILED]",0);
10478                 return(ssl_ftp_active_flag = 0);
10479             }
10480         }
10481         debug(F110,"ssl_auth","[SSL - OK]",0);
10482         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
10483     }
10484     if (ssl_debug_flag) {
10485         fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
10486         fflush(stderr);
10487     }
10488     return(ssl_ftp_active_flag);
10489 }
10490 #endif /* CK_SSL */
10491
10492 static sigtype
10493 cmdcancel(sig) int sig; {
10494 #ifdef OS2
10495     /* In Unix we "chain" to trap(), which prints this */
10496     printf("^C...\n");
10497 #endif /* OS2 */
10498     debug(F100,"ftp cmdcancel caught SIGINT ","",0);
10499     fflush(stdout);
10500     secure_getc(0,1);                   /* Initialize net input buffers */
10501     cancelfile++;
10502     cancelgroup++;
10503     mlsreset();
10504 #ifndef OS2
10505 #ifdef FTP_PROXY
10506     if (ptflag)                         /* proxy... */
10507       longjmp(ptcancel,1);
10508 #endif /* FTP_PROXY */
10509     debug(F100,"ftp cmdcancel chain to trap()...","",0);
10510     trap(SIGINT);
10511     /* NOTREACHED */
10512     debug(F100,"ftp cmdcancel return from trap()...","",0);
10513 #else
10514     debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
10515     PostCtrlCSem();
10516 #endif /* OS2 */
10517 }
10518
10519 static int
10520 #ifdef CK_ANSIC
10521 scommand(char * s)                      /* Was secure_command() */
10522 #else
10523 scommand(s) char * s;
10524 #endif /* CK_ANSIC */
10525 {
10526     int length = 0, len2;
10527     char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
10528 #ifdef CK_SSL
10529     if (ssl_ftp_active_flag) {
10530         int error, rc;
10531         length = strlen(s) + 2;
10532         length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10533         rc = SSL_write(ssl_ftp_con,out,length);
10534         error = SSL_get_error(ssl_ftp_con,rc);
10535         switch (error) {
10536           case SSL_ERROR_NONE:
10537             return(1);
10538           case SSL_ERROR_WANT_WRITE:
10539           case SSL_ERROR_WANT_READ:
10540           case SSL_ERROR_SYSCALL:
10541 #ifdef NT
10542             {
10543                 int gle = GetLastError();
10544             }
10545 #endif /* NT */
10546           case SSL_ERROR_WANT_X509_LOOKUP:
10547           case SSL_ERROR_SSL:
10548           case SSL_ERROR_ZERO_RETURN:
10549           default:
10550             lostpeer();
10551         }
10552         return(0);
10553     }
10554 #endif /* CK_SSL */
10555
10556     if (auth_type && ftp_cpl != FPL_CLR) {
10557 #ifdef FTP_SRP
10558         if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
10559           if ((length = srp_encode(ftp_cpl == FPL_PRV,
10560                                    (CHAR *)s,
10561                                    (CHAR *)out,
10562                                    strlen(s))) < 0) {
10563               fprintf(stderr, "SRP failed to encode message\n");
10564               return(0);
10565           }
10566 #endif /* FTP_SRP */
10567 #ifdef FTP_KRB4
10568         if (ck_krb4_is_installed() &&
10569             (strcmp(auth_type, "KERBEROS_V4") == 0)) {
10570             if (ftp_cpl == FPL_PRV) {
10571                 length =
10572                   krb_mk_priv((CHAR *)s, (CHAR *)out,
10573                               strlen(s), ftp_sched,
10574 #ifdef KRB524
10575                               ftp_cred.session,
10576 #else /* KRB524 */
10577                               &ftp_cred.session,
10578 #endif /* KRB524 */
10579                               &myctladdr, &hisctladdr);
10580             } else {
10581                 length =
10582                   krb_mk_safe((CHAR *)s,
10583                               (CHAR *)out,
10584                               strlen(s),
10585 #ifdef KRB524
10586                               ftp_cred.session,
10587 #else /* KRB524 */
10588                               &ftp_cred.session,
10589 #endif /* KRB524 */
10590                               &myctladdr, &hisctladdr);
10591             }
10592             if (length == -1) {
10593                 fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
10594                         ftp_cpl == FPL_PRV ? "priv" : "safe");
10595                 return(0);
10596             }
10597         }
10598 #endif /* FTP_KRB4 */
10599 #ifdef FTP_GSSAPI
10600         /* Scommand (based on level) */
10601         if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
10602             gss_buffer_desc in_buf, out_buf;
10603             OM_uint32 maj_stat, min_stat;
10604             int conf_state;
10605             in_buf.value = s;
10606             in_buf.length = strlen(s) + 1;
10607             maj_stat = gss_seal(&min_stat, gcontext,
10608                                 (ftp_cpl==FPL_PRV), /* private */
10609                                 GSS_C_QOP_DEFAULT,
10610                                 &in_buf, &conf_state,
10611                                 &out_buf);
10612             if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */
10613                 user_gss_error(maj_stat, min_stat,
10614                                (ftp_cpl==FPL_PRV)?
10615                                "gss_seal ENC didn't complete":
10616                                "gss_seal MIC didn't complete");
10617             } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
10618                 fprintf(stderr, "GSSAPI didn't encrypt message");
10619             } else {
10620                 if (ftp_deb)
10621                   fprintf(stderr, "sealed (%s) %d bytes\n",
10622                           ftp_cpl==FPL_PRV?"ENC":"MIC",
10623                           out_buf.length);
10624                 memcpy(out, out_buf.value,
10625                        length=out_buf.length);
10626                 gss_release_buffer(&min_stat, &out_buf);
10627             }
10628         }
10629 #endif /* FTP_GSSAPI */
10630         /* Other auth types go here ... */
10631
10632         len2 = FTP_BUFSIZ;
10633         if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
10634                                    length, &len2, RADIX_ENCODE))
10635             ) {
10636             fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
10637                     radix_error(kerror));
10638             return(0);
10639         }
10640         if (ftp_deb)
10641           fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
10642         len2 = ckmakmsg(out,
10643                         FTP_BUFSIZ,
10644                         ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
10645                         in,
10646                         "\r\n",
10647                         NULL
10648                         );
10649         send(csocket,(SENDARG2TYPE)out,len2,0);
10650     } else {
10651         char out[FTP_BUFSIZ];
10652         int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10653         send(csocket,(SENDARG2TYPE)out,len,0);
10654     }
10655     return(1);
10656 }
10657
10658 static int
10659 mygetc() {
10660     static char inbuf[4096];
10661     static int bp = 0, ep = 0;
10662     int rc;
10663
10664     if (bp == ep) {
10665         bp = ep = 0;
10666 #ifdef CK_SSL
10667         if (ssl_ftp_active_flag) {
10668             int error;
10669             rc = SSL_read(ssl_ftp_con,inbuf,4096);
10670             error = SSL_get_error(ssl_ftp_con,rc);
10671             switch (error) {
10672               case SSL_ERROR_NONE:
10673                 break;
10674               case SSL_ERROR_WANT_WRITE:
10675               case SSL_ERROR_WANT_READ:
10676                 return(0);
10677               case SSL_ERROR_SYSCALL:
10678                 if (rc == 0) {          /* EOF */
10679                     break;
10680                 } else {
10681 #ifdef NT
10682                     int gle = GetLastError();
10683 #endif /* NT */
10684                     break;
10685                 }
10686               case SSL_ERROR_WANT_X509_LOOKUP:
10687               case SSL_ERROR_SSL:
10688               case SSL_ERROR_ZERO_RETURN:
10689               default:
10690                 break;
10691             }
10692         } else
10693 #endif /* CK_SSL */
10694           rc = recv(csocket,(char *)inbuf,4096,0);
10695         if (rc <= 0)
10696           return(EOF);
10697         ep = rc;
10698     }
10699     return(inbuf[bp++]);
10700 }
10701
10702 /*  x l a t e c  --  Translate a character  */
10703 /*
10704     Call with:
10705       fc    = Function code: 0 = translate, 1 = initialize.
10706       c     = Character (as int).
10707       incs  = Index of charset to translate from.
10708       outcs = Index of charset to translate to.
10709
10710     Returns:
10711       0: OK
10712      -1: Error
10713 */
10714 static int
10715 xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
10716 #ifdef NOCSETS
10717     return(c);
10718 #else
10719     static char buf[128];
10720     static int cx;
10721     int c0, c1;
10722
10723     if (fc == 1) {                      /* Initialize */
10724         cx = 0;                         /* Catch-up buffer write index */
10725         xgnbp = buf;                    /* Catch-up buffer read pointer */
10726         buf[0] = NUL;                   /* Buffer is empty */
10727         return(0);
10728     }
10729     if (cx >= 127) {                    /* Catch-up buffer full */
10730         debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
10731         printf("?Translation buffer overflow\n");
10732         return(-1);
10733     }
10734     /* Add char to buffer. */
10735     /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
10736
10737     debug(F000,"xlatec buf",ckitoa(cx),c);
10738     buf[cx++] = c;
10739     buf[cx] = NUL;
10740
10741     while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
10742         if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
10743           return(-1);
10744     }
10745     /* If we're caught up, reinitialize the buffer */
10746     return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
10747 #endif /* NOCSETS */
10748 }
10749
10750
10751 /*  p a r s e f e a t  */
10752
10753 /* Note: for convenience we align keyword values with table indices */
10754 /* If you need to insert a new keyword, adjust the SFT_xxx definitions */
10755
10756 static struct keytab feattab[] = {
10757     { "$$$$", 0,        0 },            /* Dummy for sfttab[0] */
10758     { "AUTH", SFT_AUTH, 0 },
10759     { "LANG", SFT_LANG, 0 },
10760     { "MDTM", SFT_MDTM, 0 },
10761     { "MLST", SFT_MLST, 0 },
10762     { "PBSZ", SFT_PBSZ, 0 },
10763     { "PROT", SFT_PROT, 0 },
10764     { "REST", SFT_REST, 0 },
10765     { "SIZE", SFT_SIZE, 0 },
10766     { "TVFS", SFT_TVFS, 0 },
10767     { "UTF8", SFT_UTF8, 0 }
10768 };
10769 static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
10770
10771 #define FACT_CSET  1
10772 #define FACT_CREA  2
10773 #define FACT_LANG  3
10774 #define FACT_MTYP  4
10775 #define FACT_MDTM  5
10776 #define FACT_PERM  6
10777 #define FACT_SIZE  7
10778 #define FACT_TYPE  8
10779 #define FACT_UNIQ  9
10780
10781 static struct keytab facttab[] = {
10782     { "CHARSET",    FACT_CSET, 0 },
10783     { "CREATE",     FACT_CREA, 0 },
10784     { "LANG",       FACT_LANG, 0 },
10785     { "MEDIA-TYPE", FACT_MTYP, 0 },
10786     { "MODIFY",     FACT_MDTM, 0 },
10787     { "PERM",       FACT_PERM, 0 },
10788     { "SIZE",       FACT_SIZE, 0 },
10789     { "TYPE",       FACT_TYPE, 0 },
10790     { "UNIQUE",     FACT_UNIQ, 0 }
10791 };
10792 static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
10793
10794 static struct keytab ftyptab[] = {
10795     { "CDIR", FTYP_CDIR, 0 },
10796     { "DIR",  FTYP_DIR,  0 },
10797     { "FILE", FTYP_FILE, 0 },
10798     { "PDIR", FTYP_PDIR, 0 }
10799 };
10800 static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
10801
10802 static VOID
10803 parsefeat(s) char * s; {                /* Parse a FEATURE response */
10804     char kwbuf[8];
10805     int i, x;
10806     if (!s) return;
10807     if (!*s) return;
10808     while (*s < '!')
10809       s++;
10810     for (i = 0; i < 4; i++) {
10811         if (s[i] < '!')
10812           break;
10813         kwbuf[i] = s[i];
10814     }
10815     if (s[i] && s[i] != SP && s[i] != CR && s[i] != LF)
10816       return;
10817     kwbuf[i] = NUL;
10818     /* xlookup requires a full (but case independent) match */
10819     i = xlookup(feattab,kwbuf,nfeattab,&x);
10820     debug(F111,"ftp parsefeat",s,i);
10821     if (i < 0 || i > 15)
10822       return;
10823
10824     switch (i) {
10825       case SFT_MDTM:                    /* Controlled by ENABLE/DISABLE */
10826         sfttab[i] = mdtmok;
10827         if (mdtmok) sfttab[0]++;
10828         break;
10829       case SFT_MLST:                    /* ditto */
10830         sfttab[i] = mlstok;
10831         if (mlstok) sfttab[0]++;
10832         break;
10833       case SFT_SIZE:                    /* ditto */
10834         sfttab[i] = sizeok;
10835         if (sizeok) sfttab[0]++;
10836         break;
10837       case SFT_AUTH:                    /* ditto */
10838         sfttab[i] = ftp_aut;
10839         if (ftp_aut) sfttab[0]++;
10840         break;
10841       default:                          /* Others */
10842         sfttab[0]++;
10843         sfttab[i]++;
10844     }
10845 }
10846
10847 static char *
10848 parsefacts(s) char * s; {               /* Parse MLS[DT] File Facts */
10849     char * p;
10850     int i, j, x;
10851     if (!s) return(NULL);
10852     if (!*s) return(NULL);
10853
10854     /* Maybe we should make a copy of s so we can poke it... */
10855
10856     while ((p = ckstrchr(s,'='))) {
10857         *p = NUL;                       /* s points to fact */
10858         i = xlookup(facttab,s,nfacttab,&x); 
10859         debug(F111,"ftp parsefact fact",s,i);
10860         *p = '=';
10861         s = p+1;                        /* Now s points to arg */
10862         p = ckstrchr(s,';');
10863         if (!p)
10864           p = ckstrchr(s,SP);
10865         if (!p) {
10866             debug(F110,"ftp parsefact end-of-val search fail",s,0);
10867             break;
10868         }
10869         *p = NUL;
10870         debug(F110,"ftp parsefact valu",s,0);
10871         switch (i) {
10872           case FACT_CSET:               /* Ignore these for now */
10873           case FACT_CREA:
10874           case FACT_LANG:
10875           case FACT_PERM:
10876           case FACT_MTYP:
10877           case FACT_UNIQ:
10878             break;
10879           case FACT_MDTM:               /* Modtime */
10880             makestr(&havemdtm,s);
10881             debug(F110,"ftp parsefact mdtm",havemdtm,0);
10882             break;
10883           case FACT_SIZE:               /* Size */
10884             havesize = ckatofs(s);
10885             debug(F101,"ftp parsefact size","",havesize);
10886             break;
10887           case FACT_TYPE:               /* Type */
10888             j = xlookup(ftyptab,s,nftyptab,NULL);
10889             debug(F111,"ftp parsefact type",s,j);
10890             havetype = (j < 1) ? 0 : j;
10891             break;
10892         }
10893         *p = ';';
10894         s = p+1;                        /* s points next fact or name */
10895     }
10896     while (*s == SP)                    /* Skip past spaces. */
10897       s++;
10898     if (!*s)                            /* Make sure we still have a name */
10899       s = NULL;
10900     debug(F110,"ftp parsefact name",s,0);
10901     return(s);
10902 }
10903
10904 /*  g e t r e p l y  --  (to an FTP command sent to server)  */
10905
10906 /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
10907
10908 static int
10909 getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
10910     /* lcs, rcs, vbm parameters as in ftpcmd() */
10911     register int i, c, n;
10912     register int dig;
10913     register char *cp;
10914     int xlate = 0;
10915     int count = 0;
10916     int auth = 0;
10917     int originalcode = 0, continuation = 0;
10918     sig_t oldintr;
10919     int pflag = 0;
10920     char *pt = pasv;
10921     char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
10922     int safe = 0;
10923     int xquiet = 0;
10924
10925     auth = (fc == GRF_AUTH);
10926
10927 #ifndef NOCSETS
10928     debug(F101,"ftp getreply lcs","",lcs);
10929     debug(F101,"ftp getreply rcs","",rcs);
10930     if (lcs > -1 && rcs > -1 && lcs != rcs) {
10931         xlate = 1;
10932         initxlate(rcs,lcs);
10933         xlatec(1,0,rcs,lcs);
10934     }
10935 #endif /* NOCSETS */
10936     debug(F101,"ftp getreply fc","",fc);
10937
10938     if (quiet)
10939       xquiet = 1;
10940     if (vbm == 9) {
10941         xquiet = 1;
10942         vbm = 0;
10943     }
10944     if (ftp_deb)                        /* DEBUG */
10945       vbm = 1;
10946     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
10947       vbm = 0;
10948     else if (vbm < 0)                   /* VERBOSE */
10949       vbm = ftp_vbm;
10950
10951     ibuf[0] = '\0';
10952     if (reply_parse)
10953       reply_ptr = reply_buf;
10954     havesigint = 0;
10955     oldintr = signal(SIGINT, cmdcancel);
10956     for (count = 0;; count++) {
10957         obuf[0] = '\0';
10958         dig = n = ftpcode = i = 0;
10959         cp = ftp_reply_str;
10960         while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
10961             if (c == IAC) {             /* Handle telnet commands */
10962                 switch (c = mygetc()) {
10963                   case WILL:
10964                   case WONT:
10965                     c = mygetc();
10966                     obuf[0] = IAC;
10967                     obuf[1] = DONT;
10968                     obuf[2] = c;
10969                     obuf[3] = NUL;
10970 #ifdef CK_SSL
10971                     if (ssl_ftp_active_flag) {
10972                         int error, rc;
10973                         rc = SSL_write(ssl_ftp_con,obuf,3);
10974                         error = SSL_get_error(ssl_ftp_con,rc);
10975                         switch (error) {
10976                           case SSL_ERROR_NONE:
10977                             break;
10978                           case SSL_ERROR_WANT_WRITE:
10979                           case SSL_ERROR_WANT_READ:
10980                             return(0);
10981                           case SSL_ERROR_SYSCALL:
10982                             if (rc == 0) { /* EOF */
10983                                 break;
10984                             } else {
10985 #ifdef NT
10986                                 int gle = GetLastError();
10987 #endif /* NT */
10988                                 break;
10989                             }
10990                           case SSL_ERROR_WANT_X509_LOOKUP:
10991                           case SSL_ERROR_SSL:
10992                           case SSL_ERROR_ZERO_RETURN:
10993                           default:
10994                             break;
10995                         }
10996                     } else
10997 #endif /* CK_SSL */
10998                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10999                     break;
11000                   case DO:
11001                   case DONT:
11002                     c = mygetc();
11003                     obuf[0] = IAC;
11004                     obuf[1] = WONT;
11005                     obuf[2] = c;
11006                     obuf[3] = NUL;
11007 #ifdef CK_SSL
11008                     if (ssl_ftp_active_flag) {
11009                         int error, rc;
11010                         rc = SSL_write(ssl_ftp_con,obuf,3);
11011                         error = SSL_get_error(ssl_ftp_con,rc);
11012                         switch (error) {
11013                           case SSL_ERROR_NONE:
11014                             break;
11015                           case SSL_ERROR_WANT_WRITE:
11016                           case SSL_ERROR_WANT_READ:
11017                               signal(SIGINT,oldintr);
11018                               return(0);
11019                           case SSL_ERROR_SYSCALL:
11020                             if (rc == 0) { /* EOF */
11021                                 break;
11022                             } else {
11023 #ifdef NT
11024                                 int gle = GetLastError();
11025 #endif /* NT */
11026                                 break;
11027                             }
11028                           case SSL_ERROR_WANT_X509_LOOKUP:
11029                           case SSL_ERROR_SSL:
11030                           case SSL_ERROR_ZERO_RETURN:
11031                           default:
11032                             break;
11033                         }
11034                     } else
11035 #endif /* CK_SSL */
11036                       send(csocket,(SENDARG2TYPE)obuf,3,0);
11037                     break;
11038                   default:
11039                     break;
11040                 }
11041                 continue;
11042             }
11043             dig++;
11044             if (c == EOF) {
11045                 if (expecteof) {
11046                     signal(SIGINT,oldintr);
11047                     ftpcode = 221;
11048                     debug(F101,"ftp getreply EOF","",ftpcode);
11049                     return(0);
11050                 }
11051                 lostpeer();
11052                 if (!xquiet) {
11053                     if (ftp_deb)
11054                       printf("421 ");
11055                     printf(
11056                       "Service not available, connection closed by server\n");
11057                     fflush(stdout);
11058                 }
11059                 signal(SIGINT,oldintr);
11060                 ftpcode = 421;
11061                 debug(F101,"ftp getreply EOF","",ftpcode);
11062                 return(4);
11063             }
11064             if (n == 0) {               /* First digit */
11065                 n = c;                  /* Save it */
11066             }
11067             if (auth_type &&
11068 #ifdef CK_SSL
11069                 !ssl_ftp_active_flag &&
11070 #endif /* CK_SSL */
11071                 !ibuf[0] && (n == '6' || continuation)) {
11072                 if (c != '\r' && dig > 4)
11073                   obuf[i++] = c;
11074             } else {
11075                 if (auth_type &&
11076 #ifdef CK_SSL
11077                     !ssl_ftp_active_flag &&
11078 #endif /* CK_SSL */
11079                     !ibuf[0] && dig == 1 && vbm)
11080                   printf("Unauthenticated reply received from server:\n");
11081                 if (reply_parse) {
11082                     *reply_ptr++ = c;
11083                     *reply_ptr = NUL;
11084                 }
11085                 if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
11086                     ftp_cmdlin < 2) {
11087                     if ((c != '\r') &&
11088                         (ftp_deb || ((vbm || (!auth && n == '5')) &&
11089                         (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
11090                         )))))
11091                     {
11092 #ifdef FTP_PROXY
11093                         if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
11094                           printf("%s:",ftp_host);
11095 #endif /* FTP_PROXY */
11096
11097                         if (!xquiet) {
11098 #ifdef NOCSETS
11099                             printf("%c",c);
11100 #else
11101                             if (xlate) {
11102                                 xlatec(0,c,rcs,lcs);
11103                             } else {
11104                                 printf("%c",c);
11105                             }
11106 #endif /* NOCSETS */
11107                         }
11108                     }
11109                 }
11110             }
11111             if (auth_type &&
11112 #ifdef CK_SSL
11113                 !ssl_ftp_active_flag &&
11114 #endif /* CK_SSL */
11115                 !ibuf[0] && n != '6')
11116               continue;
11117             if (dig < 4 && isdigit(c))
11118               ftpcode = ftpcode * 10 + (c - '0');
11119             if (!pflag && ftpcode == 227)
11120               pflag = 1;
11121             if (dig > 4 && pflag == 1 && isdigit(c))
11122               pflag = 2;
11123             if (pflag == 2) {
11124                 if (c != '\r' && c != ')')
11125                   *pt++ = c;
11126                 else {
11127                     *pt = '\0';
11128                     pflag = 3;
11129                 }
11130             }
11131             if (dig == 4 && c == '-' && n != '6') {
11132                 if (continuation)
11133                   ftpcode = 0;
11134                 continuation++;
11135             }
11136             if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
11137                 *cp++ = c;
11138                 *cp = NUL;
11139             }
11140         }
11141         if (deblog ||
11142 #ifdef COMMENT
11143 /*
11144   Sometimes we need to print the server reply.  printlines is nonzero for any
11145   command where the results are sent back on the control connection rather
11146   than the data connection, e.g. STAT.  In the TOPS-20 case, each file line
11147   has ftpcode 213.  But if you do this with a UNIX server, it sends "213-Start
11148   STAT", <line with ftpcode == 0>, "213-End" or somesuch.  So when printlines
11149   is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
11150   lines from UNIX.  Further experimentation needed with other servers.  Of
11151   course RFC959 is mute as to the format of the server reply.
11152
11153   'printlines' is also true for PWD and BYE.
11154 */
11155             (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
11156 #else
11157 /* No, we can't be that clever -- it breaks other things like RPWD... */
11158             (printlines &&
11159              (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
11160 #endif /* COMMENT */
11161             ) {
11162             char * q = cp;
11163             char *r = ftp_reply_str;
11164             *q-- = NUL;                 /* NUL-terminate */
11165             while (*q < '!' && q > r)   /* Strip CR, etc */
11166               *q-- = NUL;
11167             if (!ftp_deb && printlines) { /* If printing */
11168                 if (ftpcode != 0)       /* strip ftpcode if any */
11169                   r += 4;
11170 #ifdef NOCSETS
11171                 printf("%s\n",r);       /* and print */
11172 #else
11173                 if (!xlate) {
11174                     printf("%s\n",r);
11175                 } else {                /* Translating */
11176                     xgnbp = r;          /* Set up strgetc() */
11177                     while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
11178                         if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
11179                             signal(SIGINT,oldintr);
11180                             return(-1);
11181                         }
11182                     }
11183                     printf("\n");
11184                 }
11185 #endif /* NOCSETS */
11186             }
11187         }
11188         debug(F110,"FTP RCVD ",ftp_reply_str,0);
11189
11190         if (fc == GRF_FEAT) {           /* Parsing FEAT command response? */
11191             if (count == 0 && n == '2') {
11192                 int i;                  /* (Re)-init server FEATure table */
11193                 debug(F100,"ftp getreply clearing feature table","",0);
11194                 for (i = 0; i < 16; i++)
11195                   sfttab[i] = 0;
11196             } else {
11197                 parsefeat((char *)ftp_reply_str);
11198             }
11199         }
11200         if (auth_type &&
11201 #ifdef CK_SSL
11202             !ssl_ftp_active_flag &&
11203 #endif /* CK_SSL */
11204              !ibuf[0] && n != '6') {
11205             signal(SIGINT,oldintr);
11206             return(getreply(expecteof,lcs,rcs,vbm,auth));
11207         }
11208         ibuf[0] = obuf[i] = '\0';
11209         if (ftpcode && n == '6')
11210           if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
11211               printf("Unknown reply: %d %s\n", ftpcode, obuf);
11212               n = '5';
11213           } else safe = (ftpcode == 631);
11214         if (obuf[0]                     /* if there is a string to decode */
11215 #ifdef CK_SSL
11216             && !ssl_ftp_active_flag     /* and not SSL/TLS */
11217 #endif /* CK_SSL */
11218             ) {
11219             if (!auth_type) {
11220                 printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
11221                 n = '5';
11222             }
11223 #ifndef CK_ENCRYPTION
11224             else if (ftpcode == 632) {
11225                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11226                 n = '5';
11227             }
11228 #endif /* CK_ENCRYPTION */
11229 #ifdef NOCONFIDENTIAL
11230             else if (ftpcode == 633) {
11231                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11232                 n = '5';
11233             }
11234 #endif /* NOCONFIDENTIAL */
11235             else {
11236                 int len = FTP_BUFSIZ;
11237                 if ((kerror = radix_encode((CHAR *)obuf,
11238                                            (CHAR *)ibuf,
11239                                            0,
11240                                            &len,
11241                                            RADIX_DECODE))
11242                     ) {
11243                     printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
11244                            ftpcode, radix_error(kerror), obuf);
11245                     n = '5';
11246                 }
11247 #ifdef FTP_SRP
11248                 else if (strcmp(auth_type, "SRP") == 0) {
11249                     int outlen;
11250                     outlen = srp_decode(!safe, (CHAR *)ibuf,
11251                                         (CHAR *) ibuf, len);
11252                     if (outlen < 0) {
11253                         printf("Warning: %d reply %s!\n",
11254                                ftpcode, safe ? "modified" : "garbled");
11255                         n = '5';
11256                     } else {
11257                         ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
11258                         if (ftp_deb)
11259                           printf("%c:", safe ? 'S' : 'P');
11260                         continue;
11261                     }
11262                 }
11263 #endif /* FTP_SRP */
11264 #ifdef FTP_KRB4
11265                 else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
11266                     if (safe) {
11267                         kerror = krb_rd_safe((CHAR *)ibuf, len,
11268 #ifdef KRB524
11269                                              ftp_cred.session,
11270 #else /* KRB524 */
11271                                              &ftp_cred.session,
11272 #endif /* KRB524 */
11273                                              &hisctladdr,
11274                                              &myctladdr,
11275                                              &ftp_msg_data
11276                                              );
11277                     } else {
11278                         kerror = krb_rd_priv((CHAR *)ibuf, len,
11279                                              ftp_sched,
11280 #ifdef KRB524
11281                                              ftp_cred.session,
11282 #else /* KRB524 */
11283                                              &ftp_cred.session,
11284 #endif /* KRB524 */
11285                                              &hisctladdr,
11286                                              &myctladdr,
11287                                              &ftp_msg_data
11288                                              );
11289                     }
11290                     if (kerror != KSUCCESS) {
11291                         printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
11292                                safe ? "modified" : "garbled",
11293                                safe ? "safe" : "priv",
11294                                krb_get_err_text(kerror));
11295                         n = '5';
11296                     } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
11297                         kerror = KFAILURE;
11298                         n = '5';
11299                         printf("reply data too large for buffer\n");
11300                     } else {
11301                         if (ftp_deb)
11302                           printf("%c:", safe ? 'S' : 'P');
11303                         memcpy(ibuf,ftp_msg_data.app_data,
11304                                ftp_msg_data.app_length);
11305                         ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
11306                                   FTP_BUFSIZ - ftp_msg_data.app_length);
11307                         continue;
11308                     }
11309                 }
11310 #endif /* FTP_KRB4 */
11311 #ifdef FTP_GSSAPI
11312                 else if (strcmp(auth_type, "GSSAPI") == 0) {
11313                     gss_buffer_desc xmit_buf, msg_buf;
11314                     OM_uint32 maj_stat, min_stat;
11315                     int conf_state;
11316                     xmit_buf.value = ibuf;
11317                     xmit_buf.length = len;
11318                     /* decrypt/verify the message */
11319                     conf_state = safe;
11320                     maj_stat = gss_unseal(&min_stat, gcontext,
11321                                           &xmit_buf, &msg_buf,
11322                                           &conf_state, NULL);
11323                     if (maj_stat != GSS_S_COMPLETE) {
11324                         user_gss_error(maj_stat, min_stat,
11325                                        "failed unsealing reply");
11326                         n = '5';
11327                     } else {
11328                         memcpy(ibuf, msg_buf.value, msg_buf.length);
11329                         ckstrncpy(&ibuf[msg_buf.length], "\r\n",
11330                                   FTP_BUFSIZ-msg_buf.length);
11331                         gss_release_buffer(&min_stat,&msg_buf);
11332                         if (ftp_deb)
11333                           printf("%c:", safe ? 'S' : 'P');
11334                         continue;
11335                     }
11336                 }
11337 #endif /* FTP_GSSAPI */
11338                 /* Other auth types go here... */
11339             }
11340         } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
11341                    !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
11342 #ifdef NOCSETS
11343             printf("%c",c);
11344 #else
11345             if (xlate) {
11346                 xlatec(0,c,rcs,lcs);
11347             } else {
11348                 printf("%c",c);
11349             }
11350 #endif /* NOCSETS */
11351             fflush (stdout);
11352         }
11353         if (continuation && ftpcode != originalcode) {
11354             if (originalcode == 0)
11355               originalcode = ftpcode;
11356             continue;
11357         }
11358         *cp = '\0';
11359         if (n != '1')
11360           cpend = 0;
11361         signal(SIGINT,oldintr);
11362         if (ftpcode == 421 || originalcode == 421) {
11363             lostpeer();
11364             if (!xquiet && !ftp_deb)
11365               printf("%s\n",reply_buf);
11366         }
11367         if ((cancelfile != 0) &&
11368 #ifndef ULTRIX3
11369             /* Ultrix 3.0 cc objects violently to this clause */
11370             (oldintr != cmdcancel) &&
11371 #endif /* ULTRIX3 */
11372             (oldintr != SIG_IGN)) {
11373             if (oldintr)
11374               (*oldintr)(SIGINT);
11375         }
11376         if (reply_parse) {
11377             *reply_ptr = '\0';
11378             if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
11379                 reply_parse = reply_ptr + strlen(reply_parse);
11380                 if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
11381                   *reply_ptr = '\0';
11382             } else
11383               reply_parse = reply_ptr;
11384         }
11385         while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
11386           *cp-- = NUL;
11387         debug(F111,"ftp getreply",ftp_reply_str,n - '0');
11388         return(n - '0');
11389     } /* for (;;) */
11390 }
11391
11392 #ifdef BSDSELECT
11393 static int
11394 #ifdef CK_ANSIC
11395 empty(fd_set * mask, int sec)
11396 #else
11397 empty(mask, sec) fd_set * mask; int sec;
11398 #endif /* CK_ANSIC */
11399 {
11400     struct timeval t;
11401     t.tv_sec = (long) sec;
11402     t.tv_usec = 0L;
11403     debug(F100,"ftp empty calling select...","",0);
11404 #ifdef INTSELECT
11405     x = select(32, (int *)mask, NULL, NULL, &t);
11406 #else
11407     x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
11408 #endif /* INTSELECT */
11409     debug(F101,"ftp empty select","",x);
11410     return(x);
11411 }
11412 #else /* BSDSELECT */
11413 #ifdef IBMSELECT
11414 static int
11415 empty(mask, cnt, sec) int * mask, sec;
11416                       int   cnt;
11417 {
11418     return(select(mask,cnt,0,0,sec*1000));
11419 }
11420 #endif /* IBMSELECT */
11421 #endif /* BSDSELECT */
11422
11423 static sigtype
11424 cancelsend(sig) int sig; {
11425     havesigint++;
11426     cancelgroup++;
11427     cancelfile = 0;
11428     printf(" Canceled...\n");
11429     secure_getc(0,1);                   /* Initialize net input buffers */
11430     debug(F100,"ftp cancelsend caught SIGINT ","",0);
11431     fflush(stdout);
11432 #ifndef OS2
11433     longjmp(sendcancel, 1);
11434 #else
11435     PostCtrlCSem();
11436 #endif /* OS2 */
11437 }
11438
11439 static VOID
11440 #ifdef CK_ANSIC
11441 secure_error(char *fmt, ...)
11442 #else
11443 /* VARARGS1 */
11444 secure_error(fmt, p1, p2, p3, p4, p5)
11445    char *fmt; int p1, p2, p3, p4, p5;
11446 #endif /* CK_ANSIC */
11447 {
11448 #ifdef CK_ANSIC
11449     va_list ap;
11450
11451     va_start(ap, fmt);
11452     vfprintf(stderr, fmt, ap);
11453     va_end(ap);
11454 #else
11455     fprintf(stderr, fmt, p1, p2, p3, p4, p5);
11456 #endif
11457     fprintf(stderr, "\n");
11458 }
11459
11460 /*
11461  * Internal form of settype; changes current type in use with server
11462  * without changing our notion of the type for data transfers.
11463  * Used to change to and from ascii for listings.
11464  */
11465 static VOID
11466 changetype(newtype, show) int newtype, show; {
11467     int rc;
11468     char * s;
11469
11470     if ((newtype == curtype) && typesent++)
11471       return;
11472     switch (newtype) {
11473       case FTT_ASC:
11474         s = "A";
11475         break;
11476       case FTT_BIN:
11477         s = "I";
11478         break;
11479       case FTT_TEN:
11480         s = "L 8";
11481         break;
11482       default:
11483         s = "I";
11484         break;
11485     }
11486     rc = ftpcmd("TYPE",s,-1,-1,show);
11487     if (rc == REPLY_COMPLETE)
11488       curtype = newtype;
11489 }
11490
11491 /* PUT a file.  Returns -1 on error, 0 on success, 1 if file skipped */
11492
11493 static VOID
11494 #ifdef CK_ANSIC
11495 doftpsend(void * threadinfo)
11496 #else
11497 doftpsend(threadinfo) VOID * threadinfo;
11498 #endif
11499 {
11500 #ifdef NTSIG
11501     if (threadinfo) {                   /* Thread local storage... */
11502         TlsSetValue(TlsIndex,threadinfo);
11503         debug(F100, "doftpsend called with threadinfo block","", 0);
11504     } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
11505 #endif /* NTSIG */
11506 #ifdef CK_LOGIN
11507 #ifdef IKSD
11508 #ifdef NT
11509     if (inserver)
11510       setntcreds();
11511 #endif /* NT */
11512 #endif /* IKSD */
11513 #endif /* CK_LOGIN */
11514
11515     if (initconn()) {
11516 #ifndef NOHTTP
11517         int y = -1;
11518         /* debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy); */
11519
11520        /*  If the connection failed and we are using an HTTP Proxy
11521         *  and the reason for the failure was an authentication
11522         *  error, then we need to give the user to ability to
11523         *  enter a username and password, just like a browser.
11524         *
11525         *  I tried to do all of this within the netopen() call
11526         *  but it is much too much work.
11527         */
11528         while (y != 0 && tcp_http_proxy != NULL ) {
11529
11530             if (tcp_http_proxy_errno == 401 ||
11531                  tcp_http_proxy_errno == 407 ) {
11532                 char uid[UIDBUFLEN];
11533                 char pwd[PWDSIZ];
11534                 struct txtbox tb[2];
11535                 int ok;
11536
11537                 tb[0].t_buf = uid;
11538                 tb[0].t_len = UIDBUFLEN;
11539                 tb[0].t_lbl = "Proxy Userid: ";
11540                 tb[0].t_dflt = NULL;
11541                 tb[0].t_echo = 1;
11542                 tb[1].t_buf = pwd;
11543                 tb[1].t_len = 256;
11544                 tb[1].t_lbl = "Proxy Passphrase: ";
11545                 tb[1].t_dflt = NULL;
11546                 tb[1].t_echo = 2;
11547
11548                 ok = uq_mtxt("Proxy Server Authentication Required\n",
11549                               NULL, 2, tb);
11550                 if (ok && uid[0]) {
11551                     char * proxy_user, * proxy_pwd;
11552
11553                     proxy_user = tcp_http_proxy_user;
11554                     proxy_pwd  = tcp_http_proxy_pwd;
11555
11556                     tcp_http_proxy_user = uid;
11557                     tcp_http_proxy_pwd = pwd;
11558
11559                     y = initconn();
11560
11561                     debug(F101,"doftpsend","initconn",y);
11562                     memset(pwd,0,PWDSIZ);
11563                     tcp_http_proxy_user = proxy_user;
11564                     tcp_http_proxy_pwd = proxy_pwd;
11565                 } else
11566                     break;
11567             } else
11568                 break;
11569         }
11570
11571         if ( y != 0 ) {
11572 #endif /* NOHTTP */
11573             signal(SIGINT, ftpsnd.oldintr);
11574 #ifdef SIGPIPE
11575             if (ftpsnd.oldintp)
11576               signal(SIGPIPE, ftpsnd.oldintp);
11577 #endif /* SIGPIPE */
11578             ftpcode = -1;
11579             zclose(ZIFILE);
11580             ftpsndret = -1;
11581 #ifdef NTSIG
11582             ckThreadEnd(threadinfo);
11583 #endif /* NTSIG */
11584             return;
11585 #ifndef NOHTTP
11586         }
11587 #endif /* NOHTTP */
11588     }
11589     ftpsndret = 0;
11590 #ifdef NTSIG
11591      ckThreadEnd(threadinfo);
11592 #endif /* NTSIG */
11593 }
11594
11595 static VOID
11596 #ifdef CK_ANSIC
11597 failftpsend(void * threadinfo)
11598 #else
11599 failftpsend(threadinfo) VOID * threadinfo;
11600 #endif /* CK_ANSIC */
11601 {
11602 #ifdef NTSIG
11603     if (threadinfo) {                   /* Thread local storage... */
11604         TlsSetValue(TlsIndex,threadinfo);
11605         debug(F100, "docmdfile called with threadinfo block","", 0);
11606     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11607 #endif /* NTSIG */
11608 #ifdef CK_LOGIN
11609 #ifdef IKSD
11610 #ifdef NT
11611     if (inserver)
11612       setntcreds();
11613 #endif /* NT */
11614 #endif /* IKSD */
11615 #endif /* CK_LOGIN */
11616
11617     while (cpend) {
11618         ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11619         debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
11620     }
11621     if (data >= 0) {
11622 #ifdef CK_SSL
11623         if (ssl_ftp_data_active_flag) {
11624             SSL_shutdown(ssl_ftp_data_con);
11625             SSL_free(ssl_ftp_data_con);
11626             ssl_ftp_data_active_flag = 0;
11627             ssl_ftp_data_con = NULL;
11628         }
11629 #endif /* CK_SSL */
11630 #ifdef TCPIPLIB
11631         socket_close(data);
11632 #else /* TCPIPLIB */
11633 #ifdef USE_SHUTDOWN
11634         shutdown(data, 1+1);
11635 #endif /* USE_SHUTDOWN */
11636         close(data);
11637 #endif /* TCPIPLIB */
11638         data = -1;
11639         globaldin = -1;
11640     }
11641     if (ftpsnd.oldintr)
11642         signal(SIGINT,ftpsnd.oldintr);
11643 #ifdef SIGPIPE
11644     if (ftpsnd.oldintp)
11645         signal(SIGPIPE,ftpsnd.oldintp);
11646 #endif /* SIGPIPE */
11647     ftpcode = -1;
11648 #ifndef OS2
11649     /* TEST ME IN K95 */
11650     if (havesigint) {
11651         havesigint = 0;
11652         debug(F100,"ftp failftpsend chain to trap()...","",0);
11653         if (ftpsnd.oldintr != SIG_IGN)
11654           (*ftpsnd.oldintr)(SIGINT);
11655         /* NOTREACHED (I hope!) */
11656         debug(F100,"ftp failftpsend return from trap()...","",0);
11657     }
11658 #endif /* OS2 */
11659 }
11660
11661 static VOID
11662 #ifdef CK_ANSIC
11663 failftpsend2(void * threadinfo)
11664 #else
11665 failftpsend2(threadinfo) VOID * threadinfo;
11666 #endif /* CK_ANSIC */
11667 {
11668 #ifdef NTSIG
11669     if (threadinfo) {                   /* Thread local storage... */
11670         TlsSetValue(TlsIndex,threadinfo);
11671         debug(F100, "docmdfile called with threadinfo block","", 0);
11672     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11673 #endif /* NTSIG */
11674 #ifdef CK_LOGIN
11675 #ifdef IKSD
11676 #ifdef NT
11677     if (inserver)
11678       setntcreds();
11679 #endif /* NT */
11680 #endif /* IKSD */
11681 #endif /* CK_LOGIN */
11682
11683     debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
11684     tfc += ffc;
11685 #ifdef GFTIMER
11686     fpfsecs = gftimer();
11687 #endif /* GFTIMER */
11688     zclose(ZIFILE);
11689 #ifdef PIPESEND
11690     if (sndfilter)
11691       pipesend = 0;
11692 #endif /* PIPESEND */
11693     signal(SIGINT, ftpsnd.oldintr);
11694 #ifdef SIGPIPE
11695     if (ftpsnd.oldintp)
11696       signal(SIGPIPE, ftpsnd.oldintp);
11697 #endif /* SIGPIPE */
11698     if (!cpend) {
11699         ftpcode = -1;
11700         ftpsndret = -1;
11701 #ifdef NTSIG
11702         ckThreadEnd(threadinfo);
11703 #endif /* NTSIG */
11704         return;
11705     }
11706     if (data >= 0) {
11707 #ifdef CK_SSL
11708         if (ssl_ftp_data_active_flag) {
11709             SSL_shutdown(ssl_ftp_data_con);
11710             SSL_free(ssl_ftp_data_con);
11711             ssl_ftp_data_active_flag = 0;
11712             ssl_ftp_data_con = NULL;
11713         }
11714 #endif /* CK_SSL */
11715 #ifdef TCPIPLIB
11716         socket_close(data);
11717 #else /* TCPIPLIB */
11718 #ifdef USE_SHUTDOWN
11719         shutdown(data, 1+1);
11720 #endif /* USE_SHUTDOWN */
11721         close(data);
11722 #endif /* TCPIPLIB */
11723         data = -1;
11724         globaldin = -1;
11725     }
11726     if (dout) {
11727 #ifdef TCPIPLIB
11728         socket_close(dout);
11729 #else /* TCPIPLIB */
11730 #ifdef USE_SHUTDOWN
11731         shutdown(dout, 1+1);
11732 #endif /* USE_SHUTDOWN */
11733         close(dout);
11734 #endif /* TCPIPLIB */
11735     }
11736     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11737     ftpcode = -1;
11738     ftpsndret = -1;
11739
11740 #ifndef OS2
11741     /* TEST ME IN K95 */
11742     if (havesigint) {
11743         havesigint = 0;
11744         debug(F100,"ftp failftpsend2 chain to trap()...","",0);
11745         if (ftpsnd.oldintr != SIG_IGN)
11746           (*ftpsnd.oldintr)(SIGINT);
11747         /* NOTREACHED (I hope!) */
11748         debug(F100,"ftp failftpsend2 return from trap()...","",0);
11749     }
11750 #endif /* OS2 */
11751 }
11752
11753 static VOID
11754 #ifdef CK_ANSIC
11755 doftpsend2(void * threadinfo)
11756 #else
11757 doftpsend2(threadinfo) VOID * threadinfo;
11758 #endif
11759 {
11760     register int c, d = 0;
11761     int n, t, x, notafile, unique = 0;
11762     char *buf, *bufp;
11763     
11764 #ifdef NTSIG
11765     if (threadinfo) {                   /* Thread local storage... */
11766         TlsSetValue(TlsIndex,threadinfo);
11767         debug(F100, "doftpsend2 called with threadinfo block","", 0);
11768     } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
11769 #endif /* NTSIG */
11770 #ifdef CK_LOGIN
11771 #ifdef IKSD
11772 #ifdef NT
11773     if (inserver)
11774       setntcreds();
11775 #endif /* NT */
11776 #endif /* IKSD */
11777 #endif /* CK_LOGIN */
11778
11779     buf = ftpsndbuf;                    /* (not on stack) */
11780
11781     unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
11782     notafile = sndarray || pipesend;
11783
11784 #ifdef FTP_RESTART
11785     if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
11786         char * p;
11787         changetype(FTT_BIN,0);          /* Change to binary */
11788
11789         /* Ask for remote file's size */
11790         x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
11791
11792         if (x == REPLY_COMPLETE) {      /* Have ftpsnd.reply */
11793             p = &ftp_reply_str[4];      /* Parse it */
11794             while (isdigit(*p)) {
11795                 sendstart = sendstart * 10 + (int)(*p - '0');
11796                 p++;
11797             }
11798             if (*p && *p != CR) {       /* Bad number */
11799                 debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
11800                 sendstart = (CK_OFF_T)0;
11801             } else if (sendstart > fsize) { /* Remote file bigger than local */
11802                 debug(F110,"doftpsend2 big size",ckfstoa(fsize),sendstart);
11803                 sendstart = (CK_OFF_T)0;
11804             }
11805             /* Local is newer */
11806             debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
11807             if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
11808                 debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
11809                 sendstart = (CK_OFF_T)0; /* Send the whole file */
11810             }
11811         }
11812         changetype(ftp_typ,0);          /* Change back to appropriate type */
11813         if (sendstart > (CK_OFF_T)0) {  /* Still restarting? */
11814             if (sendstart == fsize) {   /* Same size - no need to send */
11815                 debug(F111,"doftpsend2 /restart SKIP",
11816                       ckfstoa(fsize),sendstart);
11817                 zclose(ZIFILE);
11818                 ftpsndret = SKP_RES;
11819 #ifdef NTSIG
11820                 ckThreadEnd(threadinfo);
11821 #endif /* NTSIG */
11822                 return;
11823             }
11824             errno = 0;                  /* Restart needed, seek to the spot */
11825             if (zfseek((long)sendstart) < 0) {
11826                 debug(F111,"doftpsend2 zfseek fails",
11827                       ftpsnd.local,sendstart);
11828                 fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
11829                 sendstart = 0;
11830                 zclose(ZIFILE);
11831                 ftpsndret = -1;
11832 #ifdef NTSIG
11833                 ckThreadEnd(threadinfo);
11834 #endif /* NTSIG */
11835                 return;
11836             }
11837 #ifdef COMMENT
11838             debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
11839             x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
11840             if (x != REPLY_CONTINUE) {
11841                 sendstart = 0;
11842                 zclose(ZIFILE);
11843                 ftpsndret = -1;
11844 #ifdef NTSIG
11845                 ckThreadEnd(threadinfo);
11846 #endif /* NTSIG */
11847                 return;
11848             } else {
11849                 ftpsnd.cmd = "STOR";
11850             }
11851 #else
11852             sendmode = SM_RESEND;
11853             ftpsnd.cmd = "APPE";
11854 #endif /* COMMENT */
11855             /* sendstart = (CK_OFF_T)0; */
11856         }
11857     }
11858 #endif /* FTP_RESTART */
11859
11860     if (unique && !stouarg)             /* If we know STOU accepts no arg */
11861       ftpsnd.remote = NULL;             /* don't include one. */
11862
11863     x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
11864     debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
11865     debug(F101,"doftpsend2 ftpcmd","",x);
11866
11867     if (x != REPLY_PRELIM && unique) {
11868         /*
11869           RFC959 says STOU does not take an argument.  But every FTP server
11870           I've encountered but one accepts the arg and constructs the unique
11871           name from it, which is better than making up a totally random name
11872           for the file, which is what RFC959 calls for.  Especially because
11873           there is no way for the client to find out the name chosen by the
11874           server.  So we try STOU with the argument first, which works with
11875           most servers, and if it fails we retry it without the arg, for
11876           the benefit of the one picky server that is not "liberal in what
11877           it accepts" UNLESS the first STOU got a 502 code ("not implemented")
11878           which means STOU is not accepted, period.
11879         */
11880         if ((x == 5) && stouarg && (ftpcode != 502)) {
11881             x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); 
11882             if (x == REPLY_PRELIM)      /* If accepted */
11883               stouarg = 0;              /* flag no STOU arg for this server */
11884         }
11885     }
11886     if (x != REPLY_PRELIM) {
11887         signal(SIGINT, ftpsnd.oldintr);
11888 #ifdef SIGPIPE
11889         if (ftpsnd.oldintp)
11890           signal(SIGPIPE, ftpsnd.oldintp);
11891 #endif /* SIGPIPE */
11892         debug(F101,"doftpsend2 not REPLY_PRELIM","",x);
11893         zclose(ZIFILE);
11894 #ifdef PIPESEND
11895         if (sndfilter)
11896           pipesend = 0;
11897 #endif /* PIPESEND */
11898         ftpsndret = -1;
11899 #ifdef NTSIG
11900         ckThreadEnd(threadinfo);
11901 #endif /* NTSIG */
11902         return;
11903     }
11904     debug(F100,"doftpsend2 getting data connection...","",0);
11905     dout = dataconn(ftpsnd.lmode);             /* Get data connection */
11906     debug(F101,"doftpsend2 dataconn","",dout);
11907     if (dout == -1) {
11908         failftpsend2(threadinfo);
11909 #ifdef NTSIG
11910         ckThreadEnd(threadinfo);
11911 #endif /* NTSIG */
11912         return;
11913     }
11914     /* Initialize per-file stats */
11915     ffc = (CK_OFF_T)0;                  /* Character counter */
11916     cps = oldcps = 0L;                  /* Thruput */
11917     n = 0;
11918 #ifdef GFTIMER
11919     rftimer();                          /* reset f.p. timer */
11920 #endif /* GFTIMER */
11921
11922 #ifdef SIGPIPE
11923     ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
11924 #endif /* SIGPIPE */
11925     debug(F101,"doftpsend2 curtype","",curtype);
11926     switch (curtype) {
11927       case FTT_BIN:                     /* Binary mode */
11928       case FTT_TEN:
11929         errno = d = 0;
11930 #ifdef VMS
11931         /*
11932           This is because VMS zxin() is C-Library fread() 
11933           but the file was opened with zopeni(), which is RMS.
11934         */
11935         while (((c = zminchar()) > -1) && !cancelfile) {
11936             ffc++;
11937             if (zzout(dout,c) < 0)
11938               break;
11939         }
11940 #else  /* VMS */
11941         while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
11942             ftpsnd.bytes += n;
11943             ffc += n;
11944             debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
11945             ckhexdump("doftpsend2 zxin",buf,16);
11946 #ifdef CK_SSL
11947             if (ssl_ftp_data_active_flag) {
11948                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11949                     if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
11950                       break;
11951                     spackets++;
11952                     pktnum++;
11953                     if (fdispla != XYFD_B) {
11954                         spktl = d;
11955                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11956                     }
11957                 }
11958             } else {
11959 #endif /* CK_SSL */
11960                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11961                     if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
11962                         || iscanceled())
11963                       break;
11964                     spackets++;
11965                     pktnum++;
11966                     if (fdispla != XYFD_B) {
11967                         spktl = d;
11968                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11969                     }
11970                 }
11971 #ifdef CK_SSL
11972             }
11973 #endif /* CK_SSL */
11974             if (d <= 0)
11975               break;
11976         }
11977 #endif  /* VMS */
11978
11979         debug(F111,"doftpsend2 XX zxin",ckltoa(n),ffc);
11980         if (n < 0)
11981           fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
11982         if (d < 0 || (d = secure_flush(dout)) < 0) {
11983             if (d == -1 && errno && errno != EPIPE)
11984               perror("netout");
11985             ftpsnd.bytes = -1;
11986         }
11987         break;
11988
11989       case FTT_ASC:                     /* Text mode */
11990 #ifndef NOCSETS
11991         if (ftpsnd.xlate) {             /* With translation */
11992             initxlate(ftpsnd.incs,ftpsnd.outcs);
11993             while (!cancelfile) {
11994                 if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
11995                 if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
11996             }
11997         } else {
11998 #endif /* NOCSETS */
11999             /* Text mode, no translation */
12000             while (((c = zminchar()) > -1) && !cancelfile) {
12001                 ffc++;
12002                 if (xxout(c) < 0)
12003                   break;
12004             }
12005             d = 0;
12006 #ifndef NOCSETS
12007         }
12008 #endif /* NOCSETS */
12009         if (dout == -1 || (d = secure_flush(dout)) < 0) {
12010             if (d == -1 && errno && errno != EPIPE)
12011               perror("netout");
12012             ftpsnd.bytes = -1;
12013         }
12014         break;
12015     }
12016     tfc += ffc;                         /* Total file chars */
12017 #ifdef GFTIMER
12018     fpfsecs = gftimer();
12019 #endif /* GFTIMER */
12020     zclose(ZIFILE);                     /* Close input file */
12021 #ifdef PIPESEND
12022     if (sndfilter)                      /* Undo this (it's per file) */
12023       pipesend = 0;
12024 #endif /* PIPESEND */
12025
12026 #ifdef CK_SSL
12027         if (ssl_ftp_data_active_flag) {
12028             SSL_shutdown(ssl_ftp_data_con);
12029             SSL_free(ssl_ftp_data_con);
12030             ssl_ftp_data_active_flag = 0;
12031             ssl_ftp_data_con = NULL;
12032         }
12033 #endif /* CK_SSL */
12034
12035 #ifdef TCPIPLIB
12036     socket_close(dout);                 /* Close data connection */
12037 #else /* TCPIPLIB */
12038 #ifdef USE_SHUTDOWN
12039     shutdown(dout, 1+1);
12040 #endif /* USE_SHUTDOWN */
12041     close(dout);
12042 #endif /* TCPIPLIB */
12043     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
12044     signal(SIGINT, ftpsnd.oldintr);            /* Put back interrupts */
12045 #ifdef SIGPIPE
12046     if (ftpsnd.oldintp)
12047       signal(SIGPIPE, ftpsnd.oldintp);
12048 #endif /* SIGPIPE */
12049     if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
12050         debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
12051         ftpsndret = -1;
12052 #ifdef NTSIG
12053         ckThreadEnd(threadinfo);
12054 #endif /* NTSIG */
12055         return;
12056     } else if (cancelfile) {
12057         debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
12058         ftpsndret = -1;
12059 #ifdef NTSIG
12060         ckThreadEnd(threadinfo);
12061 #endif /* NTSIG */
12062         return;
12063     }
12064     debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
12065     ftpsndret = 0;
12066 #ifdef NTSIG
12067      ckThreadEnd(threadinfo);
12068 #endif /* NTSIG */
12069 }
12070
12071 static int
12072 sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
12073     char *cmd, *local, *remote; int xlate, incs, outcs, restart;
12074 {
12075     if (!remote) remote = "";           /* Check args */
12076     if (!*remote) remote = local;
12077     if (!local) local = "";
12078     if (!*local) return(-1);
12079     if (!cmd) cmd = "";
12080     if (!*cmd) cmd = "STOR";
12081
12082     debug(F111,"ftp sendrequest restart",local,restart);
12083
12084     nout = 0;                           /* Init output buffer count */
12085     ftpsnd.bytes = 0;                   /* File input byte count */
12086     dout = -1;
12087
12088 #ifdef FTP_PROXY
12089     if (proxy) {
12090         proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
12091         return(0);
12092     }
12093 #endif /* FTP_PROXY */
12094
12095     changetype(ftp_typ,0);              /* Change type for this file */
12096
12097     ftpsnd.oldintr = NULL;              /* Set up interrupt handler */
12098     ftpsnd.oldintp = NULL;
12099     ftpsnd.restart = restart;
12100     ftpsnd.xlate = xlate;
12101     ftpsnd.lmode = "wb";
12102
12103 #ifdef PIPESEND                         /* Use Kermit API for file i/o... */
12104     if (sndfilter) {
12105         char * p = NULL, * q;
12106 #ifndef NOSPL
12107         int n = CKMAXPATH;
12108         if (cmd_quoting && (p = (char *) malloc(n + 1))) {
12109             q = p;
12110             debug(F110,"sendrequest pipesend filter",sndfilter,0);
12111             zzstring(sndfilter,&p,&n);
12112             debug(F111,"sendrequest pipename",q,n);
12113             if (n <= 0) {
12114                 printf("?Sorry, send filter + filename too long, %d max.\n",
12115                        CKMAXPATH
12116                        );
12117                 free(q);
12118                 return(-1);
12119             }
12120             ckstrncpy(filnam,q,CKMAXPATH+1);
12121             free(q);
12122             local = filnam;
12123         }
12124 #endif /* NOSPL */
12125     }
12126
12127     if (sndfilter)                      /* If sending thru a filter */
12128       pipesend = 1;                     /* set this for open and i/o */
12129 #endif /* PIPESEND */
12130     
12131 #ifdef VMS
12132     debug(F101,"XXX before openi binary","",binary);
12133     debug(F101,"XXX before openi ftp_typ","",ftp_typ);
12134 #endif  /* VMS */
12135
12136     if (openi(local) == 0)              /* Try to open the input file */
12137       return(-1);
12138
12139 #ifdef VMS
12140     debug(F101,"XXX after openi binary","",binary);
12141     debug(F101,"XXX after openi ftp_typ","",ftp_typ);
12142     if (!forcetype) {
12143         if (binary != ftp_typ) {        /* VMS zopeni() sets binary */
12144             debug(F101,"XXX changing type","",binary);
12145             doftptyp(binary);
12146             debug(F101,"XXX after doftptyp","",ftp_typ);
12147
12148             /* **** */
12149             if (displa && fdispla) {    /* Update file type display */
12150                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,local);
12151             }
12152         }
12153     }
12154 #endif  /* VMS */
12155     ftpsndret = 0;
12156     ftpsnd.incs = incs;
12157     ftpsnd.outcs = outcs;
12158     ftpsnd.cmd = cmd;
12159     ftpsnd.local = local;
12160     ftpsnd.remote = remote;
12161     ftpsnd.oldintr = signal(SIGINT, cancelsend);
12162     havesigint = 0;
12163
12164     if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
12165       return(-1);
12166     if (ftpsndret < 0)
12167       return(-1);
12168     if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
12169       return(-1);
12170
12171     return(ftpsndret);
12172 }
12173
12174 static sigtype
12175 cancelrecv(sig) int sig; {
12176     havesigint++;
12177     cancelfile = 0;
12178     cancelgroup++;
12179     secure_getc(0,1);                   /* Initialize net input buffers */
12180     printf(" Canceling...\n");
12181     debug(F100,"ftp cancelrecv caught SIGINT","",0);
12182     fflush(stdout);
12183     if (fp_nml) {
12184         if (fp_nml != stdout)
12185           fclose(fp_nml);
12186         fp_nml = NULL;
12187     }
12188 #ifndef OS2
12189     longjmp(recvcancel, 1);
12190 #else
12191     PostCtrlCSem();
12192 #endif /* OS2 */
12193 }
12194
12195 /* Argumentless front-end for secure_getc() */
12196
12197 static int
12198 netgetc() {
12199     return(secure_getc(globaldin,0));
12200 }
12201
12202 /* Returns -1 on failure, 0 on success, 1 if file skipped */
12203
12204 /*
12205   Sets ftpcode < 0 on failure if failure reason is not server reply code:
12206     -1: interrupted by user.
12207     -2: error opening or writing output file (reason in errno).
12208     -3: failure to make data connection.
12209     -4: network read error (reason in errno).
12210 */
12211
12212 struct xx_ftprecv {
12213     int reply;
12214     int fcs;
12215     int rcs;
12216     int recover;
12217     int xlate;
12218     int din;
12219     int is_retr;
12220     sig_t oldintr, oldintp;
12221     char * cmd;
12222     char * local;
12223     char * remote;
12224     char * lmode;
12225     char * pipename;
12226     int    tcrflag;
12227     CK_OFF_T localsize;
12228 };
12229 static struct xx_ftprecv ftprecv;
12230
12231 static int ftprecvret = 0;
12232
12233 static VOID
12234 #ifdef CK_ANSIC
12235 failftprecv(VOID * threadinfo)
12236 #else
12237 failftprecv(threadinfo) VOID * threadinfo;
12238 #endif /* CK_ANSIC */
12239 {
12240 #ifdef NTSIG
12241     if (threadinfo) {                   /* Thread local storage... */
12242         TlsSetValue(TlsIndex,threadinfo);
12243         debug(F100, "docmdfile called with threadinfo block","", 0);
12244     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12245 #endif /* NTSIG */
12246
12247 #ifdef CK_LOGIN
12248 #ifdef IKSD
12249 #ifdef NT
12250     if (inserver)
12251       setntcreds();
12252 #endif /* NT */
12253 #endif /* IKSD */
12254 #endif /* CK_LOGIN */
12255
12256     while (cpend) {
12257         ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12258     }
12259     if (data >= 0) {
12260 #ifdef CK_SSL
12261         if (ssl_ftp_data_active_flag) {
12262             SSL_shutdown(ssl_ftp_data_con);
12263             SSL_free(ssl_ftp_data_con);
12264             ssl_ftp_data_active_flag = 0;
12265             ssl_ftp_data_con = NULL;
12266         }
12267 #endif /* CK_SSL */
12268 #ifdef TCPIPLIB
12269         socket_close(data);
12270 #else /* TCPIPLIB */
12271 #ifdef USE_SHUTDOWN
12272         shutdown(data, 1+1);
12273 #endif /* USE_SHUTDOWN */
12274         close(data);
12275 #endif /* TCPIPLIB */
12276         data = -1;
12277         globaldin = -1;
12278     }
12279     if (ftprecv.oldintr)
12280       signal(SIGINT, ftprecv.oldintr);
12281     ftpcode = -1;
12282     ftprecvret = -1;
12283
12284 #ifndef OS2
12285     /* TEST ME IN K95 */
12286     if (havesigint) {
12287         havesigint = 0;
12288         debug(F100,"ftp failftprecv chain to trap()...","",0);
12289         if (ftprecv.oldintr != SIG_IGN)
12290           (*ftprecv.oldintr)(SIGINT);
12291         /* NOTREACHED (I hope!) */
12292         debug(F100,"ftp failftprecv return from trap()...","",0);
12293     }
12294 #endif /* OS2 */
12295     return;
12296 }
12297
12298 static VOID
12299 #ifdef CK_ANSIC
12300 doftprecv(VOID * threadinfo)
12301 #else
12302 doftprecv(threadinfo) VOID * threadinfo;
12303 #endif /* CK_ANSIC */
12304 {
12305 #ifdef NTSIG
12306     if (threadinfo) {                   /* Thread local storage... */
12307         TlsSetValue(TlsIndex,threadinfo);
12308         debug(F100, "docmdfile called with threadinfo block","", 0);
12309     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12310 #endif /* NTSIG */
12311 #ifdef CK_LOGIN
12312 #ifdef IKSD
12313 #ifdef NT
12314     if (inserver)
12315       setntcreds();
12316 #endif /* NT */
12317 #endif /* IKSD */
12318 #endif /* CK_LOGIN */
12319
12320 #ifndef COMMENT
12321     if (!out2screen && !ftprecv.pipename) {
12322         int x;
12323         char * local;
12324         local = ftprecv.local;
12325         x = zchko(local);
12326         if (x < 0) {
12327             if ((!dpyactive || ftp_deb))
12328               fprintf(stderr,
12329                       "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
12330             signal(SIGINT, ftprecv.oldintr);
12331             ftpcode = -2;
12332             ftprecvret = -1;
12333 #ifdef NTSIG
12334             ckThreadEnd(threadinfo);
12335 #endif /* NTSIG */
12336             return;
12337         }
12338     }
12339 #endif /* COMMENT */
12340     changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
12341     if (initconn()) {                   /* Initialize the data connection */
12342         signal(SIGINT, ftprecv.oldintr);
12343         ftpcode = -1;
12344         ftprecvret = -3;
12345 #ifdef NTSIG
12346         ckThreadEnd(threadinfo);
12347 #endif /* NTSIG */
12348         return;
12349     }
12350     secure_getc(0,1);                   /* Initialize net input buffers */
12351     ftprecvret = 0;
12352
12353 #ifdef NTSIG
12354     ckThreadEnd(threadinfo);
12355 #endif /* NTSIG */
12356 }
12357
12358 static VOID
12359 #ifdef CK_ANSIC
12360 failftprecv2(VOID * threadinfo)
12361 #else
12362 failftprecv2(threadinfo) VOID * threadinfo;
12363 #endif /* CK_ANSIC */
12364 {
12365 #ifdef NTSIG
12366     if (threadinfo) {                   /* Thread local storage... */
12367         TlsSetValue(TlsIndex,threadinfo);
12368         debug(F100, "docmdfile called with threadinfo block","", 0);
12369     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12370 #endif /* NTSIG */
12371 #ifdef CK_LOGIN
12372 #ifdef IKSD
12373 #ifdef NT
12374     if (inserver)
12375       setntcreds();
12376 #endif /* NT */
12377 #endif /* IKSD */
12378 #endif /* CK_LOGIN */
12379
12380     /* Cancel using RFC959 recommended IP,SYNC sequence  */
12381
12382     debug(F100,"ftp recvrequest CANCEL","",0);
12383 #ifdef GFTIMER
12384     fpfsecs = gftimer();
12385 #endif /* GFTIMER */
12386 #ifdef SIGPIPE
12387     if (ftprecv.oldintp)
12388       signal(SIGPIPE, ftprecv.oldintr);
12389 #endif /* SIGPIPE */
12390     signal(SIGINT, SIG_IGN);
12391     if (!cpend) {
12392         ftpcode = -1;
12393         signal(SIGINT, ftprecv.oldintr);
12394         ftprecvret = -1;
12395 #ifdef NTSIG
12396         ckThreadEnd(threadinfo);
12397 #endif /* NTSIG */
12398         return;
12399     }
12400     cancel_remote(ftprecv.din);
12401
12402 #ifdef FTP_TIMEOUT
12403     if (ftp_timed_out && out2screen && !quiet)
12404       printf("\n?Timed out.\n");
12405 #endif  /* FTP_TIMEOUT */
12406
12407     if (ftpcode > -1)
12408       ftpcode = -1;
12409     if (data >= 0) {
12410 #ifdef CK_SSL
12411         if (ssl_ftp_data_active_flag) {
12412             SSL_shutdown(ssl_ftp_data_con);
12413             SSL_free(ssl_ftp_data_con);
12414             ssl_ftp_data_active_flag = 0;
12415             ssl_ftp_data_con = NULL;
12416         }
12417 #endif /* CK_SSL */
12418 #ifdef TCPIPLIB
12419         socket_close(data);
12420 #else /* TCPIPLIB */
12421 #ifdef USE_SHUTDOWN
12422         shutdown(data, 1+1);
12423 #endif /* USE_SHUTDOWN */
12424         close(data);
12425 #endif /* TCPIPLIB */
12426         data = -1;
12427         globaldin = -1;
12428     }
12429     if (!out2screen) {
12430         int x = 0;
12431         debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
12432         zclose(ZOFILE);
12433         switch (keep) {                 /* which is... */
12434           case SET_AUTO:                /* AUTO */
12435             if (curtype == FTT_ASC)     /* Delete file if TYPE A. */
12436               x = 1;
12437             break;
12438           case SET_OFF:                 /* DISCARD */
12439             x = 1;                      /* Delete file, period. */
12440             break;
12441           default:                      /* KEEP */
12442             break;
12443         }
12444         if (x) {
12445             x = zdelet(ftprecv.local);
12446             debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
12447         }
12448     }
12449     if (ftprecv.din) {
12450 #ifdef TCPIPLIB
12451         socket_close(ftprecv.din);
12452 #else /* TCPIPLIB */
12453 #ifdef USE_SHUTDOWN
12454         shutdown(ftprecv.din, 1+1);
12455 #endif /* USE_SHUTDOWN */
12456         close(ftprecv.din);
12457 #endif /* TCPIPLIB */
12458     }
12459     signal(SIGINT, ftprecv.oldintr);
12460     ftprecvret = -1;
12461
12462     if (havesigint) {
12463         havesigint = 0;
12464         debug(F100,"FTP failftprecv2 chain to trap()...","",0);
12465 #ifdef OS2
12466         debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
12467         PostCtrlCSem();
12468 #else /* OS2 */
12469         if (ftprecv.oldintr != SIG_IGN)
12470           (*ftprecv.oldintr)(SIGINT);
12471         /* NOTREACHED (I hope!) */
12472         debug(F100,"ftp failftprecv2 return from trap()...","",0);
12473 #endif /* OS2 */
12474     }
12475 }
12476
12477 static VOID
12478 #ifdef CK_ANSIC
12479 doftprecv2(VOID * threadinfo)
12480 #else
12481 doftprecv2(threadinfo) VOID * threadinfo;
12482 #endif /* CK_ANSIC */
12483 {
12484     register int c, d;
12485     CK_OFF_T bytes = (CK_OFF_T)0;
12486     int bare_lfs = 0;
12487     int blksize = 0;
12488     ULONG start = 0L, stop;
12489     char * p;
12490     static char * rcvbuf = NULL;
12491     static int rcvbufsiz = 0;
12492 #ifdef CK_URL
12493     char newname[CKMAXPATH+1];          /* For file dialog */
12494 #endif /* CK_URL */
12495     extern int adl_ask;
12496
12497 #ifdef FTP_TIMEOUT
12498     ftp_timed_out = 0;
12499 #endif  /* FTP_TIMEOUT */
12500
12501     ftprecv.din = -1;
12502 #ifdef NTSIG
12503     if (threadinfo) {                   /* Thread local storage... */
12504         TlsSetValue(TlsIndex,threadinfo);
12505         debug(F100, "docmdfile called with threadinfo block","", 0);
12506     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12507 #endif /* NTSIG */
12508 #ifdef CK_LOGIN
12509 #ifdef IKSD
12510 #ifdef NT
12511     if (inserver)
12512       setntcreds();
12513 #endif /* NT */
12514 #endif /* IKSD */
12515 #endif /* CK_LOGIN */
12516
12517     if (ftprecv.recover) {                      /* Initiate recovery */
12518         x = ftpcmd("REST",ckfstoa(ftprecv.localsize),-1,-1,ftp_vbm);
12519         debug(F111,"ftp reply","REST",x);
12520         if (x == REPLY_CONTINUE) {
12521             ftprecv.lmode = "ab";
12522             rs_len = ftprecv.localsize;
12523         } else {
12524             ftprecv.recover = 0;
12525         }
12526     }
12527     /* IMPORTANT: No FTP commands can come between REST and RETR! */
12528
12529     debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
12530
12531     /* Send the command and get reply */
12532     debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
12533     debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
12534
12535     if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
12536         != REPLY_PRELIM) {
12537         signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
12538         ftprecvret = -1;                /* ftpcode is set by ftpcmd() */
12539 #ifdef NTSIG
12540         ckThreadEnd(threadinfo);
12541 #endif /* NTSIG */
12542         return;
12543     }
12544     ftprecv.din = dataconn("r");        /* Good reply, open data connection */
12545     globaldin = ftprecv.din;            /* Global copy of file descriptor */
12546     if (ftprecv.din == -1) {            /* Check for failure */
12547         ftpcode = -3;                   /* Code for no data connection */
12548         ftprecvret = -1;
12549 #ifdef NTSIG
12550         ckThreadEnd(threadinfo);
12551 #endif /* NTSIG */
12552         return;
12553     }
12554 #ifdef CK_URL
12555     /* In K95 GUI put up a file box */
12556     if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
12557         int x;
12558         char * preface =
12559 "\r\nIncoming file from FTP server...\r\n\
12560 Please confirm output file specification or supply an alternative:";
12561
12562         x = uq_file(preface,            /* K95 GUI: Put up file box. */
12563                     NULL,
12564                     4,
12565                     NULL,
12566                     ftprecv.local ? ftprecv.local : ftprecv.remote,
12567                     newname,
12568                     CKMAXPATH+1
12569                     );
12570         if (x > 0) {
12571             ftprecv.local = newname;    /* Substitute user's file name */
12572             if (x == 2)                 /* And append if user said to */
12573               ftprecv.lmode = "ab";
12574         }
12575     }
12576 #endif /* CK_URL */
12577     x = 1;                              /* Output file open OK? */
12578     if (ftprecv.pipename) {             /* Command */
12579         x = zxcmd(ZOFILE,ftprecv.pipename);
12580         debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
12581     } else if (!out2screen) {           /* File */
12582         struct filinfo xx;
12583         xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
12584         xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
12585         /* Append or New */
12586         xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
12587         x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
12588         debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
12589     }
12590     if (x < 1) {                        /* Failure to open output file */
12591         if ((!dpyactive || ftp_deb))
12592           fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
12593         ftprecvret = -1;
12594 #ifdef NTSIG
12595         ckThreadEnd(threadinfo);
12596 #endif /* NTSIG */
12597         return;
12598     }
12599     blksize = FTP_BUFSIZ;               /* Allocate input buffer */
12600
12601     debug(F101,"ftp recvrequest blksize","",blksize);
12602     debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
12603
12604     if (rcvbufsiz < blksize) {          /* if necessary */
12605         if (rcvbuf) {
12606             free(rcvbuf);
12607             rcvbuf = NULL;
12608         }
12609         rcvbuf = (char *)malloc((unsigned)blksize);
12610         if (!rcvbuf) {
12611             debug(F100,"ftp get rcvbuf malloc failed","",0);
12612             ftpcode = -2;
12613 #ifdef ENOMEM
12614             errno = ENOMEM;
12615 #endif /* ENOMEM */
12616             if ((!dpyactive || ftp_deb))
12617               perror("malloc");
12618             rcvbufsiz = 0;
12619             ftprecvret = -1;
12620 #ifdef NTSIG
12621             ckThreadEnd(threadinfo);
12622 #endif /* NTSIG */
12623             return;
12624         }
12625         debug(F101,"ftp get rcvbuf malloc ok","",blksize);
12626         rcvbufsiz = blksize;
12627     }
12628     debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
12629
12630     ffc = (CK_OFF_T)0;                  /* Character counter */
12631     cps = oldcps = 0L;                  /* Thruput */
12632     start = gmstimer();                 /* Start time (msecs) */
12633 #ifdef GFTIMER
12634     rftimer();                          /* Start time (float) */
12635 #endif /* GFTIMER */
12636
12637     debug(F111,"ftp get type",ftprecv.local,curtype);
12638     debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
12639     switch (curtype) {
12640       case FTT_BIN:                     /* Binary mode */
12641       case FTT_TEN:                     /* TENEX mode */
12642         d = 0;
12643         while (1) {
12644             errno = 0;
12645             c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
12646             if (cancelfile) {
12647                 failftprecv2(threadinfo);
12648 #ifdef NTSIG
12649                 ckThreadEnd(threadinfo);
12650 #endif /* NTSIG */
12651                 return;
12652             }
12653             if (c < 1)
12654               break;
12655 #ifdef printf                           /* (What if it isn't?) */
12656             if (out2screen && !ftprecv.pipename) {
12657                 int i;
12658                 for (i = 0; i < c; i++)
12659                   printf("%c",rcvbuf[i]);
12660             } else
12661 #endif /* printf */
12662               {
12663                 register int i;
12664                 i = 0;
12665                 errno = 0;
12666                 while (i < c) {
12667                     if (zmchout(rcvbuf[i++]) < 0) {
12668                         d = i;
12669                         break;
12670                     }
12671                 }
12672             }
12673             bytes += c;
12674             ffc += c;
12675         }
12676 #ifdef FTP_TIMEOUT
12677         if (c == -3) {
12678             debug(F100,"ftp recvrequest timeout","",0); 
12679             bytes = (CK_OFF_T)-1;
12680             ftp_timed_out = 1;
12681             ftpcode = -3;
12682         } else
12683 #endif  /* FTP_TIMEOUT */
12684         if (c < 0) {
12685             debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
12686             if (c == -1 && errno != EPIPE)
12687               if ((!dpyactive || ftp_deb))
12688                 perror("netin");
12689             bytes = (CK_OFF_T)-1;
12690             ftpcode = -4;
12691         }
12692         if (d < c) {
12693             ftpcode = -2;
12694             if ((!dpyactive || ftp_deb)) {
12695                 char * p;
12696                 p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
12697                 if (d < 0)
12698                   fprintf(stderr,
12699                           "local(3): %s: %s\n", ftprecv.local, ck_errstr());
12700                 else
12701                   fprintf(stderr,
12702                           "%s: short write\n", ftprecv.local);
12703             }
12704         }
12705         break;
12706
12707       case FTT_ASC:                     /* Text mode */
12708         debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
12709 #ifndef NOCSETS
12710         if (ftprecv.xlate) {
12711             int t;
12712 #ifdef CK_ANSIC
12713             int (*fn)(char);
12714 #else
12715             int (*fn)();
12716 #endif /* CK_ANSIC */
12717             debug(F110,"ftp recvrequest (data)","initxlate",0);
12718             initxlate(ftprecv.rcs,ftprecv.fcs);         /* (From,To) */
12719             if (ftprecv.pipename) {
12720                 fn = pipeout;
12721                 debug(F110,"ftp recvrequest ASCII","pipeout",0);
12722             } else {
12723                 fn = out2screen ? scrnout : putfil;
12724                 debug(F110,"ftp recvrequest ASCII",
12725                       out2screen ? "scrnout" : "putfil",0);
12726             }
12727             while (1) {
12728                 /* Get byte from net */
12729                 c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12730                 if (cancelfile) {
12731                     failftprecv2(threadinfo);
12732 #ifdef NTSIG
12733                     ckThreadEnd(threadinfo);
12734 #endif /* NTSIG */
12735                     return;
12736                 }
12737                 if (c0 < 0)
12738                   break;
12739                 /* Second byte from net */
12740                 c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12741                 if (cancelfile) {
12742                     failftprecv2(threadinfo);
12743 #ifdef NTSIG
12744                     ckThreadEnd(threadinfo);
12745 #endif /* NTSIG */
12746                     return;
12747                 }
12748                 if (c1 < 0)
12749                   break;
12750 #ifdef COMMENT
12751                 /* K95: Check whether we need this */
12752                 if (fileorder > 0)      /* Little Endian */
12753                   bytswap(&c0,&c1);     /* swap bytes*/
12754 #endif /* COMMENT */
12755
12756 #ifdef OS2
12757                 if ( out2screen &&            /* we're translating to UCS-2 */ 
12758                      !k95stdout && !inserver) /* for the real screen... */     
12759                 {
12760                     union {
12761                         USHORT ucs2;
12762                         UCHAR  bytes[2];
12763                     } output;
12764
12765                     output.bytes[0] = c1;
12766                     output.bytes[1] = c0;
12767
12768                     VscrnWrtUCS2StrAtt(VCMD,
12769                                        &output.ucs2,
12770                                        1,
12771                                        wherey[VCMD],
12772                                        wherex[VCMD],
12773                                        &colorcmd
12774                                        );
12775
12776                 } else 
12777 #endif /* OS2 */
12778                 {
12779                     if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12780                     if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12781                 }
12782             }
12783         } else {
12784 #endif /* NOCSETS */
12785             while (1) {
12786                 c = secure_getc(ftprecv.din,0);
12787                 if (cancelfile
12788 #ifdef FTP_TIMEOUT
12789                     || ftp_timed_out
12790 #endif  /* FTP_TIMEOUT */
12791                     ) {
12792                     failftprecv2(threadinfo);
12793 #ifdef NTSIG
12794                     ckThreadEnd(threadinfo);
12795 #endif /* NTSIG */
12796                     return;
12797                 }
12798                 if (c < 0 || c == EOF)
12799                   break;
12800 #ifdef UNIX
12801                 /* Record format conversion for Unix */
12802                 /* SKIP THIS FOR WINDOWS! */
12803                 if (c == '\n')
12804                   bare_lfs++;
12805                 while (c == '\r') {
12806                     bytes++;
12807                     if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
12808                         ftprecv.tcrflag) {
12809                         if (cancelfile) {
12810                             failftprecv2(threadinfo);
12811 #ifdef NTSIG
12812                             ckThreadEnd(threadinfo);
12813 #endif /* NTSIG */
12814                             return;
12815                         }
12816                         if (c < 0 || c == EOF)
12817                           goto break2;
12818                         if (c == '\0') {
12819                             bytes++;
12820                             goto contin2;
12821                         }
12822                     }
12823                 }
12824                 if (c < 0)
12825                   break;
12826 #endif /* UNX */
12827
12828                 if (out2screen && !ftprecv.pipename)
12829 #ifdef printf
12830                   printf("%c",(char)c);
12831 #else
12832                   putchar((char)c);
12833 #endif /* printf */
12834                 else
12835                   if ((d = zmchout(c)) < 0)
12836                     break;
12837                 bytes++;
12838                 ffc++;
12839               contin2:
12840                 ;
12841             }
12842           break2:
12843             if (bare_lfs && (!dpyactive || ftp_deb)) {
12844                 printf("WARNING! %d bare linefeeds received in ASCII mode\n",
12845                        bare_lfs);
12846                 printf("File might not have transferred correctly.\n");
12847             }
12848             if (ftprecv.din == -1) {
12849                 bytes = (CK_OFF_T)-1;
12850             }
12851             if (c == -2)
12852               bytes = (CK_OFF_T)-1;
12853             break;
12854 #ifndef NOCSETS
12855         }
12856 #endif /* NOCSETS */
12857     }
12858     if (ftprecv.pipename || !out2screen) {
12859         zclose(ZOFILE);                 /* Close the file */
12860         debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
12861         if (ftpcode < 0) {              /* If download failed */
12862             int x = 0;
12863             switch (keep) {             /* which is... */
12864               case SET_AUTO:            /* AUTO */
12865                 if (curtype == FTT_ASC) /* Delete file if TYPE A. */
12866                   x = 1;
12867                 break;
12868               case SET_OFF:             /* DISCARD */
12869                 x = 1;                  /* Delete file, period. */
12870                 break;
12871               default:                  /* KEEP */
12872                 break;
12873             }
12874             if (x) {
12875                 x = zdelet(ftprecv.local);
12876                 debug(F111,"ftp get delete incomplete",ftprecv.local,x);
12877             }
12878         }
12879     }
12880     signal(SIGINT, ftprecv.oldintr);
12881 #ifdef SIGPIPE
12882     if (ftprecv.oldintp)
12883       signal(SIGPIPE, ftprecv.oldintp);
12884 #endif /* SIGPIPE */
12885     stop = gmstimer();
12886 #ifdef GFTIMER
12887     fpfsecs = gftimer();
12888 #endif /* GFTIMER */
12889     tfc += ffc;
12890
12891 #ifdef TCPIPLIB
12892     socket_close(ftprecv.din);
12893 #else /* TCPIPLIB */
12894 #ifdef USE_SHUTDOWN
12895     shutdown(ftprecv.din, 1+1);
12896 #endif /* USE_SHUTDOWN */
12897     close(ftprecv.din);
12898 #endif /* TCPIPLIB */
12899     ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12900     ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || 
12901                    ftprecv.reply == REPLY_ERROR) ? -1 : 0);
12902 #ifdef NTSIG
12903      ckThreadEnd(threadinfo);
12904 #endif /* NTSIG */
12905 }
12906
12907 static int
12908 recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
12909             xlate, fcs, rcs)
12910     char *cmd, *local, *remote, *lmode, *pipename;
12911     int printnames, recover, xlate, fcs, rcs;
12912 {
12913 #ifdef NT
12914     struct _stat stbuf;
12915 #else /* NT */
12916     struct stat stbuf;
12917 #endif /* NT */
12918
12919 #ifdef DEBUG
12920     if (deblog) {
12921         debug(F111,"ftp recvrequest cmd",cmd,recover);
12922         debug(F110,"ftp recvrequest local ",local,0);
12923         debug(F111,"ftp recvrequest remote",remote,ftp_typ);
12924         debug(F110,"ftp recvrequest pipename ",pipename,0);
12925         debug(F101,"ftp recvrequest xlate","",xlate);
12926         debug(F101,"ftp recvrequest fcs","",fcs);
12927         debug(F101,"ftp recvrequest rcs","",rcs);
12928     }
12929 #endif /* DEBUG */
12930
12931     ftprecv.localsize = (CK_OFF_T)0;
12932
12933     if (remfile) {                      /* See remcfm(), remtxt() */
12934         if (rempipe) {
12935             pipename = remdest;
12936         } else {
12937             local = remdest;
12938             if (remappd) lmode = "ab";
12939         }
12940     }
12941     out2screen = 0;
12942     if (!cmd) cmd = "";                 /* Core dump prevention */
12943     if (!remote) remote = "";
12944     if (!lmode) lmode = "";
12945
12946     if (pipename) {                     /* No recovery for pipes. */
12947         recover = 0;
12948         if (!local)
12949           local = pipename;
12950     } else {
12951         if (!local)                     /* Output to screen? */
12952           local = "-";
12953         out2screen = !strcmp(local,"-");
12954     }
12955     debug(F101,"ftp recvrequest out2screen","",out2screen);
12956
12957 #ifdef OS2
12958     if ( ftp_xla && out2screen && !k95stdout && !inserver )
12959         fcs = FC_UCS2;
12960 #endif /* OS2 */
12961
12962     if (out2screen)                     /* No recovery to screen */
12963       recover = 0;
12964     if (!ftp_typ)                       /* No recovery in text mode */
12965       recover = 0;
12966     ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
12967
12968     if (!ftprecv.is_retr)               /* No recovery except for RETRieve */
12969       recover = 0;
12970
12971 #ifdef COMMENT
12972     if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
12973         if (recursive && ckstrchr(local,'/')) {
12974             
12975         }
12976     }
12977 #endif /* COMMENT */
12978
12979     ftprecv.localsize = (CK_OFF_T)0;    /* Local file size */
12980     rs_len = (CK_OFF_T)0;               /* Recovery point */
12981
12982     debug(F101,"ftp recvrequest recover","",recover);
12983     if (recover) {                      /* Recovering... */
12984         if (stat(local, &stbuf) < 0) {  /* Can't stat local file */
12985             debug(F101,"ftp recvrequest recover stat failed","",errno);
12986             recover = 0;                /* So cancel recovery */
12987         } else {                        /* Have local file info */
12988             ftprecv.localsize = stbuf.st_size;  /* Get size */
12989             /* Remote file smaller than local */
12990             if (fsize < ftprecv.localsize) {
12991                 debug(F101,"ftp recvrequest recover remote smaller","",fsize);
12992                 recover = 0;            /* Recovery can't work */
12993             } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
12994                 debug(F111,"ftp recvrequest recover equal size",
12995                       remote,ftprecv.localsize);
12996                 return(1);
12997             }
12998 #ifdef COMMENT
12999 /*
13000   The problem here is that the original partial file never got its date
13001   set, either because FTP DATES was OFF, or because the partial file was
13002   downloaded by some other program that doesn't set local file dates, or
13003   because Kermit only sets the file's date when the download was complete
13004   and successful.  In all these cases, the local file has a later time
13005   than the remote.
13006 */
13007             if (recover) {              /* Remote is bigger */
13008                 x = chkmodtime(local,remote,0); /* Check file dates */
13009                 debug(F111,"ftp recvrequest chkmodtime",remote,x);
13010                 if (x != 1)             /* Dates must be equal! */
13011                   recover = 0;          /* If not, get whole file */
13012             }
13013 #endif /* COMMENT */
13014         }
13015         debug(F111,"ftp recvrequest recover",remote,recover);
13016     }
13017
13018 #ifdef FTP_PROXY
13019     if (proxy && ftprecv.is_retr)
13020       return(proxtrans(cmd, local ? local : remote, remote));
13021 #endif /* FTP_PROXY */
13022
13023     ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
13024
13025     ftprecv.reply = 0;
13026     ftprecv.fcs = fcs;
13027     ftprecv.rcs = rcs;
13028     ftprecv.recover = recover;
13029     ftprecv.xlate = xlate;
13030     ftprecv.cmd = cmd;
13031     ftprecv.local = local;
13032     ftprecv.remote = remote;
13033     ftprecv.lmode = lmode;
13034     ftprecv.pipename = pipename;
13035     ftprecv.oldintp = NULL;
13036     ftpcode = 0;
13037
13038     havesigint = 0;
13039     ftprecv.oldintr = signal(SIGINT, cancelrecv);
13040     if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
13041       return -1;
13042
13043 #ifdef FTP_TIMEOUT
13044     debug(F111,"ftp recvrequest ftprecvret",remote,ftprecvret);
13045     debug(F111,"ftp recvrequest ftp_timed_out",remote,ftp_timed_out);
13046     if (ftp_timed_out)
13047       ftprecvret = -1;
13048 #endif  /* FTP_TIMEOUT */
13049
13050     if (ftprecvret < 0)
13051       return -1;
13052
13053     if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
13054       return -1;
13055     return ftprecvret;
13056 }
13057
13058 /*
13059  * Need to start a listen on the data channel before we send the command,
13060  * otherwise the server's connect may fail.
13061  */
13062 static int
13063 initconn() {
13064     register char *p, *a;
13065     int result, tmpno = 0;
13066     int on = 1;
13067     GSOCKNAME_T len;
13068
13069 #ifndef NO_PASSIVE_MODE
13070     int a1,a2,a3,a4,p1,p2;
13071
13072     if (passivemode) {
13073         data = socket(AF_INET, SOCK_STREAM, 0);
13074         globaldin = data;
13075         if (data < 0) {
13076             perror("ftp: socket");
13077             return(-1);
13078         }
13079         if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13080             printf("Passive mode refused\n");
13081             passivemode = 0;
13082             return(initconn());
13083         }
13084 /*
13085   Now we have a string of comma-separated one-byte unsigned integer values,
13086   The first four are the an IP address.  The fifth is the MSB of the port
13087   number, the sixth is the LSB.  From that we can make a sockaddr_in.
13088 */
13089         if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
13090             printf("Passive mode address scan failure\n");
13091             return(-1);
13092         };
13093 #ifndef NOHTTP
13094         if (tcp_http_proxy) {
13095 #ifdef OS2
13096             char * agent = "Kermit 95"; /* Default user agent */
13097 #else
13098             char * agent = "C-Kermit";
13099 #endif /* OS2 */
13100             register struct hostent *hp = 0;
13101             struct servent *destsp;
13102             char host[512], *p, *q;
13103 #ifdef IP_TOS
13104 #ifdef IPTOS_THROUGHPUT
13105             int tos;
13106 #endif /* IPTOS_THROUGHPUT */
13107 #endif /* IP_TOS */
13108             int s;
13109 #ifdef DEBUG
13110             extern int debtim;
13111             int xdebtim;
13112             xdebtim = debtim;
13113             debtim = 1;
13114 #endif /* DEBUG */
13115
13116             ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
13117                       ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
13118                       NULL,NULL,NULL
13119                       );
13120             memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
13121             for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
13122               *q = *p;
13123             *q = '\0';
13124
13125             hisctladdr.sin_addr.s_addr = inet_addr(host);
13126             if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
13127             {
13128                 debug(F110,"initconn A",host,0);
13129                 hisctladdr.sin_family = AF_INET;
13130             } else {
13131                 debug(F110,"initconn B",host,0);
13132                 hp = gethostbyname(host);
13133 #ifdef HADDRLIST
13134                 hp = ck_copyhostent(hp); /* make safe copy that won't change */
13135 #endif /* HADDRLIST */
13136                 if (hp == NULL) {
13137                     fprintf(stderr, "ftp: %s: Unknown host\n", host);
13138                     ftpcode = -1;
13139 #ifdef DEBUG
13140                     debtim = xdebtim;
13141 #endif /* DEBUG */
13142                     return(0);
13143                 }
13144                 hisctladdr.sin_family = hp->h_addrtype;
13145 #ifdef HADDRLIST
13146                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
13147                        sizeof(hisctladdr.sin_addr));
13148 #else /* HADDRLIST */
13149                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
13150                        sizeof(hisctladdr.sin_addr));
13151 #endif /* HADDRLIST */
13152             }
13153             data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13154             debug(F101,"initconn socket","",data);
13155             if (data < 0) {
13156                 perror("ftp: socket");
13157                 ftpcode = -1;
13158 #ifdef DEBUG
13159                 debtim = xdebtim;
13160 #endif /* DEBUG */
13161                 return(0);
13162             }
13163             if (*p == ':')
13164               p++;
13165             else
13166               p = "http";
13167
13168             destsp = getservbyname(p,"tcp");
13169             if (destsp)
13170               hisctladdr.sin_port = destsp->s_port;
13171             else if (p)
13172               hisctladdr.sin_port = htons(atoi(p));
13173             else
13174               hisctladdr.sin_port = htons(80);
13175             errno = 0;
13176 #ifdef HADDRLIST
13177             debug(F100,"initconn HADDRLIST","",0);
13178             while
13179 #else
13180             debug(F100,"initconn no HADDRLIST","",0);
13181             if
13182 #endif /* HADDRLIST */
13183               (connect(data, (struct sockaddr *)&hisctladdr,
13184                        sizeof (hisctladdr)) < 0) {
13185                   debug(F101,"initconn connect failed","",errno);
13186 #ifdef HADDRLIST
13187                   if (hp && hp->h_addr_list[1]) {
13188                       int oerrno = errno;
13189
13190                       fprintf(stderr,
13191                               "ftp: connect to address %s: ",
13192                               inet_ntoa(hisctladdr.sin_addr)
13193                               );
13194                       errno = oerrno;
13195                       perror("ftphookup");
13196                       hp->h_addr_list++;
13197                       memcpy((char *)&hisctladdr.sin_addr,
13198                              hp->h_addr_list[0],
13199                              sizeof(hisctladdr.sin_addr));
13200                       fprintf(stdout, "Trying %s...\n",
13201                               inet_ntoa(hisctladdr.sin_addr));
13202 #ifdef TCPIPLIB
13203                       socket_close(data);
13204 #else /* TCPIPLIB */
13205                       close(data);
13206 #endif /* TCPIPLIB */
13207                       data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13208                       if (data < 0) {
13209                           perror("ftp: socket");
13210                           ftpcode = -1;
13211 #ifdef DEBUG
13212                           debtim = xdebtim;
13213 #endif /* DEBUG */
13214                           return(0);
13215                       }
13216                       continue;
13217                   }
13218 #endif /* HADDRLIST */
13219                   perror("ftp: connect");
13220                   ftpcode = -1;
13221                   goto bad;
13222               }
13223             if (http_connect(data,
13224                              tcp_http_proxy_agent ?
13225                                tcp_http_proxy_agent :
13226                                  agent,
13227                              NULL,
13228                              tcp_http_proxy_user,
13229                              tcp_http_proxy_pwd,
13230                              0,
13231                              proxyhost
13232                              ) < 0) {
13233 #ifdef TCPIPLIB
13234                 socket_close(data);
13235 #else /* TCPIPLIB */
13236                 close(data);
13237 #endif /* TCPIPLIB */
13238                 perror("ftp: connect");
13239                 ftpcode = -1;
13240                 goto bad;
13241             }
13242         } else
13243 #endif /* NOHTTP */
13244         {
13245             data_addr.sin_family = AF_INET;
13246             data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
13247             data_addr.sin_port = htons((p1<<8)|p2);
13248
13249             if (connect(data,
13250                         (struct sockaddr *)&data_addr,
13251                         sizeof(data_addr)) < 0
13252                 ) {
13253                 perror("ftp: connect");
13254                 return(-1);
13255             }
13256         }
13257         debug(F100,"initconn connect ok","",0);
13258 #ifdef IP_TOS
13259 #ifdef IPTOS_THROUGHPUT
13260         on = IPTOS_THROUGHPUT;
13261         if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13262           perror("ftp: setsockopt TOS (ignored)");
13263 #endif /* IPTOS_THROUGHPUT */
13264 #endif /* IP_TOS */
13265         memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
13266         return(0);
13267     }
13268 #endif /* NO_PASSIVE_MODE */
13269
13270   noport:
13271     memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
13272     if (sendport)
13273       data_addr.sin_port = 0;   /* let system pick one */
13274     if (data != -1) {
13275 #ifdef TCPIPLIB
13276         socket_close(data);
13277 #else /* TCPIPLIB */
13278 #ifdef USE_SHUTDOWN
13279         shutdown(data, 1+1);
13280 #endif /* USE_SHUTDOWN */
13281         close(data);
13282 #endif /* TCPIPLIB */
13283     }
13284     data = socket(AF_INET, SOCK_STREAM, 0);
13285     globaldin = data;
13286     if (data < 0) {
13287         perror("ftp: socket");
13288         if (tmpno)
13289           sendport = 1;
13290         return(-1);
13291     }
13292     if (!sendport) {
13293         if (setsockopt(data,
13294                        SOL_SOCKET,
13295                        SO_REUSEADDR,
13296                        (char *)&on,
13297                        sizeof (on)
13298                        ) < 0
13299             ) {
13300             perror("ftp: setsockopt (reuse address)");
13301             goto bad;
13302         }
13303     }
13304     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
13305         perror("ftp: bind");
13306         goto bad;
13307     }
13308     len = sizeof (data_addr);
13309     if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
13310         perror("ftp: getsockname");
13311         goto bad;
13312     }
13313     if (listen(data, 1) < 0) {
13314         perror("ftp: listen");
13315         goto bad;
13316     }
13317     if (sendport) {
13318         a = (char *)&data_addr.sin_addr;
13319         p = (char *)&data_addr.sin_port;
13320         ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
13321                   UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
13322                   UC(p[0]),",", UC(p[1]));
13323         result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
13324         if (result == REPLY_ERROR && sendport) {
13325             sendport = 0;
13326             tmpno = 1;
13327             goto noport;
13328         }
13329         return(result != REPLY_COMPLETE);
13330     }
13331     if (tmpno)
13332       sendport = 1;
13333 #ifdef IP_TOS
13334 #ifdef IPTOS_THROUGHPUT
13335     on = IPTOS_THROUGHPUT;
13336     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13337       perror("ftp: setsockopt TOS (ignored)");
13338 #endif
13339 #endif
13340     return(0);
13341   bad:
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     if (tmpno)
13353       sendport = 1;
13354     return(-1);
13355 }
13356
13357 #ifdef CK_SSL
13358 static int
13359 ssl_dataconn() {
13360     if (ssl_ftp_data_con!=NULL) {       /* Do SSL */
13361         SSL_free(ssl_ftp_data_con);
13362         ssl_ftp_data_con=NULL;
13363     }
13364     ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
13365
13366     SSL_set_fd(ssl_ftp_data_con,data);
13367     SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
13368
13369     SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
13370
13371     if (ssl_debug_flag) {
13372         fprintf(stderr,"=>START SSL connect on DATA\n");
13373         fflush(stderr);
13374     }
13375     if (SSL_connect(ssl_ftp_data_con) <= 0) {
13376         static char errbuf[1024];
13377         ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
13378                   ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
13379         fprintf(stderr,"%s\n", errbuf);
13380         fflush(stderr);
13381 #ifdef TCPIPLIB
13382         socket_close(data);
13383 #else /* TCPIPLIB */
13384 #ifdef USE_SHUTDOWN
13385         shutdown(data, 1+1);
13386 #endif /* USE_SHUTDOWN */
13387         close(data);
13388 #endif /* TCPIPLIB */
13389         data = -1;
13390         globaldin = data;
13391         return(-1);
13392     } else {
13393         ssl_ftp_data_active_flag=1;
13394
13395         if (!ssl_certsok_flag &&
13396             (ssl_verify_flag & SSL_VERIFY_PEER) && /* JEA 2013-12-10 */
13397             !tls_is_krb5(2)) {
13398             char *subject = ssl_get_subject_name(ssl_ftp_data_con);
13399
13400             if (!subject) {
13401                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
13402                     debug(F110,"dataconn","[SSL _- FAILED]",0);
13403
13404                     ssl_ftp_data_active_flag = 0;
13405 #ifdef TCPIPLIB
13406                     socket_close(data);
13407 #else /* TCPIPLIB */
13408 #ifdef USE_SHUTDOWN
13409                     shutdown(data, 1+1);
13410 #endif /* USE_SHUTDOWN */
13411                     close(data);
13412 #endif /* TCPIPLIB */
13413                     data = -1;
13414                     globaldin = data;
13415                     return(-1);
13416                 } else {
13417                     if (!out2screen && displa && fdispla) {
13418                         ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13419                         /* fdispla = XYFD_B; */
13420                     }
13421
13422                     if (uq_ok(
13423           "Warning: Server didn't provide a certificate on data connection\n",
13424                                "Continue with file transfer? (Y/N)",
13425                               3,NULL,0) <= 0) {
13426                         debug(F110, "dataconn","[SSL - FAILED]",0);
13427                         ssl_ftp_data_active_flag = 0;
13428 #ifdef TCPIPLIB
13429                         socket_close(data);
13430 #else /* TCPIPLIB */
13431 #ifdef USE_SHUTDOWN
13432                         shutdown(data, 1+1);
13433 #endif /* USE_SHUTDOWN */
13434                         close(data);
13435 #endif /* TCPIPLIB */
13436                         data = -1;
13437                         globaldin = data;
13438                         return(-1);
13439                     }
13440                 }
13441             } else {
13442                 if (!out2screen && displa && fdispla == XYFD_C) {
13443                     ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13444                     /* fdispla = XYFD_B; */
13445                 }
13446
13447                 if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
13448                     debug(F110,"dataconn","[SSL - FAILED]",0);
13449                     ssl_ftp_data_active_flag = 0;
13450 #ifdef TCPIPLIB
13451                     socket_close(data);
13452 #else /* TCPIPLIB */
13453 #ifdef USE_SHUTDOWN
13454                     shutdown(data, 1+1);
13455 #endif /* USE_SHUTDOWN */
13456                     close(data);
13457 #endif /* TCPIPLIB */
13458                     data = -1;
13459                     globaldin = data;
13460                     return(-1);
13461                 }
13462             }
13463         }
13464         debug(F110,"dataconn","[SSL - OK]",0);
13465 #ifdef COMMENT
13466         /* This messes up the full screen file transfer display */
13467         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
13468 #endif /* COMMENT */
13469     }
13470     if (ssl_debug_flag) {
13471         fprintf(stderr,"=>DONE SSL connect on DATA\n");
13472         fflush(stderr);
13473     }
13474     return(data);
13475 }
13476 #endif /* CK_SSL */
13477
13478 static int
13479 dataconn(lmode) char *lmode; {
13480     int s;
13481 #ifdef IP_TOS
13482     int tos;
13483 #endif /* IP_TOS */
13484 #ifdef UCX50
13485     static u_int fromlen;
13486 #else
13487     static SOCKOPT_T fromlen;
13488 #endif /* UCX50 */
13489
13490     fromlen = sizeof(hisdataaddr);
13491
13492 #ifndef NO_PASSIVE_MODE
13493     if (passivemode) {
13494 #ifdef CK_SSL
13495         ssl_ftp_data_active_flag=0;
13496         if (ssl_ftp_active_flag &&
13497             (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13498           return(ssl_dataconn());
13499 #endif /* CK_SSL */
13500         return(data);
13501     }
13502 #endif /* NO_PASSIVE_MODE */
13503
13504     s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
13505     if (s < 0) {
13506         perror("ftp: accept");
13507 #ifdef TCPIPLIB
13508         socket_close(data);
13509 #else /* TCPIPLIB */
13510 #ifdef USE_SHUTDOWN
13511         shutdown(data, 1+1);
13512 #endif /* USE_SHUTDOWN */
13513         close(data);
13514 #endif /* TCPIPLIB */
13515         data = -1;
13516         globaldin = data;
13517         return(-1);
13518     }
13519 #ifdef TCPIPLIB
13520     socket_close(data);
13521 #else /* TCPIPLIB */
13522 #ifdef USE_SHUTDOWN
13523     shutdown(data, 1+1);
13524 #endif /* USE_SHUTDOWN */
13525     close(data);
13526 #endif /* TCPIPLIB */
13527     data = s;
13528     globaldin = data;
13529 #ifdef IP_TOS
13530 #ifdef IPTOS_THROUGHPUT
13531     tos = IPTOS_THROUGHPUT;
13532     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
13533       perror("ftp: setsockopt TOS (ignored)");
13534 #endif /* IPTOS_THROUGHPUT */
13535 #endif /* IP_TOS */
13536
13537 #ifdef CK_SSL
13538     ssl_ftp_data_active_flag=0;
13539     if (ssl_ftp_active_flag &&
13540         (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13541       return(ssl_dataconn());
13542 #endif /* CK_SSL */
13543     return(data);
13544 }
13545
13546 #ifdef FTP_PROXY
13547 static sigtype
13548 pscancel(sig) int sig; {
13549     cancelfile++;
13550 }
13551
13552 static VOID
13553 pswitch(flag) int flag; {
13554     extern int proxy;
13555     sig_t oldintr;
13556     static struct comvars {
13557         int connect;
13558         char name[MAXHOSTNAMELEN];
13559         struct sockaddr_in mctl;
13560         struct sockaddr_in hctl;
13561         FILE *in;
13562         FILE *out;
13563         int tpe;
13564         int curtpe;
13565         int cpnd;
13566         int sunqe;
13567         int runqe;
13568         int mcse;
13569         int ntflg;
13570         char nti[17];
13571         char nto[17];
13572         int mapflg;
13573         char mi[CKMAXPATH];
13574         char mo[CKMAXPATH];
13575         char *authtype;
13576         int clvl;
13577         int dlvl;
13578 #ifdef FTP_KRB4
13579         des_cblock session;
13580         des_key_schedule ftp_sched;
13581 #endif /* FTP_KRB4 */
13582 #ifdef FTP_GSSAPI
13583         gss_ctx_id_t gcontext;
13584 #endif /* GSSAPI */
13585     } proxstruct, tmpstruct;
13586     struct comvars *ip, *op;
13587
13588     cancelfile = 0;
13589     oldintr = signal(SIGINT, pscancel);
13590     if (flag) {
13591         if (proxy)
13592           return;
13593         ip = &tmpstruct;
13594         op = &proxstruct;
13595         proxy++;
13596     } else {
13597         if (!proxy)
13598           return;
13599         ip = &proxstruct;
13600         op = &tmpstruct;
13601         proxy = 0;
13602     }
13603     ip->connect = connected;
13604     connected = op->connect;
13605     if (ftp_host) {
13606         strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
13607         ip->name[MAXHOSTNAMELEN - 1] = '\0';
13608         ip->name[strlen(ip->name)] = '\0';
13609     } else
13610       ip->name[0] = 0;
13611     ftp_host = op->name;
13612     ip->hctl = hisctladdr;
13613     hisctladdr = op->hctl;
13614     ip->mctl = myctladdr;
13615     myctladdr = op->mctl;
13616     ip->in = csocket;
13617     csocket = op->in;
13618     ip->out = csocket;
13619     csocket = op->out;
13620     ip->tpe = ftp_typ;
13621     ftp_typ = op->tpe;
13622     ip->curtpe = curtype;
13623     curtype = op->curtpe;
13624     ip->cpnd = cpend;
13625     cpend = op->cpnd;
13626     ip->sunqe = ftp_usn;
13627     ftp_usn = op->sunqe;
13628     ip->mcse = mcase;
13629     mcase = op->mcse;
13630     ip->ntflg = ntflag;
13631     ntflag = op->ntflg;
13632     strncpy(ip->nti, ntin, 16);
13633     (ip->nti)[strlen(ip->nti)] = '\0';
13634     strcpy(ntin, op->nti);
13635     strncpy(ip->nto, ntout, 16);
13636     (ip->nto)[strlen(ip->nto)] = '\0';
13637     strcpy(ntout, op->nto);
13638     ip->mapflg = mapflag;
13639     mapflag = op->mapflg;
13640     strncpy(ip->mi, mapin, CKMAXPATH - 1);
13641     (ip->mi)[strlen(ip->mi)] = '\0';
13642     strcpy(mapin, op->mi);
13643     strncpy(ip->mo, mapout, CKMAXPATH - 1);
13644     (ip->mo)[strlen(ip->mo)] = '\0';
13645     strcpy(mapout, op->mo);
13646     ip->authtype = auth_type;
13647     auth_type = op->authtype;
13648     ip->clvl = ftp_cpl;
13649     ftp_cpl = op->clvl;
13650     ip->dlvl = ftp_dpl;
13651     ftp_dpl = op->dlvl;
13652     if (!ftp_cpl)
13653       ftp_cpl = FPL_CLR;
13654     if (!ftp_dpl)
13655       ftp_dpl = FPL_CLR;
13656 #ifdef FTP_KRB4
13657     memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
13658     memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
13659     memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
13660     memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
13661 #endif /* FTP_KRB4 */
13662 #ifdef FTP_GSSAPI
13663     ip->gcontext = gcontext;
13664     gcontext = op->gcontext;
13665 #endif /* GSSAPI */
13666     signal(SIGINT, oldintr);
13667     if (cancelfile) {
13668         cancelfile = 0;
13669         debug(F101,"pswitch cancelfile B","",cancelfile);
13670         (*oldintr)(SIGINT);
13671     }
13672 }
13673
13674 static sigtype
13675 cancelpt(sig) int sig; {
13676     printf("\n");
13677     fflush(stdout);
13678     ptabflg++;
13679     cancelfile = 0;
13680 #ifndef OS2
13681     longjmp(ptcancel, 1);
13682 #else
13683     PostCtrlCSem();
13684 #endif /* OS2 */
13685 }
13686
13687 void
13688 proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
13689     sig_t oldintr;
13690     int secndflag = 0, prox_type, nfnd;
13691     char *cmd2;
13692 #ifdef BSDSELECT
13693     fd_set mask;
13694 #endif /* BSDSELECT */
13695     sigtype cancelpt();
13696
13697     if (strcmp(cmd, "RETR"))
13698       cmd2 = "RETR";
13699     else
13700       cmd2 = unique ? "STOU" : "STOR";
13701     if ((prox_type = type) == 0) {
13702         if (servertype == SYS_UNIX && unix_proxy)
13703           prox_type = FTT_BIN;
13704         else
13705           prox_type = FTT_ASC;
13706     }
13707     if (curtype != prox_type)
13708       changetype(prox_type, 1);
13709     if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13710         printf("Proxy server does not support third party transfers.\n");
13711         return;
13712     }
13713     pswitch(0);
13714     if (!connected) {
13715         printf("No primary connection\n");
13716         pswitch(1);
13717         ftpcode = -1;
13718         return;
13719     }
13720     if (curtype != prox_type)
13721       changetype(prox_type, 1);
13722
13723     if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
13724         pswitch(1);
13725         return;
13726     }
13727
13728     /* Replace with calls to cc_execute() */
13729     if (setjmp(ptcancel))
13730       goto cancel;
13731     oldintr = signal(SIGINT, cancelpt);
13732     if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
13733         signal(SIGINT, oldintr);
13734         pswitch(1);
13735         return;
13736     }
13737     sleep(2000);
13738     pswitch(1);
13739     secndflag++;
13740     if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
13741       goto cancel;
13742     ptflag++;
13743     getreply(0,-1,-1,ftp_vbm,0);
13744     pswitch(0);
13745     getreply(0,-1,-1,ftp_vbm,0);
13746     signal(SIGINT, oldintr);
13747     pswitch(1);
13748     ptflag = 0;
13749     return;
13750
13751   cancel:
13752     signal(SIGINT, SIG_IGN);
13753     ptflag = 0;
13754     if (strcmp(cmd, "RETR") && !proxy)
13755       pswitch(1);
13756     else if (!strcmp(cmd, "RETR") && proxy)
13757       pswitch(0);
13758     if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
13759         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13760             pswitch(0);
13761             if (cpend)
13762               cancel_remote(0);
13763         }
13764         pswitch(1);
13765         if (ptabflg)
13766           ftpcode = -1;
13767         signal(SIGINT, oldintr);
13768         return;
13769     }
13770     if (cpend)
13771       cancel_remote(0);
13772     pswitch(!proxy);
13773     if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
13774         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13775             pswitch(0);
13776             if (cpend)
13777               cancel_remote(0);
13778             pswitch(1);
13779             if (ptabflg)
13780               ftpcode = -1;
13781             signal(SIGINT, oldintr);
13782             return;
13783         }
13784     }
13785     if (cpend)
13786       cancel_remote(0);
13787     pswitch(!proxy);
13788     if (cpend) {
13789 #ifdef BSDSELECT
13790         FD_ZERO(&mask);
13791         FD_SET(csocket, &mask);
13792         if ((nfnd = empty(&mask, 10)) <= 0) {
13793             if (nfnd < 0) {
13794                 perror("cancel");
13795             }
13796             if (ptabflg)
13797               ftpcode = -1;
13798             lostpeer();
13799         }
13800 #else /* BSDSELECT */
13801 #ifdef IBMSELECT
13802         if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
13803             if (nfnd < 0) {
13804                 perror("cancel");
13805             }
13806             if (ptabflg)
13807               ftpcode = -1;
13808             lostpeer();
13809         }
13810 #endif /* IBMSELECT */
13811 #endif /* BSDSELECT */
13812         getreply(0,-1,-1,ftp_vbm,0);
13813         getreply(0,-1,-1,ftp_vbm,0);
13814     }
13815     if (proxy)
13816       pswitch(0);
13817     pswitch(1);
13818     if (ptabflg)
13819       ftpcode = -1;
13820     signal(SIGINT, oldintr);
13821 }
13822 #endif /* FTP_PROXY */
13823
13824 #ifdef FTP_SECURITY
13825 #ifdef FTP_GSSAPI
13826
13827 #ifdef COMMENT
13828 /* ck_gss_mech_krb5 is not declared anywhere */
13829 struct {
13830     CONST gss_OID_desc * CONST * mech_type;
13831     char *service_name;
13832 } gss_trials[] = {
13833     { &ck_gss_mech_krb5, "ftp" },
13834     { &ck_gss_mech_krb5, "host" },
13835 };
13836 #else
13837 /* This matches what is declared above */
13838 struct {
13839     CONST gss_OID_desc * CONST * mech_type;
13840     char *service_name;
13841 } gss_trials[] = {
13842     { &gss_mech_krb5, "ftp" },
13843     { &gss_mech_krb5, "host" },
13844 };
13845 #endif  /* COMMENT */
13846
13847
13848 int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
13849 #endif /* FTP_GSSAPI */
13850
13851 static int
13852 ftp_auth() {
13853     extern int setsafe();
13854     int j = 0, n;
13855 #ifdef FTP_KRB4
13856     char *service, inst[INST_SZ];
13857     ULONG cksum;
13858     ULONG checksum = (ULONG) getpid();
13859     CHAR out_buf[FTP_BUFSIZ];
13860     int i;
13861 #else /* FTP_KRB4 */
13862 #ifdef FTP_GSSAPI
13863     CHAR out_buf[FTP_BUFSIZ];
13864     int i;
13865 #endif /* FTP_GSSAPI */
13866 #endif /* FTP_KRB4 */
13867
13868     if (ssl_ftp_proxy)                  /* Do not allow AUTH over SSL proxy */
13869         return(0);
13870
13871     if (auth_type)
13872       return(1);                        /* auth already succeeded */
13873
13874     /* Try each auth type as specified by the end user */
13875     for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
13876 #ifdef FTP_GSSAPI
13877         if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
13878             n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
13879             if (n == REPLY_CONTINUE) {
13880                 OM_uint32 maj_stat, min_stat;
13881                 gss_name_t target_name;
13882                 gss_buffer_desc send_tok, recv_tok, *token_ptr;
13883                 char stbuf[FTP_BUFSIZ];
13884                 int comcode, trial;
13885                 struct gss_channel_bindings_struct chan;
13886                 char * realm = NULL;
13887                 char tgt[256];
13888
13889                 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
13890                 chan.initiator_address.length = 4;
13891                 chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
13892                 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
13893                 chan.acceptor_address.length = 4;
13894                 chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
13895                 chan.application_data.length = 0;
13896                 chan.application_data.value = 0;
13897
13898                 if (!quiet)
13899                   printf("GSSAPI accepted as authentication type\n");
13900
13901                 realm = ck_krb5_realmofhost(ftp_user_host);
13902                 if (realm) {
13903                     ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
13904                     debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
13905                     if ( krb5_autoget &&
13906                          !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
13907                             (ck_krb5_is_tgt_valid() > 0)) )
13908                         ck_krb5_autoget_TGT(realm);
13909                 }
13910
13911                 /* Blob from gss-client */
13912                 for (trial = 0; trial < n_gss_trials; trial++) {
13913                     /* ftp@hostname first, the host@hostname */
13914                     /* the V5 GSSAPI binding canonicalizes this for us... */
13915                     ckmakmsg(stbuf,FTP_BUFSIZ,
13916                              gss_trials[trial].service_name,
13917                              "@",
13918                              ftp_user_host,
13919                              NULL
13920                              );
13921                     if (ftp_deb)
13922                       fprintf(stderr,
13923                               "Authenticating to <%s>...\n", stbuf);
13924                     send_tok.value = stbuf;
13925                     send_tok.length = strlen(stbuf);
13926                     maj_stat = gss_import_name(&min_stat, &send_tok,
13927                                                gss_nt_service_name,
13928                                                &target_name
13929                                                );
13930                     if (maj_stat != GSS_S_COMPLETE) {
13931                         user_gss_error(maj_stat, min_stat, "parsing name");
13932                         secure_error("name parsed <%s>\n", stbuf);
13933                         continue;
13934                     }
13935                     token_ptr = GSS_C_NO_BUFFER;
13936                     gcontext = GSS_C_NO_CONTEXT; /* structure copy */
13937
13938                     do {
13939                         if (ftp_deb)
13940                           fprintf(stderr, "calling gss_init_sec_context\n");
13941                         maj_stat =
13942                           gss_init_sec_context(&min_stat,
13943                                                GSS_C_NO_CREDENTIAL,
13944                                                &gcontext,
13945                                                target_name,
13946                                                (gss_OID) *
13947                                                  gss_trials[trial].mech_type,
13948                                                GSS_C_MUTUAL_FLAG |
13949                                                GSS_C_REPLAY_FLAG |
13950                                                (ftp_cfw ?
13951                                                 GSS_C_DELEG_FLAG : 0),
13952                                                0,
13953                                                 /* channel bindings */
13954                                                 (krb5_d_no_addresses ?
13955                                                   GSS_C_NO_CHANNEL_BINDINGS :
13956                                                   &chan),
13957                                                 token_ptr,
13958                                                NULL,    /* ignore mech type */
13959                                                &send_tok,
13960                                                NULL,    /* ignore ret_flags */
13961                                                NULL
13962                                                );       /* ignore time_rec */
13963
13964                         if (maj_stat != GSS_S_COMPLETE &&
13965                             maj_stat != GSS_S_CONTINUE_NEEDED) {
13966                             if (trial == n_gss_trials-1)
13967                               user_gss_error(maj_stat,
13968                                              min_stat,
13969                                              "initializing context"
13970                                              );
13971                             gss_release_name(&min_stat, &target_name);
13972                             /* maybe we missed on the service name */
13973                             goto outer_loop;
13974                         }
13975                         if (send_tok.length != 0) {
13976                             int len;
13977                             reply_parse = "ADAT="; /* for ftpcmd() later */
13978                             len = FTP_BUFSIZ;
13979                             kerror =
13980                               radix_encode(send_tok.value,
13981                                            out_buf,
13982                                            send_tok.length,
13983                                            &len,
13984                                            RADIX_ENCODE
13985                                            );
13986                             if (kerror)  {
13987                                 fprintf(stderr,
13988                                         "Base 64 encoding failed: %s\n",
13989                                         radix_error(kerror)
13990                                         );
13991                                 goto gss_complete_loop;
13992                             }
13993                             comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
13994                             if (comcode != REPLY_COMPLETE
13995                                 && comcode != REPLY_CONTINUE /* (335) */
13996                                 ) {
13997                                 if (trial == n_gss_trials-1) {
13998                                     fprintf(stderr, "GSSAPI ADAT failed\n");
13999                                     /* force out of loop */
14000                                     maj_stat = GSS_S_FAILURE;
14001                                 }
14002                                 /*
14003                                   Backoff to the v1 gssapi is still possible.
14004                                   Send a new AUTH command.  If that fails,
14005                                   terminate the loop.
14006                                 */
14007                                 if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
14008                                     != REPLY_CONTINUE) {
14009                                     fprintf(stderr,
14010                                 "GSSAPI ADAT failed, AUTH restart failed\n");
14011                                     /* force out of loop */
14012                                     maj_stat = GSS_S_FAILURE;
14013                                 }
14014                                 goto outer_loop;
14015                             }
14016                             if (!reply_parse) {
14017                                 fprintf(stderr,
14018                               "No authentication data received from server\n");
14019                                 if (maj_stat == GSS_S_COMPLETE) {
14020                                     fprintf(stderr,
14021                                             "...but no more was needed\n");
14022                                     goto gss_complete_loop;
14023                                 } else {
14024                                     user_gss_error(maj_stat,
14025                                                    min_stat,
14026                                                    "no reply, huh?"
14027                                                    );
14028                                     goto gss_complete_loop;
14029                                 }
14030                             }
14031                             len = FTP_BUFSIZ;
14032                             kerror = radix_encode(reply_parse,out_buf,i,&len,
14033                                                   RADIX_DECODE);
14034                             if (kerror) {
14035                                 fprintf(stderr,
14036                                         "Base 64 decoding failed: %s\n",
14037                                         radix_error(kerror));
14038                                 goto gss_complete_loop;
14039                             }
14040
14041                             /* everything worked */
14042                             token_ptr = &recv_tok;
14043                             recv_tok.value = out_buf;
14044                             recv_tok.length = len;
14045                             continue;
14046
14047                             /* get out of loop clean */
14048                           gss_complete_loop:
14049                             trial = n_gss_trials-1;
14050                             gss_release_buffer(&min_stat, &send_tok);
14051                             gss_release_name(&min_stat, &target_name);
14052                             goto outer_loop;
14053                         }
14054                     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
14055
14056                   outer_loop:
14057                     if (maj_stat == GSS_S_COMPLETE)
14058                       break;
14059                 }
14060                 if (maj_stat == GSS_S_COMPLETE) {
14061                     printf("GSSAPI authentication succeeded\n");
14062                     reply_parse = NULL;
14063                     auth_type = "GSSAPI";
14064                     return(1);
14065                 } else {
14066                     fprintf(stderr, "GSSAPI authentication failed\n");
14067                     reply_parse = NULL;
14068                 }
14069             } else {
14070                 if (ftp_deb)
14071                 fprintf(stderr, "GSSAPI rejected as an authentication type\n");
14072                 if (ftpcode == 500 || ftpcode == 502)
14073                     return(0);
14074             }
14075         }
14076 #endif /* FTP_GSSAPI */
14077 #ifdef FTP_SRP
14078         if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
14079             if (srp_ftp_auth(ftp_user_host,NULL,NULL))
14080               return(1);
14081             else if (ftpcode == 500 || ftpcode == 502)
14082               return(0);
14083         }
14084 #endif /* FTP_SRP */
14085 #ifdef FTP_KRB4
14086         if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
14087             n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
14088             if (n == REPLY_CONTINUE) {
14089                 char tgt[4*REALM_SZ+1];
14090                 int rc;
14091
14092                 if (!quiet)
14093                   printf("KERBEROS_V4 accepted as authentication type\n");
14094                 ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
14095                 ckstrncpy(ftp_realm,
14096                           (char *)ck_krb4_realmofhost(ftp_user_host),
14097                           REALM_SZ
14098                           );
14099
14100                 ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
14101                 rc = ck_krb4_tkt_isvalid(tgt);
14102
14103                 if (rc <= 0 && krb4_autoget)
14104                   ck_krb4_autoget_TGT(ftp_realm);
14105
14106                 service = "ftp";
14107                 kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
14108                 if (kerror == KDC_PR_UNKNOWN) {
14109                     service = "rcmd";
14110                     kerror = krb_mk_req(&ftp_tkt,
14111                                         service,
14112                                         inst,
14113                                         ftp_realm,
14114                                         checksum
14115                                         );
14116                 }
14117                 if (kerror)
14118                   fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
14119                           krb_get_err_text(kerror));
14120                 if (!kerror) {
14121                     kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
14122                     if (kerror)
14123                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
14124                               krb_get_err_text(kerror));
14125                 }
14126                 if (!kerror) {
14127                     int rc;
14128                     rc = des_key_sched(ftp_cred.session, ftp_sched);
14129                     if (rc == -1) {
14130                        printf("?Invalid DES key specified in credentials\r\n");
14131                        debug(F110,"ftp_auth",
14132                              "invalid DES Key specified in credentials",0);
14133                     } else if ( rc == -2 ) {
14134                         printf("?Weak DES key specified in credentials\r\n");
14135                         debug(F110,"ftp_auth",
14136                               "weak DES Key specified in credentials",0);
14137                     } else if ( rc != 0 ) {
14138                         printf("?DES Key Schedule not set by credentials\r\n");
14139                         debug(F110,"ftp_auth",
14140                               "DES Key Schedule not set by credentials",0);
14141                     }
14142                     reply_parse = "ADAT=";
14143                     i = FTP_BUFSIZ;
14144                     kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
14145                                           &i, RADIX_ENCODE);
14146                     if (kerror) {
14147                         fprintf(stderr, "Base 64 encoding failed: %s\n",
14148                                 radix_error(kerror));
14149                         goto krb4_err;
14150                     }
14151                     if (i > FTP_BUFSIZ - 6)
14152                       printf("?ADAT data too long\n");
14153                     if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
14154                         REPLY_COMPLETE) {
14155                         fprintf(stderr, "Kerberos V4 authentication failed\n");
14156                         goto krb4_err;
14157                     }
14158                     if (!reply_parse) {
14159                         fprintf(stderr,
14160                              "No authentication data received from server\n");
14161                         goto krb4_err;
14162                     }
14163                     i = sizeof(out_buf);
14164                     kerror =
14165                       radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
14166                     if (kerror) {
14167                         fprintf(stderr, "Base 64 decoding failed: %s\n",
14168                                 radix_error(kerror));
14169                         goto krb4_err;
14170                     }
14171                     kerror = krb_rd_safe(out_buf, i,
14172 #ifdef KRB524
14173                                          ftp_cred.session,
14174 #else /* KRB524 */
14175                                          &ftp_cred.session,
14176 #endif /* KRB524 */
14177                                          &hisctladdr,
14178                                          &myctladdr,
14179                                          &ftp_msg_data
14180                                          );
14181                     if (kerror) {
14182                         fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
14183                                 krb_get_err_text(kerror));
14184                         goto krb4_err;
14185                     }
14186
14187                     /* fetch the (modified) checksum */
14188                     memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
14189                     if (ntohl(cksum) == checksum + 1) {
14190                         if (ftp_vbm)
14191                           printf("Kerberos V4 authentication succeeded\n");
14192                         reply_parse = NULL;
14193                         auth_type = "KERBEROS_V4";
14194                         return(1);
14195                     } else
14196                       fprintf(stderr,
14197                               "Kerberos V4 mutual authentication failed\n");
14198                   krb4_err:
14199                     reply_parse = NULL;
14200                 }
14201             } else {
14202                 if (ftp_deb)
14203                   fprintf(stderr,
14204                       "KERBEROS_V4 rejected as an authentication type\n");
14205                 if (ftpcode == 500 || ftpcode == 502)
14206                     return(0);
14207             }
14208         }
14209 #endif /* FTP_KRB4 */
14210 #ifdef CK_SSL
14211         if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
14212 #ifdef FTPHOST
14213             if (!hostcmd) {
14214                 ftpcmd("HOST",ftp_user_host,0,0,0);
14215                 hostcmd = 1;
14216             }
14217 #endif /* FTPHOST */
14218             n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
14219             if (n != REPLY_COMPLETE)
14220               n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
14221             if (n == REPLY_COMPLETE) {
14222                 if (!quiet)
14223                   printf("TLS accepted as authentication type\n");
14224
14225                 auth_type = "TLS";
14226                 ssl_auth();
14227                 if (ssl_ftp_active_flag ) {
14228                     ftp_dpl = FPL_CLR;
14229                     ftp_cpl = FPL_PRV;
14230                     return(1);
14231                 } else {
14232                     fprintf(stderr,"TLS authentication failed\n");
14233                     auth_type = NULL;
14234 #ifdef TCPIPLIB
14235                     socket_close(csocket);
14236 #else /* TCPIPLIB */
14237 #ifdef USE_SHUTDOWN
14238                     shutdown(csocket, 1+1);
14239 #endif /* USE_SHUTDOWN */
14240                     close(csocket);
14241 #endif /* TCPIPLIB */
14242                     csocket = -1;
14243                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14244                       return(0);
14245                 }
14246             } else {
14247                 if (ftp_deb)
14248                   fprintf(stderr,"TLS rejected as an authentication type\n");
14249                 if (ftpcode == 500 || ftpcode == 502)
14250                     return(0);
14251             }
14252         }
14253         if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
14254 #ifdef FTPHOST
14255             if (!hostcmd) {
14256                 ftpcmd("HOST",ftp_user_host,0,0,0);
14257                 hostcmd = 1;
14258             }
14259 #endif /* FTPHOST */
14260             n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
14261             if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
14262                 if (!quiet)
14263                   printf("SSL accepted as authentication type\n");
14264                 auth_type = "SSL";
14265                 ssl_auth();
14266                 if (ssl_ftp_active_flag) {
14267                     ftp_dpl = FPL_PRV;
14268                     ftp_cpl = FPL_PRV;
14269                     setprotbuf(1<<20);
14270                     return(1);
14271                 } else {
14272                     fprintf(stderr,"SSL authentication failed\n");
14273                     auth_type = NULL;
14274 #ifdef TCPIPLIB
14275                     socket_close(csocket);
14276 #else /* TCPIPLIB */
14277 #ifdef USE_SHUTDOWN
14278                     shutdown(csocket, 1+1);
14279 #endif /* USE_SHUTDOWN */
14280                     close(csocket);
14281 #endif /* TCPIPLIB */
14282                     csocket = -1;
14283                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14284                       return(0);
14285                 }
14286             } else {
14287                 if (ftp_deb)
14288                   fprintf(stderr, "SSL rejected as an authentication type\n");
14289                 if (ftpcode == 500 || ftpcode == 502)
14290                   return(0);
14291             }
14292         }
14293 #endif /* CK_SSL */
14294         /* Other auth types go here ... */
14295     } /* for (j;;) */
14296     return(0);
14297 }
14298 #endif /* FTP_SECURITY */
14299
14300 static int
14301 #ifdef CK_ANSIC
14302 setprotbuf(unsigned int size)
14303 #else
14304 setprotbuf(size) unsigned int size;
14305 #endif /* CK_ANSIC */
14306 /* setprotbuf */ {
14307     if (ucbuf)
14308       free(ucbuf);
14309     ucbuf = NULL;
14310     ucbufsiz = 0;
14311     actualbuf = size;
14312     while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
14313         if (actualbuf)
14314           actualbuf /= 2;
14315         else
14316           return(0);
14317     }
14318     ucbufsiz = actualbuf - FUDGE_FACTOR;
14319     debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
14320     if (ucbufsiz < 128) {
14321         printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
14322     } else if (ucbufsiz < 0) {
14323         printf("ERROR: ucbuf allocation failure\n");
14324         return(-1);
14325     }
14326     maxbuf = actualbuf;
14327     return(1);
14328 }
14329
14330 static int
14331 #ifdef CK_ANSIC
14332 setpbsz(unsigned int size)
14333 #else
14334 setpbsz(size) unsigned int size;
14335 #endif /* CK_ANSIC */
14336 /* setpbsz */ {
14337     if (!setprotbuf(size)) {
14338         perror("?Error while trying to malloc PROT buffer:");
14339 #ifdef FTP_SRP
14340         srp_reset();
14341 #endif /* FTP_SRP */
14342         ftpclose();
14343         return(-1);
14344     }
14345     reply_parse = "PBSZ=";
14346     ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
14347 #ifdef CK_SSL
14348              ssl_ftp_active_flag ? "0" :
14349 #endif /* CK_SSL */
14350              ckuitoa(actualbuf),NULL,NULL);
14351     if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
14352         if (connected) {
14353             printf("?Unable to negotiate PROT buffer size with FTP server\n");
14354             ftpclose();
14355         }
14356         return(-1);
14357     }
14358     if (reply_parse) {
14359         if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
14360           maxbuf = actualbuf;
14361     } else
14362       maxbuf = actualbuf;
14363     ucbufsiz = maxbuf - FUDGE_FACTOR;
14364     debug(F101,"setpbsz ucbufsiz","",ucbufsiz);    
14365     reply_parse = NULL;
14366     return(0);
14367 }
14368
14369 static VOID
14370 cancel_remote(din) int din; {
14371     CHAR buf[FTP_BUFSIZ];
14372     int x, nfnd;
14373 #ifdef BSDSELECT
14374     fd_set mask;
14375 #endif /* BSDSELECT */
14376 #ifdef IBMSELECT
14377     int fds[2], fdcnt = 0;
14378 #endif /* IBMSELECT */
14379 #ifdef DEBUG
14380     extern int debtim;
14381     int xdebtim;
14382     xdebtim = debtim;
14383     debtim = 1;
14384 #endif /* DEBUG */
14385     debug(F100,"ftp cancel_remote entry","",0);
14386 #ifdef CK_SSL
14387     if (ssl_ftp_active_flag) {
14388         /*
14389          * Send Telnet IP, Telnet DM but do so inline and within the
14390          * TLS channel
14391          */
14392         int count, error;
14393
14394         buf[0] = IAC;
14395         buf[1] = TN_IP;
14396         buf[2] = IAC;
14397         buf[3] = TN_DM;
14398         buf[4] = NUL;
14399
14400         count = SSL_write(ssl_ftp_con, buf, 4);
14401         debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
14402         error = SSL_get_error(ssl_ftp_con,count);
14403         debug(F111,"ftp cancel_remote","SSL_get_error()",error);
14404         switch (error) {
14405           case SSL_ERROR_NONE:
14406             break;
14407           case SSL_ERROR_WANT_WRITE:
14408           case SSL_ERROR_WANT_READ:
14409           case SSL_ERROR_SYSCALL:
14410 #ifdef NT
14411             {
14412                 int gle = GetLastError();
14413             }
14414 #endif /* NT */
14415           case SSL_ERROR_WANT_X509_LOOKUP:
14416           case SSL_ERROR_SSL:
14417           case SSL_ERROR_ZERO_RETURN:
14418           default:
14419             lostpeer();
14420             return;
14421         }
14422     } else
14423 #endif /* CK_SSL */
14424     {
14425         /*
14426          * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
14427          * after urgent byte rather than before as is protocol now.
14428          */
14429         buf[0] = IAC;
14430         buf[1] = TN_IP;
14431         buf[2] = IAC;
14432         buf[3] = NUL;
14433         if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
14434           perror("cancel");
14435         debug(F101,"ftp cancel_remote send 1","",x);
14436         buf[0] = TN_DM;
14437         x = send(csocket,(SENDARG2TYPE)buf,1,0);
14438         debug(F101,"ftp cancel_remote send 2","",x);
14439     }
14440     x = scommand("ABOR");
14441     debug(F101,"ftp cancel_remote scommand","",x);
14442 #ifdef BSDSELECT
14443     FD_ZERO(&mask);
14444     FD_SET(csocket, &mask);
14445     if (din) {
14446         FD_SET(din, &mask);
14447     }
14448     nfnd = empty(&mask, 10);
14449     debug(F101,"ftp cancel_remote empty","",nfnd);
14450     if ((nfnd) <= 0) {
14451         if (nfnd < 0) {
14452             perror("cancel");
14453         }
14454 #ifdef FTP_PROXY
14455         if (ptabflg)
14456           ftpcode = -1;
14457 #endif /* FTP_PROXY */
14458         lostpeer();
14459     }
14460     debug(F110,"ftp cancel_remote","D",0);
14461     if (din && FD_ISSET(din, &mask)) {
14462         /* Security: No threat associated with this read. */
14463         /* But you can't simply read the TLS data stream  */
14464 #ifdef CK_SSL
14465         if (ssl_ftp_data_active_flag) {
14466             int count, error;
14467             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14468                     /* LOOP */ ;
14469         } else
14470 #endif /* CK_SSL */
14471         {
14472             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14473                 /* LOOP */ ;
14474         }
14475     }
14476     debug(F110,"ftp cancel_remote","E",0);
14477 #else /* BSDSELECT */
14478 #ifdef IBMSELECT
14479     fds[0] = csocket;
14480     fdcnt++;
14481     if (din) {
14482         fds[1] = din;
14483         fdcnt++;
14484     }
14485     nfnd = empty(fds, fdcnt, 10);
14486     debug(F101,"ftp cancel_remote empty","",nfnd);
14487     if ((nfnd) <= 0) {
14488         if (nfnd < 0) {
14489             perror("cancel");
14490         }
14491 #ifdef FTP_PROXY
14492         if (ptabflg)
14493           ftpcode = -1;
14494 #endif /* FTP_PROXY */
14495         lostpeer();
14496     }
14497     debug(F110,"ftp cancel_remote","D",0);
14498     if (din && select(&din, 1,0,0,1) ) {
14499 #ifdef CK_SSL
14500         if (ssl_ftp_data_active_flag) {
14501             int count, error;
14502             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14503                     /* LOOP */ ;
14504         } else
14505 #endif /* CK_SSL */
14506         {
14507             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14508                 /* LOOP */ ;
14509         }
14510     }
14511     debug(F110,"ftp cancel_remote","E",0);
14512 #else /* IBMSELECT */
14513     Some form of select is required.
14514 #endif /* IBMSELECT */
14515 #endif /* BSDSELECT */
14516     if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
14517         debug(F110,"ftp cancel_remote","F",0);
14518         /* 552 needed for NIC style cancel */
14519         getreply(0,-1,-1,ftp_vbm,0);
14520         debug(F110,"ftp cancel_remote","G",0);
14521     }
14522     debug(F110,"ftp cancel_remote","H",0);
14523     getreply(0,-1,-1,ftp_vbm,0);
14524     debug(F110,"ftp cancel_remote","I",0);
14525 #ifdef DEBUG
14526     debtim = xdebtim;
14527 #endif /* DEBUG */
14528 }
14529
14530 static int
14531 fts_dpl(x) int x; {
14532     if (!auth_type
14533 #ifdef OS2
14534          || !ck_crypt_is_installed()
14535 #endif /* OS2 */
14536          ) {
14537         switch ( x ) {
14538           case FPL_PRV:
14539             printf("?Cannot set protection level to PRIVATE\n");
14540             return(0);
14541           case FPL_SAF:
14542             printf("?Cannot set protection level to SAFE\n");
14543             return(0);
14544         }
14545         ftp_dpl = x;
14546         return(1);
14547     }
14548
14549 #ifdef CK_SSL
14550     if (x == FPL_SAF &&
14551         (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
14552         printf("Cannot set protection level to safe\n");
14553         return(0);
14554     }
14555 #endif /* CK_SSL */
14556     /* Start with a PBSZ of 1 meg */
14557     if (x != FPL_CLR) {
14558         if (setpbsz(DEFAULT_PBSZ) < 0)
14559           return(0);
14560     }
14561     y = ftpcmd(x == FPL_CLR ? "PROT C" :
14562                (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
14563     if (y == REPLY_COMPLETE) {
14564         ftp_dpl = x;
14565         return(1);
14566     }
14567     return(0);
14568 }
14569
14570 static int
14571 fts_cpl(x) int x; {
14572     if (!auth_type 
14573 #ifdef OS2
14574          || !ck_crypt_is_installed()
14575 #endif /* OS2 */
14576          ) {
14577         switch ( x ) {
14578           case FPL_PRV:
14579             printf("?Cannot set protection level to PRIVATE\n");
14580             return(0);
14581           case FPL_SAF:
14582             printf("?Cannot set protection level to SAFE\n");
14583             return(0);
14584         }
14585         ftp_cpl = x;
14586         return(1);
14587     }
14588     if (x == FPL_CLR) {
14589         y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
14590         if (y == REPLY_COMPLETE) {
14591             ftp_cpl = x;
14592             return(1);
14593         }
14594         return(0);
14595     }
14596     ftp_cpl = x;
14597     return(1);
14598 }
14599
14600 #ifdef FTP_GSSAPI
14601 static VOID
14602 user_gss_error(maj_stat, min_stat, s)
14603     OM_uint32 maj_stat, min_stat;
14604     char *s;
14605 {
14606     /* a lot of work just to report the error */
14607     OM_uint32 gmaj_stat, gmin_stat, msg_ctx;
14608     gss_buffer_desc msg;
14609     msg_ctx = 0;
14610     while (!msg_ctx) {
14611         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
14612                                        GSS_C_GSS_CODE,
14613                                        GSS_C_NULL_OID,
14614                                        &msg_ctx,
14615                                        &msg
14616                                        );
14617         if ((gmaj_stat == GSS_S_COMPLETE)||
14618             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14619             fprintf(stderr, "GSSAPI error major: %s\n",
14620                     (char*)msg.value);
14621             gss_release_buffer(&gmin_stat, &msg);
14622         }
14623         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14624           break;
14625     }
14626     msg_ctx = 0;
14627     while (!msg_ctx) {
14628         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
14629                                        GSS_C_MECH_CODE,
14630                                        GSS_C_NULL_OID,
14631                                        &msg_ctx,
14632                                        &msg
14633                                        );
14634         if ((gmaj_stat == GSS_S_COMPLETE)||
14635             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14636             fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value);
14637             gss_release_buffer(&gmin_stat, &msg);
14638         }
14639         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14640           break;
14641     }
14642     fprintf(stderr, "GSSAPI error: %s\n", s);
14643 }
14644 #endif /* FTP_GSSAPI */
14645
14646 #ifndef NOMHHOST
14647 #ifdef datageneral
14648 #define NOMHHOST
14649 #else
14650 #ifdef HPUX5WINTCP
14651 #define NOMHHOST
14652 #endif /* HPUX5WINTCP */
14653 #endif /* datageneral */
14654 #endif /* NOMHHOST */
14655
14656 #ifdef INADDRX
14657 static struct in_addr inaddrx;
14658 #endif /* INADDRX */
14659
14660 static char *
14661 ftp_hookup(host, port, tls) char * host; int port; int tls; {
14662     register struct hostent *hp = 0;
14663 #ifdef IP_TOS
14664 #ifdef IPTOS_THROUGHPUT
14665     int tos;
14666 #endif /* IPTOS_THROUGHPUT */
14667 #endif /* IP_TOS */
14668     int s;
14669     GSOCKNAME_T len;
14670     static char hostnamebuf[MAXHOSTNAMELEN];
14671     char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
14672     int  cport;
14673 #ifdef DEBUG
14674     extern int debtim;
14675     int xdebtim;
14676     xdebtim = debtim;
14677     debtim = 1;
14678 #endif /* DEBUG */
14679
14680     debug(F111,"ftp_hookup",host,port);
14681
14682 #ifndef NOHTTP
14683     if (tcp_http_proxy) {
14684         struct servent *destsp;
14685         char *p, *q;
14686
14687         ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
14688         for (p = tcp_http_proxy, q = hostname;
14689              *p != '\0' && *p != ':';
14690              p++, q++
14691              )
14692           *q = *p;
14693         *q = '\0';
14694
14695         if (*p == ':')
14696           p++;
14697         else
14698           p = "http";
14699
14700         destsp = getservbyname(p,"tcp");
14701         if (destsp)
14702           cport = ntohs(destsp->s_port);
14703         else if (p) {
14704           cport = atoi(p);
14705         } else
14706           cport = 80;
14707     } else
14708 #endif /* NOHTTP */
14709     {
14710         ckstrncpy(hostname,host,MAXHOSTNAMELEN);
14711         cport = port;
14712     }
14713     memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
14714     hisctladdr.sin_addr.s_addr = inet_addr(host);
14715     if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
14716     {
14717         debug(F110,"ftp hookup A",hostname,0);
14718         hisctladdr.sin_family = AF_INET;
14719         ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
14720     } else {
14721         debug(F110,"ftp hookup B",hostname,0);
14722         hp = gethostbyname(hostname);
14723 #ifdef HADDRLIST
14724         hp = ck_copyhostent(hp);        /* make safe copy that won't change */
14725 #endif /* HADDRLIST */
14726         if (hp == NULL) {
14727             fprintf(stderr, "ftp: %s: Unknown host\n", host);
14728             ftpcode = -1;
14729 #ifdef DEBUG
14730             debtim = xdebtim;
14731 #endif /* DEBUG */
14732             return((char *) 0);
14733         }
14734         hisctladdr.sin_family = hp->h_addrtype;
14735 #ifdef HADDRLIST
14736         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
14737                sizeof(hisctladdr.sin_addr));
14738 #else /* HADDRLIST */
14739         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
14740                sizeof(hisctladdr.sin_addr));
14741 #endif /* HADDRLIST */
14742         ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
14743     }
14744     debug(F110,"ftp hookup C",hostnamebuf,0);
14745     ftp_host = hostnamebuf;
14746     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14747     debug(F101,"ftp hookup socket","",s);
14748     if (s < 0) {
14749         perror("ftp: socket");
14750         ftpcode = -1;
14751 #ifdef DEBUG
14752         debtim = xdebtim;
14753 #endif /* DEBUG */
14754         return(0);
14755     }
14756     hisctladdr.sin_port = htons(cport);
14757     errno = 0;
14758
14759 #ifdef COMMENT
14760   printf("hisctladdr=%d\n",sizeof(hisctladdr));
14761   printf("hisctladdr.sin_addr=%d\n",sizeof(hisctladdr.sin_addr));
14762   printf("sockaddr_in=%d\n",sizeof(struct sockaddr_in));
14763   printf("hisctladdr.sin_addr.s_addr=%d\n",sizeof(hisctladdr.sin_addr.s_addr));
14764 #endif  /* COMMENT */
14765
14766 #ifdef HADDRLIST
14767     debug(F100,"ftp hookup HADDRLIST","",0);
14768     while
14769 #else
14770     debug(F100,"ftp hookup no HADDRLIST","",0);
14771     if
14772 #endif /* HADDRLIST */
14773       (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
14774           debug(F101,"ftp hookup connect failed","",errno);
14775 #ifdef HADDRLIST
14776           if (hp && hp->h_addr_list[1]) {
14777               int oerrno = errno;
14778
14779               fprintf(stderr, "ftp: connect to address %s: ",
14780                       inet_ntoa(hisctladdr.sin_addr));
14781               errno = oerrno;
14782               perror((char *) 0);
14783               hp->h_addr_list++;
14784               memcpy((char *)&hisctladdr.sin_addr,
14785                      hp->h_addr_list[0],
14786                      sizeof(hisctladdr.sin_addr));
14787               fprintf(stdout, "Trying %s...\n",
14788                       inet_ntoa(hisctladdr.sin_addr));
14789 #ifdef TCPIPLIB
14790               socket_close(s);
14791 #else /* TCPIPLIB */
14792               close(s);
14793 #endif /* TCPIPLIB */
14794               s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14795               if (s < 0) {
14796                   perror("ftp: socket");
14797                   ftpcode = -1;
14798 #ifdef DEBUG
14799                   debtim = xdebtim;
14800 #endif /* DEBUG */
14801                   return(0);
14802               }
14803               continue;
14804           }
14805 #endif /* HADDRLIST */
14806           perror("ftp: connect");
14807           ftpcode = -1;
14808           goto bad;
14809       }
14810     debug(F100,"ftp hookup connect ok","",0);
14811
14812     len = sizeof (myctladdr);
14813     errno = 0;
14814     if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
14815         debug(F101,"ftp hookup getsockname failed","",errno);
14816         perror("ftp: getsockname");
14817         ftpcode = -1;
14818         goto bad;
14819     }
14820     debug(F100,"ftp hookup getsockname ok","",0);
14821
14822 #ifndef NOHTTP
14823     if (tcp_http_proxy) {
14824 #ifdef OS2
14825         char * agent = "Kermit 95";     /* Default user agent */
14826 #else
14827         char * agent = "C-Kermit";
14828 #endif /* OS2 */
14829
14830         if (http_connect(s,agent,NULL,
14831                          tcp_http_proxy_user,
14832                          tcp_http_proxy_pwd,
14833                          0,
14834                          proxyhost
14835                          ) < 0) {
14836             char * foo = NULL;
14837 #ifdef TCPIPLIB
14838             socket_close(s);
14839 #else /* TCPIPLIB */
14840             close(s);
14841 #endif /* TCPIPLIB */
14842
14843             while (foo == NULL && tcp_http_proxy != NULL ) {
14844
14845                 if (tcp_http_proxy_errno == 401 ||
14846                      tcp_http_proxy_errno == 407 ) {
14847                     char uid[UIDBUFLEN];
14848                     char pwd[PWDSIZ];
14849                     struct txtbox tb[2];
14850                     int ok;
14851
14852                     tb[0].t_buf = uid;
14853                     tb[0].t_len = UIDBUFLEN;
14854                     tb[0].t_lbl = "Proxy Userid: ";
14855                     tb[0].t_dflt = NULL;
14856                     tb[0].t_echo = 1;
14857                     tb[1].t_buf = pwd;
14858                     tb[1].t_len = 256;
14859                     tb[1].t_lbl = "Proxy Passphrase: ";
14860                     tb[1].t_dflt = NULL;
14861                     tb[1].t_echo = 2;
14862
14863                     ok = uq_mtxt("Proxy Server Authentication Required\n",
14864                                   NULL, 2, tb);
14865
14866                     if (ok && uid[0]) {
14867                         char * proxy_user, * proxy_pwd;
14868
14869                         proxy_user = tcp_http_proxy_user;
14870                         proxy_pwd  = tcp_http_proxy_pwd;
14871
14872                         tcp_http_proxy_user = uid;
14873                         tcp_http_proxy_pwd = pwd;
14874
14875                         foo = ftp_hookup(host, port, 0);
14876
14877                         debug(F110,"ftp_hookup()",foo,0);
14878                         memset(pwd,0,PWDSIZ);
14879                         tcp_http_proxy_user = proxy_user;
14880                         tcp_http_proxy_pwd = proxy_pwd;
14881                     } else
14882                         break;
14883                 } else
14884                     break;
14885             }
14886             if (foo != NULL)
14887               return(foo);
14888             perror("ftp: connect");
14889             ftpcode = -1;
14890             goto bad;
14891         }
14892         ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
14893     }
14894 #endif /* NOHTTP */
14895
14896     csocket = s;
14897
14898 #ifdef CK_SSL
14899     if (tls) {
14900         /* FTP over SSL
14901          * If the connection is over an SSL proxy then the
14902          * auth_type will be NULL.  However, I'm not sure
14903          * whether we should protect the data channel in
14904          * that case or not.
14905          */
14906
14907         debug(F100,"ftp hookup use_tls","",0);
14908         if (!ssl_auth()) {
14909             debug(F100,"ftp hookup ssl_auth failed","",0);
14910             auth_type = NULL;
14911             ftpcode = -1;
14912             csocket = -1;
14913             goto bad;
14914         }
14915         ssl_ftp_proxy = 1;
14916     }
14917 #endif /* CK_SSL */
14918
14919 #ifdef IP_TOS
14920 #ifdef IPTOS_LOWDELAY
14921     tos = IPTOS_LOWDELAY;
14922     if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
14923       perror("ftp: setsockopt TOS (ignored)");
14924 #endif
14925 #endif
14926     if (!quiet)
14927       printf("Connected to %s.\n", host);
14928
14929     /* Read greeting from server */
14930     if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
14931         debug(F100,"ftp hookup bad reply","",0);
14932 #ifdef TCPIPLIB
14933         socket_close(csocket);
14934 #else /* TCPIPLIB */
14935         close(csocket);
14936 #endif /* TCPIPLIB */
14937         ftpcode = -1;
14938         goto bad;
14939     }
14940 #ifdef SO_OOBINLINE
14941     {
14942         int on = 1;
14943         errno = 0;
14944         if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
14945                        sizeof(on)) < 0) {
14946             perror("ftp: setsockopt");
14947             debug(F101,"ftp hookup setsockopt failed","",errno);
14948         }
14949 #ifdef DEBUG
14950         else
14951           debug(F100,"ftp hookup setsockopt ok","",0);
14952 #endif /* DEBUG */
14953     }
14954 #endif /* SO_OOBINLINE */
14955
14956 #ifdef DEBUG
14957     debtim = xdebtim;
14958 #endif /* DEBUG */
14959     return(ftp_host);
14960
14961   bad:
14962     debug(F100,"ftp hookup bad","",0);
14963 #ifdef TCPIPLIB
14964     socket_close(s);
14965 #else /* TCPIPLIB */
14966     close(s);
14967 #endif /* TCPIPLIB */
14968 #ifdef DEBUG
14969     debtim = xdebtim;
14970 #endif /* DEBUG */
14971     csocket = -1;
14972     return((char *)0);
14973 }
14974
14975 static VOID
14976 ftp_init() {
14977     int i, n;
14978
14979     /* The purpose of the initial REST 0 is not clear, but other FTP */
14980     /* clients do it.  In any case, failure of this command is not a */
14981     /* reliable indication that the server does not support Restart. */
14982
14983     okrestart = 0;
14984     if (!noinit) {
14985         n = ftpcmd("REST 0",NULL,0,0,0);
14986         if (n == REPLY_COMPLETE)
14987           okrestart = 1;
14988 #ifdef COMMENT
14989         else if (ftp_deb)
14990           printf("WARNING: Unable to restore file pointer.\n");
14991 #endif /* COMMENT */
14992     }
14993     n = ftpcmd("SYST",NULL,0,0,0);      /* Get server system type */
14994     if (n == REPLY_COMPLETE) {
14995         register char *cp, c = NUL;
14996         cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
14997         if (cp == NULL)
14998           cp = ckstrchr(ftp_reply_str+4,'\r');
14999         if (cp) {
15000             if (cp[-1] == '.')
15001               cp--;
15002             c = *cp;                    /* Save this char */
15003             *cp = '\0';                 /* Replace it with NUL */
15004         }
15005         if (!quiet)
15006           printf("Remote system type is %s.\n",ftp_reply_str+4);
15007         ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
15008         if (cp)                         /* Put back saved char */
15009           *cp = c;
15010     }
15011     alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
15012
15013     if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
15014     else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
15015     else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
15016     else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
15017     else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
15018     else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
15019     else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
15020
15021 #ifdef FTP_PROXY
15022     unix_proxy = 0;
15023     if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
15024 #endif /* FTP_PROXY */
15025
15026     if (ftp_cmdlin && ftp_xfermode == XMODE_M)
15027       ftp_typ = binary;                 /* Type given on command line */
15028     else                                /* Otherwise set it automatically */
15029       ftp_typ = alike ? FTT_BIN : FTT_ASC;
15030     changetype(ftp_typ,0);              /* Change to this type */
15031     g_ftp_typ = ftp_typ;                /* Make it the global type */
15032     if (!quiet)
15033       printf("Default transfer mode is %s\n",
15034              ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
15035              );
15036     for (i = 0; i < 16; i++)            /* Init server FEATure table */
15037       sfttab[i] = 0;
15038     if (!noinit) {
15039         n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
15040 #ifdef COMMENT
15041         if (n != REPLY_COMPLETE)
15042           printf("WARNING: Server does not accept MODE S(TREAM)\n");
15043 #endif /* COMMENT */
15044         n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
15045 #ifdef COMMENT
15046         if (n != REPLY_COMPLETE)
15047           printf("WARNING: Server does not accept STRU F(ILE)\n");
15048 #endif /* COMMENT */
15049         if (featok) {
15050             n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
15051             if (n == REPLY_COMPLETE) {
15052                 debug(F101,"ftp_init FEAT","",sfttab[0]);
15053                 if (deblog || ftp_deb) {
15054                     int i;
15055                     for (i = 1; i < 16 && i < nfeattab; i++) {
15056                         debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
15057                         if (ftp_deb)
15058                           printf("  Server %s %s\n",
15059                                  sfttab[i] ? "supports" : "does not support",
15060                                  feattab[i].kwd
15061                                  );
15062                     }
15063                     /* Deal with disabled MLST opts here if necessary */
15064                     /* But why would it be? */
15065                 }
15066             }
15067         }
15068     }
15069 }
15070
15071 static int
15072 ftp_login(host) char * host; {          /* (also called from ckuusy.c) */
15073     static char ftppass[PASSBUFSIZ]="";
15074     char tmp[PASSBUFSIZ];
15075     char *user = NULL, *pass = NULL, *acct = NULL;
15076     int n, aflag = 0;
15077     extern char uidbuf[];
15078     extern char pwbuf[];
15079     extern int  pwflg, pwcrypt;
15080
15081     debug(F111,"ftp_login",ftp_logname,ftp_log);
15082
15083     if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
15084       anonymous = 1;
15085     if (!ckstrcmp(ftp_logname,"ftp",-1,0))
15086       anonymous = 1;
15087
15088 #ifdef FTP_SRP
15089     if (auth_type && !strcmp(auth_type, "SRP")) {
15090         user = srp_user;
15091         pass = srp_pass;
15092         acct = srp_acct;
15093     } else
15094 #endif /* FTP_SRP */
15095       if (anonymous) {
15096           user = "anonymous";
15097           if (ftp_tmp) {                /* They gave a password */
15098               pass = ftp_tmp;
15099           } else if (ftp_apw) {         /* SET FTP ANONYMOUS-PASSWORD */
15100               pass = ftp_apw;
15101           } else {                      /* Supply user@host */
15102               ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
15103               pass = tmp;
15104           }
15105           debug(F110,"ftp anonymous",pass,0);
15106       } else {
15107 #ifdef USE_RUSERPASS
15108           if (ruserpass(host, &user, &pass, &acct) < 0) {
15109               ftpcode = -1;
15110               return(0);
15111           }
15112 #endif /* USE_RUSERPASS */
15113           if (ftp_logname) {
15114               user = ftp_logname;
15115               pass = ftp_tmp;
15116           } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
15117               user = uidbuf;
15118               if (ftp_tmp) {
15119                   pass = ftp_tmp;
15120               } else if (pwbuf[0] && pwflg) {
15121                   ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
15122 #ifdef OS2
15123                   if ( pwcrypt )
15124                       ck_encrypt((char *)ftppass);
15125 #endif /* OS2 */
15126                   pass = ftppass;
15127               }
15128           }
15129           acct = ftp_acc;
15130           while (user == NULL) {
15131               char *myname, prompt[PROMPTSIZ];
15132               int ok;
15133
15134               myname = whoami();
15135               if (myname)
15136                 ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
15137                           NULL,NULL,NULL,NULL,NULL,NULL,NULL);
15138               else
15139                 ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
15140               tmp[0] = '\0';
15141               
15142               ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
15143                           DEFAULT_UQ_TIMEOUT);
15144               if (!ok || *tmp == '\0')
15145                 user = myname;
15146               else
15147                 user = brstrip(tmp);
15148           }
15149       }
15150     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15151     if (n == REPLY_COMPLETE) {
15152         /* determine if we need to send a dummy password */
15153         if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
15154           ftpcmd("PASS dummy",NULL,0,0,1);
15155     } else if (n == REPLY_CONTINUE) {
15156 #ifdef CK_ENCRYPTION
15157         int oldftp_cpl;
15158 #endif /* CK_ENCRYPTION */
15159
15160         if (pass == NULL) {
15161             int ok;
15162             setint();
15163             ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
15164                         DEFAULT_UQ_TIMEOUT);
15165             if (ok)
15166                 pass = brstrip(ftppass);
15167         }
15168
15169 #ifdef CK_ENCRYPTION
15170         oldftp_cpl = ftp_cpl;
15171         ftp_cpl = FPL_PRV;
15172 #endif /* CK_ENCRYPTION */
15173         n = ftpcmd("PASS",pass,-1,-1,1);
15174         if (!anonymous && pass) {
15175             char * p = pass;
15176             while (*p++) *(p-1) = NUL;
15177             makestr(&ftp_tmp,NULL);
15178         }
15179 #ifdef CK_ENCRYPTION
15180         /* level may have changed */
15181         if (ftp_cpl == FPL_PRV)
15182           ftp_cpl = oldftp_cpl;
15183 #endif /* CK_ENCRYPTION */
15184     }
15185     if (n == REPLY_CONTINUE) {
15186         aflag++;
15187         if (acct == NULL) {
15188             static char ftpacct[80];
15189             int ok;
15190             setint();
15191             ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
15192                         DEFAULT_UQ_TIMEOUT);
15193             if (ok)
15194                 acct = brstrip(ftpacct);
15195         }
15196         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15197     }
15198     if (n != REPLY_COMPLETE) {
15199         fprintf(stderr, "FTP login failed.\n");
15200         if (haveurl)
15201           doexit(BAD_EXIT,-1);
15202         return(0);
15203     }
15204     if (!aflag && acct != NULL) {
15205         ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15206     }
15207     makestr(&ftp_logname,user);
15208     loggedin = 1;
15209 #ifdef LOCUS
15210     /* Unprefixed file management commands go to server */
15211     if (autolocus && !ftp_cmdlin) {
15212         setlocus(0,1);
15213     }
15214 #endif /* LOCUS */
15215     ftp_init();
15216
15217     if (anonymous && !quiet) {
15218         printf(" Logged in as anonymous (%s)\n",pass);
15219         memset(pass, 0, strlen(pass));
15220     }
15221     if (ftp_rdir) {
15222         if (doftpcwd(ftp_rdir,-1) < 1)
15223           doexit(BAD_EXIT,-1);
15224     }
15225
15226 #ifdef FTP_PROXY
15227     if (proxy)
15228       return(1);
15229 #endif /* FTP_PROXY */
15230     return(1);
15231 }
15232
15233 static int
15234 ftp_reset() {
15235     int rc;
15236 #ifdef BSDSELECT
15237     int nfnd = 1;
15238     fd_set mask;
15239     FD_ZERO(&mask);
15240     while (nfnd > 0) {
15241         FD_SET(csocket, &mask);
15242         if ((nfnd = empty(&mask,0)) < 0) {
15243             perror("reset");
15244             ftpcode = -1;
15245             lostpeer();
15246             return(0);
15247         } else if (nfnd) {
15248             getreply(0,-1,-1,ftp_vbm,0);
15249         }
15250     }
15251 #else /* BSDSELECT */
15252 #ifdef IBMSELECT
15253     int nfnd = 1;
15254     while (nfnd > 0) {
15255         if ((nfnd = empty(&csocket,1,0)) < 0) {
15256             perror("reset");
15257             ftpcode = -1;
15258             lostpeer();
15259             return(0);
15260         } else if (nfnd) {
15261             getreply(0,-1,-1,ftp_vbm,0);
15262         }
15263     }
15264 #endif /* IBMSELECT */
15265 #endif /* BSDSELECT */
15266     rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
15267     if (rc > 0)
15268       loggedin = 0;
15269     return(rc);
15270 }
15271
15272 static int
15273 ftp_rename(from, to) char * from, * to; {
15274     int lcs = -1, rcs = -1;
15275 #ifndef NOCSETS
15276     if (ftp_xla) {
15277         lcs = ftp_csl;
15278         if (lcs < 0) lcs = fcharset;
15279         rcs = ftp_csx;
15280         if (rcs < 0) rcs = ftp_csr;
15281     }
15282 #endif /* NOCSETS */
15283     if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
15284         return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
15285     }
15286     return(0);                          /* Failure */
15287 }
15288
15289 static int
15290 ftp_umask(mask) char * mask; {
15291     int rc;
15292     rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
15293     return(rc);
15294 }
15295
15296 static int
15297 ftp_user(user,pass,acct) char * user, * pass, * acct; {
15298     int n = 0, aflag = 0;
15299     char pwd[PWDSIZ];
15300
15301     if (!auth_type && ftp_aut) {
15302 #ifdef FTP_SRP
15303         if (ck_srp_is_installed()) {
15304             if (srp_ftp_auth( NULL, user, pass)) {
15305                 makestr(&pass,srp_pass);
15306             }
15307         }
15308 #endif /* FTP_SRP */
15309     }
15310     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15311     if (n == REPLY_COMPLETE)
15312       n = ftpcmd("PASS dummy",NULL,0,0,1);
15313     else if (n == REPLY_CONTINUE) {
15314 #ifdef CK_ENCRYPTION
15315         int oldftp_cpl;
15316 #endif /* CK_ENCRYPTION */
15317         if (pass == NULL || !pass[0]) {
15318             int ok;
15319             pwd[0] = '\0';
15320             setint();
15321             ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
15322                         DEFAULT_UQ_TIMEOUT);
15323             if (ok)
15324                 pass = brstrip(pwd);
15325         }
15326
15327 #ifdef CK_ENCRYPTION
15328         if ((oldftp_cpl = ftp_cpl) == PROT_S)
15329           ftp_cpl = PROT_P;
15330 #endif /* CK_ENCRYPTION */
15331         n = ftpcmd("PASS",pass,-1,-1,1);
15332         memset(pass, 0, strlen(pass));
15333 #ifdef CK_ENCRYPTION
15334         /* level may have changed */
15335         if (ftp_cpl == PROT_P)
15336           ftp_cpl = oldftp_cpl;
15337 #endif /* CK_ENCRYPTION */
15338     }
15339     if (n == REPLY_CONTINUE) {
15340         if (acct == NULL || !acct[0]) {
15341             int ok;
15342             pwd[0] = '\0';
15343             setint();
15344             ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
15345                         DEFAULT_UQ_TIMEOUT);
15346             if (ok)
15347                 acct = pwd;
15348         }
15349         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15350         aflag++;
15351     }
15352     if (n != REPLY_COMPLETE) {
15353         printf("Login failed.\n");
15354         return(0);
15355     }
15356     if (!aflag && acct != NULL && acct[0]) {
15357         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15358     }
15359     if (n == REPLY_COMPLETE) {
15360         makestr(&ftp_logname,user);
15361         loggedin = 1;
15362         ftp_init();
15363         return(1);
15364     }
15365     return(0);
15366 }
15367
15368 char *
15369 ftp_authtype() {
15370     if (!connected)
15371       return("NULL");
15372     return(auth_type ? auth_type : "NULL");
15373 }
15374
15375 char *
15376 ftp_cpl_mode() {
15377     switch (ftp_cpl) {
15378       case FPL_CLR:
15379         return("clear");
15380       case FPL_SAF:
15381         return("safe");
15382       case FPL_PRV:
15383         return("private");
15384       case FPL_CON:
15385         return("confidential");
15386       default:
15387         return("(error)");
15388     }
15389 }
15390
15391 char *
15392 ftp_dpl_mode() {
15393     switch (ftp_dpl) {
15394       case FPL_CLR:
15395         return("clear");
15396       case FPL_SAF:
15397         return("safe");
15398       case FPL_PRV:
15399         return("private");
15400       case FPL_CON:
15401         return("confidential");
15402       default:
15403         return("(error)");
15404     }
15405 }
15406
15407
15408 /* remote_files() */
15409 /*
15410    Returns next remote filename on success;
15411    NULL on error or no more files with global rfrc set to:
15412      -1: Bad argument
15413      -2: Server error response to NLST, e.g. file not found
15414      -3: No more files
15415      -9: Internal error
15416 */
15417 #define FTPNAMBUFLEN CKMAXPATH+1024
15418
15419 /* Check: ckmaxfiles CKMAXOPEN */
15420
15421 #define MLSDEPTH 128                    /* Stack of open temp files */
15422 static int mlsdepth = 0;                /* Temp file stack depth */
15423 static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
15424 static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
15425
15426 static VOID
15427 mlsreset() {                            /* Reset MGET temp-file stack */
15428     int i;
15429     for (i = 0; i <= mlsdepth; i++) {
15430         if (tmpfilptr[i]) {
15431             fclose(tmpfilptr[i]);
15432             tmpfilptr[i] = NULL;
15433             if (tmpfilnam[i]) {
15434 #ifdef OS2
15435                 unlink(tmpfilnam[i]);
15436 #endif /* OS2 */
15437                 free(tmpfilnam[i]);
15438             }
15439         }
15440     }
15441     mlsdepth = 0;
15442 }
15443
15444 static CHAR *
15445 #ifdef CK_ANSIC
15446 remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
15447 #else /* CK_ANSIC */
15448 remote_files(new_query, arg, pattern, proxy_switch)
15449     int new_query;
15450     CHAR * arg;                         /* That we send to the server */
15451     CHAR * pattern;                     /* That we use locally */
15452     int proxy_switch;
15453 #endif /* CK_ANSIC */
15454 /* remote_files */ {
15455     static CHAR buf[FTPNAMBUFLEN];
15456     CHAR *cp, *whicharg;
15457     char * cdto = NULL;
15458     char * p;
15459     int i, x, forced = 0;
15460     int lcs = 0, rcs = 0, xlate = 0;
15461
15462     debug(F101,"ftp remote_files new_query","",new_query);
15463     debug(F110,"ftp remote_files arg",arg,0);
15464     debug(F110,"ftp remote_files pattern",pattern,0);
15465
15466     rfrc = -1;
15467     if (pattern)                        /* Treat empty pattern same as NULL */
15468       if (!*pattern)
15469         pattern = NULL;
15470     if (arg)                            /* Ditto for arg */
15471       if (!*arg)
15472         arg = NULL;
15473
15474   again:
15475
15476     if (new_query) {
15477         if (tmpfilptr[mlsdepth]) {
15478             fclose(tmpfilptr[mlsdepth]);
15479             tmpfilptr[mlsdepth] = NULL;
15480 #ifdef OS2
15481             if (!ftp_deb && !deblog)
15482               unlink(tmpfilnam[mlsdepth]);
15483 #endif /* OS2 */
15484         }
15485     }
15486     if (tmpfilptr[mlsdepth] == NULL) {
15487         extern char * tempdir;
15488         char * p;
15489         debug(F110,"ftp remote_files tempdir",tempdir,0);
15490         if (tempdir) {
15491             p = tempdir;
15492         } else {
15493 #ifdef OS2
15494 #ifdef NT
15495             p = getenv("K95TMP");
15496 #else
15497             p = getenv("K2TMP");
15498 #endif /* NT */
15499             if (!p)
15500 #endif /* OS2 */
15501               p = getenv("CK_TMP");
15502             if (!p)
15503               p = getenv("TMPDIR");
15504             if (!p) p = getenv("TEMP");
15505             if (!p) p = getenv("TMP");
15506 #ifdef OS2ORUNIX
15507             if (p) {
15508                 int len = strlen(p);
15509                 if (p[len-1] != '/'
15510 #ifdef OS2
15511                     && p[len-1] != '\\'
15512 #endif /* OS2 */
15513                      ) {
15514                     static char foo[CKMAXPATH];
15515                     ckstrncpy(foo,p,CKMAXPATH);
15516                     ckstrncat(foo,"/",CKMAXPATH);
15517                     p = foo;
15518                 }
15519             } else
15520 #else /* OS2ORUNIX */
15521             if (!p)
15522 #endif /* OS2ORUNIX */
15523 #ifdef UNIX                             /* Systems that have a standard */
15524                 p = "/tmp/";            /* temporary directory... */
15525 #else
15526 #ifdef datageneral
15527             p = ":TMP:";
15528 #else
15529             p = "";
15530 #endif /* datageneral */
15531 #endif /* UNIX */
15532         }
15533         debug(F110,"ftp remote_files p",p,0);
15534
15535         /* Get temp file */
15536
15537         if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
15538             ckmakmsg((char *)tmpfilnam[mlsdepth],
15539                      CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
15540         } else {
15541             printf("?Malloc failure: remote_files()\n");
15542             return(NULL);
15543         }
15544
15545 #ifdef NT
15546         {
15547             char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
15548             if ( tmpfil )
15549                 ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
15550         }
15551 #else /* NT */
15552 #ifdef MKTEMP
15553 #ifdef MKSTEMP
15554         x = mkstemp((char *)tmpfilnam[mlsdepth]);
15555         if (x > -1) close(x);           /* We just want the name. */
15556 #else
15557         mktemp((char *)tmpfilnam[mlsdepth]);
15558 #endif /* MKSTEMP */
15559         /* if no mktmpnam() the name will just be "ckXXXXXX"... */
15560 #endif /* MKTEMP */
15561 #endif /* NT */
15562
15563         debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
15564               tmpfilnam[mlsdepth],mlsdepth);
15565
15566 #ifdef FTP_PROXY
15567         if (proxy_switch) {
15568             pswitch(!proxy);
15569         }
15570 #endif /* FTP_PROXY */
15571
15572         debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
15573         debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
15574         debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
15575
15576 #ifndef NOCSETS
15577         xlate = ftp_xla;                /* SET FTP CHARACTER-SET-TRANSLATION */
15578         if (xlate) {                    /* ON? */
15579             lcs = ftp_csl;              /* Local charset */
15580             if (lcs < 0) lcs = fcharset;
15581             if (lcs < 0) xlate = 0;
15582         }
15583         if (xlate) {                    /* Still ON? */
15584             rcs = ftp_csx;              /* Remote (Server) charset */
15585             if (rcs < 0) rcs = ftp_csr;
15586             if (rcs < 0) xlate = 0;
15587         }
15588 #endif /* NOCSETS */
15589
15590         forced = mgetforced;            /* MGET method forced? */
15591         if (!forced || !mgetmethod)     /* Not forced... */
15592           mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
15593               SND_MLS :
15594               SND_NLS; 
15595 /*                                           
15596   User's Command:                 Result:
15597     mget /nlst                     NLST (NULL)
15598     mget /nlst foo                 NLST foo
15599     mget /nlst *.txt               NLST *.txt 
15600     mget /nlst /match:*.txt        NLST (NULL)
15601     mget /nlst /match:*.txt  foo   NLST foo   
15602     mget /mlsd                     MLSD (NULL)
15603     mget /mlsd foo                 MLSD foo
15604     mget /mlsd *.txt               MLSD (NULL)
15605     mget /mlsd /match:*.txt        MLSD (NULL)
15606     mget /mlsd /match:*.txt  foo   MLSD foo
15607 */
15608         x = -1;
15609         while (x < 0) {
15610             if (pattern) {              /* Don't simplify this! */
15611                 whicharg = arg;
15612             } else if (mgetmethod == SND_MLS) {
15613                 if (arg)
15614                   whicharg = iswild((char *)arg) ? NULL : arg;
15615                 else
15616                   whicharg = NULL;
15617             } else {
15618                 whicharg = arg;
15619             }
15620             debug(F110,"ftp remote_files mgetmethod",
15621                   mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
15622             debug(F110,"ftp remote_files whicharg",whicharg,0);
15623
15624             x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
15625                             (char *)tmpfilnam[mlsdepth],
15626                             (char *)whicharg,
15627                             "wb",
15628                             0,
15629                             0,
15630                             NULL,
15631                             xlate,
15632                             lcs,
15633                             rcs
15634                             );
15635             if (x < 0) {                /* Chosen method wasn't accepted */
15636                 if (forced) {
15637                     if (ftpcode > 500 && ftpcode < 505 && !quiet)
15638                       printf("?%s: Not supported by server\n",
15639                              mgetmethod == SND_MLS ? "MLSD" : "NLST"
15640                              );
15641                     rfrc = -2;          /* Fail */
15642                     return(NULL);
15643                 }
15644                 /* Not forced - if MLSD failed, try NLST */
15645                 if (mgetmethod == SND_MLS) {  /* Server lied about MLST */
15646                     sfttab[SFT_MLST] = 0;     /* So disable it */
15647                     mlstok = 0;               /* and */
15648                     mgetmethod = SND_NLS;     /* try NLST */
15649                     continue;
15650                 }
15651                 rfrc = -2;
15652                 return(NULL);
15653             }
15654         }
15655 #ifdef FTP_PROXY
15656         if (proxy_switch) {
15657             pswitch(!proxy);
15658         }
15659 #endif /* FTP_PROXY */
15660         tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
15661 #ifndef OS2
15662         if (tmpfilptr[mlsdepth]) {
15663             if (!ftp_deb && !deblog)
15664               unlink(tmpfilnam[mlsdepth]);
15665         }
15666 #endif /* OS2 */
15667       notemp:
15668         if (!tmpfilptr[mlsdepth]) {
15669             debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
15670             if ((!dpyactive || ftp_deb))
15671               printf("?Can't find list of remote files, oops\n");
15672             rfrc = -9;
15673             return(NULL);
15674         }
15675         if (ftp_deb)
15676           printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
15677     }
15678     buf[0] = NUL;
15679     buf[FTPNAMBUFLEN-1] = NUL;
15680     buf[FTPNAMBUFLEN-2] = NUL;
15681
15682     /* We have to redo all this because the first time was only for */
15683     /* for getting the file list, now it's for getting each file */
15684
15685     if (arg && mgetmethod == SND_MLS) { /* MLSD */
15686         if (!pattern && iswild((char *)arg)) {
15687             pattern = arg;              /* Wild arg is really a pattern */
15688             if (pattern)
15689               if (!*pattern)
15690                 pattern = NULL;
15691             arg = NULL;                 /* and not an arg */
15692         }
15693         if (new_query) {                /* Initial query? */
15694             cdto = (char *)arg;         /* (nonwild) arg given? */
15695             if (cdto)
15696               if (!*cdto)
15697                 cdto = NULL;
15698             if (cdto)                   /* If so, then CD to it */
15699               doftpcwd(cdto,0);
15700         }
15701     }
15702     new_query = 0;
15703
15704     if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
15705         fclose(tmpfilptr[mlsdepth]);
15706         tmpfilptr[mlsdepth] = NULL;
15707
15708 #ifdef OS2
15709         if (!ftp_deb && !deblog)
15710           unlink(tmpfilnam[mlsdepth]);
15711 #endif /* OS2 */
15712         if (ftp_deb && !deblog) {
15713             printf("(Temporary file %s NOT deleted)\n",
15714                    (char *)tmpfilnam[mlsdepth]);
15715         }
15716         if (mlsdepth <= 0) {            /* EOF at depth 0 */
15717             rfrc = -3;                  /* means we're done */
15718             return(NULL);
15719         }
15720         printf("POPPING(%d)...\n",mlsdepth-1); 
15721         if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
15722         mlsdepth--;
15723         doftpcdup();
15724         zchdir("..");                   /* <-- Not portable */
15725         goto again;
15726     }
15727     if (buf[FTPNAMBUFLEN-1]) {
15728         printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
15729                FTPNAMBUFLEN
15730                );
15731         debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
15732         return(NULL);
15733     }
15734     /* debug(F110,"ftp remote_files buf 1",buf,0); */
15735     if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
15736       *cp = '\0';
15737     if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
15738       *cp = '\0';
15739     debug(F110,"ftp remote_files buf",buf,0);
15740     rfrc = 0;
15741
15742     if (ftp_deb)
15743       printf("[%s]\n",(char *)buf);
15744
15745     havesize = (CK_OFF_T)-1;            /* Initialize file facts... */
15746     havetype = 0;
15747     makestr(&havemdtm,NULL);
15748     p = (char *)buf;
15749
15750     if (mgetmethod == SND_NLS) {        /* NLST... */
15751         if (pattern) {
15752             if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15753               goto again;
15754         }
15755     } else {                            /* MLSD... */
15756         p = parsefacts((char *)buf);
15757         switch (havetype) {
15758           case FTYP_FILE:               /* File: Get it if it matches */
15759             if (pattern) {
15760                 if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15761                   goto again;
15762             }
15763             break;
15764           case FTYP_CDIR:               /* Current directory */
15765           case FTYP_PDIR:               /* Parent directory */
15766             goto again;                 /* Skip */
15767           case FTYP_DIR:                /* (Sub)Directory */
15768             if (!recursive)             /* If not /RECURSIVE */
15769               goto again;               /* Skip */
15770             if (mlsdepth < MLSDEPTH) {
15771                 char * p2 = NULL;
15772                 mlsdepth++;
15773                 printf("RECURSING [%s](%d)...\n",p,mlsdepth); 
15774                 if (doftpcwd(p,0) > 0) {
15775                     int x;
15776                     if (!ckstrchr(p,'/')) {
15777                         /* zmkdir() needs dirsep */
15778                         if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
15779                             strcpy(p2,p);       /* SAFE */
15780                             strcat(p2,"/");     /* SAFE */
15781                             p = p2;
15782                         }
15783                     }
15784 #ifdef NOMKDIR
15785                     x = -1;
15786 #else
15787                     x = zmkdir(p);
15788 #endif /* NOMKDIR */
15789                     if (x > -1) {
15790                         zchdir(p);
15791                         p = (char *)remote_files(1,arg,pattern,0);
15792                         if (p2) free(p2);
15793                     } else {
15794                         printf("?mkdir failed: [%s] Depth=%d\n",
15795                                p,
15796                                mlsdepth
15797                                );
15798                         mlsreset();
15799                         if (p2) free(p2);
15800                         return(NULL);
15801                     }
15802                 } else {
15803                     printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
15804                     mlsreset();
15805                     return(NULL);
15806                 }
15807             } else {
15808                 printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
15809                        mlsdepth
15810                        );
15811                 mlsreset();
15812                 return(NULL);
15813             }
15814         }
15815     }
15816
15817 #ifdef DEBUG
15818     if (deblog) {
15819         debug(F101,"remote_files havesize","",havesize);
15820         debug(F101,"remote_files havetype","",havetype);
15821         debug(F110,"remote_files havemdtm",havemdtm,0); 
15822         debug(F110,"remote_files name",p,0);    
15823     }
15824 #endif /* DEBUG */
15825     return((CHAR *)p);
15826 }
15827
15828 /* N O T  P O R T A B L E !!! */
15829
15830 #if (SIZEOF_SHORT == 4)
15831 typedef unsigned short ftp_uint32;
15832 typedef short ftp_int32;
15833 #else
15834 #if (SIZEOF_INT == 4)
15835 typedef unsigned int ftp_uint32;
15836 typedef int ftp_int32;
15837 #else
15838 #if (SIZEOF_LONG == 4)
15839 typedef ULONG ftp_uint32;
15840 typedef long ftp_int32;
15841 #endif
15842 #endif
15843 #endif
15844
15845 /* Perhaps use these in general, certainly use them for GSSAPI */
15846
15847 #ifndef looping_write
15848 #define ftp_int32 int
15849 #define ftp_uint32 unsigned int
15850 static int
15851 looping_write(fd, buf, len)
15852     int fd;
15853     register CONST char *buf;
15854     int len;
15855 {
15856     int cc;
15857     register int wrlen = len;
15858     do {
15859         cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
15860         if (cc < 0) {
15861             if (errno == EINTR)
15862               continue;
15863             return(cc);
15864         } else {
15865             buf += cc;
15866             wrlen -= cc;
15867         }
15868     } while (wrlen > 0);
15869     return(len);
15870 }
15871 #endif
15872 #ifndef looping_read
15873 static int
15874 looping_read(fd, buf, len)
15875     int fd;
15876     register char *buf;
15877     register int len;
15878 {
15879     int cc, len2 = 0;
15880
15881     do {
15882         cc = recv(fd, (char *)buf, len,0);
15883         if (cc < 0) {
15884             if (errno == EINTR)
15885               continue;
15886             return(cc);                 /* errno is already set */
15887         } else if (cc == 0) {
15888             return(len2);
15889         } else {
15890             buf += cc;
15891             len2 += cc;
15892             len -= cc;
15893         }
15894     } while (len > 0);
15895     return(len2);
15896 }
15897 #endif /* looping_read */
15898
15899 #define ERR -2
15900
15901 #ifdef COMMENT
15902 static
15903 secure_putbyte(fd, c) int fd; CHAR c; {
15904     int ret;
15905
15906     ucbuf[nout++] = c;
15907     if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
15908         nout = 0;
15909         if (!ftpissecure())
15910           ret = send(fd, (SENDARG2TYPE)ucbuf,
15911                      (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
15912         else
15913           ret = secure_putbuf(fd,
15914                               ucbuf,
15915                               (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
15916                               );
15917         return(ret?ret:c);
15918     }
15919     return(c);
15920 }
15921 #endif /* COMMENT */
15922
15923 /* returns:
15924  *       0  on success
15925  *      -1  on error (errno set)
15926  *      -2  on security error
15927  */
15928 static int
15929 secure_flush(fd) int fd; {
15930     int rc = 0;
15931     int len = 0;
15932
15933     if (nout > 0) {
15934         len = nout;
15935         if (!ftpissecure()) {
15936             rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
15937             nout = 0;
15938             goto xflush;
15939         } else {
15940             rc = secure_putbuf(fd, ucbuf, nout);
15941             if (rc)
15942               goto xflush;
15943         }
15944     }
15945     rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
15946
15947   xflush:
15948     if (rc > -1 && len > 0 && fdispla != XYFD_B) {
15949         spackets++;
15950         spktl = len;
15951         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
15952     }
15953     return(rc);
15954 }
15955
15956 #ifdef COMMENT                          /* (not used) */
15957 /* returns:
15958  *      c>=0  on success
15959  *      -1    on error
15960  *      -2    on security error
15961  */
15962 static int
15963 #ifdef CK_ANSIC
15964 secure_putc(char c, int fd)
15965 #else
15966 secure_putc(c, fd) char c; int fd;
15967 #endif /* CK_ANSIC */
15968 /* secure_putc */ {
15969     return(secure_putbyte(fd, (CHAR) c));
15970 }
15971 #endif /* COMMENT */
15972
15973 /* returns:
15974  *      nbyte on success
15975  *      -1  on error (errno set)
15976  *      -2  on security error
15977  */
15978 static int
15979 #ifdef CK_ANSIC
15980 secure_write(int fd, CHAR * buf, unsigned int nbyte)
15981 #else
15982 secure_write(fd, buf, nbyte)
15983     int fd;
15984     CHAR * buf;
15985     unsigned int nbyte;
15986 #endif /* CK_ANSIC */
15987 {
15988     int ret;
15989
15990 #ifdef FTP_TIMEOUT    
15991     ftp_timed_out = 0;
15992     if (check_data_connection(fd,1) < 0) {
15993         ftp_timed_out = 1;
15994         return(-3);
15995     }
15996 #endif  /* FTP_TIMEOUT */
15997
15998     if (!ftpissecure()) {
15999         if (nout > 0) {
16000             if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
16001               return(ret);
16002             nout = 0;
16003         }
16004         return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
16005     } else {
16006         int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
16007         int bsent = 0;
16008
16009         while (bsent < nbyte) {
16010             int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
16011                         (ucbuflen - nout) : (nbyte - bsent));
16012 #ifdef DEBUG
16013             if (deblog) {
16014                 debug(F101,"secure_write ucbuflen","",ucbuflen);
16015                 debug(F101,"secure_write ucbufsiz","",ucbufsiz);
16016                 debug(F101,"secure_write bsent","",bsent);
16017                 debug(F101,"secure_write b2cp","",b2cp);
16018             }
16019 #endif /* DEBUG */
16020             memcpy(&ucbuf[nout],&buf[bsent],b2cp);
16021             nout += b2cp;
16022             bsent += b2cp;
16023
16024             if (nout == ucbuflen) {
16025                 nout = 0;
16026                 ret = secure_putbuf(fd, ucbuf, ucbuflen);
16027                 if (ret < 0)
16028                   return(ret);
16029             }
16030         }
16031         return(bsent);
16032     }
16033 }
16034
16035 /* returns:
16036  *       0  on success
16037  *      -1  on error (errno set)
16038  *      -2  on security error
16039  */
16040 static int
16041 #ifdef CK_ANSIC
16042 secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
16043 #else
16044 secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
16045 #endif /* CK_ANSIC */
16046 {
16047     static char *outbuf = NULL;         /* output ciphertext */
16048 #ifdef FTP_SECURITY
16049     static unsigned int bufsize = 0;    /* size of outbuf */
16050 #endif /* FTP_SECURITY */
16051     ftp_int32 length   = 0;
16052     ftp_uint32 net_len = 0;
16053
16054     /* Other auth types go here ... */
16055 #ifdef CK_SSL
16056     if (ssl_ftp_data_active_flag) {
16057         int count, error;
16058
16059         /* there is no need to send an empty buffer when using SSL/TLS */
16060         if ( nbyte == 0 )
16061           return(0);
16062
16063         count = SSL_write(ssl_ftp_data_con, buf, nbyte);
16064         error = SSL_get_error(ssl_ftp_data_con,count);
16065         switch (error) {
16066           case SSL_ERROR_NONE:
16067             return(0);
16068           case SSL_ERROR_WANT_WRITE:
16069           case SSL_ERROR_WANT_READ:
16070           case SSL_ERROR_SYSCALL:
16071 #ifdef NT
16072             {
16073                 int gle = GetLastError();
16074                 if (gle == 0)
16075                   return(0);
16076                 debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
16077             }
16078 #endif /* NT */
16079           case SSL_ERROR_WANT_X509_LOOKUP:
16080           case SSL_ERROR_SSL:
16081           case SSL_ERROR_ZERO_RETURN:
16082           default:
16083             SSL_shutdown(ssl_ftp_data_con);
16084             SSL_free(ssl_ftp_data_con);
16085             ssl_ftp_data_active_flag = 0;
16086             ssl_ftp_data_con = NULL;
16087 #ifdef TCPIPLIB
16088             socket_close(data);
16089 #else /* TCPIPLIB */
16090 #ifdef USE_SHUTDOWN
16091             shutdown(data, 1+1);
16092 #endif /* USE_SHUTDOWN */
16093             close(data);
16094 #endif /* TCPIPLIB */
16095             data = -1;
16096             globaldin = data;
16097             return(-1);
16098         }
16099         return(-1);
16100     }
16101 #endif /* CK_SSL */
16102
16103 #ifdef FTP_SRP
16104     if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
16105         if (bufsize < nbyte + FUDGE_FACTOR) {
16106             if (outbuf?
16107                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16108                 (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16109                 bufsize = nbyte + FUDGE_FACTOR;
16110             } else {
16111                 bufsize = 0;
16112                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16113                 return(ERR);
16114             }
16115         }
16116         if ((length =
16117              srp_encode(ftp_dpl == FPL_PRV,
16118                         (CHAR *) buf,
16119                         (CHAR *) outbuf,
16120                         nbyte
16121                         )
16122              ) < 0) {
16123             secure_error ("srp_encode failed");
16124             return ERR;
16125         }
16126     }
16127 #endif /* FTP_SRP */
16128 #ifdef FTP_KRB4
16129     if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
16130         struct sockaddr_in myaddr, hisaddr;
16131         GSOCKNAME_T len;
16132         len = sizeof(myaddr);
16133         if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16134             secure_error("secure_putbuf: getsockname failed");
16135             return(ERR);
16136         }
16137         len = sizeof(hisaddr);
16138         if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16139             secure_error("secure_putbuf: getpeername failed");
16140             return(ERR);
16141         }
16142         if (bufsize < nbyte + FUDGE_FACTOR) {
16143             if (outbuf ?
16144                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16145                  (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16146                 bufsize = nbyte + FUDGE_FACTOR;
16147             } else {
16148                 bufsize = 0;
16149                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16150                 return(ERR);
16151             }
16152         }
16153         if (ftp_dpl == FPL_PRV) {
16154             length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
16155                                  ftp_sched,
16156 #ifdef KRB524
16157                                  ftp_cred.session,
16158 #else /* KRB524 */
16159                                  &ftp_cred.session,
16160 #endif /* KRB524 */
16161                                  &myaddr,
16162                                  &hisaddr
16163                                  );
16164         } else {
16165             length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
16166 #ifdef KRB524
16167                                  ftp_cred.session,
16168 #else /* KRB524 */
16169                                  &ftp_cred.session,
16170 #endif /* KRB524 */
16171                                  &myaddr,
16172                                  &hisaddr
16173                                  );
16174         }
16175         if (length == -1) {
16176             secure_error("krb_mk_%s failed for KERBEROS_V4",
16177                          ftp_dpl == FPL_PRV ? "priv" : "safe");
16178             return(ERR);
16179         }
16180     }
16181 #endif /* FTP_KRB4 */
16182 #ifdef FTP_GSSAPI
16183     if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
16184         gss_buffer_desc in_buf, out_buf;
16185         OM_uint32 maj_stat, min_stat;
16186         int conf_state;
16187
16188         in_buf.value = buf;
16189         in_buf.length = nbyte;
16190         maj_stat = gss_seal(&min_stat, gcontext,
16191                             (ftp_dpl == FPL_PRV), /* confidential */
16192                             GSS_C_QOP_DEFAULT,
16193                             &in_buf,
16194                             &conf_state,
16195                             &out_buf
16196                             );
16197         if (maj_stat != GSS_S_COMPLETE) {
16198             /* generally need to deal */
16199             /* ie. should loop, but for now just fail */
16200             user_gss_error(maj_stat, min_stat,
16201                            ftp_dpl == FPL_PRV?
16202                            "GSSAPI seal failed":
16203                            "GSSAPI sign failed");
16204             return(ERR);
16205         }
16206         if (bufsize < out_buf.length) {
16207             if (outbuf ?
16208                 (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
16209                 (outbuf = malloc((unsigned) out_buf.length))) {
16210                 bufsize = out_buf.length;
16211             } else {
16212                 bufsize = 0;
16213                 secure_error("%s (in malloc of PROT buffer)",
16214                              ck_errstr());
16215                 return(ERR);
16216             }
16217         }
16218         memcpy(outbuf, out_buf.value, length=out_buf.length);
16219         gss_release_buffer(&min_stat, &out_buf);
16220     }
16221 #endif /* FTP_GSSAPI */
16222     net_len = htonl((ULONG) length);
16223     if (looping_write(fd, (char *)&net_len, 4) == -1)
16224       return(-1);
16225     if (looping_write(fd, outbuf, length) != length)
16226       return(-1);
16227     return(0);
16228 }
16229
16230
16231 /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
16232
16233 static int
16234 secure_getbyte(fd,fc) int fd,fc; {
16235     /* number of chars in ucbuf, pointer into ucbuf */
16236     static unsigned int nin = 0, bufp = 0;
16237     int kerror;
16238     ftp_uint32 length;
16239
16240     if (fc) {
16241         nin = bufp = 0;
16242         ucbuf[0] = NUL;
16243         return(0);
16244     }
16245     if (nin == 0) {
16246         if (iscanceled())
16247           return(-9);
16248
16249 #ifdef FTP_TIMEOUT
16250         if (check_data_connection(fd,0) < 0)
16251           return(-3);
16252 #endif  /* FTP_TIMEOUT */
16253
16254 #ifdef CK_SSL
16255         if (ssl_ftp_data_active_flag) {
16256             int count, error;
16257             count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
16258             error = SSL_get_error(ssl_ftp_data_con,count);
16259 #ifdef DEBUG
16260             if (error != SSL_ERROR_NONE)
16261               debug(F101,"ftp secure_getbyte error","",error);
16262             if (count == 0)
16263               debug(F101,"ftp secure_getbyte count","",count);
16264 #endif  /* DEBUG */
16265             switch (error) {
16266               case SSL_ERROR_NONE:
16267                 if (count > 0) {
16268                     nin = bufp = count;
16269                     rpackets++;
16270                     pktnum++;
16271                     if (fdispla != XYFD_B) {
16272                         rpktl = count;
16273                         ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16274                     }
16275                     break;
16276                 }
16277               case SSL_ERROR_WANT_WRITE:
16278               case SSL_ERROR_WANT_READ:
16279               case SSL_ERROR_SYSCALL:
16280 #ifdef NT
16281                 {
16282                     int gle = GetLastError();
16283                 }
16284 #endif /* NT */
16285               case SSL_ERROR_WANT_X509_LOOKUP:
16286               case SSL_ERROR_SSL:
16287               case SSL_ERROR_ZERO_RETURN:
16288               default:
16289                 nin = bufp = count = 0;
16290                 SSL_shutdown(ssl_ftp_data_con);
16291                 SSL_free(ssl_ftp_data_con);
16292                 ssl_ftp_data_active_flag = 0;
16293                 ssl_ftp_data_con = NULL;
16294 #ifdef TCPIPLIB
16295                 socket_close(data);
16296 #else /* TCPIPLIB */
16297 #ifdef USE_SHUTDOWN
16298                 shutdown(data, 1+1);
16299 #endif /* USE_SHUTDOWN */
16300                 close(data);
16301 #endif /* TCPIPLIB */
16302                 data = -1;
16303                 globaldin = data;
16304                 break;
16305             }
16306         } else
16307 #endif /* CK_SSL */
16308           {
16309               kerror = looping_read(fd, (char *)&length, sizeof(length));
16310               if (kerror != sizeof(length)) {
16311                   secure_error("Couldn't read PROT buffer length: %d/%s",
16312                                kerror,
16313                                kerror == -1 ? ck_errstr()
16314                                : "premature EOF"
16315                                );
16316                   return(ERR);
16317               }
16318               debug(F101,"secure_getbyte length","",length);
16319               debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
16320
16321               length = (ULONG) ntohl(length);
16322               if (length > maxbuf) {
16323                   secure_error("Length (%d) of PROT buffer > PBSZ=%u",
16324                                length,
16325                                maxbuf
16326                                );
16327                   return(ERR);
16328               }
16329               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
16330                   secure_error("Couldn't read %u byte PROT buffer: %s",
16331                                length,
16332                                kerror == -1 ? ck_errstr() : "premature EOF"
16333                                );
16334                   return(ERR);
16335               }
16336
16337               /* Other auth types go here ... */
16338 #ifdef FTP_SRP
16339               if (strcmp(auth_type, "SRP") == 0) {
16340                   if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
16341                                                 (CHAR *) ucbuf,
16342                                                 ucbuf,
16343                                                 length
16344                                                 )
16345                        ) == -1) {
16346                       secure_error ("srp_encode failed" );
16347                       return ERR;
16348                   }
16349               }
16350 #endif /* FTP_SRP */
16351 #ifdef FTP_KRB4
16352               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
16353                   struct sockaddr_in myaddr, hisaddr;
16354                   GSOCKNAME_T len;
16355                   len = sizeof(myaddr);
16356                   if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16357                       secure_error("secure_putbuf: getsockname failed");
16358                       return(ERR);
16359                   }
16360                   len = sizeof(hisaddr);
16361                   if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16362                       secure_error("secure_putbuf: getpeername failed");
16363                       return(ERR);
16364                   }
16365                   if (ftp_dpl) {
16366                       kerror = krb_rd_priv(ucbuf, length, ftp_sched,
16367 #ifdef KRB524
16368                                            ftp_cred.session,
16369 #else /* KRB524 */
16370                                            &ftp_cred.session,
16371 #endif /* KRB524 */
16372                                            &hisaddr, &myaddr, &ftp_msg_data);
16373                   } else {
16374                       kerror = krb_rd_safe(ucbuf, length,
16375 #ifdef KRB524
16376                                            ftp_cred.session,
16377 #else /* KRB524 */
16378                                            &ftp_cred.session,
16379 #endif /* KRB524 */
16380                                            &hisaddr, &myaddr, &ftp_msg_data);
16381                   }
16382                   if (kerror) {
16383                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
16384                                    ftp_dpl == FPL_PRV ? "priv" : "safe",
16385                                    krb_get_err_text(kerror));
16386                       return(ERR);
16387                   }
16388                   memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
16389                   nin = bufp = ftp_msg_data.app_length;
16390               }
16391 #endif /* FTP_KRB4 */
16392 #ifdef FTP_GSSAPI
16393               if (strcmp(auth_type, "GSSAPI") == 0) {
16394                   gss_buffer_desc xmit_buf, msg_buf;
16395                   OM_uint32 maj_stat, min_stat;
16396                   int conf_state;
16397
16398                   xmit_buf.value = ucbuf;
16399                   xmit_buf.length = length;
16400                   conf_state = (ftp_dpl == FPL_PRV);
16401                   /* decrypt/verify the message */
16402                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
16403                                         &msg_buf, &conf_state, NULL);
16404                   if (maj_stat != GSS_S_COMPLETE) {
16405                       user_gss_error(maj_stat, min_stat,
16406                                      (ftp_dpl == FPL_PRV)?
16407                                      "failed unsealing ENC message":
16408                                      "failed unsealing MIC message");
16409                       return ERR;
16410                   }
16411                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
16412                   gss_release_buffer(&min_stat, &msg_buf);
16413               }
16414 #endif /* FTP_GSSAPI */
16415               /* Other auth types go here ... */
16416
16417               /* Update file transfer display */
16418               rpackets++;
16419               pktnum++;
16420               if (fdispla != XYFD_B) {
16421                   rpktl = nin;
16422                   ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16423               }
16424           }
16425     }
16426     if (nin == 0)
16427       return(EOF);
16428     else
16429       return(ucbuf[bufp - nin--]);
16430 }
16431
16432 /* secure_getc(fd,fc)
16433  * Call with:
16434  *   fd = file descriptor for connection.
16435  *   fc = 0 to get a character, fc != 0 to initialize buffer pointers.
16436  * Returns:
16437  *   c>=0 on success (character value)
16438  *   -1   on EOF
16439  *   -2   on security error
16440  *   -3   on timeout (if built with FTP_TIMEOUT defined)
16441  */
16442 static int
16443 secure_getc(fd,fc) int fd,fc; {         /* file descriptor, function code */
16444
16445     if (!ftpissecure()) {
16446         static unsigned int nin = 0, bufp = 0;
16447         if (fc) {
16448             nin = bufp = 0;
16449             ucbuf[0] = NUL;
16450             return(0);
16451         }
16452         if (nin == 0) {
16453             if (iscanceled())
16454               return(-9);
16455
16456 #ifdef FTP_TIMEOUT
16457             if (check_data_connection(fd,0) < 0) {
16458                 debug(F100,"secure_getc TIMEOUT","",0);
16459                 nin = bufp = 0;
16460                 ftp_timed_out = 1;
16461                 return(-3);
16462             }           
16463 #endif  /* FTP_TIMEOUT */
16464
16465             nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
16466             if ((nin == 0) || (nin == (unsigned int)-1)) {
16467                 debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
16468                 debug(F101,"secure_getc returns EOF","",EOF);
16469                 nin = bufp = 0;
16470                 return(EOF);
16471             }
16472             debug(F101,"ftp secure_getc recv","",nin);
16473             ckhexdump("ftp secure_getc recv",ucbuf,16);
16474             rpackets++;
16475             pktnum++;
16476             if (fdispla != XYFD_B) {
16477                 rpktl = nin;
16478                 ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16479             }
16480         }
16481         return(ucbuf[bufp - nin--]);
16482     } else
16483       return(secure_getbyte(fd,fc));
16484 }
16485
16486 /* returns:
16487  *     n>0  on success (n == # of bytes read)
16488  *       0  on EOF
16489  *      -1  on error (errno set), only for FPL_CLR
16490  *      -2  on security error
16491  */
16492 static int
16493 secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
16494     static int c = 0;
16495     int i;
16496
16497     debug(F101,"secure_read bytes requested","",nbyte);
16498     if (c == EOF)
16499       return(c = 0);
16500     for (i = 0; nbyte > 0; nbyte--) {
16501         c = secure_getc(fd,0);
16502         switch (c) {
16503           case -9:                      /* Canceled from keyboard */
16504             debug(F101,"ftp secure_read interrupted","",c);
16505             return(0);
16506           case ERR:
16507             debug(F101,"ftp secure_read error","",c);
16508             return(c);
16509           case EOF:
16510             debug(F101,"ftp secure_read EOF","",c);
16511             if (!i)
16512               c = 0;
16513             return(i);
16514 #ifdef FTP_TIMEOUT
16515           case -3:
16516             debug(F101,"ftp secure_read timeout","",c);
16517             return(c);
16518 #endif  /* FTP_TIMEOUT */
16519           default:
16520             buf[i++] = c;
16521         }
16522     }
16523     return(i);
16524 }
16525
16526 #ifdef USE_RUSERPASS
16527 /* BEGIN_RUSERPASS
16528  *
16529  * Copyright (c) 1985 Regents of the University of California.
16530  * All rights reserved.
16531  *
16532  * Redistribution and use in source and binary forms, with or without
16533  * modification, are permitted provided that the following conditions
16534  * are met:
16535  * 1. Redistributions of source code must retain the above copyright
16536  *    notice, this list of conditions and the following disclaimer.
16537  * 2. Redistributions in binary form must reproduce the above copyright
16538  *    notice, this list of conditions and the following disclaimer in the
16539  *    documentation and/or other materials provided with the distribution.
16540  * 3. All advertising materials mentioning features or use of this software
16541  *    must display the following acknowledgement:
16542  *      This product includes software developed by the University of
16543  *      California, Berkeley and its contributors.
16544  * 4. Neither the name of the University nor the names of its contributors
16545  *    may be used to endorse or promote products derived from this software
16546  *    without specific prior written permission.
16547  *
16548  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16549  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16550  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16551  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
16552  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16553  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
16554  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16555  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16556  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16557  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
16558  * SUCH DAMAGE.
16559  */
16560
16561 #ifndef lint
16562 static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
16563 #endif /* not lint */
16564
16565 #ifndef MAXHOSTNAMELEN
16566 #define MAXHOSTNAMELEN 64
16567 #endif
16568
16569 char * renvlook();
16570 static FILE * cfile;
16571
16572 #define DEFAULT 1
16573 #define LOGIN   2
16574 #define PASSWD  3
16575 #define ACCOUNT 4
16576 #define MACDEF  5
16577 #define ID      10
16578 #define MACH    11
16579
16580 static char tokval[100];
16581
16582 static struct toktab {
16583     char *tokstr;
16584     int tval;
16585 } toktab[]= {
16586     "default",  DEFAULT,
16587     "login",    LOGIN,
16588     "password", PASSWD,
16589     "passwd",   PASSWD,
16590     "account",  ACCOUNT,
16591     "machine",  MACH,
16592     "macdef",   MACDEF,
16593     0,          0
16594 };
16595
16596 static int
16597 token() {
16598     char *cp;
16599     int c;
16600     struct toktab *t;
16601
16602     if (feof(cfile))
16603       return(0);
16604     while ((c = getc(cfile)) != EOF &&
16605            (c == '\n' || c == '\t' || c == ' ' || c == ','))
16606       continue;
16607     if (c == EOF)
16608       return(0);
16609     cp = tokval;
16610     if (c == '"') {
16611         while ((c = getc(cfile)) != EOF && c != '"') {
16612             if (c == '\\')
16613               c = getc(cfile);
16614             *cp++ = c;
16615         }
16616     } else {
16617         *cp++ = c;
16618         while ((c = getc(cfile)) != EOF
16619                && c != '\n' && c != '\t' && c != ' ' && c != ',') {
16620             if (c == '\\')
16621               c = getc(cfile);
16622             *cp++ = c;
16623         }
16624     }
16625     *cp = 0;
16626     if (tokval[0] == 0)
16627       return(0);
16628     for (t = toktab; t->tokstr; t++)
16629       if (!strcmp(t->tokstr, tokval))
16630         return(t->tval);
16631     return(ID);
16632 }
16633
16634 ruserpass(host, aname, apass, aacct)
16635     char *host, **aname, **apass, **aacct;
16636 {
16637     char *hdir, buf[FTP_BUFSIZ], *tmp;
16638     char myname[MAXHOSTNAMELEN], *mydomain;
16639     int t, i, c, usedefault = 0;
16640 #ifdef NT
16641     struct _stat stb;
16642 #else /* NT */
16643     struct stat stb;
16644 #endif /* NT */
16645
16646     hdir = getenv("HOME");
16647     if (hdir == NULL)
16648         hdir = ".";
16649     ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
16650     cfile = fopen(buf, "r");
16651     if (cfile == NULL) {
16652         if (errno != ENOENT)
16653           perror(buf);
16654         return(0);
16655     }
16656     if (gethostname(myname, MAXHOSTNAMELEN) < 0)
16657       myname[0] = '\0';
16658     if ((mydomain = ckstrchr(myname, '.')) == NULL)
16659       mydomain = "";
16660
16661   next:
16662     while ((t = token())) switch(t) {
16663
16664       case DEFAULT:
16665         usedefault = 1;
16666         /* FALL THROUGH */
16667
16668       case MACH:
16669         if (!usedefault) {
16670             if (token() != ID)
16671               continue;
16672             /*
16673              * Allow match either for user's input host name
16674              * or official hostname.  Also allow match of
16675              * incompletely-specified host in local domain.
16676              */
16677             if (ckstrcmp(host, tokval,-1,1) == 0)
16678               goto match;
16679             if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
16680               goto match;
16681             if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
16682                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16683                 ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
16684                 tokval[tmp - ftp_host] == '\0')
16685               goto match;
16686             if ((tmp = ckstrchr(host, '.')) != NULL &&
16687                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16688                 ckstrcmp(host, tokval, tmp - host, 0) == 0 &&
16689                 tokval[tmp - host] == '\0')
16690               goto match;
16691             continue;
16692         }
16693
16694       match:
16695         while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
16696
16697           case LOGIN:
16698             if (token())
16699               if (*aname == 0) {
16700                   *aname = malloc((unsigned) strlen(tokval) + 1);
16701                   strcpy(*aname, tokval);      /* safe */
16702               } else {
16703                   if (strcmp(*aname, tokval))
16704                     goto next;
16705               }
16706             break;
16707           case PASSWD:
16708             if (strcmp(*aname, "anonymous") &&
16709                 fstat(fileno(cfile), &stb) >= 0 &&
16710                 (stb.st_mode & 077) != 0) {
16711                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16712                 fprintf(stderr, "Remove password or correct mode.\n");
16713                 goto bad;
16714             }
16715             if (token() && *apass == 0) {
16716                 *apass = malloc((unsigned) strlen(tokval) + 1);
16717                 strcpy(*apass, tokval);          /* safe */
16718             }
16719             break;
16720           case ACCOUNT:
16721             if (fstat(fileno(cfile), &stb) >= 0
16722                 && (stb.st_mode & 077) != 0) {
16723                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16724                 fprintf(stderr, "Remove account or correct mode.\n");
16725                 goto bad;
16726             }
16727             if (token() && *aacct == 0) {
16728                 *aacct = malloc((unsigned) strlen(tokval) + 1);
16729                 strcpy(*aacct, tokval);          /* safe */
16730             }
16731             break;
16732
16733           default:
16734             fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
16735             break;
16736         }
16737         goto done;
16738     }
16739
16740   done:
16741     fclose(cfile);
16742     return(0);
16743
16744   bad:
16745     fclose(cfile);
16746     return(-1);
16747 }
16748 #endif /* USE_RUSERPASS */
16749
16750 static char *radixN =
16751   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16752
16753 static char pad = '=';
16754
16755 static int
16756 radix_encode(inbuf, outbuf, inlen, outlen, decode)
16757     CHAR inbuf[], outbuf[];
16758     int inlen, *outlen, decode;
16759 {
16760     int i, j, D = 0;
16761     char *p;
16762     CHAR c = NUL;
16763
16764     if (decode) {
16765         for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
16766             if ((p = ckstrchr(radixN, inbuf[i])) == NULL)
16767               return(1);
16768             D = p - radixN;
16769             switch (i&3) {
16770               case 0:
16771                 outbuf[j] = D<<2;
16772                 break;
16773               case 1:
16774                 outbuf[j++] |= D>>4;
16775                 outbuf[j] = (D&15)<<4;
16776                 break;
16777               case 2:
16778                 outbuf[j++] |= D>>2;
16779                 outbuf[j] = (D&3)<<6;
16780                 break;
16781               case 3:
16782                 outbuf[j++] |= D;
16783             }
16784             if (j == *outlen)
16785               return(4);
16786         }
16787         switch (i&3) {
16788           case 1: return(3);
16789           case 2: if (D&15) return(3);
16790             if (strcmp((char *)&inbuf[i], "==")) return(2);
16791             break;
16792           case 3: if (D&3) return(3);
16793             if (strcmp((char *)&inbuf[i], "="))  return(2);
16794         }
16795         *outlen = j;
16796     } else {
16797         for (i = 0, j = 0; i < inlen; i++) {
16798             switch (i%3) {
16799               case 0:
16800                 outbuf[j++] = radixN[inbuf[i]>>2];
16801                 c = (inbuf[i]&3)<<4;
16802                 break;
16803               case 1:
16804                 outbuf[j++] = radixN[c|inbuf[i]>>4];
16805                 c = (inbuf[i]&15)<<2;
16806                 break;
16807               case 2:
16808                 outbuf[j++] = radixN[c|inbuf[i]>>6];
16809                 outbuf[j++] = radixN[inbuf[i]&63];
16810                 c = 0;
16811             }
16812             if (j == *outlen)
16813               return(4);
16814         }
16815         if (i%3) outbuf[j++] = radixN[c];
16816         switch (i%3) {
16817           case 1: outbuf[j++] = pad;
16818           case 2: outbuf[j++] = pad;
16819         }
16820         outbuf[*outlen = j] = '\0';
16821     }
16822     return(0);
16823 }
16824
16825 static char *
16826 radix_error(e) int e;
16827 {
16828     switch (e) {
16829       case 0:  return("Success");
16830       case 1:  return("Bad character in encoding");
16831       case 2:  return("Encoding not properly padded");
16832       case 3:  return("Decoded # of bits not a multiple of 8");
16833       case 4:  return("Output buffer too small");
16834       default: return("Unknown error");
16835     }
16836 }
16837 /* END_RUSERPASS */
16838
16839 #ifdef FTP_SRP
16840 /*---------------------------------------------------------------------------+
16841  |                                                                           |
16842  |   Package: srpftp                                                         |
16843  |   Author: Eugene Jhong                                                    |
16844  |                                                                           |
16845  +---------------------------------------------------------------------------*/
16846
16847 /*
16848  * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
16849  * All Rights Reserved.
16850  *
16851  * Permission is hereby granted, free of charge, to any person obtaining
16852  * a copy of this software and associated documentation files (the
16853  * "Software"), to deal in the Software without restriction, including
16854  * without limitation the rights to use, copy, modify, merge, publish,
16855  * distribute, sublicense, and/or sell copies of the Software, and to
16856  * permit persons to whom the Software is furnished to do so, subject to
16857  * the following conditions:
16858  *
16859  * The above copyright notice and this permission notice shall be
16860  * included in all copies or substantial portions of the Software.
16861  *
16862  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16863  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16864  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16865  *
16866  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
16867  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
16868  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
16869  * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
16870  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16871  *
16872  * In addition, the following conditions apply:
16873  *
16874  * 1. Any software that incorporates the SRP authentication technology
16875  *    must display the following acknowlegment:
16876  *    "This product uses the 'Secure Remote Password' cryptographic
16877  *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
16878  *
16879  * 2. Any software that incorporates all or part of the SRP distribution
16880  *    itself must also display the following acknowledgment:
16881  *    "This product includes software developed by Tom Wu and Eugene
16882  *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
16883  *
16884  * 3. Redistributions in source or binary form must retain an intact copy
16885  *    of this copyright notice and list of conditions.
16886  */
16887
16888 #define SRP_PROT_VERSION        1
16889
16890 #ifdef CK_ENCRYPTION
16891 #define SRP_DEFAULT_CIPHER      CIPHER_ID_CAST5_CBC
16892 #else
16893 #define SRP_DEFAULT_CIPHER      CIPHER_ID_NONE
16894 #endif /* CK_ENCRYPTION */
16895
16896 #define SRP_DEFAULT_HASH        HASH_ID_SHA
16897
16898 CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
16899 CHAR srp_pref_hash = HASH_ID_SHA;
16900
16901 static struct t_client *tc = NULL;
16902 static CHAR *skey = NULL;
16903 static krypto_context *incrypt = NULL;
16904 static krypto_context *outcrypt = NULL;
16905
16906 typedef unsigned int srp_uint32;
16907
16908 /*--------------------------------------------------------------+
16909  | srp_selcipher: select cipher                                 |
16910  +--------------------------------------------------------------*/
16911 static int
16912 srp_selcipher (cname) char *cname; {
16913     cipher_desc *cd;
16914
16915     if (!(cd = cipher_getdescbyname (cname))) {
16916         int i;
16917         CHAR *list = cipher_getlist ();
16918
16919         fprintf (stderr, "ftp: supported ciphers:\n\n");
16920         for (i = 0; i < strlen (list); i++)
16921           fprintf (stderr, "    %s\n", (cipher_getdescbyid(list[i]))->name);
16922         fprintf (stderr, "\n");
16923         return -1;
16924     }
16925     srp_pref_cipher = cd->id;
16926     return 0;
16927 }
16928
16929 /*--------------------------------------------------------------+
16930  | srp_selhash: select hash                                     |
16931  +--------------------------------------------------------------*/
16932 static int
16933 srp_selhash (hname) char *hname; {
16934     hash_desc *hd;
16935
16936     if (!(hd = hash_getdescbyname (hname))) {
16937         int i;
16938         CHAR *list = hash_getlist ();
16939
16940         fprintf (stderr, "ftp: supported hash functions:\n\n");
16941         for (i = 0; i < strlen (list); i++)
16942           fprintf (stderr, "    %s\n", (hash_getdescbyid(list[i]))->name);
16943         fprintf (stderr, "\n");
16944         return -1;
16945     }
16946     srp_pref_hash = hd->id;
16947     return 0;
16948 }
16949
16950 /*--------------------------------------------------------------+
16951  | srp_userpass: get username and password                      |
16952  +--------------------------------------------------------------*/
16953 static int
16954 srp_userpass (host) char *host; {
16955     char tmp[BUFSIZ], prompt[PROMPTSIZ];
16956     char *user;
16957
16958     user = NULL;
16959 #ifdef USE_RUSERPASS
16960     ruserpass (host, &user, &srp_pass, &srp_acct);
16961 #endif /* USE_RUSERPASS */
16962
16963     while (user == NULL)     {
16964         char *myname;
16965         int ok;
16966
16967         myname = whoami();
16968         if (!myname) myname = "";
16969         if (myname[0])
16970           ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
16971                     NULL,NULL,NULL,NULL,NULL,NULL,NULL);
16972         else
16973           ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
16974         tmp[0] = '\0';
16975         ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
16976                     DEFAULT_UQ_TIMEOUT);
16977         if (!ok || *tmp == '\0')
16978           user = myname;
16979         else
16980           user = brstrip(tmp);
16981     }
16982     ckstrncpy (srp_user, user,BUFSIZ);
16983     return(0);
16984 }
16985
16986 /*--------------------------------------------------------------+
16987  | srp_reset: reset srp information                             |
16988  +--------------------------------------------------------------*/
16989 static int
16990 srp_reset () {
16991     if (tc) { t_clientclose (tc); tc = NULL; }
16992     if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
16993     if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
16994     return(0);
16995 }
16996
16997 /*--------------------------------------------------------------+
16998  | srp_ftp_auth: perform srp authentication                         |
16999  +--------------------------------------------------------------*/
17000 static int
17001 srp_ftp_auth(host, user, pass)
17002     char *host;
17003     char *user;
17004     char *pass;
17005 {
17006     struct t_num *wp;
17007     struct t_num N;
17008     struct t_num g;
17009     struct t_num s;
17010     struct t_num yp;
17011     CHAR buf[FTP_BUFSIZ];
17012     CHAR tmp[FTP_BUFSIZ];
17013     CHAR *bp, *cp;
17014     int n, e, clen, blen, len, i;
17015     CHAR cid = 0;
17016     CHAR hid = 0;
17017
17018     srp_pass = srp_acct = 0;
17019
17020     n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
17021     if (n != REPLY_CONTINUE) {
17022         if (ftp_deb)
17023             fprintf(stderr, "SRP rejected as an authentication type\n");
17024         return(0);
17025     } else {                            /* Send protocol version */
17026         CHAR vers[4];
17027         memset (vers, 0, 4);
17028         vers[3] = SRP_PROT_VERSION;
17029         if (!quiet)
17030           printf ("SRP accepted as authentication type.\n");
17031         bp = tmp; blen = 0;
17032         srp_put (vers, &bp, 4, &blen);
17033         len = FTP_BUFSIZ;
17034         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17035           goto encode_error;
17036         reply_parse = "ADAT=";
17037         n = ftpcmd("ADAT",buf,-1,-1,0);
17038     }
17039     if (n == REPLY_CONTINUE) {          /* Get protocol version */
17040         bp = buf;
17041         if (!reply_parse)
17042           goto data_error;
17043         blen = FTP_BUFSIZ;
17044         if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
17045           goto decode_error;
17046         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17047           goto data_error;
17048
17049         if (host) {                     /* Get username/password if needed */
17050             srp_userpass (host);
17051         } else {
17052             ckstrncpy (srp_user, user, BUFSIZ);
17053             srp_pass = pass;
17054         }
17055         bp = tmp; blen = 0;             /* Send username */
17056         srp_put (srp_user, &bp, strlen (srp_user), &blen);
17057         len = FTP_BUFSIZ;
17058         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17059           goto encode_error;
17060         reply_parse = "ADAT=";
17061         n = ftpcmd("ADAT",buf,-1,-1,0);
17062     }
17063     if (n == REPLY_CONTINUE) {          /* Get N, g and s */
17064         bp = buf;
17065         if (!reply_parse)
17066           goto data_error;
17067         blen = FTP_BUFSIZ;
17068         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17069           goto decode_error;
17070         if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
17071           goto data_error;
17072         if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
17073           goto data_error;
17074         if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
17075           goto data_error;
17076         if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
17077             fprintf (stderr, "Unable to open SRP client structure.\n");
17078             goto bad;
17079         }
17080         wp = t_clientgenexp (tc);       /* Send wp */
17081         bp = tmp; blen = 0;
17082         srp_put (wp->data, &bp, wp->len, &blen);
17083         len = FTP_BUFSIZ;
17084         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17085           goto encode_error;
17086         reply_parse = "ADAT=";
17087         n = ftpcmd("ADAT",buf,-1,-1,0);
17088     }
17089     if (n == REPLY_CONTINUE) {          /* Get yp */
17090         bp = buf;
17091         if (!reply_parse)
17092           goto data_error;
17093         blen = FTP_BUFSIZ;
17094         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17095           goto decode_error;
17096         if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
17097           goto data_error;
17098         if (!srp_pass) {
17099             static char ftppass[PASSBUFSIZ];
17100             int ok;
17101             setint();
17102             ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
17103                         DEFAULT_UQ_TIMEOUT);
17104             if (ok)
17105               srp_pass = brstrip(ftppass);
17106         }
17107         t_clientpasswd (tc, srp_pass);
17108         memset (srp_pass, 0, strlen (srp_pass));
17109         skey = t_clientgetkey (tc, &yp); /* Send response */
17110         bp = tmp; blen = 0;
17111         srp_put (t_clientresponse (tc), &bp, 20, &blen);
17112         len = FTP_BUFSIZ;
17113         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17114           goto encode_error;
17115         reply_parse = "ADAT=";
17116         n = ftpcmd("ADAT",buf,-1,-1,0);
17117     }
17118     if (n == REPLY_CONTINUE) {          /* Get response */
17119         bp = buf;
17120         if (!reply_parse)
17121           goto data_error;
17122         blen = FTP_BUFSIZ;
17123         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17124           goto encode_error;
17125         if (srp_get (&bp, &cp, &blen, &clen) != 20)
17126           goto data_error;
17127         if (t_clientverify (tc, cp)) {
17128             fprintf (stderr, "WARNING: bad response to client challenge.\n");
17129             goto bad;
17130         }
17131         bp = tmp; blen = 0;             /* Send nothing */
17132         srp_put ("\0", &bp, 1, &blen);
17133         len = FTP_BUFSIZ;
17134         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17135           goto encode_error;
17136         reply_parse = "ADAT=";
17137         n = ftpcmd("ADAT",buf,-1,-1,0);
17138     }
17139     if (n == REPLY_CONTINUE) {          /* Get cipher & hash lists, seqnum */
17140         CHAR seqnum[4];
17141         CHAR *clist;
17142         CHAR *hlist;
17143         CHAR *p1;
17144         int clist_len, hlist_len;
17145         bp = buf;
17146         if (!reply_parse)
17147           goto data_error;
17148         blen = FTP_BUFSIZ;
17149         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17150           goto encode_error;
17151         if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
17152           goto data_error;
17153         if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
17154           goto data_error;
17155         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17156           goto data_error;
17157         memcpy (seqnum, cp, 4);
17158         if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
17159           cid = srp_pref_cipher;
17160         if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
17161           cid = SRP_DEFAULT_CIPHER;
17162         if (!cid) {
17163             CHAR *loclist = cipher_getlist ();
17164             for (i = 0; i < strlen (loclist); i++)
17165               if (cipher_supported (clist, loclist[i])) {
17166                   cid = loclist[i];
17167                   break;
17168               }
17169         }
17170         if (!cid) {
17171             fprintf (stderr, "Unable to agree on cipher.\n");
17172             goto bad;
17173         }
17174         /* Choose hash */
17175
17176         if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
17177           hid = srp_pref_hash;
17178
17179         if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
17180           hid = SRP_DEFAULT_HASH;
17181
17182         if (!hid) {
17183             CHAR *loclist = hash_getlist ();
17184             for (i = 0; i < strlen (loclist); i++)
17185               if (hash_supported (hlist, loclist[i])) {
17186                   hid = loclist[i];
17187                   break;
17188               }
17189         }
17190         if (!hid) {
17191             fprintf (stderr, "Unable to agree on hash.\n");
17192             goto bad;
17193         }
17194         /* Set incrypt */
17195
17196         if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
17197                                     KRYPTO_DECODE)))
17198           goto bad;
17199
17200         /* Generate random number for outkey and outseqnum */
17201
17202         t_random (seqnum, 4);
17203
17204         /* Send cid, hid, outkey, outseqnum */
17205
17206         bp = tmp; blen = 0;
17207         srp_put (&cid, &bp, 1, &blen);
17208         srp_put (&hid, &bp, 1, &blen);
17209         srp_put (seqnum, &bp, 4, &blen);
17210         len = FTP_BUFSIZ;
17211         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17212           goto encode_error;
17213         reply_parse = "ADAT=";
17214         n = ftpcmd("ADAT",buf,-1,-1,0);
17215
17216         /* Set outcrypt */
17217
17218         if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
17219                                      KRYPTO_ENCODE)))
17220           goto bad;
17221
17222         t_clientclose (tc);
17223         tc = NULL;
17224     }
17225     if (n != REPLY_COMPLETE)
17226       goto bad;
17227
17228     if (ftp_vbm) {
17229         if (ftp_deb)
17230           printf("\n");
17231         printf ("SRP authentication succeeded.\n");
17232         printf ("Using cipher %s and hash function %s.\n",
17233                 (cipher_getdescbyid(cid))->name,
17234                 (hash_getdescbyid(hid))->name
17235                 );
17236     }
17237     reply_parse = NULL;
17238     auth_type = "SRP";
17239     return(1);
17240
17241   encode_error:
17242     fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
17243     goto bad;
17244
17245   decode_error:
17246     fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
17247     goto bad;
17248
17249   data_error:
17250     fprintf (stderr, "Unable to unmarshal authentication data.\n");
17251     goto bad;
17252
17253   bad:
17254     fprintf (stderr, "SRP authentication failed, trying regular login.\n");
17255     reply_parse = NULL;
17256     return(0);
17257 }
17258
17259 /*--------------------------------------------------------------+
17260  | srp_put: put item to send buffer                             |
17261  +--------------------------------------------------------------*/
17262 static int
17263 srp_put (in, out, inlen, outlen)
17264     CHAR *in;
17265     CHAR **out;
17266     int inlen;
17267     int *outlen;
17268 {
17269     srp_uint32 net_len;
17270
17271     net_len = htonl (inlen);
17272     memcpy (*out, &net_len, 4);
17273
17274     *out += 4; *outlen += 4;
17275
17276     memcpy (*out, in, inlen);
17277
17278     *out += inlen; *outlen += inlen;
17279     return(0);
17280 }
17281
17282 /*--------------------------------------------------------------+
17283  | srp_get: get item from receive buffer                        |
17284  +--------------------------------------------------------------*/
17285 static int
17286 srp_get (in, out, inlen, outlen)
17287     CHAR **in;
17288     CHAR **out;
17289     int *inlen;
17290     int *outlen;
17291 {
17292     srp_uint32 net_len;
17293
17294     if (*inlen < 4) return -1;
17295
17296     memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
17297     *outlen = ntohl (net_len);
17298
17299     if (*inlen < *outlen) return -1;
17300
17301     *out = *in; *inlen -= *outlen; *in += *outlen;
17302
17303     return *outlen;
17304 }
17305
17306 /*--------------------------------------------------------------+
17307  | srp_encode: encode control message                           |
17308  +--------------------------------------------------------------*/
17309 static int
17310 srp_encode (private, in, out, len)
17311     int private;
17312     CHAR *in;
17313     CHAR *out;
17314     unsigned len;
17315 {
17316     if (private)
17317       return krypto_msg_priv (outcrypt, in, out, len);
17318     else
17319       return krypto_msg_safe (outcrypt, in, out, len);
17320 }
17321
17322 /*--------------------------------------------------------------+
17323  | srp_decode: decode control message                           |
17324  +--------------------------------------------------------------*/
17325 static int
17326 srp_decode (private, in, out, len)
17327     int private;
17328     CHAR *in;
17329     CHAR *out;
17330     unsigned len;
17331 {
17332     if (private)
17333       return krypto_msg_priv (incrypt, in, out, len);
17334     else
17335       return krypto_msg_safe (incrypt, in, out, len);
17336 }
17337
17338 #endif /* FTP_SRP */
17339
17340
17341
17342 #ifdef NOT_USED
17343 /*
17344   The following code is from the Unix FTP client.  Be sure to
17345   make sure that the functionality is not lost.  Especially
17346   the Proxy stuff even though we have not yet implemented it.
17347 */
17348
17349 /* Send multiple files  */
17350
17351 static int
17352 ftp_mput(argc, argv) int argc; char **argv; {
17353     register int i;
17354     sig_t oldintr;
17355     int ointer;
17356     char *tp;
17357     sigtype mcancel();
17358
17359     if (argc < 2 && !another(&argc, &argv, "local-files")) {
17360         printf("usage: %s local-files\n", argv[0]);
17361         ftpcode = -1;
17362         return;
17363     }
17364     mname = argv[0];
17365     mflag = 1;
17366     oldintr = signal(SIGINT, mcancel);
17367
17368     /* Replace with calls to cc_execute() */
17369     setjmp(jcancel);
17370 #ifdef FTP_PROXY
17371     if (proxy) {
17372         char *cp, *tp2, tmpbuf[CKMAXPATH];
17373
17374         while ((cp = remglob(argv,0)) != NULL) {
17375             if (*cp == 0) {
17376                 mflag = 0;
17377                 continue;
17378             }
17379             if (mflag && confirm(argv[0], cp)) {
17380                 tp = cp;
17381                 if (mcase) {
17382                     while (*tp && !islower(*tp)) {
17383                         tp++;
17384                     }
17385                     if (!*tp) {
17386                         tp = cp;
17387                         tp2 = tmpbuf;
17388                         while ((*tp2 = *tp) != 0) {
17389                             if (isupper(*tp2)) {
17390                                 *tp2 = 'a' + *tp2 - 'A';
17391                             }
17392                             tp++;
17393                             tp2++;
17394                         }
17395                     }
17396                     tp = tmpbuf;
17397                 }
17398                 if (ntflag) {
17399                     tp = dotrans(tp);
17400                 }
17401                 if (mapflag) {
17402                     tp = domap(tp);
17403                 }
17404                 sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0);
17405                 if (!mflag && fromatty) {
17406                     ointer = interactive;
17407                     interactive = 1;
17408                     if (confirm("Continue with","mput")) {
17409                         mflag++;
17410                     }
17411                     interactive = ointer;
17412                 }
17413             }
17414         }
17415         signal(SIGINT, oldintr);
17416         mflag = 0;
17417         return;
17418     }
17419 #endif /* FTP_PROXY */
17420     for (i = 1; i < argc; i++) {
17421         register char **cpp, **gargs;
17422
17423         if (mflag && confirm(argv[0], argv[i])) {
17424             tp = argv[i];
17425             sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
17426             if (!mflag && fromatty) {
17427                 ointer = interactive;
17428                 interactive = 1;
17429                 if (confirm("Continue with","mput")) {
17430                     mflag++;
17431                 }
17432                 interactive = ointer;
17433             }
17434         }
17435         continue;
17436
17437         gargs = ftpglob(argv[i]);
17438         if (globerr != NULL) {
17439             printf("%s\n", globerr);
17440             if (gargs) {
17441                 blkfree(gargs);
17442                 free((char *)gargs);
17443             }
17444             continue;
17445         }
17446         for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
17447             if (mflag && confirm(argv[0], *cpp)) {
17448                 tp = *cpp;
17449                 sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
17450                 if (!mflag && fromatty) {
17451                     ointer = interactive;
17452                     interactive = 1;
17453                     if (confirm("Continue with","mput")) {
17454                         mflag++;
17455                     }
17456                     interactive = ointer;
17457                 }
17458             }
17459         }
17460         if (gargs != NULL) {
17461             blkfree(gargs);
17462             free((char *)gargs);
17463         }
17464     }
17465     signal(SIGINT, oldintr);
17466     mflag = 0;
17467 }
17468
17469 /* Get multiple files */
17470
17471 static int
17472 ftp_mget(argc, argv) int argc; char **argv; {
17473     int rc = -1;
17474     sig_t oldintr;
17475     int ointer;
17476     char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
17477     sigtype mcancel();
17478
17479     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17480         printf("usage: %s remote-files\n", argv[0]);
17481         ftpcode = -1;
17482         return(-1);
17483     }
17484     mname = argv[0];
17485     mflag = 1;
17486     oldintr = signal(SIGINT,mcancel);
17487     /* Replace with calls to cc_execute() */
17488     setjmp(jcancel);
17489     while ((cp = remglob(argv,proxy)) != NULL) {
17490         if (*cp == '\0') {
17491             mflag = 0;
17492             continue;
17493         }
17494         if (mflag && confirm(argv[0], cp)) {
17495             tp = cp;
17496             if (mcase) {
17497                 while (*tp && !islower(*tp)) {
17498                     tp++;
17499                 }
17500                 if (!*tp) {
17501                     tp = cp;
17502                     tp2 = tmpbuf;
17503                     while ((*tp2 = *tp) != 0) {
17504                         if (isupper(*tp2)) {
17505                             *tp2 = 'a' + *tp2 - 'A';
17506                         }
17507                         tp++;
17508                         tp2++;
17509                     }
17510                 }
17511                 tp = tmpbuf;
17512             }
17513             rc = (recvrequest("RETR", tp, cp, "wb",
17514                                tp != cp || !interactive) == 0,0,NULL,0,0,0);
17515             if (!mflag && fromatty) {
17516                 ointer = interactive;
17517                 interactive = 1;
17518                 if (confirm("Continue with","mget")) {
17519                     mflag++;
17520                 }
17521                 interactive = ointer;
17522             }
17523         }
17524     }
17525     signal(SIGINT,oldintr);
17526     mflag = 0;
17527     return(rc);
17528 }
17529
17530 /* Delete multiple files */
17531
17532 static int
17533 mdelete(argc, argv) int argc; char **argv; {
17534     sig_t oldintr;
17535     int ointer;
17536     char *cp;
17537     sigtype mcancel();
17538
17539     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17540         printf("usage: %s remote-files\n", argv[0]);
17541         ftpcode = -1;
17542         return(-1);
17543     }
17544     mname = argv[0];
17545     mflag = 1;
17546     oldintr = signal(SIGINT, mcancel);
17547     /* Replace with calls to cc_execute() */
17548     setjmp(jcancel);
17549     while ((cp = remglob(argv,0)) != NULL) {
17550         if (*cp == '\0') {
17551             mflag = 0;
17552             continue;
17553         }
17554         if (mflag && confirm(argv[0], cp)) {
17555             rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
17556             if (!mflag && fromatty) {
17557                 ointer = interactive;
17558                 interactive = 1;
17559                 if (confirm("Continue with", "mdelete")) {
17560                     mflag++;
17561                 }
17562                 interactive = ointer;
17563             }
17564         }
17565     }
17566     signal(SIGINT, oldintr);
17567     mflag = 0;
17568     return(rc);
17569 }
17570
17571 /* Get a directory listing of multiple remote files */
17572
17573 static int
17574 mls(argc, argv) int argc; char **argv; {
17575     sig_t oldintr;
17576     int ointer, i;
17577     char *cmd, mode[1], *dest;
17578     sigtype mcancel();
17579     int rc = -1;
17580
17581     if (argc < 2 && !another(&argc, &argv, "remote-files"))
17582       goto usage;
17583     if (argc < 3 && !another(&argc, &argv, "local-file")) {
17584       usage:
17585         printf("usage: %s remote-files local-file\n", argv[0]);
17586         ftpcode = -1;
17587         return(-1);
17588     }
17589     dest = argv[argc - 1];
17590     argv[argc - 1] = NULL;
17591     if (strcmp(dest, "-") && *dest != '|')
17592       if (!globulize(&dest) ||
17593           !confirm("output to local-file:", dest)) {
17594           ftpcode = -1;
17595           return(-1);
17596       }
17597     cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
17598     mname = argv[0];
17599     mflag = 1;
17600     oldintr = signal(SIGINT, mcancel);
17601     /* Replace with calls to cc_execute() */
17602     setjmp(jcancel);
17603     for (i = 1; mflag && i < argc-1; ++i) {
17604         *mode = (i == 1) ? 'w' : 'a';
17605         rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
17606         if (!mflag && fromatty) {
17607             ointer = interactive;
17608             interactive = 1;
17609             if (confirm("Continue with", argv[0])) {
17610                 mflag ++;
17611             }
17612             interactive = ointer;
17613         }
17614     }
17615     signal(SIGINT, oldintr);
17616     mflag = 0;
17617     return(rc);
17618 }
17619
17620 static char *
17621 remglob(argv,doswitch) char *argv[]; int doswitch; {
17622     char temp[16];
17623     static char buf[CKMAXPATH];
17624     static FILE *ftemp = NULL;
17625     static char **args;
17626     int oldhash;
17627     char *cp, *mode;
17628
17629     if (!mflag) {
17630         if (!doglob) {
17631             args = NULL;
17632         } else {
17633             if (ftemp) {
17634                 (void) fclose(ftemp);
17635                 ftemp = NULL;
17636             }
17637         }
17638         return(NULL);
17639     }
17640     if (!doglob) {
17641         if (args == NULL)
17642           args = argv;
17643         if ((cp = *++args) == NULL)
17644           args = NULL;
17645         return(cp);
17646     }
17647     if (ftemp == NULL) {
17648         (void) strcpy(temp, _PATH_TMP);
17649 #ifdef MKTEMP
17650 #ifndef MKSTEMP
17651         (void) mktemp(temp);
17652 #endif /* MKSTEMP */
17653 #endif /* MKTEMP */
17654         verbose = 0;
17655         oldhash = hash, hash = 0;
17656 #ifdef FTP_PROXY
17657         if (doswitch) {
17658             pswitch(!proxy);
17659         }
17660 #endif /* FTP_PROXY */
17661         for (mode = "wb"; *++argv != NULL; mode = "ab")
17662           recvrequest ("NLST", temp, *argv, mode, 0);
17663 #ifdef FTP_PROXY
17664         if (doswitch) {
17665             pswitch(!proxy);
17666         }
17667 #endif /* FTP_PROXY */
17668         hash = oldhash;
17669         ftemp = fopen(temp, "r");
17670         unlink(temp);
17671         if (ftemp == NULL && (!dpyactive || ftp_deb)) {
17672             printf("Can't find list of remote files, oops\n");
17673             return(NULL);
17674         }
17675     }
17676     if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
17677         fclose(ftemp), ftemp = NULL;
17678         return(NULL);
17679     }
17680     if ((cp = ckstrchr(buf,'\n')) != NULL)
17681       *cp = '\0';
17682     return(buf);
17683 }
17684 #endif /* NOT_USED */
17685 #endif /* TCPSOCKET (top of file) */
17686 #endif /* SYSFTP (top of file) */
17687 #endif /* NOFTP (top of file) */