New patch: 050_tls.patch: SSL/TLS changes from upstream dev12 version, including...
[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     } else if (ftp_bug_use_ssl_v3) {
10214         /* allow SSL 3.0 ONLY - previous default */
10215         client_method = SSLv3_client_method();
10216     } else {
10217         /* default - allow TLS 1.0 or later */
10218         client_method = TLSv1_client_method();
10219     }
10220     if (auth_type && !strcmp(auth_type,"TLS")) {
10221         ssl_ftp_ctx=SSL_CTX_new(client_method);
10222         if (!ssl_ftp_ctx)
10223           return(0);
10224         SSL_CTX_set_options(ssl_ftp_ctx,
10225                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10226                             );
10227     } else {
10228         ssl_ftp_ctx = SSL_CTX_new(client_method);
10229         if (!ssl_ftp_ctx)
10230           return(0);
10231         SSL_CTX_set_options(ssl_ftp_ctx,
10232                             (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
10233                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10234                             );
10235     }
10236     SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
10237                                   (pem_password_cb *)ssl_passwd_callback);
10238     SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
10239     SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
10240
10241 #ifdef OS2
10242 #ifdef NT
10243     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10244     {
10245         char path[CKMAXPATH];
10246         extern char exedir[];
10247
10248         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10249         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10250             debug(F110,"ftp ssl_auth unable to load path",path,0);
10251             if (ssl_debug_flag)
10252                 printf("?Unable to load verify-dir: %s\r\n",path);
10253         }
10254
10255         ckmakmsg(path,CKMAXPATH,
10256                  (char *)GetAppData(1),"kermit 95/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(0),"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,exedir,"ca_certs.pem",NULL,NULL);
10272         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10273             debug(F110,"ftp ssl_auth unable to load path",path,0);
10274             if (ssl_debug_flag)
10275                 printf("?Unable to load verify-file: %s\r\n",path);
10276         }
10277
10278         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10279                  "kermit 95/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(0),
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 #else /* NT */
10295     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10296     {
10297
10298         char path[CKMAXPATH];
10299         extern char exedir[];
10300
10301         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10302         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10303             debug(F110,"ftp ssl_auth unable to load path",path,0);
10304             if (ssl_debug_flag)
10305                 printf("?Unable to load verify-dir: %s\r\n",path);
10306         }
10307         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10308         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10309             debug(F110,"ftp ssl_auth unable to load path",path,0);
10310             if (ssl_debug_flag)
10311                 printf("?Unable to load verify-file: %s\r\n",path);
10312         }
10313     }
10314 #endif /* NT */
10315 #else /* OS2 */
10316     SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
10317 #endif /* OS2 */
10318
10319     if (ssl_verify_file &&
10320         SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
10321         debug(F110,
10322               "ftp ssl auth unable to load ssl_verify_file",
10323               ssl_verify_file,
10324               0
10325               );
10326         if (ssl_debug_flag)
10327           printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
10328     }
10329     if (ssl_verify_dir &&
10330         SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
10331         debug(F110,
10332               "ftp ssl auth unable to load ssl_verify_dir",
10333               ssl_verify_dir,
10334               0
10335               );
10336         if (ssl_debug_flag)
10337           printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
10338     }
10339
10340     /* set up the new CRL Store */
10341     crl_store = (X509_STORE *)X509_STORE_new();
10342     if (crl_store) {
10343 #ifdef OS2
10344         char path[CKMAXPATH];
10345         extern char exedir[];
10346
10347         ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
10348         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10349             debug(F110,"ftp ssl auth unable to load dir",path,0);
10350             if (ssl_debug_flag)
10351                 printf("?Unable to load crl-dir: %s\r\n",path);
10352         }
10353 #ifdef NT
10354         ckmakmsg(path,CKMAXPATH,
10355                  (char *)GetAppData(1),"kermit 95/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         ckmakmsg(path,CKMAXPATH,
10362                  (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
10363         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10364             debug(F110,"ftp ssl auth unable to load dir",path,0);
10365             if (ssl_debug_flag)
10366                 printf("?Unable to load crl-dir: %s\r\n",path);
10367         }
10368 #endif /* NT */
10369         
10370         ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
10371         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10372             debug(F110,"ftp ssl auth unable to load file",path,0);
10373             if (ssl_debug_flag)
10374                 printf("?Unable to load crl-file: %s\r\n",path);
10375         }
10376 #ifdef NT
10377         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10378                  "kermit 95/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         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10385                  "kermit 95/ca_crls.pem",NULL,NULL);
10386         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10387             debug(F110,"ftp ssl auth unable to load file",path,0);
10388             if (ssl_debug_flag)
10389                 printf("?Unable to load crl-file: %s\r\n",path);
10390         }
10391 #endif /* NT */
10392 #endif /* OS2 */
10393
10394         if (ssl_crl_file || ssl_crl_dir) {
10395             if (ssl_crl_file &&
10396                 X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
10397                 debug(F110,
10398                       "ftp ssl auth unable to load ssl_crl_file",
10399                       ssl_crl_file,
10400                       0
10401                       );
10402                 if (ssl_debug_flag)
10403                   printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
10404             }
10405             if (ssl_crl_dir &&
10406                 X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
10407                 debug(F110,
10408                       "ftp ssl auth unable to load ssl_crl_dir",
10409                       ssl_crl_dir,
10410                       0
10411                       );
10412                 if (ssl_debug_flag)
10413                   printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
10414             }
10415         } else {
10416             X509_STORE_set_default_paths(crl_store);
10417         }
10418     }
10419     SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
10420                        ssl_client_verify_callback);
10421     ssl_verify_depth = -1;
10422     ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
10423     tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
10424     SSL_set_fd(ssl_ftp_con,csocket);
10425     SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
10426     if (ssl_cipher_list) {
10427         SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
10428     } else {
10429         char * p;
10430         if (p = getenv("SSL_CIPHER")) {
10431             SSL_set_cipher_list(ssl_ftp_con,p);
10432         } else {
10433             SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
10434         }
10435     }
10436     if (ssl_debug_flag) {
10437         fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
10438         fflush(stderr);
10439     }
10440     if (SSL_connect(ssl_ftp_con) <= 0) {
10441         static char errbuf[1024];
10442         ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
10443                  ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
10444         fprintf(stderr,"%s\n", errbuf);
10445         fflush(stderr);
10446         ssl_ftp_active_flag=0;
10447         SSL_free(ssl_ftp_con);
10448         ssl_ftp_con = NULL;
10449     } else {
10450         ssl_ftp_active_flag = 1;
10451
10452         if (!ssl_certsok_flag &&
10453             (ssl_verify_flag & SSL_VERIFY_PEER) && /* JEA 2013-12-10 */
10454             !tls_is_krb5(1)) {
10455             char *subject = ssl_get_subject_name(ssl_ftp_con);
10456
10457             if (!subject) {
10458                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
10459                     debug(F110,"ssl_auth","[SSL - FAILED]",0);
10460                     return(ssl_ftp_active_flag = 0);
10461                 } else {
10462                     if (uq_ok("Warning: Server didn't provide a certificate\n",
10463                                "Continue? (Y/N)",3,NULL,0) <= 0) {
10464                         debug(F110, "ssl_auth","[SSL - FAILED]",0);
10465                         return(ssl_ftp_active_flag = 0);
10466                     }
10467                 }
10468             } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
10469                 debug(F110,"ssl_auth","[SSL - FAILED]",0);
10470                 return(ssl_ftp_active_flag = 0);
10471             }
10472         }
10473         debug(F110,"ssl_auth","[SSL - OK]",0);
10474         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
10475     }
10476     if (ssl_debug_flag) {
10477         fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
10478         fflush(stderr);
10479     }
10480     return(ssl_ftp_active_flag);
10481 }
10482 #endif /* CK_SSL */
10483
10484 static sigtype
10485 cmdcancel(sig) int sig; {
10486 #ifdef OS2
10487     /* In Unix we "chain" to trap(), which prints this */
10488     printf("^C...\n");
10489 #endif /* OS2 */
10490     debug(F100,"ftp cmdcancel caught SIGINT ","",0);
10491     fflush(stdout);
10492     secure_getc(0,1);                   /* Initialize net input buffers */
10493     cancelfile++;
10494     cancelgroup++;
10495     mlsreset();
10496 #ifndef OS2
10497 #ifdef FTP_PROXY
10498     if (ptflag)                         /* proxy... */
10499       longjmp(ptcancel,1);
10500 #endif /* FTP_PROXY */
10501     debug(F100,"ftp cmdcancel chain to trap()...","",0);
10502     trap(SIGINT);
10503     /* NOTREACHED */
10504     debug(F100,"ftp cmdcancel return from trap()...","",0);
10505 #else
10506     debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
10507     PostCtrlCSem();
10508 #endif /* OS2 */
10509 }
10510
10511 static int
10512 #ifdef CK_ANSIC
10513 scommand(char * s)                      /* Was secure_command() */
10514 #else
10515 scommand(s) char * s;
10516 #endif /* CK_ANSIC */
10517 {
10518     int length = 0, len2;
10519     char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
10520 #ifdef CK_SSL
10521     if (ssl_ftp_active_flag) {
10522         int error, rc;
10523         length = strlen(s) + 2;
10524         length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10525         rc = SSL_write(ssl_ftp_con,out,length);
10526         error = SSL_get_error(ssl_ftp_con,rc);
10527         switch (error) {
10528           case SSL_ERROR_NONE:
10529             return(1);
10530           case SSL_ERROR_WANT_WRITE:
10531           case SSL_ERROR_WANT_READ:
10532           case SSL_ERROR_SYSCALL:
10533 #ifdef NT
10534             {
10535                 int gle = GetLastError();
10536             }
10537 #endif /* NT */
10538           case SSL_ERROR_WANT_X509_LOOKUP:
10539           case SSL_ERROR_SSL:
10540           case SSL_ERROR_ZERO_RETURN:
10541           default:
10542             lostpeer();
10543         }
10544         return(0);
10545     }
10546 #endif /* CK_SSL */
10547
10548     if (auth_type && ftp_cpl != FPL_CLR) {
10549 #ifdef FTP_SRP
10550         if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
10551           if ((length = srp_encode(ftp_cpl == FPL_PRV,
10552                                    (CHAR *)s,
10553                                    (CHAR *)out,
10554                                    strlen(s))) < 0) {
10555               fprintf(stderr, "SRP failed to encode message\n");
10556               return(0);
10557           }
10558 #endif /* FTP_SRP */
10559 #ifdef FTP_KRB4
10560         if (ck_krb4_is_installed() &&
10561             (strcmp(auth_type, "KERBEROS_V4") == 0)) {
10562             if (ftp_cpl == FPL_PRV) {
10563                 length =
10564                   krb_mk_priv((CHAR *)s, (CHAR *)out,
10565                               strlen(s), ftp_sched,
10566 #ifdef KRB524
10567                               ftp_cred.session,
10568 #else /* KRB524 */
10569                               &ftp_cred.session,
10570 #endif /* KRB524 */
10571                               &myctladdr, &hisctladdr);
10572             } else {
10573                 length =
10574                   krb_mk_safe((CHAR *)s,
10575                               (CHAR *)out,
10576                               strlen(s),
10577 #ifdef KRB524
10578                               ftp_cred.session,
10579 #else /* KRB524 */
10580                               &ftp_cred.session,
10581 #endif /* KRB524 */
10582                               &myctladdr, &hisctladdr);
10583             }
10584             if (length == -1) {
10585                 fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
10586                         ftp_cpl == FPL_PRV ? "priv" : "safe");
10587                 return(0);
10588             }
10589         }
10590 #endif /* FTP_KRB4 */
10591 #ifdef FTP_GSSAPI
10592         /* Scommand (based on level) */
10593         if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
10594             gss_buffer_desc in_buf, out_buf;
10595             OM_uint32 maj_stat, min_stat;
10596             int conf_state;
10597             in_buf.value = s;
10598             in_buf.length = strlen(s) + 1;
10599             maj_stat = gss_seal(&min_stat, gcontext,
10600                                 (ftp_cpl==FPL_PRV), /* private */
10601                                 GSS_C_QOP_DEFAULT,
10602                                 &in_buf, &conf_state,
10603                                 &out_buf);
10604             if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */
10605                 user_gss_error(maj_stat, min_stat,
10606                                (ftp_cpl==FPL_PRV)?
10607                                "gss_seal ENC didn't complete":
10608                                "gss_seal MIC didn't complete");
10609             } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
10610                 fprintf(stderr, "GSSAPI didn't encrypt message");
10611             } else {
10612                 if (ftp_deb)
10613                   fprintf(stderr, "sealed (%s) %d bytes\n",
10614                           ftp_cpl==FPL_PRV?"ENC":"MIC",
10615                           out_buf.length);
10616                 memcpy(out, out_buf.value,
10617                        length=out_buf.length);
10618                 gss_release_buffer(&min_stat, &out_buf);
10619             }
10620         }
10621 #endif /* FTP_GSSAPI */
10622         /* Other auth types go here ... */
10623
10624         len2 = FTP_BUFSIZ;
10625         if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
10626                                    length, &len2, RADIX_ENCODE))
10627             ) {
10628             fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
10629                     radix_error(kerror));
10630             return(0);
10631         }
10632         if (ftp_deb)
10633           fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
10634         len2 = ckmakmsg(out,
10635                         FTP_BUFSIZ,
10636                         ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
10637                         in,
10638                         "\r\n",
10639                         NULL
10640                         );
10641         send(csocket,(SENDARG2TYPE)out,len2,0);
10642     } else {
10643         char out[FTP_BUFSIZ];
10644         int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10645         send(csocket,(SENDARG2TYPE)out,len,0);
10646     }
10647     return(1);
10648 }
10649
10650 static int
10651 mygetc() {
10652     static char inbuf[4096];
10653     static int bp = 0, ep = 0;
10654     int rc;
10655
10656     if (bp == ep) {
10657         bp = ep = 0;
10658 #ifdef CK_SSL
10659         if (ssl_ftp_active_flag) {
10660             int error;
10661             rc = SSL_read(ssl_ftp_con,inbuf,4096);
10662             error = SSL_get_error(ssl_ftp_con,rc);
10663             switch (error) {
10664               case SSL_ERROR_NONE:
10665                 break;
10666               case SSL_ERROR_WANT_WRITE:
10667               case SSL_ERROR_WANT_READ:
10668                 return(0);
10669               case SSL_ERROR_SYSCALL:
10670                 if (rc == 0) {          /* EOF */
10671                     break;
10672                 } else {
10673 #ifdef NT
10674                     int gle = GetLastError();
10675 #endif /* NT */
10676                     break;
10677                 }
10678               case SSL_ERROR_WANT_X509_LOOKUP:
10679               case SSL_ERROR_SSL:
10680               case SSL_ERROR_ZERO_RETURN:
10681               default:
10682                 break;
10683             }
10684         } else
10685 #endif /* CK_SSL */
10686           rc = recv(csocket,(char *)inbuf,4096,0);
10687         if (rc <= 0)
10688           return(EOF);
10689         ep = rc;
10690     }
10691     return(inbuf[bp++]);
10692 }
10693
10694 /*  x l a t e c  --  Translate a character  */
10695 /*
10696     Call with:
10697       fc    = Function code: 0 = translate, 1 = initialize.
10698       c     = Character (as int).
10699       incs  = Index of charset to translate from.
10700       outcs = Index of charset to translate to.
10701
10702     Returns:
10703       0: OK
10704      -1: Error
10705 */
10706 static int
10707 xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
10708 #ifdef NOCSETS
10709     return(c);
10710 #else
10711     static char buf[128];
10712     static int cx;
10713     int c0, c1;
10714
10715     if (fc == 1) {                      /* Initialize */
10716         cx = 0;                         /* Catch-up buffer write index */
10717         xgnbp = buf;                    /* Catch-up buffer read pointer */
10718         buf[0] = NUL;                   /* Buffer is empty */
10719         return(0);
10720     }
10721     if (cx >= 127) {                    /* Catch-up buffer full */
10722         debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
10723         printf("?Translation buffer overflow\n");
10724         return(-1);
10725     }
10726     /* Add char to buffer. */
10727     /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
10728
10729     debug(F000,"xlatec buf",ckitoa(cx),c);
10730     buf[cx++] = c;
10731     buf[cx] = NUL;
10732
10733     while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
10734         if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
10735           return(-1);
10736     }
10737     /* If we're caught up, reinitialize the buffer */
10738     return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
10739 #endif /* NOCSETS */
10740 }
10741
10742
10743 /*  p a r s e f e a t  */
10744
10745 /* Note: for convenience we align keyword values with table indices */
10746 /* If you need to insert a new keyword, adjust the SFT_xxx definitions */
10747
10748 static struct keytab feattab[] = {
10749     { "$$$$", 0,        0 },            /* Dummy for sfttab[0] */
10750     { "AUTH", SFT_AUTH, 0 },
10751     { "LANG", SFT_LANG, 0 },
10752     { "MDTM", SFT_MDTM, 0 },
10753     { "MLST", SFT_MLST, 0 },
10754     { "PBSZ", SFT_PBSZ, 0 },
10755     { "PROT", SFT_PROT, 0 },
10756     { "REST", SFT_REST, 0 },
10757     { "SIZE", SFT_SIZE, 0 },
10758     { "TVFS", SFT_TVFS, 0 },
10759     { "UTF8", SFT_UTF8, 0 }
10760 };
10761 static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
10762
10763 #define FACT_CSET  1
10764 #define FACT_CREA  2
10765 #define FACT_LANG  3
10766 #define FACT_MTYP  4
10767 #define FACT_MDTM  5
10768 #define FACT_PERM  6
10769 #define FACT_SIZE  7
10770 #define FACT_TYPE  8
10771 #define FACT_UNIQ  9
10772
10773 static struct keytab facttab[] = {
10774     { "CHARSET",    FACT_CSET, 0 },
10775     { "CREATE",     FACT_CREA, 0 },
10776     { "LANG",       FACT_LANG, 0 },
10777     { "MEDIA-TYPE", FACT_MTYP, 0 },
10778     { "MODIFY",     FACT_MDTM, 0 },
10779     { "PERM",       FACT_PERM, 0 },
10780     { "SIZE",       FACT_SIZE, 0 },
10781     { "TYPE",       FACT_TYPE, 0 },
10782     { "UNIQUE",     FACT_UNIQ, 0 }
10783 };
10784 static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
10785
10786 static struct keytab ftyptab[] = {
10787     { "CDIR", FTYP_CDIR, 0 },
10788     { "DIR",  FTYP_DIR,  0 },
10789     { "FILE", FTYP_FILE, 0 },
10790     { "PDIR", FTYP_PDIR, 0 }
10791 };
10792 static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
10793
10794 static VOID
10795 parsefeat(s) char * s; {                /* Parse a FEATURE response */
10796     char kwbuf[8];
10797     int i, x;
10798     if (!s) return;
10799     if (!*s) return;
10800     while (*s < '!')
10801       s++;
10802     for (i = 0; i < 4; i++) {
10803         if (s[i] < '!')
10804           break;
10805         kwbuf[i] = s[i];
10806     }
10807     if (s[i] && s[i] != SP && s[i] != CR && s[i] != LF)
10808       return;
10809     kwbuf[i] = NUL;
10810     /* xlookup requires a full (but case independent) match */
10811     i = xlookup(feattab,kwbuf,nfeattab,&x);
10812     debug(F111,"ftp parsefeat",s,i);
10813     if (i < 0 || i > 15)
10814       return;
10815
10816     switch (i) {
10817       case SFT_MDTM:                    /* Controlled by ENABLE/DISABLE */
10818         sfttab[i] = mdtmok;
10819         if (mdtmok) sfttab[0]++;
10820         break;
10821       case SFT_MLST:                    /* ditto */
10822         sfttab[i] = mlstok;
10823         if (mlstok) sfttab[0]++;
10824         break;
10825       case SFT_SIZE:                    /* ditto */
10826         sfttab[i] = sizeok;
10827         if (sizeok) sfttab[0]++;
10828         break;
10829       case SFT_AUTH:                    /* ditto */
10830         sfttab[i] = ftp_aut;
10831         if (ftp_aut) sfttab[0]++;
10832         break;
10833       default:                          /* Others */
10834         sfttab[0]++;
10835         sfttab[i]++;
10836     }
10837 }
10838
10839 static char *
10840 parsefacts(s) char * s; {               /* Parse MLS[DT] File Facts */
10841     char * p;
10842     int i, j, x;
10843     if (!s) return(NULL);
10844     if (!*s) return(NULL);
10845
10846     /* Maybe we should make a copy of s so we can poke it... */
10847
10848     while ((p = ckstrchr(s,'='))) {
10849         *p = NUL;                       /* s points to fact */
10850         i = xlookup(facttab,s,nfacttab,&x); 
10851         debug(F111,"ftp parsefact fact",s,i);
10852         *p = '=';
10853         s = p+1;                        /* Now s points to arg */
10854         p = ckstrchr(s,';');
10855         if (!p)
10856           p = ckstrchr(s,SP);
10857         if (!p) {
10858             debug(F110,"ftp parsefact end-of-val search fail",s,0);
10859             break;
10860         }
10861         *p = NUL;
10862         debug(F110,"ftp parsefact valu",s,0);
10863         switch (i) {
10864           case FACT_CSET:               /* Ignore these for now */
10865           case FACT_CREA:
10866           case FACT_LANG:
10867           case FACT_PERM:
10868           case FACT_MTYP:
10869           case FACT_UNIQ:
10870             break;
10871           case FACT_MDTM:               /* Modtime */
10872             makestr(&havemdtm,s);
10873             debug(F110,"ftp parsefact mdtm",havemdtm,0);
10874             break;
10875           case FACT_SIZE:               /* Size */
10876             havesize = ckatofs(s);
10877             debug(F101,"ftp parsefact size","",havesize);
10878             break;
10879           case FACT_TYPE:               /* Type */
10880             j = xlookup(ftyptab,s,nftyptab,NULL);
10881             debug(F111,"ftp parsefact type",s,j);
10882             havetype = (j < 1) ? 0 : j;
10883             break;
10884         }
10885         *p = ';';
10886         s = p+1;                        /* s points next fact or name */
10887     }
10888     while (*s == SP)                    /* Skip past spaces. */
10889       s++;
10890     if (!*s)                            /* Make sure we still have a name */
10891       s = NULL;
10892     debug(F110,"ftp parsefact name",s,0);
10893     return(s);
10894 }
10895
10896 /*  g e t r e p l y  --  (to an FTP command sent to server)  */
10897
10898 /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
10899
10900 static int
10901 getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
10902     /* lcs, rcs, vbm parameters as in ftpcmd() */
10903     register int i, c, n;
10904     register int dig;
10905     register char *cp;
10906     int xlate = 0;
10907     int count = 0;
10908     int auth = 0;
10909     int originalcode = 0, continuation = 0;
10910     sig_t oldintr;
10911     int pflag = 0;
10912     char *pt = pasv;
10913     char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
10914     int safe = 0;
10915     int xquiet = 0;
10916
10917     auth = (fc == GRF_AUTH);
10918
10919 #ifndef NOCSETS
10920     debug(F101,"ftp getreply lcs","",lcs);
10921     debug(F101,"ftp getreply rcs","",rcs);
10922     if (lcs > -1 && rcs > -1 && lcs != rcs) {
10923         xlate = 1;
10924         initxlate(rcs,lcs);
10925         xlatec(1,0,rcs,lcs);
10926     }
10927 #endif /* NOCSETS */
10928     debug(F101,"ftp getreply fc","",fc);
10929
10930     if (quiet)
10931       xquiet = 1;
10932     if (vbm == 9) {
10933         xquiet = 1;
10934         vbm = 0;
10935     }
10936     if (ftp_deb)                        /* DEBUG */
10937       vbm = 1;
10938     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
10939       vbm = 0;
10940     else if (vbm < 0)                   /* VERBOSE */
10941       vbm = ftp_vbm;
10942
10943     ibuf[0] = '\0';
10944     if (reply_parse)
10945       reply_ptr = reply_buf;
10946     havesigint = 0;
10947     oldintr = signal(SIGINT, cmdcancel);
10948     for (count = 0;; count++) {
10949         obuf[0] = '\0';
10950         dig = n = ftpcode = i = 0;
10951         cp = ftp_reply_str;
10952         while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
10953             if (c == IAC) {             /* Handle telnet commands */
10954                 switch (c = mygetc()) {
10955                   case WILL:
10956                   case WONT:
10957                     c = mygetc();
10958                     obuf[0] = IAC;
10959                     obuf[1] = DONT;
10960                     obuf[2] = c;
10961                     obuf[3] = NUL;
10962 #ifdef CK_SSL
10963                     if (ssl_ftp_active_flag) {
10964                         int error, rc;
10965                         rc = SSL_write(ssl_ftp_con,obuf,3);
10966                         error = SSL_get_error(ssl_ftp_con,rc);
10967                         switch (error) {
10968                           case SSL_ERROR_NONE:
10969                             break;
10970                           case SSL_ERROR_WANT_WRITE:
10971                           case SSL_ERROR_WANT_READ:
10972                             return(0);
10973                           case SSL_ERROR_SYSCALL:
10974                             if (rc == 0) { /* EOF */
10975                                 break;
10976                             } else {
10977 #ifdef NT
10978                                 int gle = GetLastError();
10979 #endif /* NT */
10980                                 break;
10981                             }
10982                           case SSL_ERROR_WANT_X509_LOOKUP:
10983                           case SSL_ERROR_SSL:
10984                           case SSL_ERROR_ZERO_RETURN:
10985                           default:
10986                             break;
10987                         }
10988                     } else
10989 #endif /* CK_SSL */
10990                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10991                     break;
10992                   case DO:
10993                   case DONT:
10994                     c = mygetc();
10995                     obuf[0] = IAC;
10996                     obuf[1] = WONT;
10997                     obuf[2] = c;
10998                     obuf[3] = NUL;
10999 #ifdef CK_SSL
11000                     if (ssl_ftp_active_flag) {
11001                         int error, rc;
11002                         rc = SSL_write(ssl_ftp_con,obuf,3);
11003                         error = SSL_get_error(ssl_ftp_con,rc);
11004                         switch (error) {
11005                           case SSL_ERROR_NONE:
11006                             break;
11007                           case SSL_ERROR_WANT_WRITE:
11008                           case SSL_ERROR_WANT_READ:
11009                               signal(SIGINT,oldintr);
11010                               return(0);
11011                           case SSL_ERROR_SYSCALL:
11012                             if (rc == 0) { /* EOF */
11013                                 break;
11014                             } else {
11015 #ifdef NT
11016                                 int gle = GetLastError();
11017 #endif /* NT */
11018                                 break;
11019                             }
11020                           case SSL_ERROR_WANT_X509_LOOKUP:
11021                           case SSL_ERROR_SSL:
11022                           case SSL_ERROR_ZERO_RETURN:
11023                           default:
11024                             break;
11025                         }
11026                     } else
11027 #endif /* CK_SSL */
11028                       send(csocket,(SENDARG2TYPE)obuf,3,0);
11029                     break;
11030                   default:
11031                     break;
11032                 }
11033                 continue;
11034             }
11035             dig++;
11036             if (c == EOF) {
11037                 if (expecteof) {
11038                     signal(SIGINT,oldintr);
11039                     ftpcode = 221;
11040                     debug(F101,"ftp getreply EOF","",ftpcode);
11041                     return(0);
11042                 }
11043                 lostpeer();
11044                 if (!xquiet) {
11045                     if (ftp_deb)
11046                       printf("421 ");
11047                     printf(
11048                       "Service not available, connection closed by server\n");
11049                     fflush(stdout);
11050                 }
11051                 signal(SIGINT,oldintr);
11052                 ftpcode = 421;
11053                 debug(F101,"ftp getreply EOF","",ftpcode);
11054                 return(4);
11055             }
11056             if (n == 0) {               /* First digit */
11057                 n = c;                  /* Save it */
11058             }
11059             if (auth_type &&
11060 #ifdef CK_SSL
11061                 !ssl_ftp_active_flag &&
11062 #endif /* CK_SSL */
11063                 !ibuf[0] && (n == '6' || continuation)) {
11064                 if (c != '\r' && dig > 4)
11065                   obuf[i++] = c;
11066             } else {
11067                 if (auth_type &&
11068 #ifdef CK_SSL
11069                     !ssl_ftp_active_flag &&
11070 #endif /* CK_SSL */
11071                     !ibuf[0] && dig == 1 && vbm)
11072                   printf("Unauthenticated reply received from server:\n");
11073                 if (reply_parse) {
11074                     *reply_ptr++ = c;
11075                     *reply_ptr = NUL;
11076                 }
11077                 if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
11078                     ftp_cmdlin < 2) {
11079                     if ((c != '\r') &&
11080                         (ftp_deb || ((vbm || (!auth && n == '5')) &&
11081                         (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
11082                         )))))
11083                     {
11084 #ifdef FTP_PROXY
11085                         if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
11086                           printf("%s:",ftp_host);
11087 #endif /* FTP_PROXY */
11088
11089                         if (!xquiet) {
11090 #ifdef NOCSETS
11091                             printf("%c",c);
11092 #else
11093                             if (xlate) {
11094                                 xlatec(0,c,rcs,lcs);
11095                             } else {
11096                                 printf("%c",c);
11097                             }
11098 #endif /* NOCSETS */
11099                         }
11100                     }
11101                 }
11102             }
11103             if (auth_type &&
11104 #ifdef CK_SSL
11105                 !ssl_ftp_active_flag &&
11106 #endif /* CK_SSL */
11107                 !ibuf[0] && n != '6')
11108               continue;
11109             if (dig < 4 && isdigit(c))
11110               ftpcode = ftpcode * 10 + (c - '0');
11111             if (!pflag && ftpcode == 227)
11112               pflag = 1;
11113             if (dig > 4 && pflag == 1 && isdigit(c))
11114               pflag = 2;
11115             if (pflag == 2) {
11116                 if (c != '\r' && c != ')')
11117                   *pt++ = c;
11118                 else {
11119                     *pt = '\0';
11120                     pflag = 3;
11121                 }
11122             }
11123             if (dig == 4 && c == '-' && n != '6') {
11124                 if (continuation)
11125                   ftpcode = 0;
11126                 continuation++;
11127             }
11128             if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
11129                 *cp++ = c;
11130                 *cp = NUL;
11131             }
11132         }
11133         if (deblog ||
11134 #ifdef COMMENT
11135 /*
11136   Sometimes we need to print the server reply.  printlines is nonzero for any
11137   command where the results are sent back on the control connection rather
11138   than the data connection, e.g. STAT.  In the TOPS-20 case, each file line
11139   has ftpcode 213.  But if you do this with a UNIX server, it sends "213-Start
11140   STAT", <line with ftpcode == 0>, "213-End" or somesuch.  So when printlines
11141   is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
11142   lines from UNIX.  Further experimentation needed with other servers.  Of
11143   course RFC959 is mute as to the format of the server reply.
11144
11145   'printlines' is also true for PWD and BYE.
11146 */
11147             (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
11148 #else
11149 /* No, we can't be that clever -- it breaks other things like RPWD... */
11150             (printlines &&
11151              (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
11152 #endif /* COMMENT */
11153             ) {
11154             char * q = cp;
11155             char *r = ftp_reply_str;
11156             *q-- = NUL;                 /* NUL-terminate */
11157             while (*q < '!' && q > r)   /* Strip CR, etc */
11158               *q-- = NUL;
11159             if (!ftp_deb && printlines) { /* If printing */
11160                 if (ftpcode != 0)       /* strip ftpcode if any */
11161                   r += 4;
11162 #ifdef NOCSETS
11163                 printf("%s\n",r);       /* and print */
11164 #else
11165                 if (!xlate) {
11166                     printf("%s\n",r);
11167                 } else {                /* Translating */
11168                     xgnbp = r;          /* Set up strgetc() */
11169                     while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
11170                         if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
11171                             signal(SIGINT,oldintr);
11172                             return(-1);
11173                         }
11174                     }
11175                     printf("\n");
11176                 }
11177 #endif /* NOCSETS */
11178             }
11179         }
11180         debug(F110,"FTP RCVD ",ftp_reply_str,0);
11181
11182         if (fc == GRF_FEAT) {           /* Parsing FEAT command response? */
11183             if (count == 0 && n == '2') {
11184                 int i;                  /* (Re)-init server FEATure table */
11185                 debug(F100,"ftp getreply clearing feature table","",0);
11186                 for (i = 0; i < 16; i++)
11187                   sfttab[i] = 0;
11188             } else {
11189                 parsefeat((char *)ftp_reply_str);
11190             }
11191         }
11192         if (auth_type &&
11193 #ifdef CK_SSL
11194             !ssl_ftp_active_flag &&
11195 #endif /* CK_SSL */
11196              !ibuf[0] && n != '6') {
11197             signal(SIGINT,oldintr);
11198             return(getreply(expecteof,lcs,rcs,vbm,auth));
11199         }
11200         ibuf[0] = obuf[i] = '\0';
11201         if (ftpcode && n == '6')
11202           if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
11203               printf("Unknown reply: %d %s\n", ftpcode, obuf);
11204               n = '5';
11205           } else safe = (ftpcode == 631);
11206         if (obuf[0]                     /* if there is a string to decode */
11207 #ifdef CK_SSL
11208             && !ssl_ftp_active_flag     /* and not SSL/TLS */
11209 #endif /* CK_SSL */
11210             ) {
11211             if (!auth_type) {
11212                 printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
11213                 n = '5';
11214             }
11215 #ifndef CK_ENCRYPTION
11216             else if (ftpcode == 632) {
11217                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11218                 n = '5';
11219             }
11220 #endif /* CK_ENCRYPTION */
11221 #ifdef NOCONFIDENTIAL
11222             else if (ftpcode == 633) {
11223                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11224                 n = '5';
11225             }
11226 #endif /* NOCONFIDENTIAL */
11227             else {
11228                 int len = FTP_BUFSIZ;
11229                 if ((kerror = radix_encode((CHAR *)obuf,
11230                                            (CHAR *)ibuf,
11231                                            0,
11232                                            &len,
11233                                            RADIX_DECODE))
11234                     ) {
11235                     printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
11236                            ftpcode, radix_error(kerror), obuf);
11237                     n = '5';
11238                 }
11239 #ifdef FTP_SRP
11240                 else if (strcmp(auth_type, "SRP") == 0) {
11241                     int outlen;
11242                     outlen = srp_decode(!safe, (CHAR *)ibuf,
11243                                         (CHAR *) ibuf, len);
11244                     if (outlen < 0) {
11245                         printf("Warning: %d reply %s!\n",
11246                                ftpcode, safe ? "modified" : "garbled");
11247                         n = '5';
11248                     } else {
11249                         ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
11250                         if (ftp_deb)
11251                           printf("%c:", safe ? 'S' : 'P');
11252                         continue;
11253                     }
11254                 }
11255 #endif /* FTP_SRP */
11256 #ifdef FTP_KRB4
11257                 else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
11258                     if (safe) {
11259                         kerror = krb_rd_safe((CHAR *)ibuf, len,
11260 #ifdef KRB524
11261                                              ftp_cred.session,
11262 #else /* KRB524 */
11263                                              &ftp_cred.session,
11264 #endif /* KRB524 */
11265                                              &hisctladdr,
11266                                              &myctladdr,
11267                                              &ftp_msg_data
11268                                              );
11269                     } else {
11270                         kerror = krb_rd_priv((CHAR *)ibuf, len,
11271                                              ftp_sched,
11272 #ifdef KRB524
11273                                              ftp_cred.session,
11274 #else /* KRB524 */
11275                                              &ftp_cred.session,
11276 #endif /* KRB524 */
11277                                              &hisctladdr,
11278                                              &myctladdr,
11279                                              &ftp_msg_data
11280                                              );
11281                     }
11282                     if (kerror != KSUCCESS) {
11283                         printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
11284                                safe ? "modified" : "garbled",
11285                                safe ? "safe" : "priv",
11286                                krb_get_err_text(kerror));
11287                         n = '5';
11288                     } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
11289                         kerror = KFAILURE;
11290                         n = '5';
11291                         printf("reply data too large for buffer\n");
11292                     } else {
11293                         if (ftp_deb)
11294                           printf("%c:", safe ? 'S' : 'P');
11295                         memcpy(ibuf,ftp_msg_data.app_data,
11296                                ftp_msg_data.app_length);
11297                         ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
11298                                   FTP_BUFSIZ - ftp_msg_data.app_length);
11299                         continue;
11300                     }
11301                 }
11302 #endif /* FTP_KRB4 */
11303 #ifdef FTP_GSSAPI
11304                 else if (strcmp(auth_type, "GSSAPI") == 0) {
11305                     gss_buffer_desc xmit_buf, msg_buf;
11306                     OM_uint32 maj_stat, min_stat;
11307                     int conf_state;
11308                     xmit_buf.value = ibuf;
11309                     xmit_buf.length = len;
11310                     /* decrypt/verify the message */
11311                     conf_state = safe;
11312                     maj_stat = gss_unseal(&min_stat, gcontext,
11313                                           &xmit_buf, &msg_buf,
11314                                           &conf_state, NULL);
11315                     if (maj_stat != GSS_S_COMPLETE) {
11316                         user_gss_error(maj_stat, min_stat,
11317                                        "failed unsealing reply");
11318                         n = '5';
11319                     } else {
11320                         memcpy(ibuf, msg_buf.value, msg_buf.length);
11321                         ckstrncpy(&ibuf[msg_buf.length], "\r\n",
11322                                   FTP_BUFSIZ-msg_buf.length);
11323                         gss_release_buffer(&min_stat,&msg_buf);
11324                         if (ftp_deb)
11325                           printf("%c:", safe ? 'S' : 'P');
11326                         continue;
11327                     }
11328                 }
11329 #endif /* FTP_GSSAPI */
11330                 /* Other auth types go here... */
11331             }
11332         } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
11333                    !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
11334 #ifdef NOCSETS
11335             printf("%c",c);
11336 #else
11337             if (xlate) {
11338                 xlatec(0,c,rcs,lcs);
11339             } else {
11340                 printf("%c",c);
11341             }
11342 #endif /* NOCSETS */
11343             fflush (stdout);
11344         }
11345         if (continuation && ftpcode != originalcode) {
11346             if (originalcode == 0)
11347               originalcode = ftpcode;
11348             continue;
11349         }
11350         *cp = '\0';
11351         if (n != '1')
11352           cpend = 0;
11353         signal(SIGINT,oldintr);
11354         if (ftpcode == 421 || originalcode == 421) {
11355             lostpeer();
11356             if (!xquiet && !ftp_deb)
11357               printf("%s\n",reply_buf);
11358         }
11359         if ((cancelfile != 0) &&
11360 #ifndef ULTRIX3
11361             /* Ultrix 3.0 cc objects violently to this clause */
11362             (oldintr != cmdcancel) &&
11363 #endif /* ULTRIX3 */
11364             (oldintr != SIG_IGN)) {
11365             if (oldintr)
11366               (*oldintr)(SIGINT);
11367         }
11368         if (reply_parse) {
11369             *reply_ptr = '\0';
11370             if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
11371                 reply_parse = reply_ptr + strlen(reply_parse);
11372                 if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
11373                   *reply_ptr = '\0';
11374             } else
11375               reply_parse = reply_ptr;
11376         }
11377         while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
11378           *cp-- = NUL;
11379         debug(F111,"ftp getreply",ftp_reply_str,n - '0');
11380         return(n - '0');
11381     } /* for (;;) */
11382 }
11383
11384 #ifdef BSDSELECT
11385 static int
11386 #ifdef CK_ANSIC
11387 empty(fd_set * mask, int sec)
11388 #else
11389 empty(mask, sec) fd_set * mask; int sec;
11390 #endif /* CK_ANSIC */
11391 {
11392     struct timeval t;
11393     t.tv_sec = (long) sec;
11394     t.tv_usec = 0L;
11395     debug(F100,"ftp empty calling select...","",0);
11396 #ifdef INTSELECT
11397     x = select(32, (int *)mask, NULL, NULL, &t);
11398 #else
11399     x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
11400 #endif /* INTSELECT */
11401     debug(F101,"ftp empty select","",x);
11402     return(x);
11403 }
11404 #else /* BSDSELECT */
11405 #ifdef IBMSELECT
11406 static int
11407 empty(mask, cnt, sec) int * mask, sec;
11408                       int   cnt;
11409 {
11410     return(select(mask,cnt,0,0,sec*1000));
11411 }
11412 #endif /* IBMSELECT */
11413 #endif /* BSDSELECT */
11414
11415 static sigtype
11416 cancelsend(sig) int sig; {
11417     havesigint++;
11418     cancelgroup++;
11419     cancelfile = 0;
11420     printf(" Canceled...\n");
11421     secure_getc(0,1);                   /* Initialize net input buffers */
11422     debug(F100,"ftp cancelsend caught SIGINT ","",0);
11423     fflush(stdout);
11424 #ifndef OS2
11425     longjmp(sendcancel, 1);
11426 #else
11427     PostCtrlCSem();
11428 #endif /* OS2 */
11429 }
11430
11431 static VOID
11432 #ifdef CK_ANSIC
11433 secure_error(char *fmt, ...)
11434 #else
11435 /* VARARGS1 */
11436 secure_error(fmt, p1, p2, p3, p4, p5)
11437    char *fmt; int p1, p2, p3, p4, p5;
11438 #endif /* CK_ANSIC */
11439 {
11440 #ifdef CK_ANSIC
11441     va_list ap;
11442
11443     va_start(ap, fmt);
11444     vfprintf(stderr, fmt, ap);
11445     va_end(ap);
11446 #else
11447     fprintf(stderr, fmt, p1, p2, p3, p4, p5);
11448 #endif
11449     fprintf(stderr, "\n");
11450 }
11451
11452 /*
11453  * Internal form of settype; changes current type in use with server
11454  * without changing our notion of the type for data transfers.
11455  * Used to change to and from ascii for listings.
11456  */
11457 static VOID
11458 changetype(newtype, show) int newtype, show; {
11459     int rc;
11460     char * s;
11461
11462     if ((newtype == curtype) && typesent++)
11463       return;
11464     switch (newtype) {
11465       case FTT_ASC:
11466         s = "A";
11467         break;
11468       case FTT_BIN:
11469         s = "I";
11470         break;
11471       case FTT_TEN:
11472         s = "L 8";
11473         break;
11474       default:
11475         s = "I";
11476         break;
11477     }
11478     rc = ftpcmd("TYPE",s,-1,-1,show);
11479     if (rc == REPLY_COMPLETE)
11480       curtype = newtype;
11481 }
11482
11483 /* PUT a file.  Returns -1 on error, 0 on success, 1 if file skipped */
11484
11485 static VOID
11486 #ifdef CK_ANSIC
11487 doftpsend(void * threadinfo)
11488 #else
11489 doftpsend(threadinfo) VOID * threadinfo;
11490 #endif
11491 {
11492 #ifdef NTSIG
11493     if (threadinfo) {                   /* Thread local storage... */
11494         TlsSetValue(TlsIndex,threadinfo);
11495         debug(F100, "doftpsend called with threadinfo block","", 0);
11496     } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
11497 #endif /* NTSIG */
11498 #ifdef CK_LOGIN
11499 #ifdef IKSD
11500 #ifdef NT
11501     if (inserver)
11502       setntcreds();
11503 #endif /* NT */
11504 #endif /* IKSD */
11505 #endif /* CK_LOGIN */
11506
11507     if (initconn()) {
11508 #ifndef NOHTTP
11509         int y = -1;
11510         /* debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy); */
11511
11512        /*  If the connection failed and we are using an HTTP Proxy
11513         *  and the reason for the failure was an authentication
11514         *  error, then we need to give the user to ability to
11515         *  enter a username and password, just like a browser.
11516         *
11517         *  I tried to do all of this within the netopen() call
11518         *  but it is much too much work.
11519         */
11520         while (y != 0 && tcp_http_proxy != NULL ) {
11521
11522             if (tcp_http_proxy_errno == 401 ||
11523                  tcp_http_proxy_errno == 407 ) {
11524                 char uid[UIDBUFLEN];
11525                 char pwd[PWDSIZ];
11526                 struct txtbox tb[2];
11527                 int ok;
11528
11529                 tb[0].t_buf = uid;
11530                 tb[0].t_len = UIDBUFLEN;
11531                 tb[0].t_lbl = "Proxy Userid: ";
11532                 tb[0].t_dflt = NULL;
11533                 tb[0].t_echo = 1;
11534                 tb[1].t_buf = pwd;
11535                 tb[1].t_len = 256;
11536                 tb[1].t_lbl = "Proxy Passphrase: ";
11537                 tb[1].t_dflt = NULL;
11538                 tb[1].t_echo = 2;
11539
11540                 ok = uq_mtxt("Proxy Server Authentication Required\n",
11541                               NULL, 2, tb);
11542                 if (ok && uid[0]) {
11543                     char * proxy_user, * proxy_pwd;
11544
11545                     proxy_user = tcp_http_proxy_user;
11546                     proxy_pwd  = tcp_http_proxy_pwd;
11547
11548                     tcp_http_proxy_user = uid;
11549                     tcp_http_proxy_pwd = pwd;
11550
11551                     y = initconn();
11552
11553                     debug(F101,"doftpsend","initconn",y);
11554                     memset(pwd,0,PWDSIZ);
11555                     tcp_http_proxy_user = proxy_user;
11556                     tcp_http_proxy_pwd = proxy_pwd;
11557                 } else
11558                     break;
11559             } else
11560                 break;
11561         }
11562
11563         if ( y != 0 ) {
11564 #endif /* NOHTTP */
11565             signal(SIGINT, ftpsnd.oldintr);
11566 #ifdef SIGPIPE
11567             if (ftpsnd.oldintp)
11568               signal(SIGPIPE, ftpsnd.oldintp);
11569 #endif /* SIGPIPE */
11570             ftpcode = -1;
11571             zclose(ZIFILE);
11572             ftpsndret = -1;
11573 #ifdef NTSIG
11574             ckThreadEnd(threadinfo);
11575 #endif /* NTSIG */
11576             return;
11577 #ifndef NOHTTP
11578         }
11579 #endif /* NOHTTP */
11580     }
11581     ftpsndret = 0;
11582 #ifdef NTSIG
11583      ckThreadEnd(threadinfo);
11584 #endif /* NTSIG */
11585 }
11586
11587 static VOID
11588 #ifdef CK_ANSIC
11589 failftpsend(void * threadinfo)
11590 #else
11591 failftpsend(threadinfo) VOID * threadinfo;
11592 #endif /* CK_ANSIC */
11593 {
11594 #ifdef NTSIG
11595     if (threadinfo) {                   /* Thread local storage... */
11596         TlsSetValue(TlsIndex,threadinfo);
11597         debug(F100, "docmdfile called with threadinfo block","", 0);
11598     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11599 #endif /* NTSIG */
11600 #ifdef CK_LOGIN
11601 #ifdef IKSD
11602 #ifdef NT
11603     if (inserver)
11604       setntcreds();
11605 #endif /* NT */
11606 #endif /* IKSD */
11607 #endif /* CK_LOGIN */
11608
11609     while (cpend) {
11610         ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11611         debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
11612     }
11613     if (data >= 0) {
11614 #ifdef CK_SSL
11615         if (ssl_ftp_data_active_flag) {
11616             SSL_shutdown(ssl_ftp_data_con);
11617             SSL_free(ssl_ftp_data_con);
11618             ssl_ftp_data_active_flag = 0;
11619             ssl_ftp_data_con = NULL;
11620         }
11621 #endif /* CK_SSL */
11622 #ifdef TCPIPLIB
11623         socket_close(data);
11624 #else /* TCPIPLIB */
11625 #ifdef USE_SHUTDOWN
11626         shutdown(data, 1+1);
11627 #endif /* USE_SHUTDOWN */
11628         close(data);
11629 #endif /* TCPIPLIB */
11630         data = -1;
11631         globaldin = -1;
11632     }
11633     if (ftpsnd.oldintr)
11634         signal(SIGINT,ftpsnd.oldintr);
11635 #ifdef SIGPIPE
11636     if (ftpsnd.oldintp)
11637         signal(SIGPIPE,ftpsnd.oldintp);
11638 #endif /* SIGPIPE */
11639     ftpcode = -1;
11640 #ifndef OS2
11641     /* TEST ME IN K95 */
11642     if (havesigint) {
11643         havesigint = 0;
11644         debug(F100,"ftp failftpsend chain to trap()...","",0);
11645         if (ftpsnd.oldintr != SIG_IGN)
11646           (*ftpsnd.oldintr)(SIGINT);
11647         /* NOTREACHED (I hope!) */
11648         debug(F100,"ftp failftpsend return from trap()...","",0);
11649     }
11650 #endif /* OS2 */
11651 }
11652
11653 static VOID
11654 #ifdef CK_ANSIC
11655 failftpsend2(void * threadinfo)
11656 #else
11657 failftpsend2(threadinfo) VOID * threadinfo;
11658 #endif /* CK_ANSIC */
11659 {
11660 #ifdef NTSIG
11661     if (threadinfo) {                   /* Thread local storage... */
11662         TlsSetValue(TlsIndex,threadinfo);
11663         debug(F100, "docmdfile called with threadinfo block","", 0);
11664     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11665 #endif /* NTSIG */
11666 #ifdef CK_LOGIN
11667 #ifdef IKSD
11668 #ifdef NT
11669     if (inserver)
11670       setntcreds();
11671 #endif /* NT */
11672 #endif /* IKSD */
11673 #endif /* CK_LOGIN */
11674
11675     debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
11676     tfc += ffc;
11677 #ifdef GFTIMER
11678     fpfsecs = gftimer();
11679 #endif /* GFTIMER */
11680     zclose(ZIFILE);
11681 #ifdef PIPESEND
11682     if (sndfilter)
11683       pipesend = 0;
11684 #endif /* PIPESEND */
11685     signal(SIGINT, ftpsnd.oldintr);
11686 #ifdef SIGPIPE
11687     if (ftpsnd.oldintp)
11688       signal(SIGPIPE, ftpsnd.oldintp);
11689 #endif /* SIGPIPE */
11690     if (!cpend) {
11691         ftpcode = -1;
11692         ftpsndret = -1;
11693 #ifdef NTSIG
11694         ckThreadEnd(threadinfo);
11695 #endif /* NTSIG */
11696         return;
11697     }
11698     if (data >= 0) {
11699 #ifdef CK_SSL
11700         if (ssl_ftp_data_active_flag) {
11701             SSL_shutdown(ssl_ftp_data_con);
11702             SSL_free(ssl_ftp_data_con);
11703             ssl_ftp_data_active_flag = 0;
11704             ssl_ftp_data_con = NULL;
11705         }
11706 #endif /* CK_SSL */
11707 #ifdef TCPIPLIB
11708         socket_close(data);
11709 #else /* TCPIPLIB */
11710 #ifdef USE_SHUTDOWN
11711         shutdown(data, 1+1);
11712 #endif /* USE_SHUTDOWN */
11713         close(data);
11714 #endif /* TCPIPLIB */
11715         data = -1;
11716         globaldin = -1;
11717     }
11718     if (dout) {
11719 #ifdef TCPIPLIB
11720         socket_close(dout);
11721 #else /* TCPIPLIB */
11722 #ifdef USE_SHUTDOWN
11723         shutdown(dout, 1+1);
11724 #endif /* USE_SHUTDOWN */
11725         close(dout);
11726 #endif /* TCPIPLIB */
11727     }
11728     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11729     ftpcode = -1;
11730     ftpsndret = -1;
11731
11732 #ifndef OS2
11733     /* TEST ME IN K95 */
11734     if (havesigint) {
11735         havesigint = 0;
11736         debug(F100,"ftp failftpsend2 chain to trap()...","",0);
11737         if (ftpsnd.oldintr != SIG_IGN)
11738           (*ftpsnd.oldintr)(SIGINT);
11739         /* NOTREACHED (I hope!) */
11740         debug(F100,"ftp failftpsend2 return from trap()...","",0);
11741     }
11742 #endif /* OS2 */
11743 }
11744
11745 static VOID
11746 #ifdef CK_ANSIC
11747 doftpsend2(void * threadinfo)
11748 #else
11749 doftpsend2(threadinfo) VOID * threadinfo;
11750 #endif
11751 {
11752     register int c, d = 0;
11753     int n, t, x, notafile, unique = 0;
11754     char *buf, *bufp;
11755     
11756 #ifdef NTSIG
11757     if (threadinfo) {                   /* Thread local storage... */
11758         TlsSetValue(TlsIndex,threadinfo);
11759         debug(F100, "doftpsend2 called with threadinfo block","", 0);
11760     } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
11761 #endif /* NTSIG */
11762 #ifdef CK_LOGIN
11763 #ifdef IKSD
11764 #ifdef NT
11765     if (inserver)
11766       setntcreds();
11767 #endif /* NT */
11768 #endif /* IKSD */
11769 #endif /* CK_LOGIN */
11770
11771     buf = ftpsndbuf;                    /* (not on stack) */
11772
11773     unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
11774     notafile = sndarray || pipesend;
11775
11776 #ifdef FTP_RESTART
11777     if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
11778         char * p;
11779         changetype(FTT_BIN,0);          /* Change to binary */
11780
11781         /* Ask for remote file's size */
11782         x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
11783
11784         if (x == REPLY_COMPLETE) {      /* Have ftpsnd.reply */
11785             p = &ftp_reply_str[4];      /* Parse it */
11786             while (isdigit(*p)) {
11787                 sendstart = sendstart * 10 + (int)(*p - '0');
11788                 p++;
11789             }
11790             if (*p && *p != CR) {       /* Bad number */
11791                 debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
11792                 sendstart = (CK_OFF_T)0;
11793             } else if (sendstart > fsize) { /* Remote file bigger than local */
11794                 debug(F110,"doftpsend2 big size",ckfstoa(fsize),sendstart);
11795                 sendstart = (CK_OFF_T)0;
11796             }
11797             /* Local is newer */
11798             debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
11799             if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
11800                 debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
11801                 sendstart = (CK_OFF_T)0; /* Send the whole file */
11802             }
11803         }
11804         changetype(ftp_typ,0);          /* Change back to appropriate type */
11805         if (sendstart > (CK_OFF_T)0) {  /* Still restarting? */
11806             if (sendstart == fsize) {   /* Same size - no need to send */
11807                 debug(F111,"doftpsend2 /restart SKIP",
11808                       ckfstoa(fsize),sendstart);
11809                 zclose(ZIFILE);
11810                 ftpsndret = SKP_RES;
11811 #ifdef NTSIG
11812                 ckThreadEnd(threadinfo);
11813 #endif /* NTSIG */
11814                 return;
11815             }
11816             errno = 0;                  /* Restart needed, seek to the spot */
11817             if (zfseek((long)sendstart) < 0) {
11818                 debug(F111,"doftpsend2 zfseek fails",
11819                       ftpsnd.local,sendstart);
11820                 fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
11821                 sendstart = 0;
11822                 zclose(ZIFILE);
11823                 ftpsndret = -1;
11824 #ifdef NTSIG
11825                 ckThreadEnd(threadinfo);
11826 #endif /* NTSIG */
11827                 return;
11828             }
11829 #ifdef COMMENT
11830             debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
11831             x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
11832             if (x != REPLY_CONTINUE) {
11833                 sendstart = 0;
11834                 zclose(ZIFILE);
11835                 ftpsndret = -1;
11836 #ifdef NTSIG
11837                 ckThreadEnd(threadinfo);
11838 #endif /* NTSIG */
11839                 return;
11840             } else {
11841                 ftpsnd.cmd = "STOR";
11842             }
11843 #else
11844             sendmode = SM_RESEND;
11845             ftpsnd.cmd = "APPE";
11846 #endif /* COMMENT */
11847             /* sendstart = (CK_OFF_T)0; */
11848         }
11849     }
11850 #endif /* FTP_RESTART */
11851
11852     if (unique && !stouarg)             /* If we know STOU accepts no arg */
11853       ftpsnd.remote = NULL;             /* don't include one. */
11854
11855     x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
11856     debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
11857     debug(F101,"doftpsend2 ftpcmd","",x);
11858
11859     if (x != REPLY_PRELIM && unique) {
11860         /*
11861           RFC959 says STOU does not take an argument.  But every FTP server
11862           I've encountered but one accepts the arg and constructs the unique
11863           name from it, which is better than making up a totally random name
11864           for the file, which is what RFC959 calls for.  Especially because
11865           there is no way for the client to find out the name chosen by the
11866           server.  So we try STOU with the argument first, which works with
11867           most servers, and if it fails we retry it without the arg, for
11868           the benefit of the one picky server that is not "liberal in what
11869           it accepts" UNLESS the first STOU got a 502 code ("not implemented")
11870           which means STOU is not accepted, period.
11871         */
11872         if ((x == 5) && stouarg && (ftpcode != 502)) {
11873             x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); 
11874             if (x == REPLY_PRELIM)      /* If accepted */
11875               stouarg = 0;              /* flag no STOU arg for this server */
11876         }
11877     }
11878     if (x != REPLY_PRELIM) {
11879         signal(SIGINT, ftpsnd.oldintr);
11880 #ifdef SIGPIPE
11881         if (ftpsnd.oldintp)
11882           signal(SIGPIPE, ftpsnd.oldintp);
11883 #endif /* SIGPIPE */
11884         debug(F101,"doftpsend2 not REPLY_PRELIM","",x);
11885         zclose(ZIFILE);
11886 #ifdef PIPESEND
11887         if (sndfilter)
11888           pipesend = 0;
11889 #endif /* PIPESEND */
11890         ftpsndret = -1;
11891 #ifdef NTSIG
11892         ckThreadEnd(threadinfo);
11893 #endif /* NTSIG */
11894         return;
11895     }
11896     debug(F100,"doftpsend2 getting data connection...","",0);
11897     dout = dataconn(ftpsnd.lmode);             /* Get data connection */
11898     debug(F101,"doftpsend2 dataconn","",dout);
11899     if (dout == -1) {
11900         failftpsend2(threadinfo);
11901 #ifdef NTSIG
11902         ckThreadEnd(threadinfo);
11903 #endif /* NTSIG */
11904         return;
11905     }
11906     /* Initialize per-file stats */
11907     ffc = (CK_OFF_T)0;                  /* Character counter */
11908     cps = oldcps = 0L;                  /* Thruput */
11909     n = 0;
11910 #ifdef GFTIMER
11911     rftimer();                          /* reset f.p. timer */
11912 #endif /* GFTIMER */
11913
11914 #ifdef SIGPIPE
11915     ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
11916 #endif /* SIGPIPE */
11917     debug(F101,"doftpsend2 curtype","",curtype);
11918     switch (curtype) {
11919       case FTT_BIN:                     /* Binary mode */
11920       case FTT_TEN:
11921         errno = d = 0;
11922 #ifdef VMS
11923         /*
11924           This is because VMS zxin() is C-Library fread() 
11925           but the file was opened with zopeni(), which is RMS.
11926         */
11927         while (((c = zminchar()) > -1) && !cancelfile) {
11928             ffc++;
11929             if (zzout(dout,c) < 0)
11930               break;
11931         }
11932 #else  /* VMS */
11933         while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
11934             ftpsnd.bytes += n;
11935             ffc += n;
11936             debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
11937             ckhexdump("doftpsend2 zxin",buf,16);
11938 #ifdef CK_SSL
11939             if (ssl_ftp_data_active_flag) {
11940                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11941                     if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
11942                       break;
11943                     spackets++;
11944                     pktnum++;
11945                     if (fdispla != XYFD_B) {
11946                         spktl = d;
11947                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11948                     }
11949                 }
11950             } else {
11951 #endif /* CK_SSL */
11952                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11953                     if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
11954                         || iscanceled())
11955                       break;
11956                     spackets++;
11957                     pktnum++;
11958                     if (fdispla != XYFD_B) {
11959                         spktl = d;
11960                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11961                     }
11962                 }
11963 #ifdef CK_SSL
11964             }
11965 #endif /* CK_SSL */
11966             if (d <= 0)
11967               break;
11968         }
11969 #endif  /* VMS */
11970
11971         debug(F111,"doftpsend2 XX zxin",ckltoa(n),ffc);
11972         if (n < 0)
11973           fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
11974         if (d < 0 || (d = secure_flush(dout)) < 0) {
11975             if (d == -1 && errno && errno != EPIPE)
11976               perror("netout");
11977             ftpsnd.bytes = -1;
11978         }
11979         break;
11980
11981       case FTT_ASC:                     /* Text mode */
11982 #ifndef NOCSETS
11983         if (ftpsnd.xlate) {             /* With translation */
11984             initxlate(ftpsnd.incs,ftpsnd.outcs);
11985             while (!cancelfile) {
11986                 if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
11987                 if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
11988             }
11989         } else {
11990 #endif /* NOCSETS */
11991             /* Text mode, no translation */
11992             while (((c = zminchar()) > -1) && !cancelfile) {
11993                 ffc++;
11994                 if (xxout(c) < 0)
11995                   break;
11996             }
11997             d = 0;
11998 #ifndef NOCSETS
11999         }
12000 #endif /* NOCSETS */
12001         if (dout == -1 || (d = secure_flush(dout)) < 0) {
12002             if (d == -1 && errno && errno != EPIPE)
12003               perror("netout");
12004             ftpsnd.bytes = -1;
12005         }
12006         break;
12007     }
12008     tfc += ffc;                         /* Total file chars */
12009 #ifdef GFTIMER
12010     fpfsecs = gftimer();
12011 #endif /* GFTIMER */
12012     zclose(ZIFILE);                     /* Close input file */
12013 #ifdef PIPESEND
12014     if (sndfilter)                      /* Undo this (it's per file) */
12015       pipesend = 0;
12016 #endif /* PIPESEND */
12017
12018 #ifdef CK_SSL
12019         if (ssl_ftp_data_active_flag) {
12020             SSL_shutdown(ssl_ftp_data_con);
12021             SSL_free(ssl_ftp_data_con);
12022             ssl_ftp_data_active_flag = 0;
12023             ssl_ftp_data_con = NULL;
12024         }
12025 #endif /* CK_SSL */
12026
12027 #ifdef TCPIPLIB
12028     socket_close(dout);                 /* Close data connection */
12029 #else /* TCPIPLIB */
12030 #ifdef USE_SHUTDOWN
12031     shutdown(dout, 1+1);
12032 #endif /* USE_SHUTDOWN */
12033     close(dout);
12034 #endif /* TCPIPLIB */
12035     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
12036     signal(SIGINT, ftpsnd.oldintr);            /* Put back interrupts */
12037 #ifdef SIGPIPE
12038     if (ftpsnd.oldintp)
12039       signal(SIGPIPE, ftpsnd.oldintp);
12040 #endif /* SIGPIPE */
12041     if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
12042         debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
12043         ftpsndret = -1;
12044 #ifdef NTSIG
12045         ckThreadEnd(threadinfo);
12046 #endif /* NTSIG */
12047         return;
12048     } else if (cancelfile) {
12049         debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
12050         ftpsndret = -1;
12051 #ifdef NTSIG
12052         ckThreadEnd(threadinfo);
12053 #endif /* NTSIG */
12054         return;
12055     }
12056     debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
12057     ftpsndret = 0;
12058 #ifdef NTSIG
12059      ckThreadEnd(threadinfo);
12060 #endif /* NTSIG */
12061 }
12062
12063 static int
12064 sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
12065     char *cmd, *local, *remote; int xlate, incs, outcs, restart;
12066 {
12067     if (!remote) remote = "";           /* Check args */
12068     if (!*remote) remote = local;
12069     if (!local) local = "";
12070     if (!*local) return(-1);
12071     if (!cmd) cmd = "";
12072     if (!*cmd) cmd = "STOR";
12073
12074     debug(F111,"ftp sendrequest restart",local,restart);
12075
12076     nout = 0;                           /* Init output buffer count */
12077     ftpsnd.bytes = 0;                   /* File input byte count */
12078     dout = -1;
12079
12080 #ifdef FTP_PROXY
12081     if (proxy) {
12082         proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
12083         return(0);
12084     }
12085 #endif /* FTP_PROXY */
12086
12087     changetype(ftp_typ,0);              /* Change type for this file */
12088
12089     ftpsnd.oldintr = NULL;              /* Set up interrupt handler */
12090     ftpsnd.oldintp = NULL;
12091     ftpsnd.restart = restart;
12092     ftpsnd.xlate = xlate;
12093     ftpsnd.lmode = "wb";
12094
12095 #ifdef PIPESEND                         /* Use Kermit API for file i/o... */
12096     if (sndfilter) {
12097         char * p = NULL, * q;
12098 #ifndef NOSPL
12099         int n = CKMAXPATH;
12100         if (cmd_quoting && (p = (char *) malloc(n + 1))) {
12101             q = p;
12102             debug(F110,"sendrequest pipesend filter",sndfilter,0);
12103             zzstring(sndfilter,&p,&n);
12104             debug(F111,"sendrequest pipename",q,n);
12105             if (n <= 0) {
12106                 printf("?Sorry, send filter + filename too long, %d max.\n",
12107                        CKMAXPATH
12108                        );
12109                 free(q);
12110                 return(-1);
12111             }
12112             ckstrncpy(filnam,q,CKMAXPATH+1);
12113             free(q);
12114             local = filnam;
12115         }
12116 #endif /* NOSPL */
12117     }
12118
12119     if (sndfilter)                      /* If sending thru a filter */
12120       pipesend = 1;                     /* set this for open and i/o */
12121 #endif /* PIPESEND */
12122     
12123 #ifdef VMS
12124     debug(F101,"XXX before openi binary","",binary);
12125     debug(F101,"XXX before openi ftp_typ","",ftp_typ);
12126 #endif  /* VMS */
12127
12128     if (openi(local) == 0)              /* Try to open the input file */
12129       return(-1);
12130
12131 #ifdef VMS
12132     debug(F101,"XXX after openi binary","",binary);
12133     debug(F101,"XXX after openi ftp_typ","",ftp_typ);
12134     if (!forcetype) {
12135         if (binary != ftp_typ) {        /* VMS zopeni() sets binary */
12136             debug(F101,"XXX changing type","",binary);
12137             doftptyp(binary);
12138             debug(F101,"XXX after doftptyp","",ftp_typ);
12139
12140             /* **** */
12141             if (displa && fdispla) {    /* Update file type display */
12142                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,local);
12143             }
12144         }
12145     }
12146 #endif  /* VMS */
12147     ftpsndret = 0;
12148     ftpsnd.incs = incs;
12149     ftpsnd.outcs = outcs;
12150     ftpsnd.cmd = cmd;
12151     ftpsnd.local = local;
12152     ftpsnd.remote = remote;
12153     ftpsnd.oldintr = signal(SIGINT, cancelsend);
12154     havesigint = 0;
12155
12156     if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
12157       return(-1);
12158     if (ftpsndret < 0)
12159       return(-1);
12160     if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
12161       return(-1);
12162
12163     return(ftpsndret);
12164 }
12165
12166 static sigtype
12167 cancelrecv(sig) int sig; {
12168     havesigint++;
12169     cancelfile = 0;
12170     cancelgroup++;
12171     secure_getc(0,1);                   /* Initialize net input buffers */
12172     printf(" Canceling...\n");
12173     debug(F100,"ftp cancelrecv caught SIGINT","",0);
12174     fflush(stdout);
12175     if (fp_nml) {
12176         if (fp_nml != stdout)
12177           fclose(fp_nml);
12178         fp_nml = NULL;
12179     }
12180 #ifndef OS2
12181     longjmp(recvcancel, 1);
12182 #else
12183     PostCtrlCSem();
12184 #endif /* OS2 */
12185 }
12186
12187 /* Argumentless front-end for secure_getc() */
12188
12189 static int
12190 netgetc() {
12191     return(secure_getc(globaldin,0));
12192 }
12193
12194 /* Returns -1 on failure, 0 on success, 1 if file skipped */
12195
12196 /*
12197   Sets ftpcode < 0 on failure if failure reason is not server reply code:
12198     -1: interrupted by user.
12199     -2: error opening or writing output file (reason in errno).
12200     -3: failure to make data connection.
12201     -4: network read error (reason in errno).
12202 */
12203
12204 struct xx_ftprecv {
12205     int reply;
12206     int fcs;
12207     int rcs;
12208     int recover;
12209     int xlate;
12210     int din;
12211     int is_retr;
12212     sig_t oldintr, oldintp;
12213     char * cmd;
12214     char * local;
12215     char * remote;
12216     char * lmode;
12217     char * pipename;
12218     int    tcrflag;
12219     CK_OFF_T localsize;
12220 };
12221 static struct xx_ftprecv ftprecv;
12222
12223 static int ftprecvret = 0;
12224
12225 static VOID
12226 #ifdef CK_ANSIC
12227 failftprecv(VOID * threadinfo)
12228 #else
12229 failftprecv(threadinfo) VOID * threadinfo;
12230 #endif /* CK_ANSIC */
12231 {
12232 #ifdef NTSIG
12233     if (threadinfo) {                   /* Thread local storage... */
12234         TlsSetValue(TlsIndex,threadinfo);
12235         debug(F100, "docmdfile called with threadinfo block","", 0);
12236     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12237 #endif /* NTSIG */
12238
12239 #ifdef CK_LOGIN
12240 #ifdef IKSD
12241 #ifdef NT
12242     if (inserver)
12243       setntcreds();
12244 #endif /* NT */
12245 #endif /* IKSD */
12246 #endif /* CK_LOGIN */
12247
12248     while (cpend) {
12249         ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12250     }
12251     if (data >= 0) {
12252 #ifdef CK_SSL
12253         if (ssl_ftp_data_active_flag) {
12254             SSL_shutdown(ssl_ftp_data_con);
12255             SSL_free(ssl_ftp_data_con);
12256             ssl_ftp_data_active_flag = 0;
12257             ssl_ftp_data_con = NULL;
12258         }
12259 #endif /* CK_SSL */
12260 #ifdef TCPIPLIB
12261         socket_close(data);
12262 #else /* TCPIPLIB */
12263 #ifdef USE_SHUTDOWN
12264         shutdown(data, 1+1);
12265 #endif /* USE_SHUTDOWN */
12266         close(data);
12267 #endif /* TCPIPLIB */
12268         data = -1;
12269         globaldin = -1;
12270     }
12271     if (ftprecv.oldintr)
12272       signal(SIGINT, ftprecv.oldintr);
12273     ftpcode = -1;
12274     ftprecvret = -1;
12275
12276 #ifndef OS2
12277     /* TEST ME IN K95 */
12278     if (havesigint) {
12279         havesigint = 0;
12280         debug(F100,"ftp failftprecv chain to trap()...","",0);
12281         if (ftprecv.oldintr != SIG_IGN)
12282           (*ftprecv.oldintr)(SIGINT);
12283         /* NOTREACHED (I hope!) */
12284         debug(F100,"ftp failftprecv return from trap()...","",0);
12285     }
12286 #endif /* OS2 */
12287     return;
12288 }
12289
12290 static VOID
12291 #ifdef CK_ANSIC
12292 doftprecv(VOID * threadinfo)
12293 #else
12294 doftprecv(threadinfo) VOID * threadinfo;
12295 #endif /* CK_ANSIC */
12296 {
12297 #ifdef NTSIG
12298     if (threadinfo) {                   /* Thread local storage... */
12299         TlsSetValue(TlsIndex,threadinfo);
12300         debug(F100, "docmdfile called with threadinfo block","", 0);
12301     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12302 #endif /* NTSIG */
12303 #ifdef CK_LOGIN
12304 #ifdef IKSD
12305 #ifdef NT
12306     if (inserver)
12307       setntcreds();
12308 #endif /* NT */
12309 #endif /* IKSD */
12310 #endif /* CK_LOGIN */
12311
12312 #ifndef COMMENT
12313     if (!out2screen && !ftprecv.pipename) {
12314         int x;
12315         char * local;
12316         local = ftprecv.local;
12317         x = zchko(local);
12318         if (x < 0) {
12319             if ((!dpyactive || ftp_deb))
12320               fprintf(stderr,
12321                       "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
12322             signal(SIGINT, ftprecv.oldintr);
12323             ftpcode = -2;
12324             ftprecvret = -1;
12325 #ifdef NTSIG
12326             ckThreadEnd(threadinfo);
12327 #endif /* NTSIG */
12328             return;
12329         }
12330     }
12331 #endif /* COMMENT */
12332     changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
12333     if (initconn()) {                   /* Initialize the data connection */
12334         signal(SIGINT, ftprecv.oldintr);
12335         ftpcode = -1;
12336         ftprecvret = -3;
12337 #ifdef NTSIG
12338         ckThreadEnd(threadinfo);
12339 #endif /* NTSIG */
12340         return;
12341     }
12342     secure_getc(0,1);                   /* Initialize net input buffers */
12343     ftprecvret = 0;
12344
12345 #ifdef NTSIG
12346     ckThreadEnd(threadinfo);
12347 #endif /* NTSIG */
12348 }
12349
12350 static VOID
12351 #ifdef CK_ANSIC
12352 failftprecv2(VOID * threadinfo)
12353 #else
12354 failftprecv2(threadinfo) VOID * threadinfo;
12355 #endif /* CK_ANSIC */
12356 {
12357 #ifdef NTSIG
12358     if (threadinfo) {                   /* Thread local storage... */
12359         TlsSetValue(TlsIndex,threadinfo);
12360         debug(F100, "docmdfile called with threadinfo block","", 0);
12361     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12362 #endif /* NTSIG */
12363 #ifdef CK_LOGIN
12364 #ifdef IKSD
12365 #ifdef NT
12366     if (inserver)
12367       setntcreds();
12368 #endif /* NT */
12369 #endif /* IKSD */
12370 #endif /* CK_LOGIN */
12371
12372     /* Cancel using RFC959 recommended IP,SYNC sequence  */
12373
12374     debug(F100,"ftp recvrequest CANCEL","",0);
12375 #ifdef GFTIMER
12376     fpfsecs = gftimer();
12377 #endif /* GFTIMER */
12378 #ifdef SIGPIPE
12379     if (ftprecv.oldintp)
12380       signal(SIGPIPE, ftprecv.oldintr);
12381 #endif /* SIGPIPE */
12382     signal(SIGINT, SIG_IGN);
12383     if (!cpend) {
12384         ftpcode = -1;
12385         signal(SIGINT, ftprecv.oldintr);
12386         ftprecvret = -1;
12387 #ifdef NTSIG
12388         ckThreadEnd(threadinfo);
12389 #endif /* NTSIG */
12390         return;
12391     }
12392     cancel_remote(ftprecv.din);
12393
12394 #ifdef FTP_TIMEOUT
12395     if (ftp_timed_out && out2screen && !quiet)
12396       printf("\n?Timed out.\n");
12397 #endif  /* FTP_TIMEOUT */
12398
12399     if (ftpcode > -1)
12400       ftpcode = -1;
12401     if (data >= 0) {
12402 #ifdef CK_SSL
12403         if (ssl_ftp_data_active_flag) {
12404             SSL_shutdown(ssl_ftp_data_con);
12405             SSL_free(ssl_ftp_data_con);
12406             ssl_ftp_data_active_flag = 0;
12407             ssl_ftp_data_con = NULL;
12408         }
12409 #endif /* CK_SSL */
12410 #ifdef TCPIPLIB
12411         socket_close(data);
12412 #else /* TCPIPLIB */
12413 #ifdef USE_SHUTDOWN
12414         shutdown(data, 1+1);
12415 #endif /* USE_SHUTDOWN */
12416         close(data);
12417 #endif /* TCPIPLIB */
12418         data = -1;
12419         globaldin = -1;
12420     }
12421     if (!out2screen) {
12422         int x = 0;
12423         debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
12424         zclose(ZOFILE);
12425         switch (keep) {                 /* which is... */
12426           case SET_AUTO:                /* AUTO */
12427             if (curtype == FTT_ASC)     /* Delete file if TYPE A. */
12428               x = 1;
12429             break;
12430           case SET_OFF:                 /* DISCARD */
12431             x = 1;                      /* Delete file, period. */
12432             break;
12433           default:                      /* KEEP */
12434             break;
12435         }
12436         if (x) {
12437             x = zdelet(ftprecv.local);
12438             debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
12439         }
12440     }
12441     if (ftprecv.din) {
12442 #ifdef TCPIPLIB
12443         socket_close(ftprecv.din);
12444 #else /* TCPIPLIB */
12445 #ifdef USE_SHUTDOWN
12446         shutdown(ftprecv.din, 1+1);
12447 #endif /* USE_SHUTDOWN */
12448         close(ftprecv.din);
12449 #endif /* TCPIPLIB */
12450     }
12451     signal(SIGINT, ftprecv.oldintr);
12452     ftprecvret = -1;
12453
12454     if (havesigint) {
12455         havesigint = 0;
12456         debug(F100,"FTP failftprecv2 chain to trap()...","",0);
12457 #ifdef OS2
12458         debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
12459         PostCtrlCSem();
12460 #else /* OS2 */
12461         if (ftprecv.oldintr != SIG_IGN)
12462           (*ftprecv.oldintr)(SIGINT);
12463         /* NOTREACHED (I hope!) */
12464         debug(F100,"ftp failftprecv2 return from trap()...","",0);
12465 #endif /* OS2 */
12466     }
12467 }
12468
12469 static VOID
12470 #ifdef CK_ANSIC
12471 doftprecv2(VOID * threadinfo)
12472 #else
12473 doftprecv2(threadinfo) VOID * threadinfo;
12474 #endif /* CK_ANSIC */
12475 {
12476     register int c, d;
12477     CK_OFF_T bytes = (CK_OFF_T)0;
12478     int bare_lfs = 0;
12479     int blksize = 0;
12480     ULONG start = 0L, stop;
12481     char * p;
12482     static char * rcvbuf = NULL;
12483     static int rcvbufsiz = 0;
12484 #ifdef CK_URL
12485     char newname[CKMAXPATH+1];          /* For file dialog */
12486 #endif /* CK_URL */
12487     extern int adl_ask;
12488
12489 #ifdef FTP_TIMEOUT
12490     ftp_timed_out = 0;
12491 #endif  /* FTP_TIMEOUT */
12492
12493     ftprecv.din = -1;
12494 #ifdef NTSIG
12495     if (threadinfo) {                   /* Thread local storage... */
12496         TlsSetValue(TlsIndex,threadinfo);
12497         debug(F100, "docmdfile called with threadinfo block","", 0);
12498     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12499 #endif /* NTSIG */
12500 #ifdef CK_LOGIN
12501 #ifdef IKSD
12502 #ifdef NT
12503     if (inserver)
12504       setntcreds();
12505 #endif /* NT */
12506 #endif /* IKSD */
12507 #endif /* CK_LOGIN */
12508
12509     if (ftprecv.recover) {                      /* Initiate recovery */
12510         x = ftpcmd("REST",ckfstoa(ftprecv.localsize),-1,-1,ftp_vbm);
12511         debug(F111,"ftp reply","REST",x);
12512         if (x == REPLY_CONTINUE) {
12513             ftprecv.lmode = "ab";
12514             rs_len = ftprecv.localsize;
12515         } else {
12516             ftprecv.recover = 0;
12517         }
12518     }
12519     /* IMPORTANT: No FTP commands can come between REST and RETR! */
12520
12521     debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
12522
12523     /* Send the command and get reply */
12524     debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
12525     debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
12526
12527     if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
12528         != REPLY_PRELIM) {
12529         signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
12530         ftprecvret = -1;                /* ftpcode is set by ftpcmd() */
12531 #ifdef NTSIG
12532         ckThreadEnd(threadinfo);
12533 #endif /* NTSIG */
12534         return;
12535     }
12536     ftprecv.din = dataconn("r");        /* Good reply, open data connection */
12537     globaldin = ftprecv.din;            /* Global copy of file descriptor */
12538     if (ftprecv.din == -1) {            /* Check for failure */
12539         ftpcode = -3;                   /* Code for no data connection */
12540         ftprecvret = -1;
12541 #ifdef NTSIG
12542         ckThreadEnd(threadinfo);
12543 #endif /* NTSIG */
12544         return;
12545     }
12546 #ifdef CK_URL
12547     /* In K95 GUI put up a file box */
12548     if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
12549         int x;
12550         char * preface =
12551 "\r\nIncoming file from FTP server...\r\n\
12552 Please confirm output file specification or supply an alternative:";
12553
12554         x = uq_file(preface,            /* K95 GUI: Put up file box. */
12555                     NULL,
12556                     4,
12557                     NULL,
12558                     ftprecv.local ? ftprecv.local : ftprecv.remote,
12559                     newname,
12560                     CKMAXPATH+1
12561                     );
12562         if (x > 0) {
12563             ftprecv.local = newname;    /* Substitute user's file name */
12564             if (x == 2)                 /* And append if user said to */
12565               ftprecv.lmode = "ab";
12566         }
12567     }
12568 #endif /* CK_URL */
12569     x = 1;                              /* Output file open OK? */
12570     if (ftprecv.pipename) {             /* Command */
12571         x = zxcmd(ZOFILE,ftprecv.pipename);
12572         debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
12573     } else if (!out2screen) {           /* File */
12574         struct filinfo xx;
12575         xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
12576         xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
12577         /* Append or New */
12578         xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
12579         x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
12580         debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
12581     }
12582     if (x < 1) {                        /* Failure to open output file */
12583         if ((!dpyactive || ftp_deb))
12584           fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
12585         ftprecvret = -1;
12586 #ifdef NTSIG
12587         ckThreadEnd(threadinfo);
12588 #endif /* NTSIG */
12589         return;
12590     }
12591     blksize = FTP_BUFSIZ;               /* Allocate input buffer */
12592
12593     debug(F101,"ftp recvrequest blksize","",blksize);
12594     debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
12595
12596     if (rcvbufsiz < blksize) {          /* if necessary */
12597         if (rcvbuf) {
12598             free(rcvbuf);
12599             rcvbuf = NULL;
12600         }
12601         rcvbuf = (char *)malloc((unsigned)blksize);
12602         if (!rcvbuf) {
12603             debug(F100,"ftp get rcvbuf malloc failed","",0);
12604             ftpcode = -2;
12605 #ifdef ENOMEM
12606             errno = ENOMEM;
12607 #endif /* ENOMEM */
12608             if ((!dpyactive || ftp_deb))
12609               perror("malloc");
12610             rcvbufsiz = 0;
12611             ftprecvret = -1;
12612 #ifdef NTSIG
12613             ckThreadEnd(threadinfo);
12614 #endif /* NTSIG */
12615             return;
12616         }
12617         debug(F101,"ftp get rcvbuf malloc ok","",blksize);
12618         rcvbufsiz = blksize;
12619     }
12620     debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
12621
12622     ffc = (CK_OFF_T)0;                  /* Character counter */
12623     cps = oldcps = 0L;                  /* Thruput */
12624     start = gmstimer();                 /* Start time (msecs) */
12625 #ifdef GFTIMER
12626     rftimer();                          /* Start time (float) */
12627 #endif /* GFTIMER */
12628
12629     debug(F111,"ftp get type",ftprecv.local,curtype);
12630     debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
12631     switch (curtype) {
12632       case FTT_BIN:                     /* Binary mode */
12633       case FTT_TEN:                     /* TENEX mode */
12634         d = 0;
12635         while (1) {
12636             errno = 0;
12637             c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
12638             if (cancelfile) {
12639                 failftprecv2(threadinfo);
12640 #ifdef NTSIG
12641                 ckThreadEnd(threadinfo);
12642 #endif /* NTSIG */
12643                 return;
12644             }
12645             if (c < 1)
12646               break;
12647 #ifdef printf                           /* (What if it isn't?) */
12648             if (out2screen && !ftprecv.pipename) {
12649                 int i;
12650                 for (i = 0; i < c; i++)
12651                   printf("%c",rcvbuf[i]);
12652             } else
12653 #endif /* printf */
12654               {
12655                 register int i;
12656                 i = 0;
12657                 errno = 0;
12658                 while (i < c) {
12659                     if (zmchout(rcvbuf[i++]) < 0) {
12660                         d = i;
12661                         break;
12662                     }
12663                 }
12664             }
12665             bytes += c;
12666             ffc += c;
12667         }
12668 #ifdef FTP_TIMEOUT
12669         if (c == -3) {
12670             debug(F100,"ftp recvrequest timeout","",0); 
12671             bytes = (CK_OFF_T)-1;
12672             ftp_timed_out = 1;
12673             ftpcode = -3;
12674         } else
12675 #endif  /* FTP_TIMEOUT */
12676         if (c < 0) {
12677             debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
12678             if (c == -1 && errno != EPIPE)
12679               if ((!dpyactive || ftp_deb))
12680                 perror("netin");
12681             bytes = (CK_OFF_T)-1;
12682             ftpcode = -4;
12683         }
12684         if (d < c) {
12685             ftpcode = -2;
12686             if ((!dpyactive || ftp_deb)) {
12687                 char * p;
12688                 p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
12689                 if (d < 0)
12690                   fprintf(stderr,
12691                           "local(3): %s: %s\n", ftprecv.local, ck_errstr());
12692                 else
12693                   fprintf(stderr,
12694                           "%s: short write\n", ftprecv.local);
12695             }
12696         }
12697         break;
12698
12699       case FTT_ASC:                     /* Text mode */
12700         debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
12701 #ifndef NOCSETS
12702         if (ftprecv.xlate) {
12703             int t;
12704 #ifdef CK_ANSIC
12705             int (*fn)(char);
12706 #else
12707             int (*fn)();
12708 #endif /* CK_ANSIC */
12709             debug(F110,"ftp recvrequest (data)","initxlate",0);
12710             initxlate(ftprecv.rcs,ftprecv.fcs);         /* (From,To) */
12711             if (ftprecv.pipename) {
12712                 fn = pipeout;
12713                 debug(F110,"ftp recvrequest ASCII","pipeout",0);
12714             } else {
12715                 fn = out2screen ? scrnout : putfil;
12716                 debug(F110,"ftp recvrequest ASCII",
12717                       out2screen ? "scrnout" : "putfil",0);
12718             }
12719             while (1) {
12720                 /* Get byte from net */
12721                 c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12722                 if (cancelfile) {
12723                     failftprecv2(threadinfo);
12724 #ifdef NTSIG
12725                     ckThreadEnd(threadinfo);
12726 #endif /* NTSIG */
12727                     return;
12728                 }
12729                 if (c0 < 0)
12730                   break;
12731                 /* Second byte from net */
12732                 c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12733                 if (cancelfile) {
12734                     failftprecv2(threadinfo);
12735 #ifdef NTSIG
12736                     ckThreadEnd(threadinfo);
12737 #endif /* NTSIG */
12738                     return;
12739                 }
12740                 if (c1 < 0)
12741                   break;
12742 #ifdef COMMENT
12743                 /* K95: Check whether we need this */
12744                 if (fileorder > 0)      /* Little Endian */
12745                   bytswap(&c0,&c1);     /* swap bytes*/
12746 #endif /* COMMENT */
12747
12748 #ifdef OS2
12749                 if ( out2screen &&            /* we're translating to UCS-2 */ 
12750                      !k95stdout && !inserver) /* for the real screen... */     
12751                 {
12752                     union {
12753                         USHORT ucs2;
12754                         UCHAR  bytes[2];
12755                     } output;
12756
12757                     output.bytes[0] = c1;
12758                     output.bytes[1] = c0;
12759
12760                     VscrnWrtUCS2StrAtt(VCMD,
12761                                        &output.ucs2,
12762                                        1,
12763                                        wherey[VCMD],
12764                                        wherex[VCMD],
12765                                        &colorcmd
12766                                        );
12767
12768                 } else 
12769 #endif /* OS2 */
12770                 {
12771                     if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12772                     if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12773                 }
12774             }
12775         } else {
12776 #endif /* NOCSETS */
12777             while (1) {
12778                 c = secure_getc(ftprecv.din,0);
12779                 if (cancelfile
12780 #ifdef FTP_TIMEOUT
12781                     || ftp_timed_out
12782 #endif  /* FTP_TIMEOUT */
12783                     ) {
12784                     failftprecv2(threadinfo);
12785 #ifdef NTSIG
12786                     ckThreadEnd(threadinfo);
12787 #endif /* NTSIG */
12788                     return;
12789                 }
12790                 if (c < 0 || c == EOF)
12791                   break;
12792 #ifdef UNIX
12793                 /* Record format conversion for Unix */
12794                 /* SKIP THIS FOR WINDOWS! */
12795                 if (c == '\n')
12796                   bare_lfs++;
12797                 while (c == '\r') {
12798                     bytes++;
12799                     if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
12800                         ftprecv.tcrflag) {
12801                         if (cancelfile) {
12802                             failftprecv2(threadinfo);
12803 #ifdef NTSIG
12804                             ckThreadEnd(threadinfo);
12805 #endif /* NTSIG */
12806                             return;
12807                         }
12808                         if (c < 0 || c == EOF)
12809                           goto break2;
12810                         if (c == '\0') {
12811                             bytes++;
12812                             goto contin2;
12813                         }
12814                     }
12815                 }
12816                 if (c < 0)
12817                   break;
12818 #endif /* UNX */
12819
12820                 if (out2screen && !ftprecv.pipename)
12821 #ifdef printf
12822                   printf("%c",(char)c);
12823 #else
12824                   putchar((char)c);
12825 #endif /* printf */
12826                 else
12827                   if ((d = zmchout(c)) < 0)
12828                     break;
12829                 bytes++;
12830                 ffc++;
12831               contin2:
12832                 ;
12833             }
12834           break2:
12835             if (bare_lfs && (!dpyactive || ftp_deb)) {
12836                 printf("WARNING! %d bare linefeeds received in ASCII mode\n",
12837                        bare_lfs);
12838                 printf("File might not have transferred correctly.\n");
12839             }
12840             if (ftprecv.din == -1) {
12841                 bytes = (CK_OFF_T)-1;
12842             }
12843             if (c == -2)
12844               bytes = (CK_OFF_T)-1;
12845             break;
12846 #ifndef NOCSETS
12847         }
12848 #endif /* NOCSETS */
12849     }
12850     if (ftprecv.pipename || !out2screen) {
12851         zclose(ZOFILE);                 /* Close the file */
12852         debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
12853         if (ftpcode < 0) {              /* If download failed */
12854             int x = 0;
12855             switch (keep) {             /* which is... */
12856               case SET_AUTO:            /* AUTO */
12857                 if (curtype == FTT_ASC) /* Delete file if TYPE A. */
12858                   x = 1;
12859                 break;
12860               case SET_OFF:             /* DISCARD */
12861                 x = 1;                  /* Delete file, period. */
12862                 break;
12863               default:                  /* KEEP */
12864                 break;
12865             }
12866             if (x) {
12867                 x = zdelet(ftprecv.local);
12868                 debug(F111,"ftp get delete incomplete",ftprecv.local,x);
12869             }
12870         }
12871     }
12872     signal(SIGINT, ftprecv.oldintr);
12873 #ifdef SIGPIPE
12874     if (ftprecv.oldintp)
12875       signal(SIGPIPE, ftprecv.oldintp);
12876 #endif /* SIGPIPE */
12877     stop = gmstimer();
12878 #ifdef GFTIMER
12879     fpfsecs = gftimer();
12880 #endif /* GFTIMER */
12881     tfc += ffc;
12882
12883 #ifdef TCPIPLIB
12884     socket_close(ftprecv.din);
12885 #else /* TCPIPLIB */
12886 #ifdef USE_SHUTDOWN
12887     shutdown(ftprecv.din, 1+1);
12888 #endif /* USE_SHUTDOWN */
12889     close(ftprecv.din);
12890 #endif /* TCPIPLIB */
12891     ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12892     ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || 
12893                    ftprecv.reply == REPLY_ERROR) ? -1 : 0);
12894 #ifdef NTSIG
12895      ckThreadEnd(threadinfo);
12896 #endif /* NTSIG */
12897 }
12898
12899 static int
12900 recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
12901             xlate, fcs, rcs)
12902     char *cmd, *local, *remote, *lmode, *pipename;
12903     int printnames, recover, xlate, fcs, rcs;
12904 {
12905 #ifdef NT
12906     struct _stat stbuf;
12907 #else /* NT */
12908     struct stat stbuf;
12909 #endif /* NT */
12910
12911 #ifdef DEBUG
12912     if (deblog) {
12913         debug(F111,"ftp recvrequest cmd",cmd,recover);
12914         debug(F110,"ftp recvrequest local ",local,0);
12915         debug(F111,"ftp recvrequest remote",remote,ftp_typ);
12916         debug(F110,"ftp recvrequest pipename ",pipename,0);
12917         debug(F101,"ftp recvrequest xlate","",xlate);
12918         debug(F101,"ftp recvrequest fcs","",fcs);
12919         debug(F101,"ftp recvrequest rcs","",rcs);
12920     }
12921 #endif /* DEBUG */
12922
12923     ftprecv.localsize = (CK_OFF_T)0;
12924
12925     if (remfile) {                      /* See remcfm(), remtxt() */
12926         if (rempipe) {
12927             pipename = remdest;
12928         } else {
12929             local = remdest;
12930             if (remappd) lmode = "ab";
12931         }
12932     }
12933     out2screen = 0;
12934     if (!cmd) cmd = "";                 /* Core dump prevention */
12935     if (!remote) remote = "";
12936     if (!lmode) lmode = "";
12937
12938     if (pipename) {                     /* No recovery for pipes. */
12939         recover = 0;
12940         if (!local)
12941           local = pipename;
12942     } else {
12943         if (!local)                     /* Output to screen? */
12944           local = "-";
12945         out2screen = !strcmp(local,"-");
12946     }
12947     debug(F101,"ftp recvrequest out2screen","",out2screen);
12948
12949 #ifdef OS2
12950     if ( ftp_xla && out2screen && !k95stdout && !inserver )
12951         fcs = FC_UCS2;
12952 #endif /* OS2 */
12953
12954     if (out2screen)                     /* No recovery to screen */
12955       recover = 0;
12956     if (!ftp_typ)                       /* No recovery in text mode */
12957       recover = 0;
12958     ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
12959
12960     if (!ftprecv.is_retr)               /* No recovery except for RETRieve */
12961       recover = 0;
12962
12963 #ifdef COMMENT
12964     if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
12965         if (recursive && ckstrchr(local,'/')) {
12966             
12967         }
12968     }
12969 #endif /* COMMENT */
12970
12971     ftprecv.localsize = (CK_OFF_T)0;    /* Local file size */
12972     rs_len = (CK_OFF_T)0;               /* Recovery point */
12973
12974     debug(F101,"ftp recvrequest recover","",recover);
12975     if (recover) {                      /* Recovering... */
12976         if (stat(local, &stbuf) < 0) {  /* Can't stat local file */
12977             debug(F101,"ftp recvrequest recover stat failed","",errno);
12978             recover = 0;                /* So cancel recovery */
12979         } else {                        /* Have local file info */
12980             ftprecv.localsize = stbuf.st_size;  /* Get size */
12981             /* Remote file smaller than local */
12982             if (fsize < ftprecv.localsize) {
12983                 debug(F101,"ftp recvrequest recover remote smaller","",fsize);
12984                 recover = 0;            /* Recovery can't work */
12985             } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
12986                 debug(F111,"ftp recvrequest recover equal size",
12987                       remote,ftprecv.localsize);
12988                 return(1);
12989             }
12990 #ifdef COMMENT
12991 /*
12992   The problem here is that the original partial file never got its date
12993   set, either because FTP DATES was OFF, or because the partial file was
12994   downloaded by some other program that doesn't set local file dates, or
12995   because Kermit only sets the file's date when the download was complete
12996   and successful.  In all these cases, the local file has a later time
12997   than the remote.
12998 */
12999             if (recover) {              /* Remote is bigger */
13000                 x = chkmodtime(local,remote,0); /* Check file dates */
13001                 debug(F111,"ftp recvrequest chkmodtime",remote,x);
13002                 if (x != 1)             /* Dates must be equal! */
13003                   recover = 0;          /* If not, get whole file */
13004             }
13005 #endif /* COMMENT */
13006         }
13007         debug(F111,"ftp recvrequest recover",remote,recover);
13008     }
13009
13010 #ifdef FTP_PROXY
13011     if (proxy && ftprecv.is_retr)
13012       return(proxtrans(cmd, local ? local : remote, remote));
13013 #endif /* FTP_PROXY */
13014
13015     ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
13016
13017     ftprecv.reply = 0;
13018     ftprecv.fcs = fcs;
13019     ftprecv.rcs = rcs;
13020     ftprecv.recover = recover;
13021     ftprecv.xlate = xlate;
13022     ftprecv.cmd = cmd;
13023     ftprecv.local = local;
13024     ftprecv.remote = remote;
13025     ftprecv.lmode = lmode;
13026     ftprecv.pipename = pipename;
13027     ftprecv.oldintp = NULL;
13028     ftpcode = 0;
13029
13030     havesigint = 0;
13031     ftprecv.oldintr = signal(SIGINT, cancelrecv);
13032     if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
13033       return -1;
13034
13035 #ifdef FTP_TIMEOUT
13036     debug(F111,"ftp recvrequest ftprecvret",remote,ftprecvret);
13037     debug(F111,"ftp recvrequest ftp_timed_out",remote,ftp_timed_out);
13038     if (ftp_timed_out)
13039       ftprecvret = -1;
13040 #endif  /* FTP_TIMEOUT */
13041
13042     if (ftprecvret < 0)
13043       return -1;
13044
13045     if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
13046       return -1;
13047     return ftprecvret;
13048 }
13049
13050 /*
13051  * Need to start a listen on the data channel before we send the command,
13052  * otherwise the server's connect may fail.
13053  */
13054 static int
13055 initconn() {
13056     register char *p, *a;
13057     int result, tmpno = 0;
13058     int on = 1;
13059     GSOCKNAME_T len;
13060
13061 #ifndef NO_PASSIVE_MODE
13062     int a1,a2,a3,a4,p1,p2;
13063
13064     if (passivemode) {
13065         data = socket(AF_INET, SOCK_STREAM, 0);
13066         globaldin = data;
13067         if (data < 0) {
13068             perror("ftp: socket");
13069             return(-1);
13070         }
13071         if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13072             printf("Passive mode refused\n");
13073             passivemode = 0;
13074             return(initconn());
13075         }
13076 /*
13077   Now we have a string of comma-separated one-byte unsigned integer values,
13078   The first four are the an IP address.  The fifth is the MSB of the port
13079   number, the sixth is the LSB.  From that we can make a sockaddr_in.
13080 */
13081         if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
13082             printf("Passive mode address scan failure\n");
13083             return(-1);
13084         };
13085 #ifndef NOHTTP
13086         if (tcp_http_proxy) {
13087 #ifdef OS2
13088             char * agent = "Kermit 95"; /* Default user agent */
13089 #else
13090             char * agent = "C-Kermit";
13091 #endif /* OS2 */
13092             register struct hostent *hp = 0;
13093             struct servent *destsp;
13094             char host[512], *p, *q;
13095 #ifdef IP_TOS
13096 #ifdef IPTOS_THROUGHPUT
13097             int tos;
13098 #endif /* IPTOS_THROUGHPUT */
13099 #endif /* IP_TOS */
13100             int s;
13101 #ifdef DEBUG
13102             extern int debtim;
13103             int xdebtim;
13104             xdebtim = debtim;
13105             debtim = 1;
13106 #endif /* DEBUG */
13107
13108             ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
13109                       ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
13110                       NULL,NULL,NULL
13111                       );
13112             memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
13113             for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
13114               *q = *p;
13115             *q = '\0';
13116
13117             hisctladdr.sin_addr.s_addr = inet_addr(host);
13118             if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
13119             {
13120                 debug(F110,"initconn A",host,0);
13121                 hisctladdr.sin_family = AF_INET;
13122             } else {
13123                 debug(F110,"initconn B",host,0);
13124                 hp = gethostbyname(host);
13125 #ifdef HADDRLIST
13126                 hp = ck_copyhostent(hp); /* make safe copy that won't change */
13127 #endif /* HADDRLIST */
13128                 if (hp == NULL) {
13129                     fprintf(stderr, "ftp: %s: Unknown host\n", host);
13130                     ftpcode = -1;
13131 #ifdef DEBUG
13132                     debtim = xdebtim;
13133 #endif /* DEBUG */
13134                     return(0);
13135                 }
13136                 hisctladdr.sin_family = hp->h_addrtype;
13137 #ifdef HADDRLIST
13138                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
13139                        sizeof(hisctladdr.sin_addr));
13140 #else /* HADDRLIST */
13141                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
13142                        sizeof(hisctladdr.sin_addr));
13143 #endif /* HADDRLIST */
13144             }
13145             data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13146             debug(F101,"initconn socket","",data);
13147             if (data < 0) {
13148                 perror("ftp: socket");
13149                 ftpcode = -1;
13150 #ifdef DEBUG
13151                 debtim = xdebtim;
13152 #endif /* DEBUG */
13153                 return(0);
13154             }
13155             if (*p == ':')
13156               p++;
13157             else
13158               p = "http";
13159
13160             destsp = getservbyname(p,"tcp");
13161             if (destsp)
13162               hisctladdr.sin_port = destsp->s_port;
13163             else if (p)
13164               hisctladdr.sin_port = htons(atoi(p));
13165             else
13166               hisctladdr.sin_port = htons(80);
13167             errno = 0;
13168 #ifdef HADDRLIST
13169             debug(F100,"initconn HADDRLIST","",0);
13170             while
13171 #else
13172             debug(F100,"initconn no HADDRLIST","",0);
13173             if
13174 #endif /* HADDRLIST */
13175               (connect(data, (struct sockaddr *)&hisctladdr,
13176                        sizeof (hisctladdr)) < 0) {
13177                   debug(F101,"initconn connect failed","",errno);
13178 #ifdef HADDRLIST
13179                   if (hp && hp->h_addr_list[1]) {
13180                       int oerrno = errno;
13181
13182                       fprintf(stderr,
13183                               "ftp: connect to address %s: ",
13184                               inet_ntoa(hisctladdr.sin_addr)
13185                               );
13186                       errno = oerrno;
13187                       perror("ftphookup");
13188                       hp->h_addr_list++;
13189                       memcpy((char *)&hisctladdr.sin_addr,
13190                              hp->h_addr_list[0],
13191                              sizeof(hisctladdr.sin_addr));
13192                       fprintf(stdout, "Trying %s...\n",
13193                               inet_ntoa(hisctladdr.sin_addr));
13194 #ifdef TCPIPLIB
13195                       socket_close(data);
13196 #else /* TCPIPLIB */
13197                       close(data);
13198 #endif /* TCPIPLIB */
13199                       data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13200                       if (data < 0) {
13201                           perror("ftp: socket");
13202                           ftpcode = -1;
13203 #ifdef DEBUG
13204                           debtim = xdebtim;
13205 #endif /* DEBUG */
13206                           return(0);
13207                       }
13208                       continue;
13209                   }
13210 #endif /* HADDRLIST */
13211                   perror("ftp: connect");
13212                   ftpcode = -1;
13213                   goto bad;
13214               }
13215             if (http_connect(data,
13216                              tcp_http_proxy_agent ?
13217                                tcp_http_proxy_agent :
13218                                  agent,
13219                              NULL,
13220                              tcp_http_proxy_user,
13221                              tcp_http_proxy_pwd,
13222                              0,
13223                              proxyhost
13224                              ) < 0) {
13225 #ifdef TCPIPLIB
13226                 socket_close(data);
13227 #else /* TCPIPLIB */
13228                 close(data);
13229 #endif /* TCPIPLIB */
13230                 perror("ftp: connect");
13231                 ftpcode = -1;
13232                 goto bad;
13233             }
13234         } else
13235 #endif /* NOHTTP */
13236         {
13237             data_addr.sin_family = AF_INET;
13238             data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
13239             data_addr.sin_port = htons((p1<<8)|p2);
13240
13241             if (connect(data,
13242                         (struct sockaddr *)&data_addr,
13243                         sizeof(data_addr)) < 0
13244                 ) {
13245                 perror("ftp: connect");
13246                 return(-1);
13247             }
13248         }
13249         debug(F100,"initconn connect ok","",0);
13250 #ifdef IP_TOS
13251 #ifdef IPTOS_THROUGHPUT
13252         on = IPTOS_THROUGHPUT;
13253         if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13254           perror("ftp: setsockopt TOS (ignored)");
13255 #endif /* IPTOS_THROUGHPUT */
13256 #endif /* IP_TOS */
13257         memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
13258         return(0);
13259     }
13260 #endif /* NO_PASSIVE_MODE */
13261
13262   noport:
13263     memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
13264     if (sendport)
13265       data_addr.sin_port = 0;   /* let system pick one */
13266     if (data != -1) {
13267 #ifdef TCPIPLIB
13268         socket_close(data);
13269 #else /* TCPIPLIB */
13270 #ifdef USE_SHUTDOWN
13271         shutdown(data, 1+1);
13272 #endif /* USE_SHUTDOWN */
13273         close(data);
13274 #endif /* TCPIPLIB */
13275     }
13276     data = socket(AF_INET, SOCK_STREAM, 0);
13277     globaldin = data;
13278     if (data < 0) {
13279         perror("ftp: socket");
13280         if (tmpno)
13281           sendport = 1;
13282         return(-1);
13283     }
13284     if (!sendport) {
13285         if (setsockopt(data,
13286                        SOL_SOCKET,
13287                        SO_REUSEADDR,
13288                        (char *)&on,
13289                        sizeof (on)
13290                        ) < 0
13291             ) {
13292             perror("ftp: setsockopt (reuse address)");
13293             goto bad;
13294         }
13295     }
13296     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
13297         perror("ftp: bind");
13298         goto bad;
13299     }
13300     len = sizeof (data_addr);
13301     if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
13302         perror("ftp: getsockname");
13303         goto bad;
13304     }
13305     if (listen(data, 1) < 0) {
13306         perror("ftp: listen");
13307         goto bad;
13308     }
13309     if (sendport) {
13310         a = (char *)&data_addr.sin_addr;
13311         p = (char *)&data_addr.sin_port;
13312         ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
13313                   UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
13314                   UC(p[0]),",", UC(p[1]));
13315         result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
13316         if (result == REPLY_ERROR && sendport) {
13317             sendport = 0;
13318             tmpno = 1;
13319             goto noport;
13320         }
13321         return(result != REPLY_COMPLETE);
13322     }
13323     if (tmpno)
13324       sendport = 1;
13325 #ifdef IP_TOS
13326 #ifdef IPTOS_THROUGHPUT
13327     on = IPTOS_THROUGHPUT;
13328     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13329       perror("ftp: setsockopt TOS (ignored)");
13330 #endif
13331 #endif
13332     return(0);
13333   bad:
13334 #ifdef TCPIPLIB
13335     socket_close(data);
13336 #else /* TCPIPLIB */
13337 #ifdef USE_SHUTDOWN
13338     shutdown(data, 1+1);
13339 #endif /* USE_SHUTDOWN */
13340     close(data);
13341 #endif /* TCPIPLIB */
13342     data = -1;
13343     globaldin = data;
13344     if (tmpno)
13345       sendport = 1;
13346     return(-1);
13347 }
13348
13349 #ifdef CK_SSL
13350 static int
13351 ssl_dataconn() {
13352     if (ssl_ftp_data_con!=NULL) {       /* Do SSL */
13353         SSL_free(ssl_ftp_data_con);
13354         ssl_ftp_data_con=NULL;
13355     }
13356     ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
13357
13358     SSL_set_fd(ssl_ftp_data_con,data);
13359     SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
13360
13361     SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
13362
13363     if (ssl_debug_flag) {
13364         fprintf(stderr,"=>START SSL connect on DATA\n");
13365         fflush(stderr);
13366     }
13367     if (SSL_connect(ssl_ftp_data_con) <= 0) {
13368         static char errbuf[1024];
13369         ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
13370                   ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
13371         fprintf(stderr,"%s\n", errbuf);
13372         fflush(stderr);
13373 #ifdef TCPIPLIB
13374         socket_close(data);
13375 #else /* TCPIPLIB */
13376 #ifdef USE_SHUTDOWN
13377         shutdown(data, 1+1);
13378 #endif /* USE_SHUTDOWN */
13379         close(data);
13380 #endif /* TCPIPLIB */
13381         data = -1;
13382         globaldin = data;
13383         return(-1);
13384     } else {
13385         ssl_ftp_data_active_flag=1;
13386
13387         if (!ssl_certsok_flag &&
13388             (ssl_verify_flag & SSL_VERIFY_PEER) && /* JEA 2013-12-10 */
13389             !tls_is_krb5(2)) {
13390             char *subject = ssl_get_subject_name(ssl_ftp_data_con);
13391
13392             if (!subject) {
13393                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
13394                     debug(F110,"dataconn","[SSL _- FAILED]",0);
13395
13396                     ssl_ftp_data_active_flag = 0;
13397 #ifdef TCPIPLIB
13398                     socket_close(data);
13399 #else /* TCPIPLIB */
13400 #ifdef USE_SHUTDOWN
13401                     shutdown(data, 1+1);
13402 #endif /* USE_SHUTDOWN */
13403                     close(data);
13404 #endif /* TCPIPLIB */
13405                     data = -1;
13406                     globaldin = data;
13407                     return(-1);
13408                 } else {
13409                     if (!out2screen && displa && fdispla) {
13410                         ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13411                         /* fdispla = XYFD_B; */
13412                     }
13413
13414                     if (uq_ok(
13415           "Warning: Server didn't provide a certificate on data connection\n",
13416                                "Continue with file transfer? (Y/N)",
13417                               3,NULL,0) <= 0) {
13418                         debug(F110, "dataconn","[SSL - FAILED]",0);
13419                         ssl_ftp_data_active_flag = 0;
13420 #ifdef TCPIPLIB
13421                         socket_close(data);
13422 #else /* TCPIPLIB */
13423 #ifdef USE_SHUTDOWN
13424                         shutdown(data, 1+1);
13425 #endif /* USE_SHUTDOWN */
13426                         close(data);
13427 #endif /* TCPIPLIB */
13428                         data = -1;
13429                         globaldin = data;
13430                         return(-1);
13431                     }
13432                 }
13433             } else {
13434                 if (!out2screen && displa && fdispla == XYFD_C) {
13435                     ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13436                     /* fdispla = XYFD_B; */
13437                 }
13438
13439                 if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
13440                     debug(F110,"dataconn","[SSL - FAILED]",0);
13441                     ssl_ftp_data_active_flag = 0;
13442 #ifdef TCPIPLIB
13443                     socket_close(data);
13444 #else /* TCPIPLIB */
13445 #ifdef USE_SHUTDOWN
13446                     shutdown(data, 1+1);
13447 #endif /* USE_SHUTDOWN */
13448                     close(data);
13449 #endif /* TCPIPLIB */
13450                     data = -1;
13451                     globaldin = data;
13452                     return(-1);
13453                 }
13454             }
13455         }
13456         debug(F110,"dataconn","[SSL - OK]",0);
13457 #ifdef COMMENT
13458         /* This messes up the full screen file transfer display */
13459         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
13460 #endif /* COMMENT */
13461     }
13462     if (ssl_debug_flag) {
13463         fprintf(stderr,"=>DONE SSL connect on DATA\n");
13464         fflush(stderr);
13465     }
13466     return(data);
13467 }
13468 #endif /* CK_SSL */
13469
13470 static int
13471 dataconn(lmode) char *lmode; {
13472     int s;
13473 #ifdef IP_TOS
13474     int tos;
13475 #endif /* IP_TOS */
13476 #ifdef UCX50
13477     static u_int fromlen;
13478 #else
13479     static SOCKOPT_T fromlen;
13480 #endif /* UCX50 */
13481
13482     fromlen = sizeof(hisdataaddr);
13483
13484 #ifndef NO_PASSIVE_MODE
13485     if (passivemode) {
13486 #ifdef CK_SSL
13487         ssl_ftp_data_active_flag=0;
13488         if (ssl_ftp_active_flag &&
13489             (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13490           return(ssl_dataconn());
13491 #endif /* CK_SSL */
13492         return(data);
13493     }
13494 #endif /* NO_PASSIVE_MODE */
13495
13496     s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
13497     if (s < 0) {
13498         perror("ftp: accept");
13499 #ifdef TCPIPLIB
13500         socket_close(data);
13501 #else /* TCPIPLIB */
13502 #ifdef USE_SHUTDOWN
13503         shutdown(data, 1+1);
13504 #endif /* USE_SHUTDOWN */
13505         close(data);
13506 #endif /* TCPIPLIB */
13507         data = -1;
13508         globaldin = data;
13509         return(-1);
13510     }
13511 #ifdef TCPIPLIB
13512     socket_close(data);
13513 #else /* TCPIPLIB */
13514 #ifdef USE_SHUTDOWN
13515     shutdown(data, 1+1);
13516 #endif /* USE_SHUTDOWN */
13517     close(data);
13518 #endif /* TCPIPLIB */
13519     data = s;
13520     globaldin = data;
13521 #ifdef IP_TOS
13522 #ifdef IPTOS_THROUGHPUT
13523     tos = IPTOS_THROUGHPUT;
13524     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
13525       perror("ftp: setsockopt TOS (ignored)");
13526 #endif /* IPTOS_THROUGHPUT */
13527 #endif /* IP_TOS */
13528
13529 #ifdef CK_SSL
13530     ssl_ftp_data_active_flag=0;
13531     if (ssl_ftp_active_flag &&
13532         (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13533       return(ssl_dataconn());
13534 #endif /* CK_SSL */
13535     return(data);
13536 }
13537
13538 #ifdef FTP_PROXY
13539 static sigtype
13540 pscancel(sig) int sig; {
13541     cancelfile++;
13542 }
13543
13544 static VOID
13545 pswitch(flag) int flag; {
13546     extern int proxy;
13547     sig_t oldintr;
13548     static struct comvars {
13549         int connect;
13550         char name[MAXHOSTNAMELEN];
13551         struct sockaddr_in mctl;
13552         struct sockaddr_in hctl;
13553         FILE *in;
13554         FILE *out;
13555         int tpe;
13556         int curtpe;
13557         int cpnd;
13558         int sunqe;
13559         int runqe;
13560         int mcse;
13561         int ntflg;
13562         char nti[17];
13563         char nto[17];
13564         int mapflg;
13565         char mi[CKMAXPATH];
13566         char mo[CKMAXPATH];
13567         char *authtype;
13568         int clvl;
13569         int dlvl;
13570 #ifdef FTP_KRB4
13571         des_cblock session;
13572         des_key_schedule ftp_sched;
13573 #endif /* FTP_KRB4 */
13574 #ifdef FTP_GSSAPI
13575         gss_ctx_id_t gcontext;
13576 #endif /* GSSAPI */
13577     } proxstruct, tmpstruct;
13578     struct comvars *ip, *op;
13579
13580     cancelfile = 0;
13581     oldintr = signal(SIGINT, pscancel);
13582     if (flag) {
13583         if (proxy)
13584           return;
13585         ip = &tmpstruct;
13586         op = &proxstruct;
13587         proxy++;
13588     } else {
13589         if (!proxy)
13590           return;
13591         ip = &proxstruct;
13592         op = &tmpstruct;
13593         proxy = 0;
13594     }
13595     ip->connect = connected;
13596     connected = op->connect;
13597     if (ftp_host) {
13598         strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
13599         ip->name[MAXHOSTNAMELEN - 1] = '\0';
13600         ip->name[strlen(ip->name)] = '\0';
13601     } else
13602       ip->name[0] = 0;
13603     ftp_host = op->name;
13604     ip->hctl = hisctladdr;
13605     hisctladdr = op->hctl;
13606     ip->mctl = myctladdr;
13607     myctladdr = op->mctl;
13608     ip->in = csocket;
13609     csocket = op->in;
13610     ip->out = csocket;
13611     csocket = op->out;
13612     ip->tpe = ftp_typ;
13613     ftp_typ = op->tpe;
13614     ip->curtpe = curtype;
13615     curtype = op->curtpe;
13616     ip->cpnd = cpend;
13617     cpend = op->cpnd;
13618     ip->sunqe = ftp_usn;
13619     ftp_usn = op->sunqe;
13620     ip->mcse = mcase;
13621     mcase = op->mcse;
13622     ip->ntflg = ntflag;
13623     ntflag = op->ntflg;
13624     strncpy(ip->nti, ntin, 16);
13625     (ip->nti)[strlen(ip->nti)] = '\0';
13626     strcpy(ntin, op->nti);
13627     strncpy(ip->nto, ntout, 16);
13628     (ip->nto)[strlen(ip->nto)] = '\0';
13629     strcpy(ntout, op->nto);
13630     ip->mapflg = mapflag;
13631     mapflag = op->mapflg;
13632     strncpy(ip->mi, mapin, CKMAXPATH - 1);
13633     (ip->mi)[strlen(ip->mi)] = '\0';
13634     strcpy(mapin, op->mi);
13635     strncpy(ip->mo, mapout, CKMAXPATH - 1);
13636     (ip->mo)[strlen(ip->mo)] = '\0';
13637     strcpy(mapout, op->mo);
13638     ip->authtype = auth_type;
13639     auth_type = op->authtype;
13640     ip->clvl = ftp_cpl;
13641     ftp_cpl = op->clvl;
13642     ip->dlvl = ftp_dpl;
13643     ftp_dpl = op->dlvl;
13644     if (!ftp_cpl)
13645       ftp_cpl = FPL_CLR;
13646     if (!ftp_dpl)
13647       ftp_dpl = FPL_CLR;
13648 #ifdef FTP_KRB4
13649     memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
13650     memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
13651     memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
13652     memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
13653 #endif /* FTP_KRB4 */
13654 #ifdef FTP_GSSAPI
13655     ip->gcontext = gcontext;
13656     gcontext = op->gcontext;
13657 #endif /* GSSAPI */
13658     signal(SIGINT, oldintr);
13659     if (cancelfile) {
13660         cancelfile = 0;
13661         debug(F101,"pswitch cancelfile B","",cancelfile);
13662         (*oldintr)(SIGINT);
13663     }
13664 }
13665
13666 static sigtype
13667 cancelpt(sig) int sig; {
13668     printf("\n");
13669     fflush(stdout);
13670     ptabflg++;
13671     cancelfile = 0;
13672 #ifndef OS2
13673     longjmp(ptcancel, 1);
13674 #else
13675     PostCtrlCSem();
13676 #endif /* OS2 */
13677 }
13678
13679 void
13680 proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
13681     sig_t oldintr;
13682     int secndflag = 0, prox_type, nfnd;
13683     char *cmd2;
13684 #ifdef BSDSELECT
13685     fd_set mask;
13686 #endif /* BSDSELECT */
13687     sigtype cancelpt();
13688
13689     if (strcmp(cmd, "RETR"))
13690       cmd2 = "RETR";
13691     else
13692       cmd2 = unique ? "STOU" : "STOR";
13693     if ((prox_type = type) == 0) {
13694         if (servertype == SYS_UNIX && unix_proxy)
13695           prox_type = FTT_BIN;
13696         else
13697           prox_type = FTT_ASC;
13698     }
13699     if (curtype != prox_type)
13700       changetype(prox_type, 1);
13701     if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13702         printf("Proxy server does not support third party transfers.\n");
13703         return;
13704     }
13705     pswitch(0);
13706     if (!connected) {
13707         printf("No primary connection\n");
13708         pswitch(1);
13709         ftpcode = -1;
13710         return;
13711     }
13712     if (curtype != prox_type)
13713       changetype(prox_type, 1);
13714
13715     if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
13716         pswitch(1);
13717         return;
13718     }
13719
13720     /* Replace with calls to cc_execute() */
13721     if (setjmp(ptcancel))
13722       goto cancel;
13723     oldintr = signal(SIGINT, cancelpt);
13724     if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
13725         signal(SIGINT, oldintr);
13726         pswitch(1);
13727         return;
13728     }
13729     sleep(2000);
13730     pswitch(1);
13731     secndflag++;
13732     if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
13733       goto cancel;
13734     ptflag++;
13735     getreply(0,-1,-1,ftp_vbm,0);
13736     pswitch(0);
13737     getreply(0,-1,-1,ftp_vbm,0);
13738     signal(SIGINT, oldintr);
13739     pswitch(1);
13740     ptflag = 0;
13741     return;
13742
13743   cancel:
13744     signal(SIGINT, SIG_IGN);
13745     ptflag = 0;
13746     if (strcmp(cmd, "RETR") && !proxy)
13747       pswitch(1);
13748     else if (!strcmp(cmd, "RETR") && proxy)
13749       pswitch(0);
13750     if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
13751         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13752             pswitch(0);
13753             if (cpend)
13754               cancel_remote(0);
13755         }
13756         pswitch(1);
13757         if (ptabflg)
13758           ftpcode = -1;
13759         signal(SIGINT, oldintr);
13760         return;
13761     }
13762     if (cpend)
13763       cancel_remote(0);
13764     pswitch(!proxy);
13765     if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
13766         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13767             pswitch(0);
13768             if (cpend)
13769               cancel_remote(0);
13770             pswitch(1);
13771             if (ptabflg)
13772               ftpcode = -1;
13773             signal(SIGINT, oldintr);
13774             return;
13775         }
13776     }
13777     if (cpend)
13778       cancel_remote(0);
13779     pswitch(!proxy);
13780     if (cpend) {
13781 #ifdef BSDSELECT
13782         FD_ZERO(&mask);
13783         FD_SET(csocket, &mask);
13784         if ((nfnd = empty(&mask, 10)) <= 0) {
13785             if (nfnd < 0) {
13786                 perror("cancel");
13787             }
13788             if (ptabflg)
13789               ftpcode = -1;
13790             lostpeer();
13791         }
13792 #else /* BSDSELECT */
13793 #ifdef IBMSELECT
13794         if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
13795             if (nfnd < 0) {
13796                 perror("cancel");
13797             }
13798             if (ptabflg)
13799               ftpcode = -1;
13800             lostpeer();
13801         }
13802 #endif /* IBMSELECT */
13803 #endif /* BSDSELECT */
13804         getreply(0,-1,-1,ftp_vbm,0);
13805         getreply(0,-1,-1,ftp_vbm,0);
13806     }
13807     if (proxy)
13808       pswitch(0);
13809     pswitch(1);
13810     if (ptabflg)
13811       ftpcode = -1;
13812     signal(SIGINT, oldintr);
13813 }
13814 #endif /* FTP_PROXY */
13815
13816 #ifdef FTP_SECURITY
13817 #ifdef FTP_GSSAPI
13818
13819 #ifdef COMMENT
13820 /* ck_gss_mech_krb5 is not declared anywhere */
13821 struct {
13822     CONST gss_OID_desc * CONST * mech_type;
13823     char *service_name;
13824 } gss_trials[] = {
13825     { &ck_gss_mech_krb5, "ftp" },
13826     { &ck_gss_mech_krb5, "host" },
13827 };
13828 #else
13829 /* This matches what is declared above */
13830 struct {
13831     CONST gss_OID_desc * CONST * mech_type;
13832     char *service_name;
13833 } gss_trials[] = {
13834     { &gss_mech_krb5, "ftp" },
13835     { &gss_mech_krb5, "host" },
13836 };
13837 #endif  /* COMMENT */
13838
13839
13840 int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
13841 #endif /* FTP_GSSAPI */
13842
13843 static int
13844 ftp_auth() {
13845     extern int setsafe();
13846     int j = 0, n;
13847 #ifdef FTP_KRB4
13848     char *service, inst[INST_SZ];
13849     ULONG cksum;
13850     ULONG checksum = (ULONG) getpid();
13851     CHAR out_buf[FTP_BUFSIZ];
13852     int i;
13853 #else /* FTP_KRB4 */
13854 #ifdef FTP_GSSAPI
13855     CHAR out_buf[FTP_BUFSIZ];
13856     int i;
13857 #endif /* FTP_GSSAPI */
13858 #endif /* FTP_KRB4 */
13859
13860     if (ssl_ftp_proxy)                  /* Do not allow AUTH over SSL proxy */
13861         return(0);
13862
13863     if (auth_type)
13864       return(1);                        /* auth already succeeded */
13865
13866     /* Try each auth type as specified by the end user */
13867     for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
13868 #ifdef FTP_GSSAPI
13869         if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
13870             n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
13871             if (n == REPLY_CONTINUE) {
13872                 OM_uint32 maj_stat, min_stat;
13873                 gss_name_t target_name;
13874                 gss_buffer_desc send_tok, recv_tok, *token_ptr;
13875                 char stbuf[FTP_BUFSIZ];
13876                 int comcode, trial;
13877                 struct gss_channel_bindings_struct chan;
13878                 char * realm = NULL;
13879                 char tgt[256];
13880
13881                 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
13882                 chan.initiator_address.length = 4;
13883                 chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
13884                 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
13885                 chan.acceptor_address.length = 4;
13886                 chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
13887                 chan.application_data.length = 0;
13888                 chan.application_data.value = 0;
13889
13890                 if (!quiet)
13891                   printf("GSSAPI accepted as authentication type\n");
13892
13893                 realm = ck_krb5_realmofhost(ftp_user_host);
13894                 if (realm) {
13895                     ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
13896                     debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
13897                     if ( krb5_autoget &&
13898                          !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
13899                             (ck_krb5_is_tgt_valid() > 0)) )
13900                         ck_krb5_autoget_TGT(realm);
13901                 }
13902
13903                 /* Blob from gss-client */
13904                 for (trial = 0; trial < n_gss_trials; trial++) {
13905                     /* ftp@hostname first, the host@hostname */
13906                     /* the V5 GSSAPI binding canonicalizes this for us... */
13907                     ckmakmsg(stbuf,FTP_BUFSIZ,
13908                              gss_trials[trial].service_name,
13909                              "@",
13910                              ftp_user_host,
13911                              NULL
13912                              );
13913                     if (ftp_deb)
13914                       fprintf(stderr,
13915                               "Authenticating to <%s>...\n", stbuf);
13916                     send_tok.value = stbuf;
13917                     send_tok.length = strlen(stbuf);
13918                     maj_stat = gss_import_name(&min_stat, &send_tok,
13919                                                gss_nt_service_name,
13920                                                &target_name
13921                                                );
13922                     if (maj_stat != GSS_S_COMPLETE) {
13923                         user_gss_error(maj_stat, min_stat, "parsing name");
13924                         secure_error("name parsed <%s>\n", stbuf);
13925                         continue;
13926                     }
13927                     token_ptr = GSS_C_NO_BUFFER;
13928                     gcontext = GSS_C_NO_CONTEXT; /* structure copy */
13929
13930                     do {
13931                         if (ftp_deb)
13932                           fprintf(stderr, "calling gss_init_sec_context\n");
13933                         maj_stat =
13934                           gss_init_sec_context(&min_stat,
13935                                                GSS_C_NO_CREDENTIAL,
13936                                                &gcontext,
13937                                                target_name,
13938                                                (gss_OID) *
13939                                                  gss_trials[trial].mech_type,
13940                                                GSS_C_MUTUAL_FLAG |
13941                                                GSS_C_REPLAY_FLAG |
13942                                                (ftp_cfw ?
13943                                                 GSS_C_DELEG_FLAG : 0),
13944                                                0,
13945                                                 /* channel bindings */
13946                                                 (krb5_d_no_addresses ?
13947                                                   GSS_C_NO_CHANNEL_BINDINGS :
13948                                                   &chan),
13949                                                 token_ptr,
13950                                                NULL,    /* ignore mech type */
13951                                                &send_tok,
13952                                                NULL,    /* ignore ret_flags */
13953                                                NULL
13954                                                );       /* ignore time_rec */
13955
13956                         if (maj_stat != GSS_S_COMPLETE &&
13957                             maj_stat != GSS_S_CONTINUE_NEEDED) {
13958                             if (trial == n_gss_trials-1)
13959                               user_gss_error(maj_stat,
13960                                              min_stat,
13961                                              "initializing context"
13962                                              );
13963                             gss_release_name(&min_stat, &target_name);
13964                             /* maybe we missed on the service name */
13965                             goto outer_loop;
13966                         }
13967                         if (send_tok.length != 0) {
13968                             int len;
13969                             reply_parse = "ADAT="; /* for ftpcmd() later */
13970                             len = FTP_BUFSIZ;
13971                             kerror =
13972                               radix_encode(send_tok.value,
13973                                            out_buf,
13974                                            send_tok.length,
13975                                            &len,
13976                                            RADIX_ENCODE
13977                                            );
13978                             if (kerror)  {
13979                                 fprintf(stderr,
13980                                         "Base 64 encoding failed: %s\n",
13981                                         radix_error(kerror)
13982                                         );
13983                                 goto gss_complete_loop;
13984                             }
13985                             comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
13986                             if (comcode != REPLY_COMPLETE
13987                                 && comcode != REPLY_CONTINUE /* (335) */
13988                                 ) {
13989                                 if (trial == n_gss_trials-1) {
13990                                     fprintf(stderr, "GSSAPI ADAT failed\n");
13991                                     /* force out of loop */
13992                                     maj_stat = GSS_S_FAILURE;
13993                                 }
13994                                 /*
13995                                   Backoff to the v1 gssapi is still possible.
13996                                   Send a new AUTH command.  If that fails,
13997                                   terminate the loop.
13998                                 */
13999                                 if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
14000                                     != REPLY_CONTINUE) {
14001                                     fprintf(stderr,
14002                                 "GSSAPI ADAT failed, AUTH restart failed\n");
14003                                     /* force out of loop */
14004                                     maj_stat = GSS_S_FAILURE;
14005                                 }
14006                                 goto outer_loop;
14007                             }
14008                             if (!reply_parse) {
14009                                 fprintf(stderr,
14010                               "No authentication data received from server\n");
14011                                 if (maj_stat == GSS_S_COMPLETE) {
14012                                     fprintf(stderr,
14013                                             "...but no more was needed\n");
14014                                     goto gss_complete_loop;
14015                                 } else {
14016                                     user_gss_error(maj_stat,
14017                                                    min_stat,
14018                                                    "no reply, huh?"
14019                                                    );
14020                                     goto gss_complete_loop;
14021                                 }
14022                             }
14023                             len = FTP_BUFSIZ;
14024                             kerror = radix_encode(reply_parse,out_buf,i,&len,
14025                                                   RADIX_DECODE);
14026                             if (kerror) {
14027                                 fprintf(stderr,
14028                                         "Base 64 decoding failed: %s\n",
14029                                         radix_error(kerror));
14030                                 goto gss_complete_loop;
14031                             }
14032
14033                             /* everything worked */
14034                             token_ptr = &recv_tok;
14035                             recv_tok.value = out_buf;
14036                             recv_tok.length = len;
14037                             continue;
14038
14039                             /* get out of loop clean */
14040                           gss_complete_loop:
14041                             trial = n_gss_trials-1;
14042                             gss_release_buffer(&min_stat, &send_tok);
14043                             gss_release_name(&min_stat, &target_name);
14044                             goto outer_loop;
14045                         }
14046                     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
14047
14048                   outer_loop:
14049                     if (maj_stat == GSS_S_COMPLETE)
14050                       break;
14051                 }
14052                 if (maj_stat == GSS_S_COMPLETE) {
14053                     printf("GSSAPI authentication succeeded\n");
14054                     reply_parse = NULL;
14055                     auth_type = "GSSAPI";
14056                     return(1);
14057                 } else {
14058                     fprintf(stderr, "GSSAPI authentication failed\n");
14059                     reply_parse = NULL;
14060                 }
14061             } else {
14062                 if (ftp_deb)
14063                 fprintf(stderr, "GSSAPI rejected as an authentication type\n");
14064                 if (ftpcode == 500 || ftpcode == 502)
14065                     return(0);
14066             }
14067         }
14068 #endif /* FTP_GSSAPI */
14069 #ifdef FTP_SRP
14070         if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
14071             if (srp_ftp_auth(ftp_user_host,NULL,NULL))
14072               return(1);
14073             else if (ftpcode == 500 || ftpcode == 502)
14074               return(0);
14075         }
14076 #endif /* FTP_SRP */
14077 #ifdef FTP_KRB4
14078         if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
14079             n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
14080             if (n == REPLY_CONTINUE) {
14081                 char tgt[4*REALM_SZ+1];
14082                 int rc;
14083
14084                 if (!quiet)
14085                   printf("KERBEROS_V4 accepted as authentication type\n");
14086                 ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
14087                 ckstrncpy(ftp_realm,
14088                           (char *)ck_krb4_realmofhost(ftp_user_host),
14089                           REALM_SZ
14090                           );
14091
14092                 ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
14093                 rc = ck_krb4_tkt_isvalid(tgt);
14094
14095                 if (rc <= 0 && krb4_autoget)
14096                   ck_krb4_autoget_TGT(ftp_realm);
14097
14098                 service = "ftp";
14099                 kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
14100                 if (kerror == KDC_PR_UNKNOWN) {
14101                     service = "rcmd";
14102                     kerror = krb_mk_req(&ftp_tkt,
14103                                         service,
14104                                         inst,
14105                                         ftp_realm,
14106                                         checksum
14107                                         );
14108                 }
14109                 if (kerror)
14110                   fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
14111                           krb_get_err_text(kerror));
14112                 if (!kerror) {
14113                     kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
14114                     if (kerror)
14115                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
14116                               krb_get_err_text(kerror));
14117                 }
14118                 if (!kerror) {
14119                     int rc;
14120                     rc = des_key_sched(ftp_cred.session, ftp_sched);
14121                     if (rc == -1) {
14122                        printf("?Invalid DES key specified in credentials\r\n");
14123                        debug(F110,"ftp_auth",
14124                              "invalid DES Key specified in credentials",0);
14125                     } else if ( rc == -2 ) {
14126                         printf("?Weak DES key specified in credentials\r\n");
14127                         debug(F110,"ftp_auth",
14128                               "weak DES Key specified in credentials",0);
14129                     } else if ( rc != 0 ) {
14130                         printf("?DES Key Schedule not set by credentials\r\n");
14131                         debug(F110,"ftp_auth",
14132                               "DES Key Schedule not set by credentials",0);
14133                     }
14134                     reply_parse = "ADAT=";
14135                     i = FTP_BUFSIZ;
14136                     kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
14137                                           &i, RADIX_ENCODE);
14138                     if (kerror) {
14139                         fprintf(stderr, "Base 64 encoding failed: %s\n",
14140                                 radix_error(kerror));
14141                         goto krb4_err;
14142                     }
14143                     if (i > FTP_BUFSIZ - 6)
14144                       printf("?ADAT data too long\n");
14145                     if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
14146                         REPLY_COMPLETE) {
14147                         fprintf(stderr, "Kerberos V4 authentication failed\n");
14148                         goto krb4_err;
14149                     }
14150                     if (!reply_parse) {
14151                         fprintf(stderr,
14152                              "No authentication data received from server\n");
14153                         goto krb4_err;
14154                     }
14155                     i = sizeof(out_buf);
14156                     kerror =
14157                       radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
14158                     if (kerror) {
14159                         fprintf(stderr, "Base 64 decoding failed: %s\n",
14160                                 radix_error(kerror));
14161                         goto krb4_err;
14162                     }
14163                     kerror = krb_rd_safe(out_buf, i,
14164 #ifdef KRB524
14165                                          ftp_cred.session,
14166 #else /* KRB524 */
14167                                          &ftp_cred.session,
14168 #endif /* KRB524 */
14169                                          &hisctladdr,
14170                                          &myctladdr,
14171                                          &ftp_msg_data
14172                                          );
14173                     if (kerror) {
14174                         fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
14175                                 krb_get_err_text(kerror));
14176                         goto krb4_err;
14177                     }
14178
14179                     /* fetch the (modified) checksum */
14180                     memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
14181                     if (ntohl(cksum) == checksum + 1) {
14182                         if (ftp_vbm)
14183                           printf("Kerberos V4 authentication succeeded\n");
14184                         reply_parse = NULL;
14185                         auth_type = "KERBEROS_V4";
14186                         return(1);
14187                     } else
14188                       fprintf(stderr,
14189                               "Kerberos V4 mutual authentication failed\n");
14190                   krb4_err:
14191                     reply_parse = NULL;
14192                 }
14193             } else {
14194                 if (ftp_deb)
14195                   fprintf(stderr,
14196                       "KERBEROS_V4 rejected as an authentication type\n");
14197                 if (ftpcode == 500 || ftpcode == 502)
14198                     return(0);
14199             }
14200         }
14201 #endif /* FTP_KRB4 */
14202 #ifdef CK_SSL
14203         if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
14204 #ifdef FTPHOST
14205             if (!hostcmd) {
14206                 ftpcmd("HOST",ftp_user_host,0,0,0);
14207                 hostcmd = 1;
14208             }
14209 #endif /* FTPHOST */
14210             n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
14211             if (n != REPLY_COMPLETE)
14212               n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
14213             if (n == REPLY_COMPLETE) {
14214                 if (!quiet)
14215                   printf("TLS accepted as authentication type\n");
14216
14217                 auth_type = "TLS";
14218                 ssl_auth();
14219                 if (ssl_ftp_active_flag ) {
14220                     ftp_dpl = FPL_CLR;
14221                     ftp_cpl = FPL_PRV;
14222                     return(1);
14223                 } else {
14224                     fprintf(stderr,"TLS authentication failed\n");
14225                     auth_type = NULL;
14226 #ifdef TCPIPLIB
14227                     socket_close(csocket);
14228 #else /* TCPIPLIB */
14229 #ifdef USE_SHUTDOWN
14230                     shutdown(csocket, 1+1);
14231 #endif /* USE_SHUTDOWN */
14232                     close(csocket);
14233 #endif /* TCPIPLIB */
14234                     csocket = -1;
14235                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14236                       return(0);
14237                 }
14238             } else {
14239                 if (ftp_deb)
14240                   fprintf(stderr,"TLS rejected as an authentication type\n");
14241                 if (ftpcode == 500 || ftpcode == 502)
14242                     return(0);
14243             }
14244         }
14245         if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
14246 #ifdef FTPHOST
14247             if (!hostcmd) {
14248                 ftpcmd("HOST",ftp_user_host,0,0,0);
14249                 hostcmd = 1;
14250             }
14251 #endif /* FTPHOST */
14252             n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
14253             if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
14254                 if (!quiet)
14255                   printf("SSL accepted as authentication type\n");
14256                 auth_type = "SSL";
14257                 ssl_auth();
14258                 if (ssl_ftp_active_flag) {
14259                     ftp_dpl = FPL_PRV;
14260                     ftp_cpl = FPL_PRV;
14261                     setprotbuf(1<<20);
14262                     return(1);
14263                 } else {
14264                     fprintf(stderr,"SSL authentication failed\n");
14265                     auth_type = NULL;
14266 #ifdef TCPIPLIB
14267                     socket_close(csocket);
14268 #else /* TCPIPLIB */
14269 #ifdef USE_SHUTDOWN
14270                     shutdown(csocket, 1+1);
14271 #endif /* USE_SHUTDOWN */
14272                     close(csocket);
14273 #endif /* TCPIPLIB */
14274                     csocket = -1;
14275                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14276                       return(0);
14277                 }
14278             } else {
14279                 if (ftp_deb)
14280                   fprintf(stderr, "SSL rejected as an authentication type\n");
14281                 if (ftpcode == 500 || ftpcode == 502)
14282                   return(0);
14283             }
14284         }
14285 #endif /* CK_SSL */
14286         /* Other auth types go here ... */
14287     } /* for (j;;) */
14288     return(0);
14289 }
14290 #endif /* FTP_SECURITY */
14291
14292 static int
14293 #ifdef CK_ANSIC
14294 setprotbuf(unsigned int size)
14295 #else
14296 setprotbuf(size) unsigned int size;
14297 #endif /* CK_ANSIC */
14298 /* setprotbuf */ {
14299     if (ucbuf)
14300       free(ucbuf);
14301     ucbuf = NULL;
14302     ucbufsiz = 0;
14303     actualbuf = size;
14304     while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
14305         if (actualbuf)
14306           actualbuf /= 2;
14307         else
14308           return(0);
14309     }
14310     ucbufsiz = actualbuf - FUDGE_FACTOR;
14311     debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
14312     if (ucbufsiz < 128) {
14313         printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
14314     } else if (ucbufsiz < 0) {
14315         printf("ERROR: ucbuf allocation failure\n");
14316         return(-1);
14317     }
14318     maxbuf = actualbuf;
14319     return(1);
14320 }
14321
14322 static int
14323 #ifdef CK_ANSIC
14324 setpbsz(unsigned int size)
14325 #else
14326 setpbsz(size) unsigned int size;
14327 #endif /* CK_ANSIC */
14328 /* setpbsz */ {
14329     if (!setprotbuf(size)) {
14330         perror("?Error while trying to malloc PROT buffer:");
14331 #ifdef FTP_SRP
14332         srp_reset();
14333 #endif /* FTP_SRP */
14334         ftpclose();
14335         return(-1);
14336     }
14337     reply_parse = "PBSZ=";
14338     ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
14339 #ifdef CK_SSL
14340              ssl_ftp_active_flag ? "0" :
14341 #endif /* CK_SSL */
14342              ckuitoa(actualbuf),NULL,NULL);
14343     if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
14344         if (connected) {
14345             printf("?Unable to negotiate PROT buffer size with FTP server\n");
14346             ftpclose();
14347         }
14348         return(-1);
14349     }
14350     if (reply_parse) {
14351         if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
14352           maxbuf = actualbuf;
14353     } else
14354       maxbuf = actualbuf;
14355     ucbufsiz = maxbuf - FUDGE_FACTOR;
14356     debug(F101,"setpbsz ucbufsiz","",ucbufsiz);    
14357     reply_parse = NULL;
14358     return(0);
14359 }
14360
14361 static VOID
14362 cancel_remote(din) int din; {
14363     CHAR buf[FTP_BUFSIZ];
14364     int x, nfnd;
14365 #ifdef BSDSELECT
14366     fd_set mask;
14367 #endif /* BSDSELECT */
14368 #ifdef IBMSELECT
14369     int fds[2], fdcnt = 0;
14370 #endif /* IBMSELECT */
14371 #ifdef DEBUG
14372     extern int debtim;
14373     int xdebtim;
14374     xdebtim = debtim;
14375     debtim = 1;
14376 #endif /* DEBUG */
14377     debug(F100,"ftp cancel_remote entry","",0);
14378 #ifdef CK_SSL
14379     if (ssl_ftp_active_flag) {
14380         /*
14381          * Send Telnet IP, Telnet DM but do so inline and within the
14382          * TLS channel
14383          */
14384         int count, error;
14385
14386         buf[0] = IAC;
14387         buf[1] = TN_IP;
14388         buf[2] = IAC;
14389         buf[3] = TN_DM;
14390         buf[4] = NUL;
14391
14392         count = SSL_write(ssl_ftp_con, buf, 4);
14393         debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
14394         error = SSL_get_error(ssl_ftp_con,count);
14395         debug(F111,"ftp cancel_remote","SSL_get_error()",error);
14396         switch (error) {
14397           case SSL_ERROR_NONE:
14398             break;
14399           case SSL_ERROR_WANT_WRITE:
14400           case SSL_ERROR_WANT_READ:
14401           case SSL_ERROR_SYSCALL:
14402 #ifdef NT
14403             {
14404                 int gle = GetLastError();
14405             }
14406 #endif /* NT */
14407           case SSL_ERROR_WANT_X509_LOOKUP:
14408           case SSL_ERROR_SSL:
14409           case SSL_ERROR_ZERO_RETURN:
14410           default:
14411             lostpeer();
14412             return;
14413         }
14414     } else
14415 #endif /* CK_SSL */
14416     {
14417         /*
14418          * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
14419          * after urgent byte rather than before as is protocol now.
14420          */
14421         buf[0] = IAC;
14422         buf[1] = TN_IP;
14423         buf[2] = IAC;
14424         buf[3] = NUL;
14425         if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
14426           perror("cancel");
14427         debug(F101,"ftp cancel_remote send 1","",x);
14428         buf[0] = TN_DM;
14429         x = send(csocket,(SENDARG2TYPE)buf,1,0);
14430         debug(F101,"ftp cancel_remote send 2","",x);
14431     }
14432     x = scommand("ABOR");
14433     debug(F101,"ftp cancel_remote scommand","",x);
14434 #ifdef BSDSELECT
14435     FD_ZERO(&mask);
14436     FD_SET(csocket, &mask);
14437     if (din) {
14438         FD_SET(din, &mask);
14439     }
14440     nfnd = empty(&mask, 10);
14441     debug(F101,"ftp cancel_remote empty","",nfnd);
14442     if ((nfnd) <= 0) {
14443         if (nfnd < 0) {
14444             perror("cancel");
14445         }
14446 #ifdef FTP_PROXY
14447         if (ptabflg)
14448           ftpcode = -1;
14449 #endif /* FTP_PROXY */
14450         lostpeer();
14451     }
14452     debug(F110,"ftp cancel_remote","D",0);
14453     if (din && FD_ISSET(din, &mask)) {
14454         /* Security: No threat associated with this read. */
14455         /* But you can't simply read the TLS data stream  */
14456 #ifdef CK_SSL
14457         if (ssl_ftp_data_active_flag) {
14458             int count, error;
14459             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14460                     /* LOOP */ ;
14461         } else
14462 #endif /* CK_SSL */
14463         {
14464             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14465                 /* LOOP */ ;
14466         }
14467     }
14468     debug(F110,"ftp cancel_remote","E",0);
14469 #else /* BSDSELECT */
14470 #ifdef IBMSELECT
14471     fds[0] = csocket;
14472     fdcnt++;
14473     if (din) {
14474         fds[1] = din;
14475         fdcnt++;
14476     }
14477     nfnd = empty(fds, fdcnt, 10);
14478     debug(F101,"ftp cancel_remote empty","",nfnd);
14479     if ((nfnd) <= 0) {
14480         if (nfnd < 0) {
14481             perror("cancel");
14482         }
14483 #ifdef FTP_PROXY
14484         if (ptabflg)
14485           ftpcode = -1;
14486 #endif /* FTP_PROXY */
14487         lostpeer();
14488     }
14489     debug(F110,"ftp cancel_remote","D",0);
14490     if (din && select(&din, 1,0,0,1) ) {
14491 #ifdef CK_SSL
14492         if (ssl_ftp_data_active_flag) {
14493             int count, error;
14494             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14495                     /* LOOP */ ;
14496         } else
14497 #endif /* CK_SSL */
14498         {
14499             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14500                 /* LOOP */ ;
14501         }
14502     }
14503     debug(F110,"ftp cancel_remote","E",0);
14504 #else /* IBMSELECT */
14505     Some form of select is required.
14506 #endif /* IBMSELECT */
14507 #endif /* BSDSELECT */
14508     if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
14509         debug(F110,"ftp cancel_remote","F",0);
14510         /* 552 needed for NIC style cancel */
14511         getreply(0,-1,-1,ftp_vbm,0);
14512         debug(F110,"ftp cancel_remote","G",0);
14513     }
14514     debug(F110,"ftp cancel_remote","H",0);
14515     getreply(0,-1,-1,ftp_vbm,0);
14516     debug(F110,"ftp cancel_remote","I",0);
14517 #ifdef DEBUG
14518     debtim = xdebtim;
14519 #endif /* DEBUG */
14520 }
14521
14522 static int
14523 fts_dpl(x) int x; {
14524     if (!auth_type
14525 #ifdef OS2
14526          || !ck_crypt_is_installed()
14527 #endif /* OS2 */
14528          ) {
14529         switch ( x ) {
14530           case FPL_PRV:
14531             printf("?Cannot set protection level to PRIVATE\n");
14532             return(0);
14533           case FPL_SAF:
14534             printf("?Cannot set protection level to SAFE\n");
14535             return(0);
14536         }
14537         ftp_dpl = x;
14538         return(1);
14539     }
14540
14541 #ifdef CK_SSL
14542     if (x == FPL_SAF &&
14543         (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
14544         printf("Cannot set protection level to safe\n");
14545         return(0);
14546     }
14547 #endif /* CK_SSL */
14548     /* Start with a PBSZ of 1 meg */
14549     if (x != FPL_CLR) {
14550         if (setpbsz(DEFAULT_PBSZ) < 0)
14551           return(0);
14552     }
14553     y = ftpcmd(x == FPL_CLR ? "PROT C" :
14554                (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
14555     if (y == REPLY_COMPLETE) {
14556         ftp_dpl = x;
14557         return(1);
14558     }
14559     return(0);
14560 }
14561
14562 static int
14563 fts_cpl(x) int x; {
14564     if (!auth_type 
14565 #ifdef OS2
14566          || !ck_crypt_is_installed()
14567 #endif /* OS2 */
14568          ) {
14569         switch ( x ) {
14570           case FPL_PRV:
14571             printf("?Cannot set protection level to PRIVATE\n");
14572             return(0);
14573           case FPL_SAF:
14574             printf("?Cannot set protection level to SAFE\n");
14575             return(0);
14576         }
14577         ftp_cpl = x;
14578         return(1);
14579     }
14580     if (x == FPL_CLR) {
14581         y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
14582         if (y == REPLY_COMPLETE) {
14583             ftp_cpl = x;
14584             return(1);
14585         }
14586         return(0);
14587     }
14588     ftp_cpl = x;
14589     return(1);
14590 }
14591
14592 #ifdef FTP_GSSAPI
14593 static VOID
14594 user_gss_error(maj_stat, min_stat, s)
14595     OM_uint32 maj_stat, min_stat;
14596     char *s;
14597 {
14598     /* a lot of work just to report the error */
14599     OM_uint32 gmaj_stat, gmin_stat, msg_ctx;
14600     gss_buffer_desc msg;
14601     msg_ctx = 0;
14602     while (!msg_ctx) {
14603         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
14604                                        GSS_C_GSS_CODE,
14605                                        GSS_C_NULL_OID,
14606                                        &msg_ctx,
14607                                        &msg
14608                                        );
14609         if ((gmaj_stat == GSS_S_COMPLETE)||
14610             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14611             fprintf(stderr, "GSSAPI error major: %s\n",
14612                     (char*)msg.value);
14613             gss_release_buffer(&gmin_stat, &msg);
14614         }
14615         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14616           break;
14617     }
14618     msg_ctx = 0;
14619     while (!msg_ctx) {
14620         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
14621                                        GSS_C_MECH_CODE,
14622                                        GSS_C_NULL_OID,
14623                                        &msg_ctx,
14624                                        &msg
14625                                        );
14626         if ((gmaj_stat == GSS_S_COMPLETE)||
14627             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14628             fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value);
14629             gss_release_buffer(&gmin_stat, &msg);
14630         }
14631         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14632           break;
14633     }
14634     fprintf(stderr, "GSSAPI error: %s\n", s);
14635 }
14636 #endif /* FTP_GSSAPI */
14637
14638 #ifndef NOMHHOST
14639 #ifdef datageneral
14640 #define NOMHHOST
14641 #else
14642 #ifdef HPUX5WINTCP
14643 #define NOMHHOST
14644 #endif /* HPUX5WINTCP */
14645 #endif /* datageneral */
14646 #endif /* NOMHHOST */
14647
14648 #ifdef INADDRX
14649 static struct in_addr inaddrx;
14650 #endif /* INADDRX */
14651
14652 static char *
14653 ftp_hookup(host, port, tls) char * host; int port; int tls; {
14654     register struct hostent *hp = 0;
14655 #ifdef IP_TOS
14656 #ifdef IPTOS_THROUGHPUT
14657     int tos;
14658 #endif /* IPTOS_THROUGHPUT */
14659 #endif /* IP_TOS */
14660     int s;
14661     GSOCKNAME_T len;
14662     static char hostnamebuf[MAXHOSTNAMELEN];
14663     char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
14664     int  cport;
14665 #ifdef DEBUG
14666     extern int debtim;
14667     int xdebtim;
14668     xdebtim = debtim;
14669     debtim = 1;
14670 #endif /* DEBUG */
14671
14672     debug(F111,"ftp_hookup",host,port);
14673
14674 #ifndef NOHTTP
14675     if (tcp_http_proxy) {
14676         struct servent *destsp;
14677         char *p, *q;
14678
14679         ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
14680         for (p = tcp_http_proxy, q = hostname;
14681              *p != '\0' && *p != ':';
14682              p++, q++
14683              )
14684           *q = *p;
14685         *q = '\0';
14686
14687         if (*p == ':')
14688           p++;
14689         else
14690           p = "http";
14691
14692         destsp = getservbyname(p,"tcp");
14693         if (destsp)
14694           cport = ntohs(destsp->s_port);
14695         else if (p) {
14696           cport = atoi(p);
14697         } else
14698           cport = 80;
14699     } else
14700 #endif /* NOHTTP */
14701     {
14702         ckstrncpy(hostname,host,MAXHOSTNAMELEN);
14703         cport = port;
14704     }
14705     memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
14706     hisctladdr.sin_addr.s_addr = inet_addr(host);
14707     if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
14708     {
14709         debug(F110,"ftp hookup A",hostname,0);
14710         hisctladdr.sin_family = AF_INET;
14711         ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
14712     } else {
14713         debug(F110,"ftp hookup B",hostname,0);
14714         hp = gethostbyname(hostname);
14715 #ifdef HADDRLIST
14716         hp = ck_copyhostent(hp);        /* make safe copy that won't change */
14717 #endif /* HADDRLIST */
14718         if (hp == NULL) {
14719             fprintf(stderr, "ftp: %s: Unknown host\n", host);
14720             ftpcode = -1;
14721 #ifdef DEBUG
14722             debtim = xdebtim;
14723 #endif /* DEBUG */
14724             return((char *) 0);
14725         }
14726         hisctladdr.sin_family = hp->h_addrtype;
14727 #ifdef HADDRLIST
14728         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
14729                sizeof(hisctladdr.sin_addr));
14730 #else /* HADDRLIST */
14731         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
14732                sizeof(hisctladdr.sin_addr));
14733 #endif /* HADDRLIST */
14734         ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
14735     }
14736     debug(F110,"ftp hookup C",hostnamebuf,0);
14737     ftp_host = hostnamebuf;
14738     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14739     debug(F101,"ftp hookup socket","",s);
14740     if (s < 0) {
14741         perror("ftp: socket");
14742         ftpcode = -1;
14743 #ifdef DEBUG
14744         debtim = xdebtim;
14745 #endif /* DEBUG */
14746         return(0);
14747     }
14748     hisctladdr.sin_port = htons(cport);
14749     errno = 0;
14750
14751 #ifdef COMMENT
14752   printf("hisctladdr=%d\n",sizeof(hisctladdr));
14753   printf("hisctladdr.sin_addr=%d\n",sizeof(hisctladdr.sin_addr));
14754   printf("sockaddr_in=%d\n",sizeof(struct sockaddr_in));
14755   printf("hisctladdr.sin_addr.s_addr=%d\n",sizeof(hisctladdr.sin_addr.s_addr));
14756 #endif  /* COMMENT */
14757
14758 #ifdef HADDRLIST
14759     debug(F100,"ftp hookup HADDRLIST","",0);
14760     while
14761 #else
14762     debug(F100,"ftp hookup no HADDRLIST","",0);
14763     if
14764 #endif /* HADDRLIST */
14765       (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
14766           debug(F101,"ftp hookup connect failed","",errno);
14767 #ifdef HADDRLIST
14768           if (hp && hp->h_addr_list[1]) {
14769               int oerrno = errno;
14770
14771               fprintf(stderr, "ftp: connect to address %s: ",
14772                       inet_ntoa(hisctladdr.sin_addr));
14773               errno = oerrno;
14774               perror((char *) 0);
14775               hp->h_addr_list++;
14776               memcpy((char *)&hisctladdr.sin_addr,
14777                      hp->h_addr_list[0],
14778                      sizeof(hisctladdr.sin_addr));
14779               fprintf(stdout, "Trying %s...\n",
14780                       inet_ntoa(hisctladdr.sin_addr));
14781 #ifdef TCPIPLIB
14782               socket_close(s);
14783 #else /* TCPIPLIB */
14784               close(s);
14785 #endif /* TCPIPLIB */
14786               s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14787               if (s < 0) {
14788                   perror("ftp: socket");
14789                   ftpcode = -1;
14790 #ifdef DEBUG
14791                   debtim = xdebtim;
14792 #endif /* DEBUG */
14793                   return(0);
14794               }
14795               continue;
14796           }
14797 #endif /* HADDRLIST */
14798           perror("ftp: connect");
14799           ftpcode = -1;
14800           goto bad;
14801       }
14802     debug(F100,"ftp hookup connect ok","",0);
14803
14804     len = sizeof (myctladdr);
14805     errno = 0;
14806     if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
14807         debug(F101,"ftp hookup getsockname failed","",errno);
14808         perror("ftp: getsockname");
14809         ftpcode = -1;
14810         goto bad;
14811     }
14812     debug(F100,"ftp hookup getsockname ok","",0);
14813
14814 #ifndef NOHTTP
14815     if (tcp_http_proxy) {
14816 #ifdef OS2
14817         char * agent = "Kermit 95";     /* Default user agent */
14818 #else
14819         char * agent = "C-Kermit";
14820 #endif /* OS2 */
14821
14822         if (http_connect(s,agent,NULL,
14823                          tcp_http_proxy_user,
14824                          tcp_http_proxy_pwd,
14825                          0,
14826                          proxyhost
14827                          ) < 0) {
14828             char * foo = NULL;
14829 #ifdef TCPIPLIB
14830             socket_close(s);
14831 #else /* TCPIPLIB */
14832             close(s);
14833 #endif /* TCPIPLIB */
14834
14835             while (foo == NULL && tcp_http_proxy != NULL ) {
14836
14837                 if (tcp_http_proxy_errno == 401 ||
14838                      tcp_http_proxy_errno == 407 ) {
14839                     char uid[UIDBUFLEN];
14840                     char pwd[PWDSIZ];
14841                     struct txtbox tb[2];
14842                     int ok;
14843
14844                     tb[0].t_buf = uid;
14845                     tb[0].t_len = UIDBUFLEN;
14846                     tb[0].t_lbl = "Proxy Userid: ";
14847                     tb[0].t_dflt = NULL;
14848                     tb[0].t_echo = 1;
14849                     tb[1].t_buf = pwd;
14850                     tb[1].t_len = 256;
14851                     tb[1].t_lbl = "Proxy Passphrase: ";
14852                     tb[1].t_dflt = NULL;
14853                     tb[1].t_echo = 2;
14854
14855                     ok = uq_mtxt("Proxy Server Authentication Required\n",
14856                                   NULL, 2, tb);
14857
14858                     if (ok && uid[0]) {
14859                         char * proxy_user, * proxy_pwd;
14860
14861                         proxy_user = tcp_http_proxy_user;
14862                         proxy_pwd  = tcp_http_proxy_pwd;
14863
14864                         tcp_http_proxy_user = uid;
14865                         tcp_http_proxy_pwd = pwd;
14866
14867                         foo = ftp_hookup(host, port, 0);
14868
14869                         debug(F110,"ftp_hookup()",foo,0);
14870                         memset(pwd,0,PWDSIZ);
14871                         tcp_http_proxy_user = proxy_user;
14872                         tcp_http_proxy_pwd = proxy_pwd;
14873                     } else
14874                         break;
14875                 } else
14876                     break;
14877             }
14878             if (foo != NULL)
14879               return(foo);
14880             perror("ftp: connect");
14881             ftpcode = -1;
14882             goto bad;
14883         }
14884         ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
14885     }
14886 #endif /* NOHTTP */
14887
14888     csocket = s;
14889
14890 #ifdef CK_SSL
14891     if (tls) {
14892         /* FTP over SSL
14893          * If the connection is over an SSL proxy then the
14894          * auth_type will be NULL.  However, I'm not sure
14895          * whether we should protect the data channel in
14896          * that case or not.
14897          */
14898
14899         debug(F100,"ftp hookup use_tls","",0);
14900         if (!ssl_auth()) {
14901             debug(F100,"ftp hookup ssl_auth failed","",0);
14902             auth_type = NULL;
14903             ftpcode = -1;
14904             csocket = -1;
14905             goto bad;
14906         }
14907         ssl_ftp_proxy = 1;
14908     }
14909 #endif /* CK_SSL */
14910
14911 #ifdef IP_TOS
14912 #ifdef IPTOS_LOWDELAY
14913     tos = IPTOS_LOWDELAY;
14914     if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
14915       perror("ftp: setsockopt TOS (ignored)");
14916 #endif
14917 #endif
14918     if (!quiet)
14919       printf("Connected to %s.\n", host);
14920
14921     /* Read greeting from server */
14922     if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
14923         debug(F100,"ftp hookup bad reply","",0);
14924 #ifdef TCPIPLIB
14925         socket_close(csocket);
14926 #else /* TCPIPLIB */
14927         close(csocket);
14928 #endif /* TCPIPLIB */
14929         ftpcode = -1;
14930         goto bad;
14931     }
14932 #ifdef SO_OOBINLINE
14933     {
14934         int on = 1;
14935         errno = 0;
14936         if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
14937                        sizeof(on)) < 0) {
14938             perror("ftp: setsockopt");
14939             debug(F101,"ftp hookup setsockopt failed","",errno);
14940         }
14941 #ifdef DEBUG
14942         else
14943           debug(F100,"ftp hookup setsockopt ok","",0);
14944 #endif /* DEBUG */
14945     }
14946 #endif /* SO_OOBINLINE */
14947
14948 #ifdef DEBUG
14949     debtim = xdebtim;
14950 #endif /* DEBUG */
14951     return(ftp_host);
14952
14953   bad:
14954     debug(F100,"ftp hookup bad","",0);
14955 #ifdef TCPIPLIB
14956     socket_close(s);
14957 #else /* TCPIPLIB */
14958     close(s);
14959 #endif /* TCPIPLIB */
14960 #ifdef DEBUG
14961     debtim = xdebtim;
14962 #endif /* DEBUG */
14963     csocket = -1;
14964     return((char *)0);
14965 }
14966
14967 static VOID
14968 ftp_init() {
14969     int i, n;
14970
14971     /* The purpose of the initial REST 0 is not clear, but other FTP */
14972     /* clients do it.  In any case, failure of this command is not a */
14973     /* reliable indication that the server does not support Restart. */
14974
14975     okrestart = 0;
14976     if (!noinit) {
14977         n = ftpcmd("REST 0",NULL,0,0,0);
14978         if (n == REPLY_COMPLETE)
14979           okrestart = 1;
14980 #ifdef COMMENT
14981         else if (ftp_deb)
14982           printf("WARNING: Unable to restore file pointer.\n");
14983 #endif /* COMMENT */
14984     }
14985     n = ftpcmd("SYST",NULL,0,0,0);      /* Get server system type */
14986     if (n == REPLY_COMPLETE) {
14987         register char *cp, c = NUL;
14988         cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
14989         if (cp == NULL)
14990           cp = ckstrchr(ftp_reply_str+4,'\r');
14991         if (cp) {
14992             if (cp[-1] == '.')
14993               cp--;
14994             c = *cp;                    /* Save this char */
14995             *cp = '\0';                 /* Replace it with NUL */
14996         }
14997         if (!quiet)
14998           printf("Remote system type is %s.\n",ftp_reply_str+4);
14999         ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
15000         if (cp)                         /* Put back saved char */
15001           *cp = c;
15002     }
15003     alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
15004
15005     if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
15006     else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
15007     else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
15008     else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
15009     else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
15010     else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
15011     else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
15012
15013 #ifdef FTP_PROXY
15014     unix_proxy = 0;
15015     if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
15016 #endif /* FTP_PROXY */
15017
15018     if (ftp_cmdlin && ftp_xfermode == XMODE_M)
15019       ftp_typ = binary;                 /* Type given on command line */
15020     else                                /* Otherwise set it automatically */
15021       ftp_typ = alike ? FTT_BIN : FTT_ASC;
15022     changetype(ftp_typ,0);              /* Change to this type */
15023     g_ftp_typ = ftp_typ;                /* Make it the global type */
15024     if (!quiet)
15025       printf("Default transfer mode is %s\n",
15026              ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
15027              );
15028     for (i = 0; i < 16; i++)            /* Init server FEATure table */
15029       sfttab[i] = 0;
15030     if (!noinit) {
15031         n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
15032 #ifdef COMMENT
15033         if (n != REPLY_COMPLETE)
15034           printf("WARNING: Server does not accept MODE S(TREAM)\n");
15035 #endif /* COMMENT */
15036         n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
15037 #ifdef COMMENT
15038         if (n != REPLY_COMPLETE)
15039           printf("WARNING: Server does not accept STRU F(ILE)\n");
15040 #endif /* COMMENT */
15041         if (featok) {
15042             n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
15043             if (n == REPLY_COMPLETE) {
15044                 debug(F101,"ftp_init FEAT","",sfttab[0]);
15045                 if (deblog || ftp_deb) {
15046                     int i;
15047                     for (i = 1; i < 16 && i < nfeattab; i++) {
15048                         debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
15049                         if (ftp_deb)
15050                           printf("  Server %s %s\n",
15051                                  sfttab[i] ? "supports" : "does not support",
15052                                  feattab[i].kwd
15053                                  );
15054                     }
15055                     /* Deal with disabled MLST opts here if necessary */
15056                     /* But why would it be? */
15057                 }
15058             }
15059         }
15060     }
15061 }
15062
15063 static int
15064 ftp_login(host) char * host; {          /* (also called from ckuusy.c) */
15065     static char ftppass[PASSBUFSIZ]="";
15066     char tmp[PASSBUFSIZ];
15067     char *user = NULL, *pass = NULL, *acct = NULL;
15068     int n, aflag = 0;
15069     extern char uidbuf[];
15070     extern char pwbuf[];
15071     extern int  pwflg, pwcrypt;
15072
15073     debug(F111,"ftp_login",ftp_logname,ftp_log);
15074
15075     if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
15076       anonymous = 1;
15077     if (!ckstrcmp(ftp_logname,"ftp",-1,0))
15078       anonymous = 1;
15079
15080 #ifdef FTP_SRP
15081     if (auth_type && !strcmp(auth_type, "SRP")) {
15082         user = srp_user;
15083         pass = srp_pass;
15084         acct = srp_acct;
15085     } else
15086 #endif /* FTP_SRP */
15087       if (anonymous) {
15088           user = "anonymous";
15089           if (ftp_tmp) {                /* They gave a password */
15090               pass = ftp_tmp;
15091           } else if (ftp_apw) {         /* SET FTP ANONYMOUS-PASSWORD */
15092               pass = ftp_apw;
15093           } else {                      /* Supply user@host */
15094               ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
15095               pass = tmp;
15096           }
15097           debug(F110,"ftp anonymous",pass,0);
15098       } else {
15099 #ifdef USE_RUSERPASS
15100           if (ruserpass(host, &user, &pass, &acct) < 0) {
15101               ftpcode = -1;
15102               return(0);
15103           }
15104 #endif /* USE_RUSERPASS */
15105           if (ftp_logname) {
15106               user = ftp_logname;
15107               pass = ftp_tmp;
15108           } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
15109               user = uidbuf;
15110               if (ftp_tmp) {
15111                   pass = ftp_tmp;
15112               } else if (pwbuf[0] && pwflg) {
15113                   ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
15114 #ifdef OS2
15115                   if ( pwcrypt )
15116                       ck_encrypt((char *)ftppass);
15117 #endif /* OS2 */
15118                   pass = ftppass;
15119               }
15120           }
15121           acct = ftp_acc;
15122           while (user == NULL) {
15123               char *myname, prompt[PROMPTSIZ];
15124               int ok;
15125
15126               myname = whoami();
15127               if (myname)
15128                 ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
15129                           NULL,NULL,NULL,NULL,NULL,NULL,NULL);
15130               else
15131                 ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
15132               tmp[0] = '\0';
15133               
15134               ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
15135                           DEFAULT_UQ_TIMEOUT);
15136               if (!ok || *tmp == '\0')
15137                 user = myname;
15138               else
15139                 user = brstrip(tmp);
15140           }
15141       }
15142     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15143     if (n == REPLY_COMPLETE) {
15144         /* determine if we need to send a dummy password */
15145         if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
15146           ftpcmd("PASS dummy",NULL,0,0,1);
15147     } else if (n == REPLY_CONTINUE) {
15148 #ifdef CK_ENCRYPTION
15149         int oldftp_cpl;
15150 #endif /* CK_ENCRYPTION */
15151
15152         if (pass == NULL) {
15153             int ok;
15154             setint();
15155             ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
15156                         DEFAULT_UQ_TIMEOUT);
15157             if (ok)
15158                 pass = brstrip(ftppass);
15159         }
15160
15161 #ifdef CK_ENCRYPTION
15162         oldftp_cpl = ftp_cpl;
15163         ftp_cpl = FPL_PRV;
15164 #endif /* CK_ENCRYPTION */
15165         n = ftpcmd("PASS",pass,-1,-1,1);
15166         if (!anonymous && pass) {
15167             char * p = pass;
15168             while (*p++) *(p-1) = NUL;
15169             makestr(&ftp_tmp,NULL);
15170         }
15171 #ifdef CK_ENCRYPTION
15172         /* level may have changed */
15173         if (ftp_cpl == FPL_PRV)
15174           ftp_cpl = oldftp_cpl;
15175 #endif /* CK_ENCRYPTION */
15176     }
15177     if (n == REPLY_CONTINUE) {
15178         aflag++;
15179         if (acct == NULL) {
15180             static char ftpacct[80];
15181             int ok;
15182             setint();
15183             ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
15184                         DEFAULT_UQ_TIMEOUT);
15185             if (ok)
15186                 acct = brstrip(ftpacct);
15187         }
15188         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15189     }
15190     if (n != REPLY_COMPLETE) {
15191         fprintf(stderr, "FTP login failed.\n");
15192         if (haveurl)
15193           doexit(BAD_EXIT,-1);
15194         return(0);
15195     }
15196     if (!aflag && acct != NULL) {
15197         ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15198     }
15199     makestr(&ftp_logname,user);
15200     loggedin = 1;
15201 #ifdef LOCUS
15202     /* Unprefixed file management commands go to server */
15203     if (autolocus && !ftp_cmdlin) {
15204         setlocus(0,1);
15205     }
15206 #endif /* LOCUS */
15207     ftp_init();
15208
15209     if (anonymous && !quiet) {
15210         printf(" Logged in as anonymous (%s)\n",pass);
15211         memset(pass, 0, strlen(pass));
15212     }
15213     if (ftp_rdir) {
15214         if (doftpcwd(ftp_rdir,-1) < 1)
15215           doexit(BAD_EXIT,-1);
15216     }
15217
15218 #ifdef FTP_PROXY
15219     if (proxy)
15220       return(1);
15221 #endif /* FTP_PROXY */
15222     return(1);
15223 }
15224
15225 static int
15226 ftp_reset() {
15227     int rc;
15228 #ifdef BSDSELECT
15229     int nfnd = 1;
15230     fd_set mask;
15231     FD_ZERO(&mask);
15232     while (nfnd > 0) {
15233         FD_SET(csocket, &mask);
15234         if ((nfnd = empty(&mask,0)) < 0) {
15235             perror("reset");
15236             ftpcode = -1;
15237             lostpeer();
15238             return(0);
15239         } else if (nfnd) {
15240             getreply(0,-1,-1,ftp_vbm,0);
15241         }
15242     }
15243 #else /* BSDSELECT */
15244 #ifdef IBMSELECT
15245     int nfnd = 1;
15246     while (nfnd > 0) {
15247         if ((nfnd = empty(&csocket,1,0)) < 0) {
15248             perror("reset");
15249             ftpcode = -1;
15250             lostpeer();
15251             return(0);
15252         } else if (nfnd) {
15253             getreply(0,-1,-1,ftp_vbm,0);
15254         }
15255     }
15256 #endif /* IBMSELECT */
15257 #endif /* BSDSELECT */
15258     rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
15259     if (rc > 0)
15260       loggedin = 0;
15261     return(rc);
15262 }
15263
15264 static int
15265 ftp_rename(from, to) char * from, * to; {
15266     int lcs = -1, rcs = -1;
15267 #ifndef NOCSETS
15268     if (ftp_xla) {
15269         lcs = ftp_csl;
15270         if (lcs < 0) lcs = fcharset;
15271         rcs = ftp_csx;
15272         if (rcs < 0) rcs = ftp_csr;
15273     }
15274 #endif /* NOCSETS */
15275     if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
15276         return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
15277     }
15278     return(0);                          /* Failure */
15279 }
15280
15281 static int
15282 ftp_umask(mask) char * mask; {
15283     int rc;
15284     rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
15285     return(rc);
15286 }
15287
15288 static int
15289 ftp_user(user,pass,acct) char * user, * pass, * acct; {
15290     int n = 0, aflag = 0;
15291     char pwd[PWDSIZ];
15292
15293     if (!auth_type && ftp_aut) {
15294 #ifdef FTP_SRP
15295         if (ck_srp_is_installed()) {
15296             if (srp_ftp_auth( NULL, user, pass)) {
15297                 makestr(&pass,srp_pass);
15298             }
15299         }
15300 #endif /* FTP_SRP */
15301     }
15302     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15303     if (n == REPLY_COMPLETE)
15304       n = ftpcmd("PASS dummy",NULL,0,0,1);
15305     else if (n == REPLY_CONTINUE) {
15306 #ifdef CK_ENCRYPTION
15307         int oldftp_cpl;
15308 #endif /* CK_ENCRYPTION */
15309         if (pass == NULL || !pass[0]) {
15310             int ok;
15311             pwd[0] = '\0';
15312             setint();
15313             ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
15314                         DEFAULT_UQ_TIMEOUT);
15315             if (ok)
15316                 pass = brstrip(pwd);
15317         }
15318
15319 #ifdef CK_ENCRYPTION
15320         if ((oldftp_cpl = ftp_cpl) == PROT_S)
15321           ftp_cpl = PROT_P;
15322 #endif /* CK_ENCRYPTION */
15323         n = ftpcmd("PASS",pass,-1,-1,1);
15324         memset(pass, 0, strlen(pass));
15325 #ifdef CK_ENCRYPTION
15326         /* level may have changed */
15327         if (ftp_cpl == PROT_P)
15328           ftp_cpl = oldftp_cpl;
15329 #endif /* CK_ENCRYPTION */
15330     }
15331     if (n == REPLY_CONTINUE) {
15332         if (acct == NULL || !acct[0]) {
15333             int ok;
15334             pwd[0] = '\0';
15335             setint();
15336             ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
15337                         DEFAULT_UQ_TIMEOUT);
15338             if (ok)
15339                 acct = pwd;
15340         }
15341         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15342         aflag++;
15343     }
15344     if (n != REPLY_COMPLETE) {
15345         printf("Login failed.\n");
15346         return(0);
15347     }
15348     if (!aflag && acct != NULL && acct[0]) {
15349         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15350     }
15351     if (n == REPLY_COMPLETE) {
15352         makestr(&ftp_logname,user);
15353         loggedin = 1;
15354         ftp_init();
15355         return(1);
15356     }
15357     return(0);
15358 }
15359
15360 char *
15361 ftp_authtype() {
15362     if (!connected)
15363       return("NULL");
15364     return(auth_type ? auth_type : "NULL");
15365 }
15366
15367 char *
15368 ftp_cpl_mode() {
15369     switch (ftp_cpl) {
15370       case FPL_CLR:
15371         return("clear");
15372       case FPL_SAF:
15373         return("safe");
15374       case FPL_PRV:
15375         return("private");
15376       case FPL_CON:
15377         return("confidential");
15378       default:
15379         return("(error)");
15380     }
15381 }
15382
15383 char *
15384 ftp_dpl_mode() {
15385     switch (ftp_dpl) {
15386       case FPL_CLR:
15387         return("clear");
15388       case FPL_SAF:
15389         return("safe");
15390       case FPL_PRV:
15391         return("private");
15392       case FPL_CON:
15393         return("confidential");
15394       default:
15395         return("(error)");
15396     }
15397 }
15398
15399
15400 /* remote_files() */
15401 /*
15402    Returns next remote filename on success;
15403    NULL on error or no more files with global rfrc set to:
15404      -1: Bad argument
15405      -2: Server error response to NLST, e.g. file not found
15406      -3: No more files
15407      -9: Internal error
15408 */
15409 #define FTPNAMBUFLEN CKMAXPATH+1024
15410
15411 /* Check: ckmaxfiles CKMAXOPEN */
15412
15413 #define MLSDEPTH 128                    /* Stack of open temp files */
15414 static int mlsdepth = 0;                /* Temp file stack depth */
15415 static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
15416 static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
15417
15418 static VOID
15419 mlsreset() {                            /* Reset MGET temp-file stack */
15420     int i;
15421     for (i = 0; i <= mlsdepth; i++) {
15422         if (tmpfilptr[i]) {
15423             fclose(tmpfilptr[i]);
15424             tmpfilptr[i] = NULL;
15425             if (tmpfilnam[i]) {
15426 #ifdef OS2
15427                 unlink(tmpfilnam[i]);
15428 #endif /* OS2 */
15429                 free(tmpfilnam[i]);
15430             }
15431         }
15432     }
15433     mlsdepth = 0;
15434 }
15435
15436 static CHAR *
15437 #ifdef CK_ANSIC
15438 remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
15439 #else /* CK_ANSIC */
15440 remote_files(new_query, arg, pattern, proxy_switch)
15441     int new_query;
15442     CHAR * arg;                         /* That we send to the server */
15443     CHAR * pattern;                     /* That we use locally */
15444     int proxy_switch;
15445 #endif /* CK_ANSIC */
15446 /* remote_files */ {
15447     static CHAR buf[FTPNAMBUFLEN];
15448     CHAR *cp, *whicharg;
15449     char * cdto = NULL;
15450     char * p;
15451     int i, x, forced = 0;
15452     int lcs = 0, rcs = 0, xlate = 0;
15453
15454     debug(F101,"ftp remote_files new_query","",new_query);
15455     debug(F110,"ftp remote_files arg",arg,0);
15456     debug(F110,"ftp remote_files pattern",pattern,0);
15457
15458     rfrc = -1;
15459     if (pattern)                        /* Treat empty pattern same as NULL */
15460       if (!*pattern)
15461         pattern = NULL;
15462     if (arg)                            /* Ditto for arg */
15463       if (!*arg)
15464         arg = NULL;
15465
15466   again:
15467
15468     if (new_query) {
15469         if (tmpfilptr[mlsdepth]) {
15470             fclose(tmpfilptr[mlsdepth]);
15471             tmpfilptr[mlsdepth] = NULL;
15472 #ifdef OS2
15473             if (!ftp_deb && !deblog)
15474               unlink(tmpfilnam[mlsdepth]);
15475 #endif /* OS2 */
15476         }
15477     }
15478     if (tmpfilptr[mlsdepth] == NULL) {
15479         extern char * tempdir;
15480         char * p;
15481         debug(F110,"ftp remote_files tempdir",tempdir,0);
15482         if (tempdir) {
15483             p = tempdir;
15484         } else {
15485 #ifdef OS2
15486 #ifdef NT
15487             p = getenv("K95TMP");
15488 #else
15489             p = getenv("K2TMP");
15490 #endif /* NT */
15491             if (!p)
15492 #endif /* OS2 */
15493               p = getenv("CK_TMP");
15494             if (!p)
15495               p = getenv("TMPDIR");
15496             if (!p) p = getenv("TEMP");
15497             if (!p) p = getenv("TMP");
15498 #ifdef OS2ORUNIX
15499             if (p) {
15500                 int len = strlen(p);
15501                 if (p[len-1] != '/'
15502 #ifdef OS2
15503                     && p[len-1] != '\\'
15504 #endif /* OS2 */
15505                      ) {
15506                     static char foo[CKMAXPATH];
15507                     ckstrncpy(foo,p,CKMAXPATH);
15508                     ckstrncat(foo,"/",CKMAXPATH);
15509                     p = foo;
15510                 }
15511             } else
15512 #else /* OS2ORUNIX */
15513             if (!p)
15514 #endif /* OS2ORUNIX */
15515 #ifdef UNIX                             /* Systems that have a standard */
15516                 p = "/tmp/";            /* temporary directory... */
15517 #else
15518 #ifdef datageneral
15519             p = ":TMP:";
15520 #else
15521             p = "";
15522 #endif /* datageneral */
15523 #endif /* UNIX */
15524         }
15525         debug(F110,"ftp remote_files p",p,0);
15526
15527         /* Get temp file */
15528
15529         if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
15530             ckmakmsg((char *)tmpfilnam[mlsdepth],
15531                      CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
15532         } else {
15533             printf("?Malloc failure: remote_files()\n");
15534             return(NULL);
15535         }
15536
15537 #ifdef NT
15538         {
15539             char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
15540             if ( tmpfil )
15541                 ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
15542         }
15543 #else /* NT */
15544 #ifdef MKTEMP
15545 #ifdef MKSTEMP
15546         x = mkstemp((char *)tmpfilnam[mlsdepth]);
15547         if (x > -1) close(x);           /* We just want the name. */
15548 #else
15549         mktemp((char *)tmpfilnam[mlsdepth]);
15550 #endif /* MKSTEMP */
15551         /* if no mktmpnam() the name will just be "ckXXXXXX"... */
15552 #endif /* MKTEMP */
15553 #endif /* NT */
15554
15555         debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
15556               tmpfilnam[mlsdepth],mlsdepth);
15557
15558 #ifdef FTP_PROXY
15559         if (proxy_switch) {
15560             pswitch(!proxy);
15561         }
15562 #endif /* FTP_PROXY */
15563
15564         debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
15565         debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
15566         debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
15567
15568 #ifndef NOCSETS
15569         xlate = ftp_xla;                /* SET FTP CHARACTER-SET-TRANSLATION */
15570         if (xlate) {                    /* ON? */
15571             lcs = ftp_csl;              /* Local charset */
15572             if (lcs < 0) lcs = fcharset;
15573             if (lcs < 0) xlate = 0;
15574         }
15575         if (xlate) {                    /* Still ON? */
15576             rcs = ftp_csx;              /* Remote (Server) charset */
15577             if (rcs < 0) rcs = ftp_csr;
15578             if (rcs < 0) xlate = 0;
15579         }
15580 #endif /* NOCSETS */
15581
15582         forced = mgetforced;            /* MGET method forced? */
15583         if (!forced || !mgetmethod)     /* Not forced... */
15584           mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
15585               SND_MLS :
15586               SND_NLS; 
15587 /*                                           
15588   User's Command:                 Result:
15589     mget /nlst                     NLST (NULL)
15590     mget /nlst foo                 NLST foo
15591     mget /nlst *.txt               NLST *.txt 
15592     mget /nlst /match:*.txt        NLST (NULL)
15593     mget /nlst /match:*.txt  foo   NLST foo   
15594     mget /mlsd                     MLSD (NULL)
15595     mget /mlsd foo                 MLSD foo
15596     mget /mlsd *.txt               MLSD (NULL)
15597     mget /mlsd /match:*.txt        MLSD (NULL)
15598     mget /mlsd /match:*.txt  foo   MLSD foo
15599 */
15600         x = -1;
15601         while (x < 0) {
15602             if (pattern) {              /* Don't simplify this! */
15603                 whicharg = arg;
15604             } else if (mgetmethod == SND_MLS) {
15605                 if (arg)
15606                   whicharg = iswild((char *)arg) ? NULL : arg;
15607                 else
15608                   whicharg = NULL;
15609             } else {
15610                 whicharg = arg;
15611             }
15612             debug(F110,"ftp remote_files mgetmethod",
15613                   mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
15614             debug(F110,"ftp remote_files whicharg",whicharg,0);
15615
15616             x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
15617                             (char *)tmpfilnam[mlsdepth],
15618                             (char *)whicharg,
15619                             "wb",
15620                             0,
15621                             0,
15622                             NULL,
15623                             xlate,
15624                             lcs,
15625                             rcs
15626                             );
15627             if (x < 0) {                /* Chosen method wasn't accepted */
15628                 if (forced) {
15629                     if (ftpcode > 500 && ftpcode < 505 && !quiet)
15630                       printf("?%s: Not supported by server\n",
15631                              mgetmethod == SND_MLS ? "MLSD" : "NLST"
15632                              );
15633                     rfrc = -2;          /* Fail */
15634                     return(NULL);
15635                 }
15636                 /* Not forced - if MLSD failed, try NLST */
15637                 if (mgetmethod == SND_MLS) {  /* Server lied about MLST */
15638                     sfttab[SFT_MLST] = 0;     /* So disable it */
15639                     mlstok = 0;               /* and */
15640                     mgetmethod = SND_NLS;     /* try NLST */
15641                     continue;
15642                 }
15643                 rfrc = -2;
15644                 return(NULL);
15645             }
15646         }
15647 #ifdef FTP_PROXY
15648         if (proxy_switch) {
15649             pswitch(!proxy);
15650         }
15651 #endif /* FTP_PROXY */
15652         tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
15653 #ifndef OS2
15654         if (tmpfilptr[mlsdepth]) {
15655             if (!ftp_deb && !deblog)
15656               unlink(tmpfilnam[mlsdepth]);
15657         }
15658 #endif /* OS2 */
15659       notemp:
15660         if (!tmpfilptr[mlsdepth]) {
15661             debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
15662             if ((!dpyactive || ftp_deb))
15663               printf("?Can't find list of remote files, oops\n");
15664             rfrc = -9;
15665             return(NULL);
15666         }
15667         if (ftp_deb)
15668           printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
15669     }
15670     buf[0] = NUL;
15671     buf[FTPNAMBUFLEN-1] = NUL;
15672     buf[FTPNAMBUFLEN-2] = NUL;
15673
15674     /* We have to redo all this because the first time was only for */
15675     /* for getting the file list, now it's for getting each file */
15676
15677     if (arg && mgetmethod == SND_MLS) { /* MLSD */
15678         if (!pattern && iswild((char *)arg)) {
15679             pattern = arg;              /* Wild arg is really a pattern */
15680             if (pattern)
15681               if (!*pattern)
15682                 pattern = NULL;
15683             arg = NULL;                 /* and not an arg */
15684         }
15685         if (new_query) {                /* Initial query? */
15686             cdto = (char *)arg;         /* (nonwild) arg given? */
15687             if (cdto)
15688               if (!*cdto)
15689                 cdto = NULL;
15690             if (cdto)                   /* If so, then CD to it */
15691               doftpcwd(cdto,0);
15692         }
15693     }
15694     new_query = 0;
15695
15696     if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
15697         fclose(tmpfilptr[mlsdepth]);
15698         tmpfilptr[mlsdepth] = NULL;
15699
15700 #ifdef OS2
15701         if (!ftp_deb && !deblog)
15702           unlink(tmpfilnam[mlsdepth]);
15703 #endif /* OS2 */
15704         if (ftp_deb && !deblog) {
15705             printf("(Temporary file %s NOT deleted)\n",
15706                    (char *)tmpfilnam[mlsdepth]);
15707         }
15708         if (mlsdepth <= 0) {            /* EOF at depth 0 */
15709             rfrc = -3;                  /* means we're done */
15710             return(NULL);
15711         }
15712         printf("POPPING(%d)...\n",mlsdepth-1); 
15713         if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
15714         mlsdepth--;
15715         doftpcdup();
15716         zchdir("..");                   /* <-- Not portable */
15717         goto again;
15718     }
15719     if (buf[FTPNAMBUFLEN-1]) {
15720         printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
15721                FTPNAMBUFLEN
15722                );
15723         debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
15724         return(NULL);
15725     }
15726     /* debug(F110,"ftp remote_files buf 1",buf,0); */
15727     if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
15728       *cp = '\0';
15729     if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
15730       *cp = '\0';
15731     debug(F110,"ftp remote_files buf",buf,0);
15732     rfrc = 0;
15733
15734     if (ftp_deb)
15735       printf("[%s]\n",(char *)buf);
15736
15737     havesize = (CK_OFF_T)-1;            /* Initialize file facts... */
15738     havetype = 0;
15739     makestr(&havemdtm,NULL);
15740     p = (char *)buf;
15741
15742     if (mgetmethod == SND_NLS) {        /* NLST... */
15743         if (pattern) {
15744             if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15745               goto again;
15746         }
15747     } else {                            /* MLSD... */
15748         p = parsefacts((char *)buf);
15749         switch (havetype) {
15750           case FTYP_FILE:               /* File: Get it if it matches */
15751             if (pattern) {
15752                 if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15753                   goto again;
15754             }
15755             break;
15756           case FTYP_CDIR:               /* Current directory */
15757           case FTYP_PDIR:               /* Parent directory */
15758             goto again;                 /* Skip */
15759           case FTYP_DIR:                /* (Sub)Directory */
15760             if (!recursive)             /* If not /RECURSIVE */
15761               goto again;               /* Skip */
15762             if (mlsdepth < MLSDEPTH) {
15763                 char * p2 = NULL;
15764                 mlsdepth++;
15765                 printf("RECURSING [%s](%d)...\n",p,mlsdepth); 
15766                 if (doftpcwd(p,0) > 0) {
15767                     int x;
15768                     if (!ckstrchr(p,'/')) {
15769                         /* zmkdir() needs dirsep */
15770                         if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
15771                             strcpy(p2,p);       /* SAFE */
15772                             strcat(p2,"/");     /* SAFE */
15773                             p = p2;
15774                         }
15775                     }
15776 #ifdef NOMKDIR
15777                     x = -1;
15778 #else
15779                     x = zmkdir(p);
15780 #endif /* NOMKDIR */
15781                     if (x > -1) {
15782                         zchdir(p);
15783                         p = (char *)remote_files(1,arg,pattern,0);
15784                         if (p2) free(p2);
15785                     } else {
15786                         printf("?mkdir failed: [%s] Depth=%d\n",
15787                                p,
15788                                mlsdepth
15789                                );
15790                         mlsreset();
15791                         if (p2) free(p2);
15792                         return(NULL);
15793                     }
15794                 } else {
15795                     printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
15796                     mlsreset();
15797                     return(NULL);
15798                 }
15799             } else {
15800                 printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
15801                        mlsdepth
15802                        );
15803                 mlsreset();
15804                 return(NULL);
15805             }
15806         }
15807     }
15808
15809 #ifdef DEBUG
15810     if (deblog) {
15811         debug(F101,"remote_files havesize","",havesize);
15812         debug(F101,"remote_files havetype","",havetype);
15813         debug(F110,"remote_files havemdtm",havemdtm,0); 
15814         debug(F110,"remote_files name",p,0);    
15815     }
15816 #endif /* DEBUG */
15817     return((CHAR *)p);
15818 }
15819
15820 /* N O T  P O R T A B L E !!! */
15821
15822 #if (SIZEOF_SHORT == 4)
15823 typedef unsigned short ftp_uint32;
15824 typedef short ftp_int32;
15825 #else
15826 #if (SIZEOF_INT == 4)
15827 typedef unsigned int ftp_uint32;
15828 typedef int ftp_int32;
15829 #else
15830 #if (SIZEOF_LONG == 4)
15831 typedef ULONG ftp_uint32;
15832 typedef long ftp_int32;
15833 #endif
15834 #endif
15835 #endif
15836
15837 /* Perhaps use these in general, certainly use them for GSSAPI */
15838
15839 #ifndef looping_write
15840 #define ftp_int32 int
15841 #define ftp_uint32 unsigned int
15842 static int
15843 looping_write(fd, buf, len)
15844     int fd;
15845     register CONST char *buf;
15846     int len;
15847 {
15848     int cc;
15849     register int wrlen = len;
15850     do {
15851         cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
15852         if (cc < 0) {
15853             if (errno == EINTR)
15854               continue;
15855             return(cc);
15856         } else {
15857             buf += cc;
15858             wrlen -= cc;
15859         }
15860     } while (wrlen > 0);
15861     return(len);
15862 }
15863 #endif
15864 #ifndef looping_read
15865 static int
15866 looping_read(fd, buf, len)
15867     int fd;
15868     register char *buf;
15869     register int len;
15870 {
15871     int cc, len2 = 0;
15872
15873     do {
15874         cc = recv(fd, (char *)buf, len,0);
15875         if (cc < 0) {
15876             if (errno == EINTR)
15877               continue;
15878             return(cc);                 /* errno is already set */
15879         } else if (cc == 0) {
15880             return(len2);
15881         } else {
15882             buf += cc;
15883             len2 += cc;
15884             len -= cc;
15885         }
15886     } while (len > 0);
15887     return(len2);
15888 }
15889 #endif /* looping_read */
15890
15891 #define ERR -2
15892
15893 #ifdef COMMENT
15894 static
15895 secure_putbyte(fd, c) int fd; CHAR c; {
15896     int ret;
15897
15898     ucbuf[nout++] = c;
15899     if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
15900         nout = 0;
15901         if (!ftpissecure())
15902           ret = send(fd, (SENDARG2TYPE)ucbuf,
15903                      (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
15904         else
15905           ret = secure_putbuf(fd,
15906                               ucbuf,
15907                               (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
15908                               );
15909         return(ret?ret:c);
15910     }
15911     return(c);
15912 }
15913 #endif /* COMMENT */
15914
15915 /* returns:
15916  *       0  on success
15917  *      -1  on error (errno set)
15918  *      -2  on security error
15919  */
15920 static int
15921 secure_flush(fd) int fd; {
15922     int rc = 0;
15923     int len = 0;
15924
15925     if (nout > 0) {
15926         len = nout;
15927         if (!ftpissecure()) {
15928             rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
15929             nout = 0;
15930             goto xflush;
15931         } else {
15932             rc = secure_putbuf(fd, ucbuf, nout);
15933             if (rc)
15934               goto xflush;
15935         }
15936     }
15937     rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
15938
15939   xflush:
15940     if (rc > -1 && len > 0 && fdispla != XYFD_B) {
15941         spackets++;
15942         spktl = len;
15943         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
15944     }
15945     return(rc);
15946 }
15947
15948 #ifdef COMMENT                          /* (not used) */
15949 /* returns:
15950  *      c>=0  on success
15951  *      -1    on error
15952  *      -2    on security error
15953  */
15954 static int
15955 #ifdef CK_ANSIC
15956 secure_putc(char c, int fd)
15957 #else
15958 secure_putc(c, fd) char c; int fd;
15959 #endif /* CK_ANSIC */
15960 /* secure_putc */ {
15961     return(secure_putbyte(fd, (CHAR) c));
15962 }
15963 #endif /* COMMENT */
15964
15965 /* returns:
15966  *      nbyte on success
15967  *      -1  on error (errno set)
15968  *      -2  on security error
15969  */
15970 static int
15971 #ifdef CK_ANSIC
15972 secure_write(int fd, CHAR * buf, unsigned int nbyte)
15973 #else
15974 secure_write(fd, buf, nbyte)
15975     int fd;
15976     CHAR * buf;
15977     unsigned int nbyte;
15978 #endif /* CK_ANSIC */
15979 {
15980     int ret;
15981
15982 #ifdef FTP_TIMEOUT    
15983     ftp_timed_out = 0;
15984     if (check_data_connection(fd,1) < 0) {
15985         ftp_timed_out = 1;
15986         return(-3);
15987     }
15988 #endif  /* FTP_TIMEOUT */
15989
15990     if (!ftpissecure()) {
15991         if (nout > 0) {
15992             if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
15993               return(ret);
15994             nout = 0;
15995         }
15996         return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
15997     } else {
15998         int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
15999         int bsent = 0;
16000
16001         while (bsent < nbyte) {
16002             int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
16003                         (ucbuflen - nout) : (nbyte - bsent));
16004 #ifdef DEBUG
16005             if (deblog) {
16006                 debug(F101,"secure_write ucbuflen","",ucbuflen);
16007                 debug(F101,"secure_write ucbufsiz","",ucbufsiz);
16008                 debug(F101,"secure_write bsent","",bsent);
16009                 debug(F101,"secure_write b2cp","",b2cp);
16010             }
16011 #endif /* DEBUG */
16012             memcpy(&ucbuf[nout],&buf[bsent],b2cp);
16013             nout += b2cp;
16014             bsent += b2cp;
16015
16016             if (nout == ucbuflen) {
16017                 nout = 0;
16018                 ret = secure_putbuf(fd, ucbuf, ucbuflen);
16019                 if (ret < 0)
16020                   return(ret);
16021             }
16022         }
16023         return(bsent);
16024     }
16025 }
16026
16027 /* returns:
16028  *       0  on success
16029  *      -1  on error (errno set)
16030  *      -2  on security error
16031  */
16032 static int
16033 #ifdef CK_ANSIC
16034 secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
16035 #else
16036 secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
16037 #endif /* CK_ANSIC */
16038 {
16039     static char *outbuf = NULL;         /* output ciphertext */
16040 #ifdef FTP_SECURITY
16041     static unsigned int bufsize = 0;    /* size of outbuf */
16042 #endif /* FTP_SECURITY */
16043     ftp_int32 length   = 0;
16044     ftp_uint32 net_len = 0;
16045
16046     /* Other auth types go here ... */
16047 #ifdef CK_SSL
16048     if (ssl_ftp_data_active_flag) {
16049         int count, error;
16050
16051         /* there is no need to send an empty buffer when using SSL/TLS */
16052         if ( nbyte == 0 )
16053           return(0);
16054
16055         count = SSL_write(ssl_ftp_data_con, buf, nbyte);
16056         error = SSL_get_error(ssl_ftp_data_con,count);
16057         switch (error) {
16058           case SSL_ERROR_NONE:
16059             return(0);
16060           case SSL_ERROR_WANT_WRITE:
16061           case SSL_ERROR_WANT_READ:
16062           case SSL_ERROR_SYSCALL:
16063 #ifdef NT
16064             {
16065                 int gle = GetLastError();
16066                 if (gle == 0)
16067                   return(0);
16068                 debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
16069             }
16070 #endif /* NT */
16071           case SSL_ERROR_WANT_X509_LOOKUP:
16072           case SSL_ERROR_SSL:
16073           case SSL_ERROR_ZERO_RETURN:
16074           default:
16075             SSL_shutdown(ssl_ftp_data_con);
16076             SSL_free(ssl_ftp_data_con);
16077             ssl_ftp_data_active_flag = 0;
16078             ssl_ftp_data_con = NULL;
16079 #ifdef TCPIPLIB
16080             socket_close(data);
16081 #else /* TCPIPLIB */
16082 #ifdef USE_SHUTDOWN
16083             shutdown(data, 1+1);
16084 #endif /* USE_SHUTDOWN */
16085             close(data);
16086 #endif /* TCPIPLIB */
16087             data = -1;
16088             globaldin = data;
16089             return(-1);
16090         }
16091         return(-1);
16092     }
16093 #endif /* CK_SSL */
16094
16095 #ifdef FTP_SRP
16096     if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
16097         if (bufsize < nbyte + FUDGE_FACTOR) {
16098             if (outbuf?
16099                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16100                 (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16101                 bufsize = nbyte + FUDGE_FACTOR;
16102             } else {
16103                 bufsize = 0;
16104                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16105                 return(ERR);
16106             }
16107         }
16108         if ((length =
16109              srp_encode(ftp_dpl == FPL_PRV,
16110                         (CHAR *) buf,
16111                         (CHAR *) outbuf,
16112                         nbyte
16113                         )
16114              ) < 0) {
16115             secure_error ("srp_encode failed");
16116             return ERR;
16117         }
16118     }
16119 #endif /* FTP_SRP */
16120 #ifdef FTP_KRB4
16121     if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
16122         struct sockaddr_in myaddr, hisaddr;
16123         GSOCKNAME_T len;
16124         len = sizeof(myaddr);
16125         if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16126             secure_error("secure_putbuf: getsockname failed");
16127             return(ERR);
16128         }
16129         len = sizeof(hisaddr);
16130         if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16131             secure_error("secure_putbuf: getpeername failed");
16132             return(ERR);
16133         }
16134         if (bufsize < nbyte + FUDGE_FACTOR) {
16135             if (outbuf ?
16136                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16137                  (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16138                 bufsize = nbyte + FUDGE_FACTOR;
16139             } else {
16140                 bufsize = 0;
16141                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16142                 return(ERR);
16143             }
16144         }
16145         if (ftp_dpl == FPL_PRV) {
16146             length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
16147                                  ftp_sched,
16148 #ifdef KRB524
16149                                  ftp_cred.session,
16150 #else /* KRB524 */
16151                                  &ftp_cred.session,
16152 #endif /* KRB524 */
16153                                  &myaddr,
16154                                  &hisaddr
16155                                  );
16156         } else {
16157             length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
16158 #ifdef KRB524
16159                                  ftp_cred.session,
16160 #else /* KRB524 */
16161                                  &ftp_cred.session,
16162 #endif /* KRB524 */
16163                                  &myaddr,
16164                                  &hisaddr
16165                                  );
16166         }
16167         if (length == -1) {
16168             secure_error("krb_mk_%s failed for KERBEROS_V4",
16169                          ftp_dpl == FPL_PRV ? "priv" : "safe");
16170             return(ERR);
16171         }
16172     }
16173 #endif /* FTP_KRB4 */
16174 #ifdef FTP_GSSAPI
16175     if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
16176         gss_buffer_desc in_buf, out_buf;
16177         OM_uint32 maj_stat, min_stat;
16178         int conf_state;
16179
16180         in_buf.value = buf;
16181         in_buf.length = nbyte;
16182         maj_stat = gss_seal(&min_stat, gcontext,
16183                             (ftp_dpl == FPL_PRV), /* confidential */
16184                             GSS_C_QOP_DEFAULT,
16185                             &in_buf,
16186                             &conf_state,
16187                             &out_buf
16188                             );
16189         if (maj_stat != GSS_S_COMPLETE) {
16190             /* generally need to deal */
16191             /* ie. should loop, but for now just fail */
16192             user_gss_error(maj_stat, min_stat,
16193                            ftp_dpl == FPL_PRV?
16194                            "GSSAPI seal failed":
16195                            "GSSAPI sign failed");
16196             return(ERR);
16197         }
16198         if (bufsize < out_buf.length) {
16199             if (outbuf ?
16200                 (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
16201                 (outbuf = malloc((unsigned) out_buf.length))) {
16202                 bufsize = out_buf.length;
16203             } else {
16204                 bufsize = 0;
16205                 secure_error("%s (in malloc of PROT buffer)",
16206                              ck_errstr());
16207                 return(ERR);
16208             }
16209         }
16210         memcpy(outbuf, out_buf.value, length=out_buf.length);
16211         gss_release_buffer(&min_stat, &out_buf);
16212     }
16213 #endif /* FTP_GSSAPI */
16214     net_len = htonl((ULONG) length);
16215     if (looping_write(fd, (char *)&net_len, 4) == -1)
16216       return(-1);
16217     if (looping_write(fd, outbuf, length) != length)
16218       return(-1);
16219     return(0);
16220 }
16221
16222
16223 /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
16224
16225 static int
16226 secure_getbyte(fd,fc) int fd,fc; {
16227     /* number of chars in ucbuf, pointer into ucbuf */
16228     static unsigned int nin = 0, bufp = 0;
16229     int kerror;
16230     ftp_uint32 length;
16231
16232     if (fc) {
16233         nin = bufp = 0;
16234         ucbuf[0] = NUL;
16235         return(0);
16236     }
16237     if (nin == 0) {
16238         if (iscanceled())
16239           return(-9);
16240
16241 #ifdef FTP_TIMEOUT
16242         if (check_data_connection(fd,0) < 0)
16243           return(-3);
16244 #endif  /* FTP_TIMEOUT */
16245
16246 #ifdef CK_SSL
16247         if (ssl_ftp_data_active_flag) {
16248             int count, error;
16249             count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
16250             error = SSL_get_error(ssl_ftp_data_con,count);
16251 #ifdef DEBUG
16252             if (error != SSL_ERROR_NONE)
16253               debug(F101,"ftp secure_getbyte error","",error);
16254             if (count == 0)
16255               debug(F101,"ftp secure_getbyte count","",count);
16256 #endif  /* DEBUG */
16257             switch (error) {
16258               case SSL_ERROR_NONE:
16259                 if (count > 0) {
16260                     nin = bufp = count;
16261                     rpackets++;
16262                     pktnum++;
16263                     if (fdispla != XYFD_B) {
16264                         rpktl = count;
16265                         ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16266                     }
16267                     break;
16268                 }
16269               case SSL_ERROR_WANT_WRITE:
16270               case SSL_ERROR_WANT_READ:
16271               case SSL_ERROR_SYSCALL:
16272 #ifdef NT
16273                 {
16274                     int gle = GetLastError();
16275                 }
16276 #endif /* NT */
16277               case SSL_ERROR_WANT_X509_LOOKUP:
16278               case SSL_ERROR_SSL:
16279               case SSL_ERROR_ZERO_RETURN:
16280               default:
16281                 nin = bufp = count = 0;
16282                 SSL_shutdown(ssl_ftp_data_con);
16283                 SSL_free(ssl_ftp_data_con);
16284                 ssl_ftp_data_active_flag = 0;
16285                 ssl_ftp_data_con = NULL;
16286 #ifdef TCPIPLIB
16287                 socket_close(data);
16288 #else /* TCPIPLIB */
16289 #ifdef USE_SHUTDOWN
16290                 shutdown(data, 1+1);
16291 #endif /* USE_SHUTDOWN */
16292                 close(data);
16293 #endif /* TCPIPLIB */
16294                 data = -1;
16295                 globaldin = data;
16296                 break;
16297             }
16298         } else
16299 #endif /* CK_SSL */
16300           {
16301               kerror = looping_read(fd, (char *)&length, sizeof(length));
16302               if (kerror != sizeof(length)) {
16303                   secure_error("Couldn't read PROT buffer length: %d/%s",
16304                                kerror,
16305                                kerror == -1 ? ck_errstr()
16306                                : "premature EOF"
16307                                );
16308                   return(ERR);
16309               }
16310               debug(F101,"secure_getbyte length","",length);
16311               debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
16312
16313               length = (ULONG) ntohl(length);
16314               if (length > maxbuf) {
16315                   secure_error("Length (%d) of PROT buffer > PBSZ=%u",
16316                                length,
16317                                maxbuf
16318                                );
16319                   return(ERR);
16320               }
16321               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
16322                   secure_error("Couldn't read %u byte PROT buffer: %s",
16323                                length,
16324                                kerror == -1 ? ck_errstr() : "premature EOF"
16325                                );
16326                   return(ERR);
16327               }
16328
16329               /* Other auth types go here ... */
16330 #ifdef FTP_SRP
16331               if (strcmp(auth_type, "SRP") == 0) {
16332                   if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
16333                                                 (CHAR *) ucbuf,
16334                                                 ucbuf,
16335                                                 length
16336                                                 )
16337                        ) == -1) {
16338                       secure_error ("srp_encode failed" );
16339                       return ERR;
16340                   }
16341               }
16342 #endif /* FTP_SRP */
16343 #ifdef FTP_KRB4
16344               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
16345                   struct sockaddr_in myaddr, hisaddr;
16346                   GSOCKNAME_T len;
16347                   len = sizeof(myaddr);
16348                   if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16349                       secure_error("secure_putbuf: getsockname failed");
16350                       return(ERR);
16351                   }
16352                   len = sizeof(hisaddr);
16353                   if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16354                       secure_error("secure_putbuf: getpeername failed");
16355                       return(ERR);
16356                   }
16357                   if (ftp_dpl) {
16358                       kerror = krb_rd_priv(ucbuf, length, ftp_sched,
16359 #ifdef KRB524
16360                                            ftp_cred.session,
16361 #else /* KRB524 */
16362                                            &ftp_cred.session,
16363 #endif /* KRB524 */
16364                                            &hisaddr, &myaddr, &ftp_msg_data);
16365                   } else {
16366                       kerror = krb_rd_safe(ucbuf, length,
16367 #ifdef KRB524
16368                                            ftp_cred.session,
16369 #else /* KRB524 */
16370                                            &ftp_cred.session,
16371 #endif /* KRB524 */
16372                                            &hisaddr, &myaddr, &ftp_msg_data);
16373                   }
16374                   if (kerror) {
16375                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
16376                                    ftp_dpl == FPL_PRV ? "priv" : "safe",
16377                                    krb_get_err_text(kerror));
16378                       return(ERR);
16379                   }
16380                   memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
16381                   nin = bufp = ftp_msg_data.app_length;
16382               }
16383 #endif /* FTP_KRB4 */
16384 #ifdef FTP_GSSAPI
16385               if (strcmp(auth_type, "GSSAPI") == 0) {
16386                   gss_buffer_desc xmit_buf, msg_buf;
16387                   OM_uint32 maj_stat, min_stat;
16388                   int conf_state;
16389
16390                   xmit_buf.value = ucbuf;
16391                   xmit_buf.length = length;
16392                   conf_state = (ftp_dpl == FPL_PRV);
16393                   /* decrypt/verify the message */
16394                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
16395                                         &msg_buf, &conf_state, NULL);
16396                   if (maj_stat != GSS_S_COMPLETE) {
16397                       user_gss_error(maj_stat, min_stat,
16398                                      (ftp_dpl == FPL_PRV)?
16399                                      "failed unsealing ENC message":
16400                                      "failed unsealing MIC message");
16401                       return ERR;
16402                   }
16403                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
16404                   gss_release_buffer(&min_stat, &msg_buf);
16405               }
16406 #endif /* FTP_GSSAPI */
16407               /* Other auth types go here ... */
16408
16409               /* Update file transfer display */
16410               rpackets++;
16411               pktnum++;
16412               if (fdispla != XYFD_B) {
16413                   rpktl = nin;
16414                   ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16415               }
16416           }
16417     }
16418     if (nin == 0)
16419       return(EOF);
16420     else
16421       return(ucbuf[bufp - nin--]);
16422 }
16423
16424 /* secure_getc(fd,fc)
16425  * Call with:
16426  *   fd = file descriptor for connection.
16427  *   fc = 0 to get a character, fc != 0 to initialize buffer pointers.
16428  * Returns:
16429  *   c>=0 on success (character value)
16430  *   -1   on EOF
16431  *   -2   on security error
16432  *   -3   on timeout (if built with FTP_TIMEOUT defined)
16433  */
16434 static int
16435 secure_getc(fd,fc) int fd,fc; {         /* file descriptor, function code */
16436
16437     if (!ftpissecure()) {
16438         static unsigned int nin = 0, bufp = 0;
16439         if (fc) {
16440             nin = bufp = 0;
16441             ucbuf[0] = NUL;
16442             return(0);
16443         }
16444         if (nin == 0) {
16445             if (iscanceled())
16446               return(-9);
16447
16448 #ifdef FTP_TIMEOUT
16449             if (check_data_connection(fd,0) < 0) {
16450                 debug(F100,"secure_getc TIMEOUT","",0);
16451                 nin = bufp = 0;
16452                 ftp_timed_out = 1;
16453                 return(-3);
16454             }           
16455 #endif  /* FTP_TIMEOUT */
16456
16457             nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
16458             if ((nin == 0) || (nin == (unsigned int)-1)) {
16459                 debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
16460                 debug(F101,"secure_getc returns EOF","",EOF);
16461                 nin = bufp = 0;
16462                 return(EOF);
16463             }
16464             debug(F101,"ftp secure_getc recv","",nin);
16465             ckhexdump("ftp secure_getc recv",ucbuf,16);
16466             rpackets++;
16467             pktnum++;
16468             if (fdispla != XYFD_B) {
16469                 rpktl = nin;
16470                 ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16471             }
16472         }
16473         return(ucbuf[bufp - nin--]);
16474     } else
16475       return(secure_getbyte(fd,fc));
16476 }
16477
16478 /* returns:
16479  *     n>0  on success (n == # of bytes read)
16480  *       0  on EOF
16481  *      -1  on error (errno set), only for FPL_CLR
16482  *      -2  on security error
16483  */
16484 static int
16485 secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
16486     static int c = 0;
16487     int i;
16488
16489     debug(F101,"secure_read bytes requested","",nbyte);
16490     if (c == EOF)
16491       return(c = 0);
16492     for (i = 0; nbyte > 0; nbyte--) {
16493         c = secure_getc(fd,0);
16494         switch (c) {
16495           case -9:                      /* Canceled from keyboard */
16496             debug(F101,"ftp secure_read interrupted","",c);
16497             return(0);
16498           case ERR:
16499             debug(F101,"ftp secure_read error","",c);
16500             return(c);
16501           case EOF:
16502             debug(F101,"ftp secure_read EOF","",c);
16503             if (!i)
16504               c = 0;
16505             return(i);
16506 #ifdef FTP_TIMEOUT
16507           case -3:
16508             debug(F101,"ftp secure_read timeout","",c);
16509             return(c);
16510 #endif  /* FTP_TIMEOUT */
16511           default:
16512             buf[i++] = c;
16513         }
16514     }
16515     return(i);
16516 }
16517
16518 #ifdef USE_RUSERPASS
16519 /* BEGIN_RUSERPASS
16520  *
16521  * Copyright (c) 1985 Regents of the University of California.
16522  * All rights reserved.
16523  *
16524  * Redistribution and use in source and binary forms, with or without
16525  * modification, are permitted provided that the following conditions
16526  * are met:
16527  * 1. Redistributions of source code must retain the above copyright
16528  *    notice, this list of conditions and the following disclaimer.
16529  * 2. Redistributions in binary form must reproduce the above copyright
16530  *    notice, this list of conditions and the following disclaimer in the
16531  *    documentation and/or other materials provided with the distribution.
16532  * 3. All advertising materials mentioning features or use of this software
16533  *    must display the following acknowledgement:
16534  *      This product includes software developed by the University of
16535  *      California, Berkeley and its contributors.
16536  * 4. Neither the name of the University nor the names of its contributors
16537  *    may be used to endorse or promote products derived from this software
16538  *    without specific prior written permission.
16539  *
16540  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16541  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16542  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16543  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
16544  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16545  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
16546  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16547  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16548  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16549  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
16550  * SUCH DAMAGE.
16551  */
16552
16553 #ifndef lint
16554 static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
16555 #endif /* not lint */
16556
16557 #ifndef MAXHOSTNAMELEN
16558 #define MAXHOSTNAMELEN 64
16559 #endif
16560
16561 char * renvlook();
16562 static FILE * cfile;
16563
16564 #define DEFAULT 1
16565 #define LOGIN   2
16566 #define PASSWD  3
16567 #define ACCOUNT 4
16568 #define MACDEF  5
16569 #define ID      10
16570 #define MACH    11
16571
16572 static char tokval[100];
16573
16574 static struct toktab {
16575     char *tokstr;
16576     int tval;
16577 } toktab[]= {
16578     "default",  DEFAULT,
16579     "login",    LOGIN,
16580     "password", PASSWD,
16581     "passwd",   PASSWD,
16582     "account",  ACCOUNT,
16583     "machine",  MACH,
16584     "macdef",   MACDEF,
16585     0,          0
16586 };
16587
16588 static int
16589 token() {
16590     char *cp;
16591     int c;
16592     struct toktab *t;
16593
16594     if (feof(cfile))
16595       return(0);
16596     while ((c = getc(cfile)) != EOF &&
16597            (c == '\n' || c == '\t' || c == ' ' || c == ','))
16598       continue;
16599     if (c == EOF)
16600       return(0);
16601     cp = tokval;
16602     if (c == '"') {
16603         while ((c = getc(cfile)) != EOF && c != '"') {
16604             if (c == '\\')
16605               c = getc(cfile);
16606             *cp++ = c;
16607         }
16608     } else {
16609         *cp++ = c;
16610         while ((c = getc(cfile)) != EOF
16611                && c != '\n' && c != '\t' && c != ' ' && c != ',') {
16612             if (c == '\\')
16613               c = getc(cfile);
16614             *cp++ = c;
16615         }
16616     }
16617     *cp = 0;
16618     if (tokval[0] == 0)
16619       return(0);
16620     for (t = toktab; t->tokstr; t++)
16621       if (!strcmp(t->tokstr, tokval))
16622         return(t->tval);
16623     return(ID);
16624 }
16625
16626 ruserpass(host, aname, apass, aacct)
16627     char *host, **aname, **apass, **aacct;
16628 {
16629     char *hdir, buf[FTP_BUFSIZ], *tmp;
16630     char myname[MAXHOSTNAMELEN], *mydomain;
16631     int t, i, c, usedefault = 0;
16632 #ifdef NT
16633     struct _stat stb;
16634 #else /* NT */
16635     struct stat stb;
16636 #endif /* NT */
16637
16638     hdir = getenv("HOME");
16639     if (hdir == NULL)
16640         hdir = ".";
16641     ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
16642     cfile = fopen(buf, "r");
16643     if (cfile == NULL) {
16644         if (errno != ENOENT)
16645           perror(buf);
16646         return(0);
16647     }
16648     if (gethostname(myname, MAXHOSTNAMELEN) < 0)
16649       myname[0] = '\0';
16650     if ((mydomain = ckstrchr(myname, '.')) == NULL)
16651       mydomain = "";
16652
16653   next:
16654     while ((t = token())) switch(t) {
16655
16656       case DEFAULT:
16657         usedefault = 1;
16658         /* FALL THROUGH */
16659
16660       case MACH:
16661         if (!usedefault) {
16662             if (token() != ID)
16663               continue;
16664             /*
16665              * Allow match either for user's input host name
16666              * or official hostname.  Also allow match of
16667              * incompletely-specified host in local domain.
16668              */
16669             if (ckstrcmp(host, tokval,-1,1) == 0)
16670               goto match;
16671             if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
16672               goto match;
16673             if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
16674                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16675                 ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
16676                 tokval[tmp - ftp_host] == '\0')
16677               goto match;
16678             if ((tmp = ckstrchr(host, '.')) != NULL &&
16679                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16680                 ckstrcmp(host, tokval, tmp - host, 0) == 0 &&
16681                 tokval[tmp - host] == '\0')
16682               goto match;
16683             continue;
16684         }
16685
16686       match:
16687         while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
16688
16689           case LOGIN:
16690             if (token())
16691               if (*aname == 0) {
16692                   *aname = malloc((unsigned) strlen(tokval) + 1);
16693                   strcpy(*aname, tokval);      /* safe */
16694               } else {
16695                   if (strcmp(*aname, tokval))
16696                     goto next;
16697               }
16698             break;
16699           case PASSWD:
16700             if (strcmp(*aname, "anonymous") &&
16701                 fstat(fileno(cfile), &stb) >= 0 &&
16702                 (stb.st_mode & 077) != 0) {
16703                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16704                 fprintf(stderr, "Remove password or correct mode.\n");
16705                 goto bad;
16706             }
16707             if (token() && *apass == 0) {
16708                 *apass = malloc((unsigned) strlen(tokval) + 1);
16709                 strcpy(*apass, tokval);          /* safe */
16710             }
16711             break;
16712           case ACCOUNT:
16713             if (fstat(fileno(cfile), &stb) >= 0
16714                 && (stb.st_mode & 077) != 0) {
16715                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16716                 fprintf(stderr, "Remove account or correct mode.\n");
16717                 goto bad;
16718             }
16719             if (token() && *aacct == 0) {
16720                 *aacct = malloc((unsigned) strlen(tokval) + 1);
16721                 strcpy(*aacct, tokval);          /* safe */
16722             }
16723             break;
16724
16725           default:
16726             fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
16727             break;
16728         }
16729         goto done;
16730     }
16731
16732   done:
16733     fclose(cfile);
16734     return(0);
16735
16736   bad:
16737     fclose(cfile);
16738     return(-1);
16739 }
16740 #endif /* USE_RUSERPASS */
16741
16742 static char *radixN =
16743   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16744
16745 static char pad = '=';
16746
16747 static int
16748 radix_encode(inbuf, outbuf, inlen, outlen, decode)
16749     CHAR inbuf[], outbuf[];
16750     int inlen, *outlen, decode;
16751 {
16752     int i, j, D = 0;
16753     char *p;
16754     CHAR c = NUL;
16755
16756     if (decode) {
16757         for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
16758             if ((p = ckstrchr(radixN, inbuf[i])) == NULL)
16759               return(1);
16760             D = p - radixN;
16761             switch (i&3) {
16762               case 0:
16763                 outbuf[j] = D<<2;
16764                 break;
16765               case 1:
16766                 outbuf[j++] |= D>>4;
16767                 outbuf[j] = (D&15)<<4;
16768                 break;
16769               case 2:
16770                 outbuf[j++] |= D>>2;
16771                 outbuf[j] = (D&3)<<6;
16772                 break;
16773               case 3:
16774                 outbuf[j++] |= D;
16775             }
16776             if (j == *outlen)
16777               return(4);
16778         }
16779         switch (i&3) {
16780           case 1: return(3);
16781           case 2: if (D&15) return(3);
16782             if (strcmp((char *)&inbuf[i], "==")) return(2);
16783             break;
16784           case 3: if (D&3) return(3);
16785             if (strcmp((char *)&inbuf[i], "="))  return(2);
16786         }
16787         *outlen = j;
16788     } else {
16789         for (i = 0, j = 0; i < inlen; i++) {
16790             switch (i%3) {
16791               case 0:
16792                 outbuf[j++] = radixN[inbuf[i]>>2];
16793                 c = (inbuf[i]&3)<<4;
16794                 break;
16795               case 1:
16796                 outbuf[j++] = radixN[c|inbuf[i]>>4];
16797                 c = (inbuf[i]&15)<<2;
16798                 break;
16799               case 2:
16800                 outbuf[j++] = radixN[c|inbuf[i]>>6];
16801                 outbuf[j++] = radixN[inbuf[i]&63];
16802                 c = 0;
16803             }
16804             if (j == *outlen)
16805               return(4);
16806         }
16807         if (i%3) outbuf[j++] = radixN[c];
16808         switch (i%3) {
16809           case 1: outbuf[j++] = pad;
16810           case 2: outbuf[j++] = pad;
16811         }
16812         outbuf[*outlen = j] = '\0';
16813     }
16814     return(0);
16815 }
16816
16817 static char *
16818 radix_error(e) int e;
16819 {
16820     switch (e) {
16821       case 0:  return("Success");
16822       case 1:  return("Bad character in encoding");
16823       case 2:  return("Encoding not properly padded");
16824       case 3:  return("Decoded # of bits not a multiple of 8");
16825       case 4:  return("Output buffer too small");
16826       default: return("Unknown error");
16827     }
16828 }
16829 /* END_RUSERPASS */
16830
16831 #ifdef FTP_SRP
16832 /*---------------------------------------------------------------------------+
16833  |                                                                           |
16834  |   Package: srpftp                                                         |
16835  |   Author: Eugene Jhong                                                    |
16836  |                                                                           |
16837  +---------------------------------------------------------------------------*/
16838
16839 /*
16840  * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
16841  * All Rights Reserved.
16842  *
16843  * Permission is hereby granted, free of charge, to any person obtaining
16844  * a copy of this software and associated documentation files (the
16845  * "Software"), to deal in the Software without restriction, including
16846  * without limitation the rights to use, copy, modify, merge, publish,
16847  * distribute, sublicense, and/or sell copies of the Software, and to
16848  * permit persons to whom the Software is furnished to do so, subject to
16849  * the following conditions:
16850  *
16851  * The above copyright notice and this permission notice shall be
16852  * included in all copies or substantial portions of the Software.
16853  *
16854  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16855  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16856  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16857  *
16858  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
16859  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
16860  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
16861  * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
16862  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16863  *
16864  * In addition, the following conditions apply:
16865  *
16866  * 1. Any software that incorporates the SRP authentication technology
16867  *    must display the following acknowlegment:
16868  *    "This product uses the 'Secure Remote Password' cryptographic
16869  *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
16870  *
16871  * 2. Any software that incorporates all or part of the SRP distribution
16872  *    itself must also display the following acknowledgment:
16873  *    "This product includes software developed by Tom Wu and Eugene
16874  *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
16875  *
16876  * 3. Redistributions in source or binary form must retain an intact copy
16877  *    of this copyright notice and list of conditions.
16878  */
16879
16880 #define SRP_PROT_VERSION        1
16881
16882 #ifdef CK_ENCRYPTION
16883 #define SRP_DEFAULT_CIPHER      CIPHER_ID_CAST5_CBC
16884 #else
16885 #define SRP_DEFAULT_CIPHER      CIPHER_ID_NONE
16886 #endif /* CK_ENCRYPTION */
16887
16888 #define SRP_DEFAULT_HASH        HASH_ID_SHA
16889
16890 CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
16891 CHAR srp_pref_hash = HASH_ID_SHA;
16892
16893 static struct t_client *tc = NULL;
16894 static CHAR *skey = NULL;
16895 static krypto_context *incrypt = NULL;
16896 static krypto_context *outcrypt = NULL;
16897
16898 typedef unsigned int srp_uint32;
16899
16900 /*--------------------------------------------------------------+
16901  | srp_selcipher: select cipher                                 |
16902  +--------------------------------------------------------------*/
16903 static int
16904 srp_selcipher (cname) char *cname; {
16905     cipher_desc *cd;
16906
16907     if (!(cd = cipher_getdescbyname (cname))) {
16908         int i;
16909         CHAR *list = cipher_getlist ();
16910
16911         fprintf (stderr, "ftp: supported ciphers:\n\n");
16912         for (i = 0; i < strlen (list); i++)
16913           fprintf (stderr, "    %s\n", (cipher_getdescbyid(list[i]))->name);
16914         fprintf (stderr, "\n");
16915         return -1;
16916     }
16917     srp_pref_cipher = cd->id;
16918     return 0;
16919 }
16920
16921 /*--------------------------------------------------------------+
16922  | srp_selhash: select hash                                     |
16923  +--------------------------------------------------------------*/
16924 static int
16925 srp_selhash (hname) char *hname; {
16926     hash_desc *hd;
16927
16928     if (!(hd = hash_getdescbyname (hname))) {
16929         int i;
16930         CHAR *list = hash_getlist ();
16931
16932         fprintf (stderr, "ftp: supported hash functions:\n\n");
16933         for (i = 0; i < strlen (list); i++)
16934           fprintf (stderr, "    %s\n", (hash_getdescbyid(list[i]))->name);
16935         fprintf (stderr, "\n");
16936         return -1;
16937     }
16938     srp_pref_hash = hd->id;
16939     return 0;
16940 }
16941
16942 /*--------------------------------------------------------------+
16943  | srp_userpass: get username and password                      |
16944  +--------------------------------------------------------------*/
16945 static int
16946 srp_userpass (host) char *host; {
16947     char tmp[BUFSIZ], prompt[PROMPTSIZ];
16948     char *user;
16949
16950     user = NULL;
16951 #ifdef USE_RUSERPASS
16952     ruserpass (host, &user, &srp_pass, &srp_acct);
16953 #endif /* USE_RUSERPASS */
16954
16955     while (user == NULL)     {
16956         char *myname;
16957         int ok;
16958
16959         myname = whoami();
16960         if (!myname) myname = "";
16961         if (myname[0])
16962           ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
16963                     NULL,NULL,NULL,NULL,NULL,NULL,NULL);
16964         else
16965           ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
16966         tmp[0] = '\0';
16967         ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
16968                     DEFAULT_UQ_TIMEOUT);
16969         if (!ok || *tmp == '\0')
16970           user = myname;
16971         else
16972           user = brstrip(tmp);
16973     }
16974     ckstrncpy (srp_user, user,BUFSIZ);
16975     return(0);
16976 }
16977
16978 /*--------------------------------------------------------------+
16979  | srp_reset: reset srp information                             |
16980  +--------------------------------------------------------------*/
16981 static int
16982 srp_reset () {
16983     if (tc) { t_clientclose (tc); tc = NULL; }
16984     if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
16985     if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
16986     return(0);
16987 }
16988
16989 /*--------------------------------------------------------------+
16990  | srp_ftp_auth: perform srp authentication                         |
16991  +--------------------------------------------------------------*/
16992 static int
16993 srp_ftp_auth(host, user, pass)
16994     char *host;
16995     char *user;
16996     char *pass;
16997 {
16998     struct t_num *wp;
16999     struct t_num N;
17000     struct t_num g;
17001     struct t_num s;
17002     struct t_num yp;
17003     CHAR buf[FTP_BUFSIZ];
17004     CHAR tmp[FTP_BUFSIZ];
17005     CHAR *bp, *cp;
17006     int n, e, clen, blen, len, i;
17007     CHAR cid = 0;
17008     CHAR hid = 0;
17009
17010     srp_pass = srp_acct = 0;
17011
17012     n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
17013     if (n != REPLY_CONTINUE) {
17014         if (ftp_deb)
17015             fprintf(stderr, "SRP rejected as an authentication type\n");
17016         return(0);
17017     } else {                            /* Send protocol version */
17018         CHAR vers[4];
17019         memset (vers, 0, 4);
17020         vers[3] = SRP_PROT_VERSION;
17021         if (!quiet)
17022           printf ("SRP accepted as authentication type.\n");
17023         bp = tmp; blen = 0;
17024         srp_put (vers, &bp, 4, &blen);
17025         len = FTP_BUFSIZ;
17026         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17027           goto encode_error;
17028         reply_parse = "ADAT=";
17029         n = ftpcmd("ADAT",buf,-1,-1,0);
17030     }
17031     if (n == REPLY_CONTINUE) {          /* Get protocol version */
17032         bp = buf;
17033         if (!reply_parse)
17034           goto data_error;
17035         blen = FTP_BUFSIZ;
17036         if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
17037           goto decode_error;
17038         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17039           goto data_error;
17040
17041         if (host) {                     /* Get username/password if needed */
17042             srp_userpass (host);
17043         } else {
17044             ckstrncpy (srp_user, user, BUFSIZ);
17045             srp_pass = pass;
17046         }
17047         bp = tmp; blen = 0;             /* Send username */
17048         srp_put (srp_user, &bp, strlen (srp_user), &blen);
17049         len = FTP_BUFSIZ;
17050         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17051           goto encode_error;
17052         reply_parse = "ADAT=";
17053         n = ftpcmd("ADAT",buf,-1,-1,0);
17054     }
17055     if (n == REPLY_CONTINUE) {          /* Get N, g and s */
17056         bp = buf;
17057         if (!reply_parse)
17058           goto data_error;
17059         blen = FTP_BUFSIZ;
17060         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17061           goto decode_error;
17062         if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
17063           goto data_error;
17064         if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
17065           goto data_error;
17066         if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
17067           goto data_error;
17068         if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
17069             fprintf (stderr, "Unable to open SRP client structure.\n");
17070             goto bad;
17071         }
17072         wp = t_clientgenexp (tc);       /* Send wp */
17073         bp = tmp; blen = 0;
17074         srp_put (wp->data, &bp, wp->len, &blen);
17075         len = FTP_BUFSIZ;
17076         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17077           goto encode_error;
17078         reply_parse = "ADAT=";
17079         n = ftpcmd("ADAT",buf,-1,-1,0);
17080     }
17081     if (n == REPLY_CONTINUE) {          /* Get yp */
17082         bp = buf;
17083         if (!reply_parse)
17084           goto data_error;
17085         blen = FTP_BUFSIZ;
17086         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17087           goto decode_error;
17088         if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
17089           goto data_error;
17090         if (!srp_pass) {
17091             static char ftppass[PASSBUFSIZ];
17092             int ok;
17093             setint();
17094             ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
17095                         DEFAULT_UQ_TIMEOUT);
17096             if (ok)
17097               srp_pass = brstrip(ftppass);
17098         }
17099         t_clientpasswd (tc, srp_pass);
17100         memset (srp_pass, 0, strlen (srp_pass));
17101         skey = t_clientgetkey (tc, &yp); /* Send response */
17102         bp = tmp; blen = 0;
17103         srp_put (t_clientresponse (tc), &bp, 20, &blen);
17104         len = FTP_BUFSIZ;
17105         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17106           goto encode_error;
17107         reply_parse = "ADAT=";
17108         n = ftpcmd("ADAT",buf,-1,-1,0);
17109     }
17110     if (n == REPLY_CONTINUE) {          /* Get response */
17111         bp = buf;
17112         if (!reply_parse)
17113           goto data_error;
17114         blen = FTP_BUFSIZ;
17115         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17116           goto encode_error;
17117         if (srp_get (&bp, &cp, &blen, &clen) != 20)
17118           goto data_error;
17119         if (t_clientverify (tc, cp)) {
17120             fprintf (stderr, "WARNING: bad response to client challenge.\n");
17121             goto bad;
17122         }
17123         bp = tmp; blen = 0;             /* Send nothing */
17124         srp_put ("\0", &bp, 1, &blen);
17125         len = FTP_BUFSIZ;
17126         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17127           goto encode_error;
17128         reply_parse = "ADAT=";
17129         n = ftpcmd("ADAT",buf,-1,-1,0);
17130     }
17131     if (n == REPLY_CONTINUE) {          /* Get cipher & hash lists, seqnum */
17132         CHAR seqnum[4];
17133         CHAR *clist;
17134         CHAR *hlist;
17135         CHAR *p1;
17136         int clist_len, hlist_len;
17137         bp = buf;
17138         if (!reply_parse)
17139           goto data_error;
17140         blen = FTP_BUFSIZ;
17141         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17142           goto encode_error;
17143         if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
17144           goto data_error;
17145         if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
17146           goto data_error;
17147         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17148           goto data_error;
17149         memcpy (seqnum, cp, 4);
17150         if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
17151           cid = srp_pref_cipher;
17152         if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
17153           cid = SRP_DEFAULT_CIPHER;
17154         if (!cid) {
17155             CHAR *loclist = cipher_getlist ();
17156             for (i = 0; i < strlen (loclist); i++)
17157               if (cipher_supported (clist, loclist[i])) {
17158                   cid = loclist[i];
17159                   break;
17160               }
17161         }
17162         if (!cid) {
17163             fprintf (stderr, "Unable to agree on cipher.\n");
17164             goto bad;
17165         }
17166         /* Choose hash */
17167
17168         if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
17169           hid = srp_pref_hash;
17170
17171         if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
17172           hid = SRP_DEFAULT_HASH;
17173
17174         if (!hid) {
17175             CHAR *loclist = hash_getlist ();
17176             for (i = 0; i < strlen (loclist); i++)
17177               if (hash_supported (hlist, loclist[i])) {
17178                   hid = loclist[i];
17179                   break;
17180               }
17181         }
17182         if (!hid) {
17183             fprintf (stderr, "Unable to agree on hash.\n");
17184             goto bad;
17185         }
17186         /* Set incrypt */
17187
17188         if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
17189                                     KRYPTO_DECODE)))
17190           goto bad;
17191
17192         /* Generate random number for outkey and outseqnum */
17193
17194         t_random (seqnum, 4);
17195
17196         /* Send cid, hid, outkey, outseqnum */
17197
17198         bp = tmp; blen = 0;
17199         srp_put (&cid, &bp, 1, &blen);
17200         srp_put (&hid, &bp, 1, &blen);
17201         srp_put (seqnum, &bp, 4, &blen);
17202         len = FTP_BUFSIZ;
17203         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17204           goto encode_error;
17205         reply_parse = "ADAT=";
17206         n = ftpcmd("ADAT",buf,-1,-1,0);
17207
17208         /* Set outcrypt */
17209
17210         if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
17211                                      KRYPTO_ENCODE)))
17212           goto bad;
17213
17214         t_clientclose (tc);
17215         tc = NULL;
17216     }
17217     if (n != REPLY_COMPLETE)
17218       goto bad;
17219
17220     if (ftp_vbm) {
17221         if (ftp_deb)
17222           printf("\n");
17223         printf ("SRP authentication succeeded.\n");
17224         printf ("Using cipher %s and hash function %s.\n",
17225                 (cipher_getdescbyid(cid))->name,
17226                 (hash_getdescbyid(hid))->name
17227                 );
17228     }
17229     reply_parse = NULL;
17230     auth_type = "SRP";
17231     return(1);
17232
17233   encode_error:
17234     fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
17235     goto bad;
17236
17237   decode_error:
17238     fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
17239     goto bad;
17240
17241   data_error:
17242     fprintf (stderr, "Unable to unmarshal authentication data.\n");
17243     goto bad;
17244
17245   bad:
17246     fprintf (stderr, "SRP authentication failed, trying regular login.\n");
17247     reply_parse = NULL;
17248     return(0);
17249 }
17250
17251 /*--------------------------------------------------------------+
17252  | srp_put: put item to send buffer                             |
17253  +--------------------------------------------------------------*/
17254 static int
17255 srp_put (in, out, inlen, outlen)
17256     CHAR *in;
17257     CHAR **out;
17258     int inlen;
17259     int *outlen;
17260 {
17261     srp_uint32 net_len;
17262
17263     net_len = htonl (inlen);
17264     memcpy (*out, &net_len, 4);
17265
17266     *out += 4; *outlen += 4;
17267
17268     memcpy (*out, in, inlen);
17269
17270     *out += inlen; *outlen += inlen;
17271     return(0);
17272 }
17273
17274 /*--------------------------------------------------------------+
17275  | srp_get: get item from receive buffer                        |
17276  +--------------------------------------------------------------*/
17277 static int
17278 srp_get (in, out, inlen, outlen)
17279     CHAR **in;
17280     CHAR **out;
17281     int *inlen;
17282     int *outlen;
17283 {
17284     srp_uint32 net_len;
17285
17286     if (*inlen < 4) return -1;
17287
17288     memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
17289     *outlen = ntohl (net_len);
17290
17291     if (*inlen < *outlen) return -1;
17292
17293     *out = *in; *inlen -= *outlen; *in += *outlen;
17294
17295     return *outlen;
17296 }
17297
17298 /*--------------------------------------------------------------+
17299  | srp_encode: encode control message                           |
17300  +--------------------------------------------------------------*/
17301 static int
17302 srp_encode (private, in, out, len)
17303     int private;
17304     CHAR *in;
17305     CHAR *out;
17306     unsigned len;
17307 {
17308     if (private)
17309       return krypto_msg_priv (outcrypt, in, out, len);
17310     else
17311       return krypto_msg_safe (outcrypt, in, out, len);
17312 }
17313
17314 /*--------------------------------------------------------------+
17315  | srp_decode: decode control message                           |
17316  +--------------------------------------------------------------*/
17317 static int
17318 srp_decode (private, in, out, len)
17319     int private;
17320     CHAR *in;
17321     CHAR *out;
17322     unsigned len;
17323 {
17324     if (private)
17325       return krypto_msg_priv (incrypt, in, out, len);
17326     else
17327       return krypto_msg_safe (incrypt, in, out, len);
17328 }
17329
17330 #endif /* FTP_SRP */
17331
17332
17333
17334 #ifdef NOT_USED
17335 /*
17336   The following code is from the Unix FTP client.  Be sure to
17337   make sure that the functionality is not lost.  Especially
17338   the Proxy stuff even though we have not yet implemented it.
17339 */
17340
17341 /* Send multiple files  */
17342
17343 static int
17344 ftp_mput(argc, argv) int argc; char **argv; {
17345     register int i;
17346     sig_t oldintr;
17347     int ointer;
17348     char *tp;
17349     sigtype mcancel();
17350
17351     if (argc < 2 && !another(&argc, &argv, "local-files")) {
17352         printf("usage: %s local-files\n", argv[0]);
17353         ftpcode = -1;
17354         return;
17355     }
17356     mname = argv[0];
17357     mflag = 1;
17358     oldintr = signal(SIGINT, mcancel);
17359
17360     /* Replace with calls to cc_execute() */
17361     setjmp(jcancel);
17362 #ifdef FTP_PROXY
17363     if (proxy) {
17364         char *cp, *tp2, tmpbuf[CKMAXPATH];
17365
17366         while ((cp = remglob(argv,0)) != NULL) {
17367             if (*cp == 0) {
17368                 mflag = 0;
17369                 continue;
17370             }
17371             if (mflag && confirm(argv[0], cp)) {
17372                 tp = cp;
17373                 if (mcase) {
17374                     while (*tp && !islower(*tp)) {
17375                         tp++;
17376                     }
17377                     if (!*tp) {
17378                         tp = cp;
17379                         tp2 = tmpbuf;
17380                         while ((*tp2 = *tp) != 0) {
17381                             if (isupper(*tp2)) {
17382                                 *tp2 = 'a' + *tp2 - 'A';
17383                             }
17384                             tp++;
17385                             tp2++;
17386                         }
17387                     }
17388                     tp = tmpbuf;
17389                 }
17390                 if (ntflag) {
17391                     tp = dotrans(tp);
17392                 }
17393                 if (mapflag) {
17394                     tp = domap(tp);
17395                 }
17396                 sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0);
17397                 if (!mflag && fromatty) {
17398                     ointer = interactive;
17399                     interactive = 1;
17400                     if (confirm("Continue with","mput")) {
17401                         mflag++;
17402                     }
17403                     interactive = ointer;
17404                 }
17405             }
17406         }
17407         signal(SIGINT, oldintr);
17408         mflag = 0;
17409         return;
17410     }
17411 #endif /* FTP_PROXY */
17412     for (i = 1; i < argc; i++) {
17413         register char **cpp, **gargs;
17414
17415         if (mflag && confirm(argv[0], argv[i])) {
17416             tp = argv[i];
17417             sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
17418             if (!mflag && fromatty) {
17419                 ointer = interactive;
17420                 interactive = 1;
17421                 if (confirm("Continue with","mput")) {
17422                     mflag++;
17423                 }
17424                 interactive = ointer;
17425             }
17426         }
17427         continue;
17428
17429         gargs = ftpglob(argv[i]);
17430         if (globerr != NULL) {
17431             printf("%s\n", globerr);
17432             if (gargs) {
17433                 blkfree(gargs);
17434                 free((char *)gargs);
17435             }
17436             continue;
17437         }
17438         for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
17439             if (mflag && confirm(argv[0], *cpp)) {
17440                 tp = *cpp;
17441                 sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
17442                 if (!mflag && fromatty) {
17443                     ointer = interactive;
17444                     interactive = 1;
17445                     if (confirm("Continue with","mput")) {
17446                         mflag++;
17447                     }
17448                     interactive = ointer;
17449                 }
17450             }
17451         }
17452         if (gargs != NULL) {
17453             blkfree(gargs);
17454             free((char *)gargs);
17455         }
17456     }
17457     signal(SIGINT, oldintr);
17458     mflag = 0;
17459 }
17460
17461 /* Get multiple files */
17462
17463 static int
17464 ftp_mget(argc, argv) int argc; char **argv; {
17465     int rc = -1;
17466     sig_t oldintr;
17467     int ointer;
17468     char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
17469     sigtype mcancel();
17470
17471     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17472         printf("usage: %s remote-files\n", argv[0]);
17473         ftpcode = -1;
17474         return(-1);
17475     }
17476     mname = argv[0];
17477     mflag = 1;
17478     oldintr = signal(SIGINT,mcancel);
17479     /* Replace with calls to cc_execute() */
17480     setjmp(jcancel);
17481     while ((cp = remglob(argv,proxy)) != NULL) {
17482         if (*cp == '\0') {
17483             mflag = 0;
17484             continue;
17485         }
17486         if (mflag && confirm(argv[0], cp)) {
17487             tp = cp;
17488             if (mcase) {
17489                 while (*tp && !islower(*tp)) {
17490                     tp++;
17491                 }
17492                 if (!*tp) {
17493                     tp = cp;
17494                     tp2 = tmpbuf;
17495                     while ((*tp2 = *tp) != 0) {
17496                         if (isupper(*tp2)) {
17497                             *tp2 = 'a' + *tp2 - 'A';
17498                         }
17499                         tp++;
17500                         tp2++;
17501                     }
17502                 }
17503                 tp = tmpbuf;
17504             }
17505             rc = (recvrequest("RETR", tp, cp, "wb",
17506                                tp != cp || !interactive) == 0,0,NULL,0,0,0);
17507             if (!mflag && fromatty) {
17508                 ointer = interactive;
17509                 interactive = 1;
17510                 if (confirm("Continue with","mget")) {
17511                     mflag++;
17512                 }
17513                 interactive = ointer;
17514             }
17515         }
17516     }
17517     signal(SIGINT,oldintr);
17518     mflag = 0;
17519     return(rc);
17520 }
17521
17522 /* Delete multiple files */
17523
17524 static int
17525 mdelete(argc, argv) int argc; char **argv; {
17526     sig_t oldintr;
17527     int ointer;
17528     char *cp;
17529     sigtype mcancel();
17530
17531     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17532         printf("usage: %s remote-files\n", argv[0]);
17533         ftpcode = -1;
17534         return(-1);
17535     }
17536     mname = argv[0];
17537     mflag = 1;
17538     oldintr = signal(SIGINT, mcancel);
17539     /* Replace with calls to cc_execute() */
17540     setjmp(jcancel);
17541     while ((cp = remglob(argv,0)) != NULL) {
17542         if (*cp == '\0') {
17543             mflag = 0;
17544             continue;
17545         }
17546         if (mflag && confirm(argv[0], cp)) {
17547             rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
17548             if (!mflag && fromatty) {
17549                 ointer = interactive;
17550                 interactive = 1;
17551                 if (confirm("Continue with", "mdelete")) {
17552                     mflag++;
17553                 }
17554                 interactive = ointer;
17555             }
17556         }
17557     }
17558     signal(SIGINT, oldintr);
17559     mflag = 0;
17560     return(rc);
17561 }
17562
17563 /* Get a directory listing of multiple remote files */
17564
17565 static int
17566 mls(argc, argv) int argc; char **argv; {
17567     sig_t oldintr;
17568     int ointer, i;
17569     char *cmd, mode[1], *dest;
17570     sigtype mcancel();
17571     int rc = -1;
17572
17573     if (argc < 2 && !another(&argc, &argv, "remote-files"))
17574       goto usage;
17575     if (argc < 3 && !another(&argc, &argv, "local-file")) {
17576       usage:
17577         printf("usage: %s remote-files local-file\n", argv[0]);
17578         ftpcode = -1;
17579         return(-1);
17580     }
17581     dest = argv[argc - 1];
17582     argv[argc - 1] = NULL;
17583     if (strcmp(dest, "-") && *dest != '|')
17584       if (!globulize(&dest) ||
17585           !confirm("output to local-file:", dest)) {
17586           ftpcode = -1;
17587           return(-1);
17588       }
17589     cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
17590     mname = argv[0];
17591     mflag = 1;
17592     oldintr = signal(SIGINT, mcancel);
17593     /* Replace with calls to cc_execute() */
17594     setjmp(jcancel);
17595     for (i = 1; mflag && i < argc-1; ++i) {
17596         *mode = (i == 1) ? 'w' : 'a';
17597         rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
17598         if (!mflag && fromatty) {
17599             ointer = interactive;
17600             interactive = 1;
17601             if (confirm("Continue with", argv[0])) {
17602                 mflag ++;
17603             }
17604             interactive = ointer;
17605         }
17606     }
17607     signal(SIGINT, oldintr);
17608     mflag = 0;
17609     return(rc);
17610 }
17611
17612 static char *
17613 remglob(argv,doswitch) char *argv[]; int doswitch; {
17614     char temp[16];
17615     static char buf[CKMAXPATH];
17616     static FILE *ftemp = NULL;
17617     static char **args;
17618     int oldhash;
17619     char *cp, *mode;
17620
17621     if (!mflag) {
17622         if (!doglob) {
17623             args = NULL;
17624         } else {
17625             if (ftemp) {
17626                 (void) fclose(ftemp);
17627                 ftemp = NULL;
17628             }
17629         }
17630         return(NULL);
17631     }
17632     if (!doglob) {
17633         if (args == NULL)
17634           args = argv;
17635         if ((cp = *++args) == NULL)
17636           args = NULL;
17637         return(cp);
17638     }
17639     if (ftemp == NULL) {
17640         (void) strcpy(temp, _PATH_TMP);
17641 #ifdef MKTEMP
17642 #ifndef MKSTEMP
17643         (void) mktemp(temp);
17644 #endif /* MKSTEMP */
17645 #endif /* MKTEMP */
17646         verbose = 0;
17647         oldhash = hash, hash = 0;
17648 #ifdef FTP_PROXY
17649         if (doswitch) {
17650             pswitch(!proxy);
17651         }
17652 #endif /* FTP_PROXY */
17653         for (mode = "wb"; *++argv != NULL; mode = "ab")
17654           recvrequest ("NLST", temp, *argv, mode, 0);
17655 #ifdef FTP_PROXY
17656         if (doswitch) {
17657             pswitch(!proxy);
17658         }
17659 #endif /* FTP_PROXY */
17660         hash = oldhash;
17661         ftemp = fopen(temp, "r");
17662         unlink(temp);
17663         if (ftemp == NULL && (!dpyactive || ftp_deb)) {
17664             printf("Can't find list of remote files, oops\n");
17665             return(NULL);
17666         }
17667     }
17668     if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
17669         fclose(ftemp), ftemp = NULL;
17670         return(NULL);
17671     }
17672     if ((cp = ckstrchr(buf,'\n')) != NULL)
17673       *cp = '\0';
17674     return(buf);
17675 }
17676 #endif /* NOT_USED */
17677 #endif /* TCPSOCKET (top of file) */
17678 #endif /* SYSFTP (top of file) */
17679 #endif /* NOFTP (top of file) */