Imported Upstream version 302
[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.260, 14 Jul 2011";
6
7 /*
8   Authors:
9     Jeffrey E Altman <jaltman@secure-endpoints.com>
10       Secure Endpoints Inc., New York City
11     Frank da Cruz <fdc@columbia.edu>,
12       The Kermit Project, Columbia University.
13
14   Copyright (C) 2000, 2011,
15     Trustees of Columbia University in the City of New York.
16     All rights reserved.  See the C-Kermit COPYING.TXT file or the
17     copyright text in the ckcmai.c module for disclaimer and permissions.
18
19   Portions of conditionally included code Copyright Regents of the
20     University of California and The Stanford SRP Authentication Project;
21     see notices below.
22 */
23
24 /*
25   Pending...
26
27   . Implement recursive NLST downloads by trying to CD to each filename.
28     If it works, it's a directory; if not, it's a file -- GET it.  But
29     that won't work with servers like wu-ftpd that don't send directory 
30     names.  Recursion with MLSD is done.
31
32   . Make syslog entries for session?  Files?
33
34   . Messages are printed to stdout and stderr in random fashion.  We should
35     either print everything to stdout, or else be systematic about when
36     to use stderr.
37
38   . Implement mail (MAIL, MLFL, MSOM, etc) if any servers support it.
39
40   . Adapt to VMS.  Big job because of its record-oriented file system.
41     RMS programmer required.  There are probably also some VMS TCP/IP
42     product-specific wrinkles, e.g. attribute preservation in VMS-to-VMS
43     transfers using special options for Multinet or other FTP servers
44     (find out about STRU VMS).
45 */
46
47 /*
48   Quick FTP command reference:
49
50   RFC765 (1980) and earlier:
51     MODE  S(tream), B(lock), C(ompressed)
52     STRU  F(ILE), R(ECORD), P(AGE)
53     TYPE  A(SCII) <format>,  E(BCDIC) <format>, I(MAGE), L(OCAL) <bytesize>
54     PORT  - Port
55     PASV  - Passive mode
56     USER  - User
57     PASS  - Password
58     ACCT  - Account
59     CWD   - Change Working Directory
60     REIN  - Logout but not disconnect
61     QUIT  - Bye
62     RETR  - Retreive
63     STOR  - Store
64     APPE  - Append
65     ALLO  - Allocate
66     REST  - Restart
67     RNFR  - Rename from
68     RNTO  - Rename to
69     ABOR  - Cancel
70     DELE  - Delete
71     LIST  - Directory
72     NLST  - Name List
73     SITE  - Site parameters or commands
74     STAT  - Status
75     HELP  - Help
76     NOOP  - Noop
77
78   RFC959 (1985):
79     CDUP  - Change to Parent Directory
80     SMNT  - Structure Mount
81     STOU  - Store Unique
82     RMD   - Remove Directory
83     MKD   - Make Directory
84     PWD   - Print Directory
85     SYST  - System
86
87   RFC2389 (1998):
88     FEAT  - List Features (done)
89     OPTS  - Send options (done)
90
91   RFC2640 (1999):
92     LANG  - Specify language for messages (not done)
93
94   Pending (Internet Drafts):
95     SIZE  - File size (done)
96     MDTM  - File modification date-time (done)
97     MLST  - File name and attribute list (single file) (not done)
98     MLSD  - File list with attributes (multiple files) (done)
99     MAIL, MLFL, MSOM - mail delivery (not done)
100
101   Alphabetical syntax list:
102     ABOR <CRLF>
103     ACCT <SP> <account-information> <CRLF>
104     ALLO <SP> <decimal-integer> [<SP> R <SP> <decimal-integer>] <CRLF>
105     APPE <SP> <pathname> <CRLF>
106     CDUP <CRLF>
107     CWD  <SP> <pathname> <CRLF>
108     DELE <SP> <pathname> <CRLF>
109     FEAT <CRLF>
110     HELP [<SP> <string>] <CRLF>
111     LANG [<SP> <language-tag> ] <CRLF>
112     LIST [<SP> <pathname>] <CRLF>
113     MKD  <SP> <pathname> <CRLF>
114     MLSD [<SP> <pathname>] <CRLF>
115     MLST [<SP> <pathname>] <CRLF>
116     MODE <SP> <mode-code> <CRLF>
117     NLST [<SP> <pathname-or-wildcard>] <CRLF>
118     NOOP <CRLF>
119     OPTS <SP> <commandname> [ <SP> <command-options> ] <CRLF>
120     PASS <SP> <password> <CRLF>
121     PASV <CRLF>
122     PORT <SP> <host-port> <CRLF>
123     PWD  <CRLF>
124     QUIT <CRLF>
125     REIN <CRLF>
126     REST <SP> <marker> <CRLF>
127     RETR <SP> <pathname> <CRLF>
128     RMD  <SP> <pathname> <CRLF>
129     RNFR <SP> <pathname> <CRLF>
130     RNTO <SP> <pathname> <CRLF>
131     SITE <SP> <string> <CRLF>
132     SIZE <SP> <pathname> <CRLF>
133     SMNT <SP> <pathname> <CRLF>
134     STAT [<SP> <pathname>] <CRLF>
135     STOR <SP> <pathname> <CRLF>
136     STOU <CRLF>
137     STRU <SP> <structure-code> <CRLF>
138     SYST <CRLF>
139     TYPE <SP> <type-code> <CRLF>
140     USER <SP> <username> <CRLF>
141 */
142 #include "ckcsym.h"                     /* Standard includes */
143 #include "ckcdeb.h"
144
145 #ifndef NOFTP                           /* NOFTP  = no FTP */
146 #ifndef SYSFTP                          /* SYSFTP = use external ftp client */
147 #ifdef TCPSOCKET                        /* Build only if TCP/IP included */
148 #define CKCFTP_C
149
150 /* Note: much of the following duplicates what was done in ckcdeb.h */
151 /* but let's not mess with it unless it causes trouble. */
152
153 #ifdef CK_ANSIC
154 #include <stdarg.h>
155 #else /* CK_ANSIC */
156 #include <varargs.h>
157 #endif /* CK_ANSIC */
158 #include <signal.h>
159 #ifdef OS2
160 #ifdef OS2ONLY
161 #include <os2.h>
162 #endif /* OS2ONLY */
163 #include "ckowin.h"
164 #include "ckocon.h"
165 #endif /* OS2 */
166 #ifndef ZILOG
167 #ifdef NT
168 #include <setjmpex.h>
169 #ifdef NTSIG
170 extern int TlsIndex;
171 #endif /* NTSIG */
172 #else /* NT */
173 #include <setjmp.h>
174 #endif /* NT */
175 #else
176 #include <setret.h>
177 #endif /* ZILOG */
178 #include "ckcsig.h"
179 #ifdef VMS
180 /* 2010-03-09 SMS.  VAX C needs help to find "sys".  It's easier not to try. */
181 #include <stat.h>
182 #else /* def VMS */
183 #include <sys/stat.h>
184 #endif /* def VMS [else] */
185 #include <ctype.h>
186
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 #endif /* CK_SSL */
1096
1097 static int
1098 #ifdef NOCSETS
1099   ftp_csr = -1,                         /* Remote (server) character set */
1100 #else
1101   ftp_csr = FC_UTF8,
1102 #endif /* NOCSETS */
1103   ftp_xla = 0;                          /* Character-set translation on/off */
1104 int
1105   ftp_csx = -1,                         /* Remote charset currently in use */
1106   ftp_csl = -1;                         /* Local charset currently in use */
1107
1108 static int g_ftp_typ = FTT_ASC;         /* For saving and restoring ftp_typ */
1109
1110 char * ftp_nml = NULL;                  /* /NAMELIST */
1111 char * ftp_tmp = NULL;                  /* Temporary string */
1112 static char * ftp_acc = NULL;           /* Account string */
1113 static char * auth_type = NULL;         /* Authentication type */
1114 static char * srv_renam = NULL;         /* Server-rename string */
1115 FILE * fp_nml = NULL;                   /* Namelist file pointer */
1116
1117 static int csocket = -1;                /* Control socket */
1118 static int connected = 0;               /* Connected to FTP server */
1119 /* static unsigned short ftp_port = 0; */ /* FTP port */ 
1120 /* static int ftp_port = 0; */          /* SMS 2007/02/15 */
1121 static int ftp_port = 0;                /* fdc 2007/08/30 */
1122 #ifdef FTPHOST
1123 static int hostcmd = 0;                 /* Has HOST command been sent */
1124 #endif /* FTPHOST */
1125 static int form, mode, stru, bytesize, curtype = FTT_ASC;
1126 static char bytename[8];
1127
1128 /* For parsing replies to FTP server command */
1129 static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
1130
1131 #ifdef FTP_PROXY
1132 static int proxy, unix_proxy
1133 #endif /* FTP_PROXY */
1134
1135 static char pasv[64];                   /* Passive-mode port */
1136 static int passivemode = 0;
1137 static int sendport = 0;
1138 static int servertype = 0;              /* FTP server's OS type */
1139
1140 static int testing = 0;
1141 static char ftpcmdbuf[FTP_BUFSIZ];
1142
1143 /* Macro definitions */
1144
1145 #define UC(b) ckitoa(((int)b)&0xff)
1146 #define nz(x) ((x) == 0 ? 1 : (x))
1147
1148 /* Command tables and definitions */
1149
1150 #define FTP_ACC  1                      /* FTP command keyword codes */
1151 #define FTP_APP  2
1152 #define FTP_CWD  3
1153 #define FTP_CHM  4
1154 #define FTP_CLS  5
1155 #define FTP_DEL  6
1156 #define FTP_DIR  7
1157 #define FTP_GET  8
1158 #define FTP_IDL  9
1159 #define FTP_MDE 10
1160 #define FTP_MDI 11
1161 #define FTP_MGE 12
1162 #define FTP_MKD 13
1163 #define FTP_MOD 14
1164 #define FTP_MPU 15
1165 #define FTP_OPN 16
1166 #define FTP_PUT 17
1167 #define FTP_PWD 18
1168 #define FTP_RGE 19
1169 #define FTP_REN 20
1170 #define FTP_RES 21
1171 #define FTP_HLP 22
1172 #define FTP_RMD 23
1173 #define FTP_STA 24
1174 #define FTP_SIT 25
1175 #define FTP_SIZ 26
1176 #define FTP_SYS 27
1177 #define FTP_UMA 28
1178 #define FTP_GUP 29
1179 #define FTP_USR 30
1180 #define FTP_QUO 31
1181 #define FTP_TYP 32
1182 #define FTP_FEA 33
1183 #define FTP_OPT 34
1184 #define FTP_CHK 35
1185 #define FTP_VDI 36
1186 #define FTP_ENA 37
1187 #define FTP_DIS 38
1188 #define FTP_REP 39
1189
1190 struct keytab gprtab[] = {              /* GET-PUT-REMOTE keywords */
1191     { "auto",    2, 0 },
1192     { "ftp",     1, 0 },
1193     { "kermit",  0, 0  }
1194 };
1195
1196 static struct keytab qorp[] = {         /* QUIT or PROCEED keywords */
1197     { "proceed", 0, 0 },                /* 0 = proceed */
1198     { "quit",    1, 0 }                 /* 1 = quit */
1199 };
1200
1201 static struct keytab ftpcmdtab[] = {    /* FTP command table */
1202     { "account",   FTP_ACC, 0 },
1203     { "append",    FTP_APP, 0 },
1204     { "bye",       FTP_CLS, 0 },
1205     { "cd",        FTP_CWD, 0 },
1206     { "cdup",      FTP_GUP, 0 },
1207     { "check",     FTP_CHK, 0 },
1208     { "chmod",     FTP_CHM, 0 },
1209     { "close",     FTP_CLS, 0 },
1210     { "cwd",       FTP_CWD, CM_INV },
1211     { "delete",    FTP_MDE, 0 },
1212     { "directory", FTP_DIR, 0 },
1213     { "disable",   FTP_DIS, 0 },
1214     { "enable",    FTP_ENA, 0 },
1215     { "features",  FTP_FEA, 0 },
1216     { "get",       FTP_GET, 0 },
1217     { "help",      FTP_HLP, 0 },
1218     { "idle",      FTP_IDL, 0 },
1219     { "login",     FTP_USR, CM_INV },
1220     { "mdelete",   FTP_MDE, CM_INV },
1221     { "mget",      FTP_MGE, 0 },
1222     { "mkdir",     FTP_MKD, 0 },
1223     { "modtime",   FTP_MOD, 0 },
1224     { "mput",      FTP_MPU, 0 },
1225     { "open",      FTP_OPN, 0 },
1226     { "opt",       FTP_OPT, CM_INV|CM_ABR },
1227     { "opts",      FTP_OPT, CM_INV },
1228     { "options",   FTP_OPT, 0 },
1229     { "put",       FTP_PUT, 0 },
1230     { "pwd",       FTP_PWD, 0 },
1231     { "quit",      FTP_CLS, CM_INV },
1232     { "quote",     FTP_QUO, 0 },
1233     { "reget",     FTP_RGE, 0 },
1234     { "rename",    FTP_REN, 0 },
1235     { "reput",     FTP_REP, 0 },
1236     { "resend",    FTP_REP, CM_INV },
1237     { "reset",     FTP_RES, 0 },
1238     { "rmdir",     FTP_RMD, 0 },
1239     { "send",      FTP_PUT, CM_INV },
1240     { "site",      FTP_SIT, 0 },
1241     { "size",      FTP_SIZ, 0 },
1242     { "status",    FTP_STA, 0 },
1243     { "system",    FTP_SYS, 0 },
1244     { "type",      FTP_TYP, 0 },
1245     { "umask",     FTP_UMA, 0 },
1246     { "up",        FTP_GUP, CM_INV },
1247     { "user",      FTP_USR, 0 },
1248     { "vdirectory",FTP_VDI, 0 },
1249     { "", 0, 0 }
1250 };
1251 static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1;
1252
1253 #define OPN_ANO 1                       /* FTP OPEN switch codes */
1254 #define OPN_PSW 2
1255 #define OPN_USR 3
1256 #define OPN_ACC 4
1257 #define OPN_ACT 5
1258 #define OPN_PSV 6
1259 #define OPN_TLS 7
1260 #define OPN_NIN 8
1261 #define OPN_NOL 9
1262
1263 #ifdef FTP_SECURITY
1264 #ifdef CK_SSL
1265 #define USETLSTAB
1266 static struct keytab tlstab[] = {       /* FTP SSL/TLS switches */
1267     { "/ssl",       OPN_TLS, 0    },
1268     { "/tls",       OPN_TLS, 0    },
1269     { "", 0, 0 }
1270 };
1271 static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1;
1272 #endif /* CK_SSL */
1273 #endif /* FTP_SECURITY */
1274
1275 static struct keytab ftpswitab[] = {    /* FTP command switches */
1276     { "/account",   OPN_ACC, CM_ARG },
1277     { "/active",    OPN_ACT, 0      },
1278     { "/anonymous", OPN_ANO, 0      },
1279     { "/noinit",    OPN_NIN, 0      },
1280     { "/nologin",   OPN_NOL, 0      },
1281     { "/passive",   OPN_PSV, 0      },
1282     { "/password",  OPN_PSW, CM_ARG },
1283     { "/user",      OPN_USR, CM_ARG },
1284     { "", 0, 0 }
1285 };
1286 static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1;
1287
1288 /* FTP { ENABLE, DISABLE } items */
1289
1290 #define ENA_FEAT 1
1291 #define ENA_MDTM 2
1292 #define ENA_MLST 3
1293 #define ENA_SIZE 4
1294 #define ENA_AUTH 5
1295
1296 static struct keytab ftpenatab[] = {
1297     { "AUTH",  ENA_AUTH, 0 },
1298     { "FEAT",  ENA_FEAT, 0 },
1299     { "MDTM",  ENA_MDTM, 0 },
1300     { "ML",    ENA_MLST, CM_INV|CM_ABR },
1301     { "MLS",   ENA_MLST, CM_INV|CM_ABR },
1302     { "MLSD",  ENA_MLST, CM_INV },
1303     { "MLST",  ENA_MLST, 0 },
1304     { "SIZE",  ENA_SIZE, 0 },
1305     { "", 0, 0 }
1306 };
1307 static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1;
1308
1309 /* SET FTP command keyword indices */
1310
1311 #define FTS_AUT  1                      /* Autoauthentication */
1312 #define FTS_CRY  2                      /* Encryption */
1313 #define FTS_LOG  3                      /* Autologin */
1314 #define FTS_CPL  4                      /* Command protection level */
1315 #define FTS_CFW  5                      /* Credentials forwarding */
1316 #define FTS_DPL  6                      /* Data protection level */
1317 #define FTS_DBG  7                      /* Debugging */
1318 #define FTS_PSV  8                      /* Passive mode */
1319 #define FTS_SPC  9                      /* Send port commands */
1320 #define FTS_TYP 10                      /* (file) Type */
1321 #define FTS_USN 11                      /* Unique server names (for files) */
1322 #define FTS_VBM 12                      /* Verbose mode */
1323 #define FTS_ATP 13                      /* Authentication type */
1324 #define FTS_CNV 14                      /* Filename conversion */
1325 #define FTS_TST 15                      /* Test (progress) messages */
1326 #define FTS_PRM 16                      /* (file) Permissions */
1327 #define FTS_XLA 17                      /* Charset translation */
1328 #define FTS_CSR 18                      /* Server charset */
1329 #define FTS_ERR 19                      /* Error action */
1330 #define FTS_FNC 20                      /* Collision */
1331 #define FTS_SRP 21                      /* SRP options */
1332 #define FTS_GFT 22                      /* GET automatic file-type switching */
1333 #define FTS_DAT 23                      /* Set file dates */
1334 #define FTS_STO 24                      /* Server time offset */
1335 #define FTS_APW 25                      /* Anonymous password */
1336 #define FTS_DIS 26                      /* File-transfer display style */
1337 #define FTS_BUG 27                      /* Bug(s) */
1338 #define FTS_TMO 28                      /* Timeout */
1339
1340 /* FTP BUGS */
1341
1342 #define FTB_SV2  1                      /* use SSLv2 */
1343
1344 static struct keytab ftpbugtab[] = {
1345     { "use-ssl-v2",     FTB_SV2,        0 }
1346 };
1347 static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab));
1348
1349 /* FTP PUT options (mutually exclusive, not a bitmask) */
1350
1351 #define PUT_UPD 1                       /* Update */
1352 #define PUT_RES 2                       /* Restart */
1353 #define PUT_SIM 4                       /* Simulation */
1354 #define PUT_DIF 8                       /* Dates Differ */
1355
1356 static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */
1357 #ifndef MAC
1358     { "append",    XYFX_A, 0 },         /* append to old file */
1359 #endif /* MAC */
1360 #ifdef COMMENT
1361     { "ask",       XYFX_Q, 0 },         /* ask what to do (not implemented) */
1362 #endif
1363     { "backup",    XYFX_B, 0 },         /* rename old file */
1364 #ifndef MAC
1365     { "dates-differ", XYFX_M, 0 },      /* accept if dates differ */
1366     { "discard",   XYFX_D, 0 },         /* don't accept new file */
1367     { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */
1368 #endif /* MAC */
1369     { "overwrite", XYFX_X, 0 },         /* overwrite the old file */
1370     { "rename",    XYFX_R, 0 },         /* rename the incoming file */
1371 #ifndef MAC                             /* This crashes Mac Kermit. */
1372     { "update",    XYFX_U, 0 },         /* replace if newer */
1373 #endif /* MAC */
1374     { "", 0, 0 }
1375 };
1376 static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1;
1377
1378
1379 #ifdef FTP_SECURITY
1380 /* FTP authentication options */
1381
1382 #define FTA_AUTO 0                      /* Auto */
1383 #define FTA_SRP  1                      /* SRP */
1384 #define FTA_GK5  2                      /* Kerberos 5 */
1385 #define FTA_K4   3                      /* Kerberos 4 */
1386 #define FTA_SSL  4                      /* SSL */
1387 #define FTA_TLS  5                      /* TLS */
1388
1389 /* FTP authentication types */
1390
1391 #define FTPATYPS 8
1392 static int ftp_auth_type[FTPATYPS] = {
1393 #ifdef FTP_GSSAPI
1394     FTA_GK5,                            /* GSSAPI Kerberos 5 */
1395 #endif /* FTP_GK5 */
1396 #ifdef FTP_SRP
1397     FTA_SRP,                            /* SRP */
1398 #endif /* FTP_SRP */
1399 #ifdef FTP_KRB4
1400     FTA_K4,                             /* Kerberos 4 */
1401 #endif /* FTP_KRB4 */
1402 #ifdef CK_SSL
1403     FTA_TLS,                            /* TLS */
1404     FTA_SSL,                            /* SSL */
1405 #endif /* CK_SSL */
1406     0
1407 };
1408
1409 static struct keytab ftpauth[] = {      /* SET FTP AUTHTYPE cmd table */
1410     { "automatic", FTA_AUTO,  CM_INV },
1411 #ifdef FTP_GSSAPI
1412     { "gssapi-krb5", FTA_GK5, 0 },
1413 #endif /* FTP_GSSAPI */
1414 #ifdef FTP_KRB4
1415     { "k4",       FTA_K4,     CM_INV },
1416 #endif /* FTP_KRB4 */
1417 #ifdef FTP_GSSAPI
1418     { "k5",        FTA_GK5,   CM_INV },
1419 #endif /* FTP_GSSAPI */
1420 #ifdef FTP_KRB4
1421     { "kerberos4", FTA_K4,    0 },
1422 #endif /* FTP_KRB4 */
1423 #ifdef FTP_GSSAPI
1424     { "kerberos5", FTA_GK5,   CM_INV },
1425 #endif /* FTP_GSSAPI */
1426 #ifdef FTP_KRB4
1427     { "kerberos_iv",FTA_K4,   CM_INV },
1428 #endif /* FTP_KRB4 */
1429 #ifdef FTP_GSSAPI
1430     { "kerberos_v", FTA_GK5,  CM_INV },
1431 #endif /* FTP_GSSAPI */
1432 #ifdef FTP_KRB4
1433     { "krb4",     FTA_K4,     CM_INV },
1434 #endif /* FTP_KRB4 */
1435 #ifdef FTP_GSSAPI
1436     { "krb5",     FTA_GK5,    CM_INV },
1437 #endif /* FTP_GSSAPI */
1438 #ifdef FTP_SRP
1439     { "srp",      FTA_SRP,     0 },
1440 #endif /* FTP_SRP */
1441 #ifdef CK_SSL
1442     { "ssl",      FTA_SSL,     0 },
1443     { "tls",      FTA_TLS,     0 },
1444 #endif /* CK_SSL */
1445     { "", 0, 0 }
1446 };
1447 static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1;
1448
1449 #ifdef FTP_SRP
1450 #define SRP_CIPHER 1
1451 #define SRP_HASH   2
1452 static struct keytab ftpsrp[] = {      /* SET FTP SRP command table */
1453     { "cipher",   SRP_CIPHER,     0 },
1454     { "hash",     SRP_HASH,       0 },
1455     { "", 0, 0 }
1456 };
1457 static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1;
1458 #endif /* FTP_SRP */
1459 #endif /* FTP_SECURITY */
1460
1461 static struct keytab ftpset[] = {       /* SET FTP commmand table */
1462     { "anonymous-password",       FTS_APW, 0 },
1463 #ifdef FTP_SECURITY
1464     { "authtype",                 FTS_ATP, 0 },
1465     { "autoauthentication",       FTS_AUT, 0 },
1466     { "autoencryption",           FTS_CRY, 0 },
1467 #endif /* FTP_SECURITY */
1468     { "autologin",                FTS_LOG, 0 },
1469     { "bug",                      FTS_BUG, 0 },
1470 #ifndef NOCSETS
1471     { "character-set-translation",FTS_XLA, 0 },
1472 #endif /* NOCSETS */
1473     { "collision",                FTS_FNC, 0 },
1474 #ifdef FTP_SECURITY
1475     { "command-protection-level", FTS_CPL, 0 },
1476     { "cpl",                      FTS_CPL, CM_INV },
1477     { "credential-forwarding",    FTS_CFW, 0 },
1478     { "da",                       FTS_DAT, CM_INV|CM_ABR },
1479     { "data-protection-level",    FTS_DPL, 0 },
1480 #endif /* FTP_SECURITY */
1481     { "dates",                    FTS_DAT, 0 },
1482     { "debug",                    FTS_DBG, 0 },
1483     { "display",                  FTS_DIS, 0 },
1484 #ifdef FTP_SECURITY
1485     { "dpl",                      FTS_DPL, CM_INV },
1486 #endif /* FTP_SECURITY */
1487     { "error-action",             FTS_ERR, 0 },
1488     { "filenames",                FTS_CNV, 0 },
1489     { "get-filetype-switching",   FTS_GFT, 0 },
1490     { "passive-mode",             FTS_PSV, 0 },
1491     { "pasv",                     FTS_PSV, CM_INV },
1492     { "permissions",              FTS_PRM, 0 },
1493     { "progress-messages",        FTS_TST, 0 },
1494     { "send-port-commands",       FTS_SPC, 0 },
1495 #ifndef NOCSETS
1496     { "server-character-set",     FTS_CSR, 0 },
1497 #endif /* NOCSETS */
1498     { "server-time-offset",       FTS_STO, 0 },
1499 #ifdef FTP_SRP
1500     { "srp",                      FTS_SRP, 0 },
1501 #else
1502     { "srp",                      FTS_SRP, CM_INV },
1503 #endif /* FTP_SRP */
1504 #ifdef FTP_TIMEOUT
1505     { "timeout",                  FTS_TMO, 0 },
1506 #endif  /* FTP_TIMEOUT */
1507     { "type",                     FTS_TYP, 0 },
1508     { "unique-server-names",      FTS_USN, 0 },
1509     { "verbose-mode",             FTS_VBM, 0 },
1510     { "", 0, 0 }
1511 };
1512 static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1;
1513
1514 /*
1515   GET and PUT switches are approximately the same as Kermit GET and SEND,
1516   and use the same SND_xxx definitions, but hijack a couple for FTP use.
1517   Don't just make up new ones, since the number of SND_xxx options must be
1518   known in advance for the switch-parsing arrays.
1519 */
1520 #define SND_USN SND_PRO                 /* /UNIQUE instead of /PROTOCOL */
1521 #define SND_PRM SND_PIP                 /* /PERMISSIONS instead of /PIPES */
1522 #define SND_TEN SND_CAL                 /* /TENEX instead of /CALIBRATE */
1523
1524 static struct keytab putswi[] = {       /* FTP PUT switch table */
1525     { "/after",                SND_AFT, CM_ARG },
1526 #ifdef PUTARRAY
1527     { "/array",                SND_ARR, CM_ARG },
1528 #endif /* PUTARRAY */
1529     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1530     { "/as-name",              SND_ASN, CM_ARG },
1531     { "/ascii",                SND_TXT, CM_INV },
1532     { "/b",                    SND_BIN, CM_INV|CM_ABR },
1533     { "/before",               SND_BEF, CM_ARG },
1534     { "/binary",               SND_BIN, 0 },
1535 #ifdef PUTPIPE
1536     { "/command",              SND_CMD, CM_PSH },
1537 #endif /* PUTPIPE */
1538 #ifdef COMMENT
1539 /* This works but it's dangerous */
1540 #ifdef DOUPDATE
1541     { "/dates-differ",         SND_DIF, CM_INV },
1542 #endif /* DOUPDATE */
1543 #endif /* COMMENT */
1544     { "/delete",               SND_DEL, 0 },
1545 #ifdef UNIXOROSK
1546     { "/dotfiles",             SND_DOT, 0 },
1547 #endif /* UNIXOROSK */
1548     { "/error-action",         SND_ERR, CM_ARG },
1549     { "/except",               SND_EXC, CM_ARG },
1550     { "/filenames",            SND_NAM, CM_ARG },
1551 #ifdef PIPESEND
1552 #ifndef NOSPL
1553     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1554 #endif /* NOSPL */
1555 #endif /* PIPESEND */
1556 #ifdef CKSYMLINK
1557     { "/followlinks",          SND_LNK, 0 },
1558 #endif /* CKSYMLINK */
1559 #ifdef VMS
1560     { "/image",                SND_IMG, 0 },
1561 #else
1562     { "/image",                SND_BIN, CM_INV },
1563 #endif /* VMS */
1564     { "/larger-than",          SND_LAR, CM_ARG },
1565     { "/listfile",             SND_FIL, CM_ARG },
1566 #ifndef NOCSETS
1567     { "/local-character-set",  SND_CSL, CM_ARG },
1568 #endif /* NOCSETS */
1569 #ifdef CK_TMPDIR
1570     { "/move-to",              SND_MOV, CM_ARG },
1571 #endif /* CK_TMPDIR */
1572     { "/nobackupfiles",        SND_NOB, 0 },
1573 #ifdef UNIXOROSK
1574     { "/nodotfiles",           SND_NOD, 0 },
1575 #endif /* UNIXOROSK */
1576 #ifdef CKSYMLINK
1577     { "/nofollowlinks",        SND_NLK, 0 },
1578 #endif /* CKSYMLINK */
1579
1580     { "/not-after",            SND_NAF, CM_ARG },
1581     { "/not-before",           SND_NBE, CM_ARG },
1582 #ifdef UNIX
1583     { "/permissions",          SND_PRM, CM_ARG },
1584 #else
1585     { "/permissions",          SND_PRM, CM_ARG|CM_INV },
1586 #endif /* UNIX */
1587     { "/quiet",                SND_SHH, 0 },
1588 #ifdef FTP_RESTART
1589     { "/recover",              SND_RES, 0 },
1590 #endif /* FTP_RESTART */
1591 #ifdef RECURSIVE
1592     { "/recursive",            SND_REC, 0 },
1593 #endif /* RECURSIVE */
1594     { "/rename-to",            SND_REN, CM_ARG },
1595 #ifdef FTP_RESTART
1596     { "/restart",              SND_RES, CM_INV },
1597 #endif /* FTP_RESTART */
1598 #ifndef NOCSETS
1599     { "/server-character-set", SND_CSR, CM_ARG },
1600 #endif /* NOCSETS */
1601     { "/server-rename-to",     SND_SRN, CM_ARG },
1602     { "/simulate",             SND_SIM, 0 },
1603     { "/since",                SND_AFT, CM_INV|CM_ARG },
1604     { "/smaller-than",         SND_SMA, CM_ARG },
1605 #ifdef COMMENT
1606     { "/starting-at",          SND_STA, CM_ARG },
1607 #endif /* COMMENT */
1608 #ifdef RECURSIVE
1609     { "/subdirectories",       SND_REC, CM_INV },
1610 #endif /* RECURSIVE */
1611     { "/tenex",                SND_TEN, 0 },
1612     { "/text",                 SND_TXT, 0 },
1613 #ifndef NOCSETS
1614     { "/transparent",          SND_XPA, 0 },
1615 #endif /* NOCSETS */
1616     { "/type",                 SND_TYP, CM_ARG },
1617 #ifdef DOUPDATE
1618     { "/update",               SND_UPD, 0 },
1619 #endif /* DOUPDATE */
1620     { "/unique-server-names",  SND_USN, 0 },
1621     { "", 0, 0 }
1622 };
1623 static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1;
1624
1625 static struct keytab getswi[] = {       /* FTP [M]GET switch table */
1626     { "/after",                SND_AFT, CM_INV },
1627     { "/as",                   SND_ASN, CM_ARG|CM_INV|CM_ABR },
1628     { "/as-name",              SND_ASN, CM_ARG },
1629     { "/ascii",                SND_TXT, CM_INV },
1630     { "/before",               SND_BEF, CM_INV },
1631     { "/binary",               SND_BIN, 0 },
1632     { "/collision",            SND_COL, CM_ARG },
1633 #ifdef PUTPIPE
1634     { "/command",              SND_CMD, CM_PSH },
1635 #endif /* PUTPIPE */
1636     { "/delete",               SND_DEL, 0 },
1637     { "/error-action",         SND_ERR, CM_ARG },
1638     { "/except",               SND_EXC, CM_ARG },
1639     { "/filenames",            SND_NAM, CM_ARG },
1640 #ifdef PIPESEND
1641 #ifndef NOSPL
1642     { "/filter",               SND_FLT, CM_ARG|CM_PSH },
1643 #endif /* NOSPL */
1644 #endif /* PIPESEND */
1645 #ifdef VMS
1646     { "/image",                SND_IMG, 0 },
1647 #else
1648     { "/image",                SND_BIN, CM_INV },
1649 #endif /* VMS */
1650     { "/larger-than",          SND_LAR, CM_ARG },
1651     { "/listfile",             SND_FIL, CM_ARG },
1652 #ifndef NOCSETS
1653     { "/local-character-set",  SND_CSL, CM_ARG },
1654 #endif /* NOCSETS */
1655     { "/match",                SND_PAT, CM_ARG },
1656     { "/ml",                   SND_MLS, CM_INV|CM_ABR },
1657     { "/mls",                  SND_MLS, CM_INV|CM_ABR },
1658     { "/mlsd",                 SND_MLS, 0 },
1659     { "/mlst",                 SND_MLS, CM_INV },
1660 #ifdef CK_TMPDIR
1661     { "/move-to",              SND_MOV, CM_ARG },
1662 #endif /* CK_TMPDIR */
1663     { "/namelist",             SND_NML, CM_ARG },
1664     { "/nlst",                 SND_NLS, 0 },
1665     { "/nobackupfiles",        SND_NOB, 0 },
1666     { "/nodotfiles",           SND_NOD, 0 },
1667 #ifdef DOUPDATE
1668     { "/dates-differ",         SND_DIF, CM_INV },
1669 #endif /* DOUPDATE */
1670     { "/not-after",            SND_NAF, CM_INV },
1671     { "/not-before",           SND_NBE, CM_INV },
1672     { "/permissions",          SND_PRM, CM_INV },
1673     { "/quiet",                SND_SHH, 0 },
1674 #ifdef FTP_RESTART
1675     { "/recover",              SND_RES, 0 },
1676 #endif /* FTP_RESTART */
1677 #ifdef RECURSIVE
1678     { "/recursive",            SND_REC, 0 },
1679 #endif /* RECURSIVE */
1680     { "/rename-to",            SND_REN, CM_ARG },
1681 #ifdef FTP_RESTART
1682     { "/restart",              SND_RES, CM_INV },
1683 #endif /* FTP_RESTART */
1684 #ifndef NOCSETS
1685     { "/server-character-set", SND_CSR, CM_ARG },
1686 #endif /* NOCSETS */
1687     { "/server-rename-to",     SND_SRN, CM_ARG },
1688     { "/smaller-than",         SND_SMA, CM_ARG },
1689 #ifdef RECURSIVE
1690     { "/subdirectories",       SND_REC, CM_INV },
1691 #endif /* RECURSIVE */
1692     { "/text",                 SND_TXT, 0 },
1693     { "/tenex",                SND_TEN, 0 },
1694 #ifndef NOCSETS
1695     { "/transparent",          SND_XPA, 0 },
1696 #endif /* NOCSETS */
1697     { "/to-screen",            SND_MAI, 0 },
1698 #ifdef DOUPDATE
1699     { "/update",               SND_UPD, CM_INV },
1700 #endif /* DOUPDATE */
1701     { "", 0, 0 }
1702 };
1703 static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1;
1704
1705 static struct keytab delswi[] = {       /* FTP [M]DELETE switch table */
1706     { "/error-action",         SND_ERR, CM_ARG },
1707     { "/except",               SND_EXC, CM_ARG },
1708     { "/filenames",            SND_NAM, CM_ARG },
1709     { "/larger-than",          SND_LAR, CM_ARG },
1710     { "/nobackupfiles",        SND_NOB, 0 },
1711 #ifdef UNIXOROSK
1712     { "/nodotfiles",           SND_NOD, 0 },
1713 #endif /* UNIXOROSK */
1714     { "/quiet",                SND_SHH, 0 },
1715 #ifdef RECURSIVE
1716     { "/recursive",            SND_REC, 0 },
1717 #endif /* RECURSIVE */
1718     { "/smaller-than",         SND_SMA, CM_ARG },
1719 #ifdef RECURSIVE
1720     { "/subdirectories",       SND_REC, CM_INV },
1721 #endif /* RECURSIVE */
1722     { "", 0, 0 }
1723 };
1724 static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1;
1725
1726 static struct keytab fntab[] = {        /* Filename conversion keyword table */
1727     { "automatic",    2, CNV_AUTO },
1728     { "converted",    1, CNV_CNV  },
1729     { "literal",      0, CNV_LIT  }
1730 };
1731 static int nfntab = (sizeof(fntab) / sizeof(struct keytab));
1732
1733 static struct keytab ftptyp[] = {       /* SET FTP TYPE table */
1734     { "ascii",        FTT_ASC, 0 },
1735     { "binary",       FTT_BIN, 0 },
1736     { "tenex",        FTT_TEN, 0 },
1737     { "text",         FTT_ASC, CM_INV },
1738     { "", 0, 0 }
1739 };
1740 static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1;
1741
1742 #ifdef FTP_SECURITY
1743 static struct keytab ftppro[] = {       /* SET FTP PROTECTION-LEVEL table */
1744     { "clear",        FPL_CLR, 0 },
1745     { "confidential", FPL_CON, 0 },
1746     { "private",      FPL_PRV, 0 },
1747     { "safe",         FPL_SAF, 0 },
1748     { "", 0, 0 }
1749 };
1750 static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1;
1751 #endif /* FTP_SECURITY */
1752
1753 /* Definitions for FTP from RFC765. */
1754
1755 /* Reply codes */
1756
1757 #define REPLY_PRELIM    1               /* Positive preliminary */
1758 #define REPLY_COMPLETE  2               /* Positive completion */
1759 #define REPLY_CONTINUE  3               /* Positive intermediate */
1760 #define REPLY_TRANSIENT 4               /* Transient negative completion */
1761 #define REPLY_ERROR     5               /* Permanent negative completion */
1762 #define REPLY_SECURE    6               /* Security encoded message */
1763
1764 /* Form codes and names */
1765
1766 #define FORM_N 1                        /* Non-print */
1767 #define FORM_T 2                        /* Telnet format effectors */
1768 #define FORM_C 3                        /* Carriage control (ASA) */
1769
1770 /* Structure codes and names */
1771
1772 #define STRU_F 1                        /* File (no record structure) */
1773 #define STRU_R 2                        /* Record structure */
1774 #define STRU_P 3                        /* Page structure */
1775
1776 /* Mode types and names */
1777
1778 #define MODE_S 1                        /* Stream */
1779 #define MODE_B 2                        /* Block */
1780 #define MODE_C 3                        /* Compressed */
1781
1782 /* Protection levels and names */
1783
1784 #define PROT_C 1                        /* Clear */
1785 #define PROT_S 2                        /* Safe */
1786 #define PROT_P 3                        /* Private */
1787 #define PROT_E 4                        /* Confidential */
1788
1789 #ifdef COMMENT                          /* Not used */
1790 #ifdef FTP_NAMES
1791 char *strunames[]  =  {"0", "File",     "Record", "Page" };
1792 char *formnames[]  =  {"0", "Nonprint", "Telnet", "Carriage-control" };
1793 char *modenames[]  =  {"0", "Stream",   "Block",  "Compressed" };
1794 char *levelnames[] =  {"0", "Clear",    "Safe",   "Private",  "Confidential" };
1795 #endif /* FTP_NAMES */
1796
1797 /* Record Tokens */
1798
1799 #define REC_ESC '\377'                  /* Record-mode Escape */
1800 #define REC_EOR '\001'                  /* Record-mode End-of-Record */
1801 #define REC_EOF '\002'                  /* Record-mode End-of-File */
1802
1803 /* Block Header */
1804
1805 #define BLK_EOR           0x80          /* Block is End-of-Record */
1806 #define BLK_EOF           0x40          /* Block is End-of-File */
1807 #define BLK_REPLY_ERRORS  0x20          /* Block might have errors */
1808 #define BLK_RESTART       0x10          /* Block is Restart Marker */
1809 #define BLK_BYTECOUNT 2                 /* Bytes in this block */
1810 #endif /* COMMENT */
1811
1812 #define RADIX_ENCODE 0                  /* radix_encode() function codes */
1813 #define RADIX_DECODE 1
1814
1815 /*
1816   The default setpbsz() value in the Unix FTP client is 1<<20 (1MB).  This
1817   results in a serious performance degradation due to the increased number
1818   of page faults and the inability to overlap encrypt/decrypt, file i/o, and
1819   network i/o.  So instead we set the value to 1<<13 (8K), about half the size
1820   of the typical TCP window.  Maybe we should add a command to allow the value
1821   to be changed.
1822 */
1823 #define DEFAULT_PBSZ 1<<13
1824
1825 /* Prototypes */
1826
1827 _PROTOTYP(int remtxt, (char **) );
1828 _PROTOTYP(char * gskreason, (int) );
1829 _PROTOTYP(static int ftpclose,(void));
1830 _PROTOTYP(static int zzsend, (int, CHAR));
1831 _PROTOTYP(static int getreply,(int,int,int,int,int));
1832 _PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int));
1833 _PROTOTYP(static int setpbsz,(unsigned int));
1834 _PROTOTYP(static int recvrequest,(char *,char *,char *,char *,
1835   int,int,char *,int,int,int));
1836 _PROTOTYP(static int ftpcmd,(char *,char *,int,int,int));
1837 _PROTOTYP(static int fts_cpl,(int));
1838 _PROTOTYP(static int fts_dpl,(int));
1839 #ifdef FTP_SECURITY
1840 _PROTOTYP(static int ftp_auth, (void));
1841 #endif /* FTP_SECURITY */
1842 _PROTOTYP(static int ftp_user, (char *, char *, char *));
1843 _PROTOTYP(static int ftp_login, (char *));
1844 _PROTOTYP(static int ftp_reset, (void));
1845 _PROTOTYP(static int ftp_rename, (char *, char *));
1846 _PROTOTYP(static int ftp_umask, (char *));
1847 _PROTOTYP(static int secure_flush, (int));
1848 #ifdef COMMENT
1849 _PROTOTYP(static int secure_putc, (char, int));
1850 #endif /* COMMENT */
1851 _PROTOTYP(static int secure_write, (int, CHAR *, unsigned int));
1852 _PROTOTYP(static int scommand, (char *));
1853 _PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int));
1854 _PROTOTYP(static int secure_getc, (int, int));
1855 _PROTOTYP(static int secure_getbyte, (int, int));
1856 _PROTOTYP(static int secure_read, (int, char *, int));
1857 _PROTOTYP(static int initconn, (void));
1858 _PROTOTYP(static int dataconn, (char *));
1859 _PROTOTYP(static int setprotbuf,(unsigned int));
1860 _PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int));
1861
1862 _PROTOTYP(static char * radix_error,(int));
1863 _PROTOTYP(static char * ftp_hookup,(char *, int, int));
1864 _PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int));
1865
1866 _PROTOTYP(static VOID mlsreset, (void));
1867 _PROTOTYP(static VOID secure_error, (char *fmt, ...));
1868 _PROTOTYP(static VOID lostpeer, (void));
1869 _PROTOTYP(static VOID cancel_remote, (int));
1870 _PROTOTYP(static VOID changetype, (int, int));
1871
1872 _PROTOTYP(static sigtype cmdcancel, (int));
1873
1874 #ifdef FTP_SRP
1875 _PROTOTYP(static int srp_reset, ());
1876 _PROTOTYP(static int srp_ftp_auth, (char *,char *,char *));
1877 _PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *));
1878 _PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *));
1879 _PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int));
1880 _PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int));
1881 _PROTOTYP(static int srp_selcipher, (char *));
1882 _PROTOTYP(static int srp_selhash, (char *));
1883 #endif /* FTP_SRP */
1884
1885 #ifdef FTP_GSSAPI
1886 _PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *));
1887 #endif /* FTP_GSSAPI */
1888
1889 /*  D O F T P A R G  --  Do an FTP command-line argument.  */
1890
1891 #ifdef FTP_SECURITY
1892 #ifndef NOICP
1893 #define FT_NOGSS   1
1894 #define FT_NOK4    2
1895 #define FT_NOSRP   3
1896 #define FT_NOSSL   4
1897 #define FT_NOTLS   5
1898 #define FT_CERTFI  6
1899 #define FT_OKCERT  7
1900 #define FT_DEBUG   8
1901 #define FT_KEY     9
1902 #define FT_SECURE 10
1903 #define FT_VERIFY 11
1904
1905 static struct keytab ftpztab[] = {
1906     { "!gss",    FT_NOGSS,  0 },
1907     { "!krb4",   FT_NOK4,   0 },
1908     { "!srp",    FT_NOSRP,  0 },
1909     { "!ssl",    FT_NOSSL,  0 },
1910     { "!tls",    FT_NOTLS,  0 },
1911     { "cert",    FT_CERTFI, CM_ARG },
1912     { "certsok", FT_OKCERT, 0 },
1913     { "debug",   FT_DEBUG,  0 },
1914     { "key",     FT_KEY,    CM_ARG },
1915     { "nogss",   FT_NOGSS,  0 },
1916     { "nokrb4",  FT_NOK4,   0 },
1917     { "nosrp",   FT_NOSRP,  0 },
1918     { "nossl",   FT_NOSSL,  0 },
1919     { "notls",   FT_NOTLS,  0 },
1920 #ifdef COMMENT
1921     { "secure",  FT_SECURE, 0 },
1922 #endif /* COMMENT */
1923     { "verify",  FT_VERIFY, CM_ARG },
1924     { "", 0, 0 }
1925 };
1926 static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1;
1927
1928 /*
1929   The following cipher and hash tables should be replaced with
1930   dynamicly created versions based upon the linked library.
1931 */
1932 #define SRP_BLOWFISH_ECB    1
1933 #define SRP_BLOWFISH_CBC    2
1934 #define SRP_BLOWFISH_CFB64  3
1935 #define SRP_BLOWFISH_OFB64  4
1936 #define SRP_CAST5_ECB       5
1937 #define SRP_CAST5_CBC       6
1938 #define SRP_CAST5_CFB64     7
1939 #define SRP_CAST5_OFB64     8
1940 #define SRP_DES_ECB         9
1941 #define SRP_DES_CBC        10
1942 #define SRP_DES_CFB64      11
1943 #define SRP_DES_OFB64      12
1944 #define SRP_DES3_ECB       13
1945 #define SRP_DES3_CBC       14
1946 #define SRP_DES3_CFB64     15
1947 #define SRP_DES3_OFB64     16
1948
1949 static struct keytab ciphertab[] = {
1950     { "blowfish_ecb",   SRP_BLOWFISH_ECB,   0 },
1951     { "blowfish_cbc",   SRP_BLOWFISH_CBC,   0 },
1952     { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 },
1953     { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 },
1954     { "cast5_ecb",      SRP_CAST5_ECB,      0 },
1955     { "cast5_cbc",      SRP_CAST5_CBC,      0 },
1956     { "cast5_cfb64",    SRP_CAST5_CFB64,    0 },
1957     { "cast5_ofb64",    SRP_CAST5_OFB64,    0 },
1958     { "des_ecb",        SRP_DES_ECB,        0 },
1959     { "des_cbc",        SRP_DES_CBC,        0 },
1960     { "des_cfb64",      SRP_DES_CFB64,      0 },
1961     { "des_ofb64",      SRP_DES_OFB64,      0 },
1962     { "des3_ecb",       SRP_DES3_ECB,       0 },
1963     { "des3_cbc",       SRP_DES3_CBC,       0 },
1964     { "des3_cfb64",     SRP_DES3_CFB64,     0 },
1965     { "des3_ofb64",     SRP_DES3_OFB64,     0 },
1966     { "none",           0, 0 },
1967     { "", 0, 0 }
1968 };
1969 static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1;
1970
1971 #define SRP_MD5  1
1972 #define SRP_SHA  2
1973 static struct keytab hashtab[] = {
1974     { "md5",              SRP_MD5,        0 },
1975     { "none",             0,              0 },
1976     { "sha",              SRP_SHA,        0 },
1977     { "", 0, 0 }
1978 };
1979 static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1;
1980 #endif /* NOICP */
1981 #endif /* FTP_SECURITY */
1982
1983 static char *
1984 strval(s1,s2) char * s1, * s2; {
1985     if (!s1) s1 = "";
1986     if (!s2) s2 = "";
1987     return(*s1 ? s1 : (*s2 ? s2 : "(none)"));
1988 }
1989
1990 #ifndef NOCSETS
1991 static char * rfnptr = NULL;
1992 static int rfnlen = 0;
1993 static char rfnbuf[RFNBUFSIZ];          /* Remote filename translate buffer */
1994 static char * xgnbp = NULL;
1995
1996 static int
1997 strgetc() {                             /* Helper function for xgnbyte() */
1998     int c;
1999     if (!xgnbp)
2000       return(-1);
2001     if (!*xgnbp)
2002       return(-1);
2003     c = (unsigned) *xgnbp++;
2004     return(((unsigned) c) & 0xff);
2005 }
2006
2007 static int                              /* Helper function for xpnbyte() */
2008 #ifdef CK_ANSIC
2009 strputc(char c)
2010 #else
2011 strputc(c) char c;
2012 #endif /* CK_ANSIC */
2013 {
2014     rfnlen = rfnptr - rfnbuf;
2015     if (rfnlen >= (RFNBUFSIZ - 1))
2016       return(-1);
2017     *rfnptr++ = c;
2018     *rfnptr = NUL;
2019     return(0);
2020 }
2021
2022 static int
2023 #ifdef CK_ANSIC
2024 xprintc(char c)
2025 #else
2026 xprintc(c) char c;
2027 #endif /* CK_ANSIC */
2028 {
2029     printf("%c",c);
2030     return(0);
2031 }
2032
2033 static VOID
2034 bytswap(c0,c1) int * c0, * c1; {
2035     int t;
2036     t = *c0;
2037     *c0 = *c1;
2038     *c1 = t;
2039 }
2040 #endif /* NOCSETS */
2041
2042 #ifdef CKLOGDIAL
2043 char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */
2044 int ftplogactive = 0;
2045 long ftplogprev = 0L;
2046
2047 VOID
2048 ftplogend() {
2049     extern int dialog;
2050     extern char diafil[];
2051     long d1, d2, t1, t2;
2052     char buf[32], * p;
2053
2054     debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive);
2055     debug(F110,"ftp cx log buf",ftplogbuf,0);
2056
2057     if (!ftplogactive || !ftplogbuf[0]) /* No active record */
2058       return;
2059
2060     ftplogactive = 0;                   /* Record is not active */
2061
2062     d1 = mjd((char *)ftplogbuf);        /* Get start date of this session */
2063     ckstrncpy(buf,ckdate(),31);         /* Get current date */
2064     d2 = mjd(buf);                      /* Convert them to mjds */
2065     p = ftplogbuf;                      /* Get start time */
2066     p[11] = NUL;
2067     p[14] = NUL;                        /* Convert to seconds */
2068     t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2069     p[11] = ':';
2070     p[14] = ':';
2071     p = buf;                            /* Get end time */
2072     p[11] = NUL;
2073     p[14] = NUL;
2074     t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15);
2075     t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */
2076     if (t2 > -1L) {
2077         ftplogprev = t2;
2078         p = hhmmss(t2);
2079         ckstrncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */
2080         ckstrncat(ftplogbuf,p,CXLOGBUFL);
2081     } else
2082       ftplogprev = 0L;
2083     debug(F101,"ftp cx log dialog","",dialog);
2084     if (dialog) {                       /* If logging */
2085         int x;
2086         x = diaopn(diafil,1,1);         /* Open log in append mode */
2087         if (x > 0) {
2088             debug(F101,"ftp cx log open","",x);
2089             x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */
2090             debug(F101,"ftp cx log write","",x);
2091             x = zclose(ZDIFIL);         /* Close the log */
2092             debug(F101,"ftp cx log close","",x);
2093         }
2094     }
2095 }
2096
2097 VOID
2098 dologftp() {
2099     ftplogend();                        /* Previous session not closed out? */
2100     ftplogprev = 0L;
2101     ftplogactive = 1;                   /* Record is active */
2102
2103     ckmakxmsg(ftplogbuf,CXLOGBUFL,
2104               ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(),
2105               " T=FTP N=", strval(ftp_host,NULL)," H=",myhost,
2106               " P=", ckitoa(ftp_port)," "); /* SMS 2007/02/15 */
2107     debug(F110,"ftp cx log begin",ftplogbuf,0);
2108 }
2109 #endif /* CKLOGDIAL */
2110
2111 static char * dummy[2] = { NULL, NULL };
2112
2113 static struct keytab modetab[] = {
2114     { "active",  0, 0 },
2115     { "passive", 1, 0 }
2116 };
2117
2118 #ifndef NOCMDL
2119 int                                     /* Called from ckuusy.c */
2120 #ifdef CK_ANSIC
2121 doftparg(char c)
2122 #else
2123 doftparg(c) char c;
2124 #endif /* CK_ANSIC */
2125 /* doftparg */ {
2126     int x, z;
2127     char *xp;
2128     extern char **xargv, *xarg0;
2129     extern int xargc, stayflg, haveftpuid;
2130     extern char uidbuf[];
2131
2132     xp = *xargv+1;                      /* Pointer for bundled args */
2133     while (c) {
2134         if (ckstrchr("MuDPkcHzm",c)) {  /* Options that take arguments */
2135             if (*(xp+1)) {
2136                 fatal("?Invalid argument bundling");
2137             }
2138             xargv++, xargc--;
2139             if ((xargc < 1) || (**xargv == '-')) {
2140                 fatal("?Required argument missing");
2141             }
2142         }
2143         switch (c) {                    /* Big switch on arg */
2144           case 'h':                     /* help */
2145            printf("C-Kermit's FTP client command-line personality.  Usage:\n");
2146             printf("  %s [ options ] host [ port ] [-pg files ]\n\n",xarg0);
2147             printf("Options:\n");
2148             printf("  -h           = help (this message)\n");
2149             printf("  -m mode      = \"passive\" (default) or \"active\"\n");
2150             printf("  -u name      = username for autologin (or -M)\n");
2151             printf("  -P password  = password for autologin (RISKY)\n");
2152             printf("  -A           = autologin anonymously\n");
2153             printf("  -D directory = cd after autologin\n");
2154             printf("  -b           = force binary mode\n");
2155             printf("  -a           = force text (\"ascii\") mode (or -T)\n");
2156             printf("  -d           = debug (double to add timestamps)\n");
2157             printf("  -n           = no autologin\n");
2158             printf("  -v           = verbose (default)\n");
2159             printf("  -q           = quiet\n");
2160             printf("  -S           = Stay (issue command prompt when done)\n");
2161             printf("  -Y           = do not execute Kermit init file\n");
2162             printf("  -p files     = files to put after autologin (or -s)\n");
2163             printf("  -g files     = files to get after autologin\n");
2164             printf("  -R           = recursive (for use with -p)\n");
2165
2166 #ifdef FTP_SECURITY
2167             printf("\nSecurity options:\n");
2168             printf("  -k realm     = Kerberos 4 realm\n");
2169             printf("  -f           = Kerboros 5 credentials forwarding\n");
2170             printf("  -x           = autoencryption mode\n");
2171             printf("  -c cipher    = SRP cipher type\n");
2172             printf("  -H hash      = SRP encryption hash\n");
2173             printf("  -z option    = Security options\n");
2174 #endif /* FTP_SECURITY */
2175
2176             printf("\n-p or -g, if given, should be last.  Example:\n");
2177             printf("  ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n");
2178
2179             doexit(GOOD_EXIT,-1);
2180             break;
2181
2182           case 'R':                     /* Recursive */
2183             recursive = 1;
2184             break;
2185
2186           case 'd':                     /* Debug */
2187 #ifdef DEBUG
2188             if (deblog) {
2189                 extern int debtim;
2190                 debtim = 1;
2191             } else {
2192                 deblog = debopn("debug.log",0);
2193                 debok = 1;
2194             }
2195 #endif /* DEBUG */
2196             /* fall thru on purpose */
2197
2198           case 't':                     /* Trace */
2199             ftp_deb++;
2200             break;
2201
2202           case 'n':                     /* No autologin */
2203             ftp_log = 0;
2204             break;
2205
2206           case 'i':                     /* No prompt */
2207           case 'v':                     /* Verbose */
2208             break;                      /* (ignored) */
2209
2210           case 'q':                     /* Quiet */
2211             quiet = 1;
2212             break;
2213
2214           case 'S':                     /* Stay */
2215             stayflg = 1;
2216             break;
2217
2218           case 'M':
2219           case 'u':                     /* My User Name */
2220             if ((int)strlen(*xargv) > 63) {
2221                 fatal("username too long");
2222             }
2223             ckstrncpy(uidbuf,*xargv,UIDBUFLEN);
2224             haveftpuid = 1;
2225             break;
2226
2227           case 'A':
2228             ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2229             haveftpuid = 1;
2230             break;
2231
2232           case 'T':                     /* Text */
2233           case 'a':                     /* "ascii" */
2234           case 'b':                     /* Binary */
2235             binary = (c == 'b') ? FTT_BIN : FTT_ASC;
2236             ftp_xfermode = XMODE_M;
2237             filepeek = 0;
2238             patterns = 0;
2239             break;
2240
2241           case 'g':                     /* Get */
2242           case 'p':                     /* Put */
2243           case 's': {                   /* Send (= Put) */
2244               int havefiles, rc;
2245               if (ftp_action) {
2246                   fatal("Only one FTP action at a time please");
2247               }
2248               if (*(xp+1)) {
2249                   fatal("invalid argument bundling after -s");
2250               }
2251               nfils = 0;                /* Initialize file counter */
2252               havefiles = 0;            /* Assume nothing to send  */
2253               cmlist = xargv + 1;       /* Remember this pointer */
2254
2255               while (++xargv, --xargc > 0) { /* Traverse the list */
2256                   if (c == 'g') {
2257                       havefiles++;
2258                       nfils++;
2259                       continue;
2260                   }
2261 #ifdef RECURSIVE
2262                   if (!strcmp(*xargv,".")) {
2263                       havefiles = 1;
2264                       nfils++;
2265                       recursive = 1;
2266                   } else
2267 #endif /* RECURSIVE */
2268                     if ((rc = zchki(*xargv)) > -1 || (rc == -2)) {
2269                         if  (rc != -2)
2270                           havefiles = 1;
2271                         nfils++;
2272                     } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) {
2273                         havefiles = 1;
2274                         nfils++;
2275                     }
2276               }
2277               xargc++, xargv--;         /* Adjust argv/argc */
2278               if (!havefiles) {
2279                   if (c == 'g') {
2280                       fatal("No files to put");
2281                   } else {
2282                       fatal("No files to get");
2283                   }
2284               }
2285               ftp_action = c;
2286               break;
2287           }
2288           case 'D':                     /* Directory */
2289             makestr(&ftp_rdir,*xargv);
2290             break;
2291
2292           case 'm':                     /* Mode (Active/Passive */
2293             ftp_psv = lookup(modetab,*xargv,2,NULL);
2294             if (ftp_psv < 0) fatal("Invalid mode");
2295             break;
2296
2297           case 'P':
2298             makestr(&ftp_tmp,*xargv);   /* You-Know-What */
2299             break;
2300
2301           case 'Y':                     /* No initialization file */
2302             break;                      /* (already done in prescan) */
2303
2304 #ifdef CK_URL
2305           case 'U': {                   /* URL */
2306               /* These are set by urlparse() - any not set are NULL */
2307               if (g_url.hos) {
2308 /*
2309   Kermit has accepted host:port notation since many years before URLs were
2310   invented.  Unfortunately, URLs conflict with this notation.  Thus "ftp
2311   host:449" looks like a URL and results in service = host and host = 449.
2312   Here we try to catch this situation transparently to the user.
2313 */
2314                   if (ckstrcmp(g_url.svc,"ftp",-1,0)
2315 #ifdef CK_SSL
2316                        && ckstrcmp(g_url.svc,"ftps",-1,0)
2317 #endif /* CK_SSL */
2318                        ) {
2319                       if (!g_url.usr &&
2320                           !g_url.psw &&
2321                           !g_url.por &&
2322                           !g_url.pth) {
2323                           g_url.por = g_url.hos;
2324                           g_url.hos = g_url.svc;
2325                           g_url.svc = "ftp";
2326                       } else {
2327                           ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=",
2328                                    g_url.svc," host=",g_url.hos);
2329                           fatal(tmpbuf);
2330                       }
2331                   }
2332                   makestr(&ftp_host,g_url.hos);
2333                   if (g_url.usr) {
2334                       haveftpuid = 1;
2335                       ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN);
2336                       makestr(&ftp_logname,uidbuf);
2337                   }
2338                   if (g_url.psw) {
2339                       makestr(&ftp_tmp,g_url.psw);
2340                   }
2341                   if (g_url.pth) {
2342                       if (!g_url.usr) {
2343                           haveftpuid = 1;
2344                           ckstrncpy(uidbuf,"anonymous",UIDBUFLEN);
2345                           makestr(&ftp_logname,uidbuf);
2346                       }
2347                       if (ftp_action) {
2348                           fatal("Only one FTP action at a time please");
2349                       }
2350                       if (!stayflg)
2351                         quiet = 1;
2352                       nfils = 1;
2353                       dummy[0] = g_url.pth;
2354                       cmlist = dummy;
2355                       ftp_action = 'g';
2356                   }
2357                   xp = NULL;
2358                   haveurl = 1;
2359               }
2360               break;
2361           }
2362 #endif /* CK_URL */
2363
2364 #ifdef FTP_SECURITY
2365           case 'k': {                   /* K4 Realm */
2366 #ifdef FTP_KRB4
2367               ckstrncpy(ftp_realm,*xargv, REALM_SZ);
2368 #endif /* FTP_KRB4 */
2369               if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv);
2370               break;
2371           }
2372           case 'f': {
2373 #ifdef FTP_GSSAPI
2374               ftp_cfw = 1;
2375               if (ftp_deb) printf("K5 Credentials Forwarding\n");
2376 #else /* FTP_GSSAPI */
2377               printf("K5 Credentials Forwarding not supported\n");
2378 #endif /* FTP_GSSAPI */
2379               break;
2380           }
2381           case 'x': {
2382               ftp_cry = 1;
2383               if (ftp_deb) printf("Autoencryption\n");
2384               break;
2385           }
2386           case 'c': {                   /* Cipher */
2387 #ifdef FTP_SRP
2388               if (!srp_selcipher(*xargv)) {
2389                   if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv);
2390               } else
2391                 printf("?Invalid SRP cipher type: \"%s\"\n",*xargv);
2392 #else /* FTP_SRP */
2393               printf("?SRP not supported\n");
2394 #endif /* FTP_SRP */
2395               break;
2396           }
2397           case 'H': {
2398 #ifdef FTP_SRP
2399               if (!srp_selhash(*xargv)) {
2400                   if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv);
2401               } else
2402                 printf("?Invalid SRP hash type: \"%s\"\n",*xargv);
2403 #else /* FTP_SRP */
2404               printf("?SRP not supported\n");
2405 #endif /* FTP_SRP */
2406               break;
2407           }
2408           case 'z': {
2409               /* *xargv contains a value of the form tag=value */
2410               /* we need to lookup the tag and save the value  */
2411               char * p = NULL, * q = NULL;
2412               makestr(&p,*xargv);
2413               y = ckindex("=",p,0,0,1);
2414               if (y > 0)
2415                 p[y-1] = '\0';
2416               x = lookup(ftpztab,p,nftpztab,&z);
2417               if (x < 0) {
2418                   printf("?Invalid security option: \"%s\"\n",p);
2419               } else {
2420                   if (ftp_deb)
2421                     printf("Security option: \"%s",p);
2422                   if (ftpztab[z].flgs & CM_ARG) {
2423                       if (y <= 0)
2424                         fatal("?Missing required value");
2425                       q = &p[y];
2426                       if (!*q)
2427                         fatal("?Missing required value");
2428                       if (ftp_deb)
2429                         printf("=%s\"",q);
2430                   }
2431                   switch (ftpztab[z].kwval) { /* -z options w/args */
2432                     case FT_NOGSS:
2433 #ifdef FTP_GSSAPI
2434                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2435                           if (ftp_auth_type[z] == FTA_GK5) {
2436                               for (y = z;
2437                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2438                                    y++
2439                                    )
2440                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2441                               ftp_auth_type[FTPATYPS-1] = 0;
2442                               break;
2443                           }
2444                       }
2445 #endif /* FTP_GSSAPI */
2446                       break;
2447                     case FT_NOK4:
2448 #ifdef FTP_KRB4
2449                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2450                           if (ftp_auth_type[z] == FTA_K4) {
2451                               for (y = z;
2452                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2453                                    y++
2454                                    )
2455                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2456                               ftp_auth_type[FTPATYPS-1] = 0;
2457                               break;
2458                           }
2459                       }
2460 #endif /* FTP_KRB4 */
2461                       break;
2462                     case FT_NOSRP:
2463 #ifdef FTP_SRP
2464                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2465                           if (ftp_auth_type[z] == FTA_SRP) {
2466                               for (y = z;
2467                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2468                                    y++
2469                                    )
2470                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2471                               ftp_auth_type[FTPATYPS-1] = 0;
2472                               break;
2473                           }
2474                       }
2475 #endif /* FTP_SRP */
2476                       break;
2477                     case FT_NOSSL:
2478 #ifdef CK_SSL
2479                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2480                           if (ftp_auth_type[z] == FTA_SSL) {
2481                               for (y = z;
2482                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2483                                    y++
2484                                    )
2485                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2486                               ftp_auth_type[FTPATYPS-1] = 0;
2487                               break;
2488                           }
2489                       }
2490 #endif /* CK_SSL */
2491                       break;
2492                     case FT_NOTLS:
2493 #ifdef CK_SSL
2494                       for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) {
2495                           if (ftp_auth_type[z] == FTA_TLS) {
2496                               for (y = z;
2497                                    y < (FTPATYPS-1) && ftp_auth_type[y];
2498                                    y++
2499                                    )
2500                                 ftp_auth_type[y] = ftp_auth_type[y+1];
2501                               ftp_auth_type[FTPATYPS-1] = 0;
2502                               break;
2503                           }
2504                       }
2505 #endif /* CK_SSL */
2506                       break;
2507                     case FT_CERTFI:
2508 #ifdef CK_SSL
2509                       makestr(&ssl_rsa_cert_file,q);
2510 #endif /* CK_SSL */
2511                       break;
2512                     case FT_OKCERT:
2513 #ifdef CK_SSL
2514                       ssl_certsok_flag = 1;
2515 #endif /* CK_SSL */
2516                       break;
2517                     case FT_DEBUG:
2518 #ifdef DEBUG
2519                       if (deblog) {
2520                           extern int debtim;
2521                           debtim = 1;
2522                       } else {
2523                           deblog = debopn("debug.log",0);
2524                       }
2525 #endif /* DEBUG */
2526                       break;
2527                     case FT_KEY:
2528 #ifdef CK_SSL
2529                       makestr(&ssl_rsa_key_file,q);
2530 #endif /* CK_SSL */
2531                       break;
2532                     case FT_SECURE:
2533                       /* no equivalent */
2534                       break;
2535                     case FT_VERIFY:
2536 #ifdef CK_SSL
2537                       if (!rdigits(q))
2538                         printf("?Bad number: %s\n",q);
2539                       ssl_verify_flag = atoi(q);
2540 #endif /* CK_SSL */
2541                       break;
2542                   }
2543               }
2544               if (ftp_deb) printf("\"\n");
2545               free(p);
2546               break;
2547           }
2548 #endif /* FTP_SECURITY */
2549
2550           default:
2551             fatal2(*xargv,
2552                    "unknown command-line option, type \"ftp -h\" for help"
2553                    );
2554         }
2555         if (!xp) break;
2556         c = *++xp;                      /* See if options are bundled */
2557     }
2558     return(0);
2559 }
2560 #endif /* NOCMDL */
2561
2562 int
2563 ftpisconnected() {
2564     return(connected);
2565 }
2566
2567 int
2568 ftpisloggedin() {
2569     return(connected ? loggedin : 0);
2570 }
2571
2572 int
2573 ftpissecure() {
2574     return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1);
2575 }
2576
2577 static VOID
2578 ftscreen(n, c, z, s) int n; char c; CK_OFF_T z; char * s; {
2579     if (displa && fdispla && !backgrd && !quiet && !out2screen) {
2580         if (!dpyactive) {
2581             ckscreen(SCR_PT,'S',(CK_OFF_T)0,"");
2582             dpyactive = 1;
2583         }
2584         ckscreen(n,c,z,s);
2585     }
2586 }
2587
2588 #ifndef OS2
2589 /*  g m s t i m e r  --  Millisecond timer */
2590
2591 long
2592 gmstimer() {
2593 #ifdef HAVE_MSECS
2594     /* For those versions of ztime() that also set global ztmsec. */
2595     char *p = NULL;
2596     long z;
2597     ztime(&p);
2598     if (!p) return(0L);
2599     if (!*p) return(0L);
2600     z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17);
2601     return(z * 1000 + ztmsec);
2602 #else
2603     return((long)time(NULL) * 1000L);
2604 #endif /* HAVE_MSECS */
2605 }
2606 #endif /* OS2 */
2607
2608 /*  d o s e t f t p  --  The SET FTP command  */
2609
2610 int
2611 dosetftp() {
2612     int cx;
2613     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */
2614       return(cx);
2615     switch (cx) {
2616
2617       case FTS_FNC:                     /* Filename collision action */
2618         if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
2619           return(x);
2620         if ((y = cmcfm()) < 0)
2621           return(y);
2622         ftp_fnc = x;
2623         return(1);
2624
2625       case FTS_CNV:                     /* Filename conversion */
2626         if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
2627           return(x);
2628         if ((y = cmcfm()) < 0)
2629           return(y);
2630         ftp_cnv = x;
2631         return(1);
2632
2633       case FTS_DBG:                     /* Debug messages */
2634         return(seton(&ftp_deb));
2635
2636       case FTS_LOG:                     /* Auto-login */
2637         return(seton(&ftp_log));
2638
2639       case FTS_PSV:                     /* Passive mode */
2640         return(dosetftppsv());
2641
2642       case FTS_SPC:                     /* Send port commands */
2643         x = seton(&ftp_spc);
2644         if (x > 0) sendport = ftp_spc;
2645         return(x);
2646
2647       case FTS_TYP:                     /* Type */
2648         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
2649           return(x);
2650         if ((y = cmcfm()) < 0) return(y);
2651         ftp_typ = x;
2652         g_ftp_typ = x;
2653         tenex = (ftp_typ == FTT_TEN);
2654         return(1);
2655
2656       case FTS_USN:                     /* Unique server names */
2657         return(seton(&ftp_usn));
2658
2659       case FTS_VBM:                     /* Verbose mode */
2660         if ((x = seton(&ftp_vbm)) < 0)  /* Per-command copy */
2661           return(x);
2662         ftp_vbx = ftp_vbm;              /* Global sticky copy */
2663         return(x);
2664
2665       case FTS_TST:                     /* "if (testing)" messages */
2666         return(seton(&testing));
2667
2668       case FTS_PRM:                     /* Send permissions */
2669         return(setonaut(&ftp_prm));
2670
2671       case FTS_AUT:                     /* Auto-authentication */
2672         return(seton(&ftp_aut));
2673
2674       case FTS_ERR:                     /* Error action */
2675         if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
2676           return(x);
2677         if ((y = cmcfm()) < 0)
2678           return(y);
2679         ftp_err = x;
2680         return(success = 1);
2681
2682 #ifndef NOCSETS
2683       case FTS_XLA:                     /* Translation */
2684         return(seton(&ftp_xla));
2685
2686       case FTS_CSR:                     /* Server charset */
2687         if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0)
2688           return(x);
2689         if ((y = cmcfm()) < 0)
2690           return(y);
2691         ftp_csr = x;
2692         ftp_xla = 1;                    /* Also enable translation */
2693         return(success = 1);
2694 #endif /* NOCSETS */
2695
2696       case FTS_GFT:
2697         return(seton(&get_auto));       /* GET-filetype-switching */
2698
2699       case FTS_DAT:
2700         return(seton(&ftp_dates));      /* Set file dates */
2701
2702 #ifdef FTP_TIMEOUT
2703       case FTS_TMO:                     /* Timeout */
2704         if ((x = cmnum("Number of seconds","0",10,&z,xxstring)) < 0)
2705           return(x);
2706         if ((y = cmcfm()) < 0)
2707           return(y);
2708         ftp_timeout = z;
2709         return(success = 1);
2710 #endif  /* FTP_TIMEOUT */
2711
2712       case FTS_STO: {                   /* Server time offset */
2713           char * s, * p = NULL;
2714           long k;
2715           if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0)
2716             return(x);
2717           if (!strcmp(s,"+0")) {
2718               s = NULL;
2719           } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */
2720               printf("?Invalid time offset\n");
2721               return(-9);
2722           }
2723           makestr(&p,s);                /* Make a safe copy the string */
2724           if ((x = cmcfm()) < 0) {      /* Get confirmation */
2725               if (p)
2726                 makestr(&p,NULL);
2727               return(x);
2728           }
2729           fts_sto = p;                  /* Confirmed - set the string. */
2730           return(success = 1);
2731       }
2732       case FTS_APW: {
2733           char * s;
2734           if ((x = cmtxt("Text", "", &s, xxstring)) < 0)
2735             return(x);
2736           makestr(&ftp_apw, *s ? s : NULL);
2737           return(success = 1);
2738       }
2739
2740       case FTS_BUG: {
2741           if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0) 
2742             return(x);
2743           switch (x) {
2744 #ifdef CK_SSL
2745           case FTB_SV2:
2746             return seton(&ftp_bug_use_ssl_v2);
2747 #endif /* CK_SSL */
2748           default:
2749             return(-2);
2750           }
2751       }
2752
2753 #ifdef FTP_SECURITY
2754       case FTS_CRY:                     /* Auto-encryption */
2755         return(seton(&ftp_cry));
2756
2757       case FTS_CFW:                     /* Credential-forwarding */
2758         return(seton(&ftp_cfw));
2759
2760       case FTS_CPL:                     /* Command protection level */
2761         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2762         if ((y = cmcfm()) < 0) return(y);
2763         success = fts_cpl(x);
2764         return(success);
2765
2766       case FTS_DPL:                     /* Data protection level */
2767         if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x);
2768         if ((y = cmcfm()) < 0) return(y);
2769           success = fts_dpl(x);
2770           return(success);
2771
2772       case FTS_ATP: {                   /* FTP Auth Type */
2773           int i, j, atypes[8];
2774
2775           for (i = 0; i < 8; i++) {
2776               if ((y = cmkey(ftpauth,nftpauth,"",
2777                              (i == 0) ? "automatic" : "",
2778                              xxstring)) < 0) {
2779                   if (y == -3)
2780                     break;
2781                   return(y);
2782               }
2783               if (i > 0 && (y == FTA_AUTO)) {
2784                   printf("?Choice may only be used in first position.\r\n");
2785                   return(-9);
2786               }
2787               for (j = 0; j < i; j++) {
2788                   if (atypes[j] == y) {
2789                       printf("\r\n?Choice has already been used.\r\n");
2790                       return(-9);
2791                   }
2792               }
2793               atypes[i] = y;
2794               if (y == FTA_AUTO) {
2795                   i++;
2796                   break;
2797               }
2798           }
2799           if (i < 8)
2800             atypes[i] = 0;
2801           if ((z = cmcfm()) < 0)
2802             return(z);
2803           if (atypes[0] == FTA_AUTO) {
2804               i = 0;
2805 #ifdef FTP_GSSAPI
2806               ftp_auth_type[i++] = FTA_GK5;
2807 #endif /* FTP_GSSAPI */
2808 #ifdef FTP_SRP
2809               ftp_auth_type[i++] = FTA_SRP;
2810 #endif /* FTP_SRP */
2811 #ifdef FTP_KRB4
2812               ftp_auth_type[i++] = FTA_K4;
2813 #endif /* FTP_KRB4 */
2814 #ifdef CK_SSL
2815               ftp_auth_type[i++] = FTA_TLS;
2816               ftp_auth_type[i++] = FTA_SSL;
2817 #endif /* CK_SSL */
2818               ftp_auth_type[i] = 0;
2819           } else {
2820               for (i = 0; i < 8; i++)
2821                 ftp_auth_type[i] = atypes[i];
2822           }
2823           return(success = 1);
2824       }
2825
2826       case FTS_SRP:
2827 #ifdef FTP_SRP
2828         if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0)
2829           return(x);
2830         switch (x) {
2831           case SRP_CIPHER:
2832             if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0)
2833               return(x);
2834             if ((z = cmcfm()) < 0)
2835               return(z);
2836             success = !srp_selcipher(ciphertab[x].kwd);
2837             return(success);
2838           case SRP_HASH:
2839             if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0)
2840               return(x);
2841             if ((z = cmcfm()) < 0)
2842               return(z);
2843             success = !srp_selhash(hashtab[x].kwd);
2844             return(success = 1);
2845           default:
2846             if ((z = cmcfm()) < 0)
2847               return(z);
2848             return(-2);
2849         }
2850 #else /* FTP_SRP */
2851         if ((z = cmcfm()) < 0)
2852           return(z);
2853         return(-2);
2854 #endif /* FTP_SRP */
2855 #endif /* FTP_SECURITY */
2856
2857       case FTS_DIS:
2858         doxdis(2);                      /* 2 == ftp */
2859         return(success = 1);
2860
2861       default:
2862         return(-2);
2863     }
2864 }
2865
2866 int
2867 ftpbye() {
2868     int x;
2869     if (!connected)
2870       return(1);
2871     if (testing)
2872       printf(" ftp closing %s...\n",ftp_host);
2873     x = ftpclose();
2874     return((x > -1) ? 1 : 0);
2875 }
2876
2877 /*  o p e n f t p  --  Parse FTP hostname & port and open */
2878
2879 static int
2880 openftp(s,opn_tls) char * s; int opn_tls; {
2881     char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL;
2882     int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0;
2883     int haveuser = 0;
2884     struct FDB sw, fl, cm;
2885     extern int nnetdir;                 /* Network services directory */
2886     extern int nhcount;                 /* Lookup result */
2887     extern char *nh_p[];                /* Network directory entry pointers */
2888     extern char *nh_p2[];               /* Network directory entry nettype */
2889
2890     if (!s) return(-2);
2891     if (!*s) return(-2);
2892
2893     makestr(&hostname,s);
2894     hostsave = hostname;
2895     makestr(&ftp_logname,NULL);
2896     anonymous = 0;
2897     noinit = 0;
2898
2899     debug(F110,"ftp open",hostname,0);
2900
2901     if (sav_psv > -1) {                 /* Restore prevailing active/passive */
2902         ftp_psv = sav_psv;              /* selection in case it was */
2903         sav_psv = -1;                   /* temporarily overriden by a switch */
2904     }
2905     if (sav_log > -1) {                 /* Ditto for autologin */
2906         ftp_log = sav_log;
2907         sav_log = -1;
2908     }
2909     cmfdbi(&sw,                         /* Switches */
2910            _CMKEY,
2911            "Service name or port;\n or switch",
2912            "",                          /* default */
2913            "",                          /* addtl string data */
2914            nftpswi,                     /* addtl numeric data 1: tbl size */
2915            4,                           /* addtl numeric data 2: none */
2916            xxstring,                    /* Processing function */
2917            ftpswitab,                   /* Keyword table */
2918            &fl                          /* Pointer to next FDB */
2919            );
2920     cmfdbi(&fl,                         /* A host name or address */
2921            _CMFLD,                      /* fcode */
2922            "",                          /* help */
2923            "xYzBoo",                    /* default */
2924            "",                          /* addtl string data */
2925            0,                           /* addtl numeric data 1 */
2926            0,                           /* addtl numeric data 2 */
2927            xxstring,
2928            NULL,
2929            &cm
2930            );
2931     cmfdbi(&cm,                         /* Command confirmation */
2932            _CMCFM,
2933            "",
2934            "",
2935            "",
2936            0,
2937            0,
2938            NULL,
2939            NULL,
2940            NULL
2941            );
2942
2943     for (n = 0;; n++) {
2944         rc = cmfdb(&sw);                /* Parse a service name or a switch */
2945         if (rc < 0)
2946           goto xopenftp;
2947
2948         if (cmresult.fcode == _CMCFM) { /* Done? */
2949             break;
2950         } else if (cmresult.fcode == _CMFLD) {  /* Port */
2951             if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1))
2952               makestr(&service,cmresult.sresult);
2953             else
2954               makestr(&service,opn_tls?"ftps":"ftp");
2955         } else if (cmresult.fcode == _CMKEY) { /* Have a switch */
2956             c = cmgbrk();               /* get break character */
2957             getval = (c == ':' || c == '=');
2958             rc = -9;
2959             if (getval && !(cmresult.kflags & CM_ARG)) {
2960                 printf("?This switch does not take arguments\n");
2961                 goto xopenftp;
2962             }
2963             if (!getval && (cmresult.kflags & CM_ARG)) {
2964                 printf("?This switch requires an argument\n");
2965                 goto xopenftp;
2966             }
2967             switch (cmresult.nresult) { /* Switch */
2968               case OPN_ANO:             /* /ANONYMOUS */
2969                 anonymous++;
2970                 nologin = 0;
2971                 break;
2972               case OPN_NIN:             /* /NOINIT */
2973                 noinit++;
2974                 break;
2975               case OPN_NOL:             /* /NOLOGIN */
2976                 nologin++;
2977                 anonymous = 0;
2978                 makestr(&ftp_logname,NULL);
2979                 break;
2980               case OPN_PSW:             /* /PASSWORD */
2981                 if (!anonymous)         /* Don't log real passwords */
2982                   debok = 0;
2983                 rc = cmfld("Password for FTP server","",&p,xxstring);
2984                 if (rc == -3) {
2985                     makestr(&ftp_tmp,NULL);
2986                 } else if (rc < 0) {
2987                     goto xopenftp;
2988                 } else {
2989                     makestr(&ftp_tmp,brstrip(p));
2990                     nologin = 0;
2991                 }
2992                 break;
2993               case OPN_USR:             /* /USER */
2994                 rc = cmfld("Username for FTP server","",&p,xxstring);
2995                 if (rc == -3) {
2996                     makestr(&ftp_logname,NULL);
2997                 } else if (rc < 0) {
2998                     goto xopenftp;
2999                 } else {
3000                     nologin = 0;
3001                     anonymous = 0;
3002                     haveuser = 1;
3003                     makestr(&ftp_logname,brstrip(p));
3004                 }
3005                 break;
3006               case OPN_ACC:
3007                 rc = cmfld("Account for FTP server","",&p,xxstring);
3008                 if (rc == -3) {
3009                     makestr(&ftp_acc,NULL);
3010                 } else if (rc < 0) {
3011                     goto xopenftp;
3012                 } else {
3013                     makestr(&ftp_acc,brstrip(p));
3014                 }
3015                 break;
3016               case OPN_ACT:
3017                 opn_psv = 0;
3018                 break;
3019               case OPN_PSV:
3020                 opn_psv = 1;
3021                 break;
3022               case OPN_TLS:
3023                 opn_tls = 1;
3024                 break;
3025               default:
3026                 break;
3027             }
3028         }
3029         if (n == 0) {                   /* After first time through */
3030             cmfdbi(&sw,                 /* accept only switches */
3031                    _CMKEY,
3032                    "\nCarriage return to confirm to command, or switch",
3033                    "",
3034                    "",
3035                    nftpswi,
3036                    4,
3037                    xxstring,
3038                    ftpswitab,
3039                    &cm
3040                    );
3041         }
3042     }
3043 #ifdef COMMENT
3044     debug(F100,"ftp openftp while exit","",0);
3045     rc = cmcfm();
3046     debug(F101,"ftp openftp cmcfm rc","",rc);
3047     if (rc < 0)
3048       goto xopenftp;
3049 #endif /* COMMENT */
3050
3051     if (opn_psv > -1) {                 /* /PASSIVE or /ACTIVE switch given */
3052         sav_psv = ftp_psv;
3053         ftp_psv = opn_psv;
3054     }
3055     if (nologin || haveuser) {          /* /NOLOGIN or /USER switch given */
3056         sav_log = ftp_log;
3057         ftp_log = haveuser ? 1 : 0;
3058     }
3059     if (*hostname == '=') {             /* Bypass directory lookup */
3060         hostname++;                     /* if hostname starts with '=' */
3061         havehost++;
3062     } else if (isdigit(*hostname)) {    /* or if it starts with a digit */
3063         havehost++;
3064     }
3065     if (!service)
3066       makestr(&service,opn_tls?"ftps":"ftp");
3067
3068 #ifndef NODIAL
3069     if (!havehost && nnetdir > 0) {     /* If there is a networks directory */
3070         lunet(hostname);                /* Look up the name */
3071         debug(F111,"ftp openftp lunet",hostname,nhcount);
3072         if (nhcount == 0) {
3073             if (testing)
3074               printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3075             success = ftpopen(hostname,service,opn_tls);
3076             debug(F101,"ftp openftp A ftpopen success","",success);
3077             rc = success;
3078         } else {
3079             int found = 0;
3080             for (i = 0; i < nhcount; i++) {
3081                 if (nh_p2[i])           /* If network type specified */
3082                   if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0))
3083                     continue;
3084                 found++;
3085                 makestr(&hostname,nh_p[i]);
3086                 debug(F111,"ftpopen lunet substitution",hostname,i);
3087                 if (testing)
3088                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3089                 success = ftpopen(hostname,service,opn_tls);
3090                 debug(F101,"ftp openftp B ftpopen success","",success);
3091                 rc = success;
3092                 if (success)
3093                   break;
3094             }
3095             if (!found) {               /* E.g. if no network types match */
3096                 if (testing)
3097                   printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3098                 success = ftpopen(hostname,service,opn_tls);
3099                 debug(F101,"ftp openftp C ftpopen success","",success);
3100                 rc = success;
3101             }
3102         }
3103     } else {
3104 #endif /* NODIAL */
3105         if (testing)
3106           printf(" ftp open trying \"%s %s\"...\n",hostname,service);
3107         success = ftpopen(hostname,service,opn_tls);
3108         debug(F111,"ftp openftp D ftpopen success",hostname,success);
3109         debug(F111,"ftp openftp D ftpopen connected",hostname,connected);
3110         rc = success;
3111 #ifndef NODIAL
3112     }
3113 #endif /* NODIAL */
3114
3115   xopenftp:
3116     debug(F101,"ftp openftp xopenftp rc","",rc);
3117     if (hostsave) free(hostsave);
3118     if (service) free(service);
3119     if (rc < 0 && ftp_logname) {
3120         free(ftp_logname);
3121         ftp_logname = NULL;
3122     }
3123     if (ftp_tmp) {
3124         free(ftp_tmp);
3125         ftp_tmp = NULL;
3126     }
3127     return(rc);
3128 }
3129
3130 VOID                                    /* 12 Aug 2007 */
3131 doftpglobaltype(x) int x; {
3132     ftp_xfermode = XMODE_M;             /* Set manual FTP transfer mode */
3133     ftp_typ = x;                        /* Used by top-level BINARY and */
3134     g_ftp_typ = x;                      /* ASCII commands. */
3135     get_auto = 0;
3136     forcetype = 1;
3137 }
3138
3139 int
3140 doftpacct() {
3141     int x;
3142     char * s;
3143     if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
3144       return(x);
3145     CHECKCONN();
3146     makestr(&ftp_acc,brstrip(s));
3147     if (testing)
3148       printf(" ftp account: \"%s\"\n",ftp_acc);
3149     success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
3150     return(success);
3151 }
3152
3153 int
3154 doftpusr() {                            /* Log in as USER */
3155     extern char uidbuf[];
3156     extern char pwbuf[];
3157     extern int  pwflg, pwcrypt;
3158     int x;
3159     char *s, * acct = "";
3160
3161     debok = 0;                          /* Don't log */
3162
3163     if ((x = cmfld("Remote username or ID",uidbuf,&s,xxstring)) < 0)
3164       return(x);
3165     ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */
3166     if ((x = cmfld("Remote password","",&s,xxstring)) < 0) {
3167         if (x == -3) { /* no input */
3168             if ( pwbuf[0] && pwflg ) {
3169                 ckstrncpy(tmpbuf,(char *)pwbuf,TMPBUFSIZ);
3170 #ifdef OS2
3171                 if ( pwcrypt )
3172                     ck_encrypt((char *)tmpbuf);
3173 #endif /* OS2 */
3174             }
3175         } else {
3176             return(x);
3177         }
3178     } else {
3179         ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ);
3180     }
3181     if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command",
3182                    "", &s, xxstring)) < 0)
3183       return(x);
3184     CHECKCONN();
3185     if (*s) {
3186         x = strlen(tmpbuf);
3187         if (x > 0) {
3188             acct = &tmpbuf[x+2];
3189             ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2);
3190         }
3191     }
3192     if (testing)
3193       printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf);
3194     success = ftp_user(line,tmpbuf,acct);
3195 #ifdef CKLOGDIAL
3196     dologftp();
3197 #endif /* CKLOGDIAL */
3198     return(success);
3199 }
3200
3201 /* DO (various FTP commands)... */
3202
3203 int
3204 doftptyp(type) int type; {              /* TYPE */
3205     CHECKCONN();
3206     ftp_typ = type;
3207     changetype(ftp_typ,ftp_vbm);
3208     debug(F101,"doftptyp changed type","",type);
3209     return(1);
3210 }
3211
3212 static int
3213 doftpxmkd(s,vbm) char * s; int vbm; {   /* MKDIR action */
3214     int lcs = -1, rcs = -1;
3215 #ifndef NOCSETS
3216     if (ftp_xla) {
3217         lcs = ftp_csl;
3218         if (lcs < 0) lcs = fcharset;
3219         rcs = ftp_csx;
3220         if (rcs < 0) rcs = ftp_csr;
3221     }
3222 #endif /* NOCSETS */
3223     debug(F110,"ftp doftpmkd",s,0);
3224     if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3225       return(success = 1);
3226     if (ftpcode == 500 || ftpcode == 502) {
3227         if (!quiet)
3228           printf("MKD command not recognized, trying XMKD\n");
3229         if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3230           return(success = 1);
3231     }
3232     return(success = 0);
3233 }
3234
3235 static int
3236 doftpmkd() {                            /* MKDIR parse */
3237     int x;
3238     char * s;
3239     if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0)
3240       return(x);
3241     CHECKCONN();
3242     ckstrncpy(line,s,LINBUFSIZ);
3243     if (testing)
3244       printf(" ftp mkdir \"%s\"...\n",line);
3245     return(success = doftpxmkd(line,-1));
3246 }
3247
3248 static int
3249 doftprmd() {                            /* RMDIR */
3250     int x, lcs = -1, rcs = -1;
3251     char * s;
3252     if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
3253       return(x);
3254     CHECKCONN();
3255     ckstrncpy(line,s,LINBUFSIZ);
3256     if (testing)
3257       printf(" ftp rmdir \"%s\"...\n",line);
3258 #ifndef NOCSETS
3259     if (ftp_xla) {
3260         lcs = ftp_csl;
3261         if (lcs < 0) lcs = fcharset;
3262         rcs = ftp_csx;
3263         if (rcs < 0) rcs = ftp_csr;
3264     }
3265 #endif /* NOCSETS */
3266     if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE)
3267       return(success = 1);
3268     if (ftpcode == 500 || ftpcode == 502) {
3269         if (!quiet)
3270           printf("RMD command not recognized, trying XMKD\n");
3271         success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
3272     } else
3273       success = 0;
3274     return(success);
3275 }
3276
3277 static int
3278 doftpren() {                            /* RENAME */
3279     int x;
3280     char * s;
3281     if ((x = cmfld("Remote filename","",&s,xxstring)) < 0)
3282       return(x);
3283     ckstrncpy(line,s,LINBUFSIZ);
3284     if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0)
3285       return(x);
3286     ckstrncpy(tmpbuf,s,TMPBUFSIZ);
3287     if ((x = cmcfm()) < 0)
3288       return(x);
3289     CHECKCONN();
3290     if (testing)
3291       printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf);
3292     success = ftp_rename(line,tmpbuf);
3293     return(success);
3294 }
3295
3296 int
3297 doftpres() {                            /* RESET (log out without close) */
3298     int x;
3299     if ((x = cmcfm()) < 0)
3300       return(x);
3301     CHECKCONN();
3302     if (testing)
3303       printf(" ftp reset...\n");
3304     return(success = ftp_reset());
3305 }
3306
3307 static int
3308 doftpxhlp() {                           /* HELP */
3309     int x;
3310     char * s;
3311     if ((x = cmtxt("Command name", "", &s, xxstring)) < 0)
3312       return(x);
3313     CHECKCONN();
3314     ckstrncpy(line,s,LINBUFSIZ);
3315     if (testing)
3316       printf(" ftp help \"%s\"...\n",line);
3317     /* No need to translate -- all FTP commands are ASCII */
3318     return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE));
3319 }
3320
3321 static int
3322 doftpdir(cx) int cx; {                  /* [V]DIRECTORY */
3323     int x, lcs = 0, rcs = 0, xlate = 0;
3324     char * p, * s, * m = "";
3325     if (cx == FTP_VDI) {
3326         switch (servertype) {
3327           case SYS_VMS:
3328           case SYS_DOS:
3329           case SYS_TOPS10:
3330           case SYS_TOPS20:
3331             m = "*.*";
3332             break;
3333           default:
3334             m = "*";
3335         }
3336     }
3337     if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0)
3338       return(x);
3339     if ((x = remtxt(&s)) < 0)
3340       return(x);
3341 #ifdef NOCSETS
3342     xlate = 0;
3343 #else
3344     xlate = ftp_xla;
3345 #endif /* NOCSETS */
3346     line[0] = NUL;
3347     ckstrncpy(line,s,LINBUFSIZ);
3348     s = line;
3349     CHECKCONN();
3350
3351 #ifndef NOCSETS
3352     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
3353         lcs = ftp_csl;                  /* Local charset */
3354         if (lcs < 0) lcs = fcharset;
3355         if (lcs < 0) xlate = 0;
3356     }
3357     if (xlate) {                        /* Still ON? */
3358         rcs = ftp_csx;                  /* Remote (Server) charset */
3359         if (rcs < 0) rcs = ftp_csr;
3360         if (rcs < 0) xlate = 0;
3361     }
3362 #endif /* NOCSETS */
3363
3364     if (testing) {
3365         p = s;
3366         if (!p) p = "";
3367         if (*p)
3368           printf("Directory of files %s at %s:\n", line, ftp_host);
3369         else
3370           printf("Directory of files at %s:\n", ftp_host);
3371     }
3372     debug(F111,"doftpdir",s,cx);
3373
3374     if (cx == FTP_DIR) {
3375         /* Translation of line[] is done inside recvrequest() */
3376         /* when it calls ftpcmd(). */
3377         return(success =
3378           (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0));
3379     }
3380     success = 1;                        /* VDIR - one file at a time... */
3381     p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */
3382     cancelgroup = 0;
3383     if (!ftp_vbm && !quiet)
3384       printlines = 1;
3385     while (p && !cancelfile && !cancelgroup) { /* STAT one file */
3386         if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) {
3387             success = 0;
3388             break;
3389         }
3390         p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */
3391         debug(F110,"ftp vdir file",s,0);
3392     }
3393     return(success);
3394 }
3395
3396 static int
3397 doftppwd() {                            /* PWD */
3398     int x, lcs = -1, rcs = -1;
3399 #ifndef NOCSETS
3400     if (ftp_xla) {
3401         lcs = ftp_csl;
3402         if (lcs < 0) lcs = fcharset;
3403         rcs = ftp_csx;
3404         if (rcs < 0) rcs = ftp_csr;
3405     }
3406 #endif /* NOCSETS */
3407     if ((x = cmcfm()) < 0)
3408       return(x);
3409     CHECKCONN();
3410     if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) {
3411         success = 1;
3412     } else if (ftpcode == 500 || ftpcode == 502) {
3413         if (ftp_deb)
3414           printf("PWD command not recognized, trying XPWD\n");
3415         success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE);
3416     }
3417     return(success);
3418 }
3419
3420 static int
3421 doftpcwd(s,vbm) char * s; int vbm; {    /* CD (CWD) */
3422     int lcs = -1, rcs = -1;
3423 #ifndef NOCSETS
3424     if (ftp_xla) {
3425         lcs = ftp_csl;
3426         if (lcs < 0) lcs = fcharset;
3427         rcs = ftp_csx;
3428         if (rcs < 0) rcs = ftp_csr;
3429     }
3430 #endif /* NOCSETS */
3431
3432     debug(F110,"ftp doftpcwd",s,0);
3433     if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3434       return(success = 1);
3435     if (ftpcode == 500 || ftpcode == 502) {
3436         if (!quiet)
3437           printf("CWD command not recognized, trying XCWD\n");
3438         if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE)
3439           return(success = 1);
3440     }
3441     return(success = 0);
3442 }
3443
3444 static int
3445 doftpcdup() {                           /* CDUP */
3446     debug(F100,"ftp doftpcdup","",0);
3447     if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE)
3448       return(success = 1);
3449     if (ftpcode == 500 || ftpcode == 502) {
3450         if (!quiet)
3451           printf("CDUP command not recognized, trying XCUP\n");
3452         if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE)
3453           return(success = 1);
3454     }
3455     return(success = 0);
3456 }
3457
3458 /* s y n c d i r  --  Synchronizes client & server directories */
3459
3460 /*
3461   Call with:
3462     local = pointer to pathname of local file to be sent.
3463     sim   = 1 for simulation, 0 for real uploading.
3464   Returns 0 on failure, 1 on success.
3465
3466   The 'local' argument is relative to the initial directory of the MPUT,
3467   i.e. the root of the tree being uploaded.  If the directory of the
3468   argument file is different from the directory of the previous file
3469   (which is stored in global putpath[]), this routine does the appropriate
3470   CWDs, CDUPs, and/or MKDIRs to position the FTP server in the same place.
3471 */
3472 static int cdlevel = 0, cdsimlvl = 0;   /* Tree-level trackers */
3473
3474 static int
3475 syncdir(local,sim) char * local; int sim; {
3476     char buf[CKMAXPATH+1];
3477     char tmp[CKMAXPATH+1];
3478     char msgbuf[CKMAXPATH+64];
3479     char c, * p = local, * s = buf, * q = buf, * psep, * ssep;
3480     int i, k = 0, done = 0, itsadir = 0, saveq;
3481
3482     debug(F110,"ftp syncdir local (new)",local,0);
3483     debug(F110,"ftp syncdir putpath (old)",putpath,0);
3484
3485     itsadir = isdir(local);             /* Is the local file a directory? */
3486     saveq = quiet;
3487
3488     while ((*s = *p)) {                 /* Copy the argument filename */
3489         if (++k == CKMAXPATH)           /* so we can poke it. */
3490           return(-1);
3491         if (*s == '/')                  /* Pointer to rightmost dirsep */
3492           q = s;
3493         s++;
3494         p++;
3495     }
3496     if (!itsadir)                       /* If it's a regular file */
3497       *q = NUL;                         /* keep just the path part */
3498
3499     debug(F110,"ftp syncdir buf",buf,0);
3500     if (!strcmp(buf,putpath)) {         /* Same path as previous file? */
3501         if (itsadir) {                  /* This file is a directory? */
3502             if (doftpcwd(local,0)) {    /* Try to CD to it */
3503                 doftpcdup();            /* Worked - CD back up */
3504             } else if (sim) {           /* Simulating... */
3505                 if (fdispla == XYFD_B) {
3506                     printf("WOULD CREATE DIRECTORY %s\n",local);
3507                 } else if (fdispla) {
3508                     ckmakmsg(msgbuf,CKMAXPATH,
3509                              "WOULD CREATE DIRECTORY",local,NULL,NULL);
3510                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3511                 }
3512                 /* See note above */
3513                 return(0);
3514             } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */
3515                 return(0);
3516             } else {                    /* Remote directory created OK */
3517                 if (fdispla == XYFD_B) {
3518                     printf("CREATED DIRECTORY %s\n",local);
3519                 } else if (fdispla) {
3520                     ckmakmsg(msgbuf,CKMAXPATH+64,
3521                              "CREATED DIRECTORY ",local,NULL,NULL);
3522                     ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3523                 }
3524             }
3525         }
3526         debug(F110,"ftp syncdir no change",buf,0);
3527         return(1);                      /* Yes, done. */
3528     }
3529     ckstrncpy(tmp,buf,CKMAXPATH+1);     /* Make a safe (pre-poked) copy */
3530     debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */
3531
3532     p = buf;                            /* New */
3533     s = putpath;                        /* Old */
3534
3535     debug(F110,"ftp syncdir A (old) s",s,0); /* Previous */
3536     debug(F110,"ftp syncdir A (new) p",p,0); /* New */
3537
3538     psep = buf;
3539     ssep = putpath;
3540     while (*p != NUL && *s != NUL && *p == *s) {
3541         if (*p == '/') { psep = p+1; ssep = s+1; }
3542         p++,s++;
3543     }
3544     /*
3545       psep and ssep point to the first path segment that differs.
3546       We have to do as many CDUPs as there are path segments in ssep.
3547       then we have to do as many MKDs and CWDs as there are segments in psep.
3548     */
3549     s = ssep;
3550     p = psep;
3551
3552     debug(F110,"ftp syncdir B (old) s",s,0); /* Previous */
3553     debug(F110,"ftp syncdir B (new) p",p,0); /* New */
3554
3555     /* p and s now point to the leftmost spot where the paths differ */
3556
3557     if (*s) {                           /* We have to back up */
3558         k = 1;                          /* How many levels counting this one */
3559         while ((c = *s++)) {            /* Count dirseps remaining in prev */
3560             if (c == '/' && *s)
3561               k++;
3562         }
3563         debug(F101,"ftp syncdir levels up","",k);
3564
3565         for (i = 1; i <= k; i++) {       /* Do that many CDUPs */
3566             debug(F111,"ftp syncdir CDUP A",p,i);
3567             if (fdispla == XYFD_B)
3568               printf(" CDUP\n");
3569             if (sim && cdsimlvl) {
3570                 cdsimlvl--;
3571             } else {
3572                 if (!doftpcdup()) {
3573                     quiet = saveq;
3574                     return(0);
3575                 }
3576             }
3577             cdlevel--;
3578         }
3579         if (!*p)                        /* If we don't have to go down */
3580           goto xcwd;                    /* we're done. */
3581     }
3582 #ifdef COMMENT
3583     while (p > buf && *p && *p != '/')  /* If in middle of segment */
3584       p--;                              /* back up to beginning */
3585     if (*p == '/')                      /* and terminate there */
3586       p++;
3587 #endif  /* COMMENT */
3588
3589     debug(F110,"ftp syncdir NEW PATH",p,0);
3590
3591     s = p;                              /* Point to start of new down path. */
3592     while (1) {                         /* Loop through characters. */
3593         if (*s == '/' || !*s) {         /* Have a segment. */
3594             if (!*s)                    /* If end of string, */
3595               done++;                   /* after this segment we're done. */
3596             else
3597               *s = NUL;                 /* NUL out the separator. */
3598             if (*p) {                   /* If segment is not empty */
3599                 debug(F110,"ftp syncdir down segment",p,0);
3600                 if (!doftpcwd(p,0)) {   /* Try to CD to it */
3601                     if (sim) {
3602                         if (fdispla == XYFD_B) {
3603                             printf(" WOULD CREATE DIRECTORY %s\n",local);
3604                         } else if (fdispla) {
3605                             ckmakmsg(msgbuf,CKMAXPATH,
3606                                      "WOULD CREATE DIRECTORY",
3607                                      local,NULL,NULL);
3608                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3609                         }
3610                         cdsimlvl++;
3611                     } else {
3612                         if (!doftpxmkd(p,0)) { /* Can't CD - try to create */
3613                             debug(F110,"ftp syncdir mkdir failed",p,0); 
3614 /*
3615   Suppose we are executing SEND /RECURSIVE.  Locally we have a directory
3616   FOO but the remote has a regular file with the same name.  We can't CD
3617   to it, can't MKDIR it either.  There's no way out but to fail and let
3618   the user handle the problem.
3619 */
3620                             quiet = saveq;
3621                             return(0);
3622                         }
3623                         debug(F110,"ftp syncdir mkdir OK",p,0); 
3624                         if (fdispla == XYFD_B) {
3625                             printf(" CREATED DIRECTORY %s\n",p);
3626                         } else if (fdispla) {
3627                             ckmakmsg(msgbuf,CKMAXPATH,
3628                                      "CREATED DIRECTORY ",p,NULL,NULL);
3629                             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf);
3630                         }
3631                         if (!doftpcwd(p,0)) { /* Try again to CD */
3632                             debug(F110,"ftp syncdir CD failed",p,0); 
3633                             quiet = saveq;
3634                             return(0);
3635                         }
3636                         if (fdispla == XYFD_B) printf(" CWD %s\n",p);
3637                         debug(F110,"ftp syncdir CD OK",p,0); 
3638                     }
3639                 }
3640                 cdlevel++;
3641             }
3642             if (done)                   /* Quit if no next segment */
3643               break;
3644             p = s+1;                    /* Point to next segment */
3645         }
3646         s++;                            /* Point to next source char */
3647     }
3648
3649   xcwd:
3650     ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */
3651     quiet = saveq;
3652     return(1);
3653 }
3654
3655 #ifdef DOUPDATE
3656 #ifdef DEBUG
3657 static VOID
3658 dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */
3659     if (deblog) {
3660         debug(F111,"ftp year ",s,xx->tm_year);
3661         debug(F111,"ftp month",s,xx->tm_mon);
3662         debug(F111,"ftp day  ",s,xx->tm_mday);
3663         debug(F111,"ftp hour ",s,xx->tm_hour);
3664         debug(F111,"ftp min  ",s,xx->tm_min);
3665         debug(F111,"ftp sec  ",s,xx->tm_sec);
3666     }
3667 }
3668 #endif /* DEBUG */
3669
3670 /*  t m c o m p a r e  --  Compare two struct tm's */
3671
3672 /*  Like strcmp() but for struct tm's  */
3673 /*  Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */
3674
3675 static int
3676 tmcompare(xx,yy) struct tm * xx, * yy; {
3677
3678     if (xx->tm_year < yy->tm_year)      /* First year less than second */
3679       return(-1);
3680     if (xx->tm_year > yy->tm_year)      /* First year greater than second */
3681       return(1);
3682
3683     /* Years are equal so compare months */
3684
3685     if (xx->tm_mon  < yy->tm_mon)       /* And so on... */
3686       return(-1);
3687     if (xx->tm_mon  > yy->tm_mon)
3688       return(1);
3689
3690     if (xx->tm_mday < yy->tm_mday)
3691       return(-1);
3692     if (xx->tm_mday > yy->tm_mday)
3693       return(1);
3694
3695     if (xx->tm_hour < yy->tm_hour)
3696       return(-1);
3697     if (xx->tm_hour > yy->tm_hour)
3698       return(1);
3699
3700     if (xx->tm_min  < yy->tm_min)
3701       return(-1);
3702     if (xx->tm_min  > yy->tm_min)
3703       return(1);
3704
3705     if (xx->tm_sec  < yy->tm_sec)
3706       return(-1);
3707     if (xx->tm_sec  > yy->tm_sec)
3708       return(1);
3709
3710     return(0);
3711 }
3712 #endif /* DOUPDATE */
3713
3714 #ifndef HAVE_TIMEGM             /* For platforms that do not have timegm() */
3715 static CONST int MONTHDAYS[] = { /* Number of days in each month. */
3716     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
3717 };
3718
3719 /* Macro for whether a given year is a leap year. */
3720 #define ISLEAP(year) \
3721 (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
3722 #endif /* HAVE_TIMEGM */
3723
3724 /*  m k u t i m e  --  Like mktime() but argument is already UTC */
3725
3726 static time_t
3727 #ifdef CK_ANSIC
3728 mkutime(struct tm * tm)
3729 #else
3730 mkutime(tm) struct tm * tm;
3731 #endif /* CK_ANSIC */
3732 /* mkutime */ {
3733 #ifdef HAVE_TIMEGM
3734     return(timegm(tm));                 /* Have system service, use it. */
3735 #else
3736 /*
3737   Contributed by Russ Allbery (rra@stanford.edu), used by permission.
3738   Given a struct tm representing a calendar time in UTC, convert it to
3739   seconds since epoch.  Returns (time_t) -1 if the time is not
3740   convertable.  Note that this function does not canonicalize the provided
3741   struct tm, nor does it allow out-of-range values or years before 1970.
3742   Result should be identical with timegm().
3743 */
3744     time_t result = 0;
3745     int i;
3746     /*
3747       We do allow some ill-formed dates, but we don't do anything special
3748       with them and our callers really shouldn't pass them to us.  Do
3749       explicitly disallow the ones that would cause invalid array accesses
3750       or other algorithm problems.
3751     */
3752 #ifdef DEBUG
3753     if (deblog) {
3754         debug(F101,"mkutime tm_mon","",tm->tm_mon);
3755         debug(F101,"mkutime tm_year","",tm->tm_year);
3756     }
3757 #endif /* DEBUG */
3758     if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70)
3759       return((time_t) -1);
3760
3761     /* Convert to time_t. */
3762     for (i = 1970; i < tm->tm_year + 1900; i++)
3763       result += 365 + ISLEAP(i);
3764     for (i = 0; i < tm->tm_mon; i++)
3765       result += MONTHDAYS[i];
3766     if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900))
3767       result++;
3768     result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
3769     result = 60 * result + tm->tm_min;
3770     result = 60 * result + tm->tm_sec;
3771     debug(F101,"mkutime result","",result);
3772     return(result);
3773 #endif /* HAVE_TIMEGM */
3774 }
3775
3776
3777 /*
3778   s e t m o d t i m e  --  Set file modification time.
3779
3780   f = char * filename;
3781   t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local)
3782
3783   UNIX-specific; isolates mainline code from hideous #ifdefs.
3784   Returns:
3785     0 on success,
3786    -1 on error.
3787
3788 */
3789 static int
3790 #ifdef CK_ANSIC
3791 setmodtime(char * f, time_t t)
3792 #else
3793 setmodtime(f,t) char * f; time_t t;
3794 #endif /* CK_ANSIC */
3795 /* setmodtime */ {
3796 #ifdef NT
3797     struct _stat sb;
3798 #else /* NT */
3799     struct stat sb;
3800 #endif /* NT */
3801     int x, rc = 0;
3802 #ifdef BSD44
3803     struct timeval tp[2];
3804 #else  /* def BSD44 */
3805 #ifdef V7
3806     struct utimbuf {
3807         time_t timep[2];
3808     } tp;
3809 #else  /* def V7 */
3810 #ifdef SYSUTIMEH
3811 #ifdef NT
3812     struct _utimbuf tp;
3813 #else /* NT */
3814     struct utimbuf tp;
3815 #endif /* NT */
3816 #else /* def SYSUTIMEH */
3817 #ifdef VMS
3818     struct utimbuf tp;
3819 #define SYSUTIMEH               /* Our utimbuf matches this one. */
3820 #else /* def VMS */
3821     struct utimbuf {
3822         time_t atime;
3823         time_t mtime;
3824     } tp;
3825 #endif /* def VMS [else] */
3826 #endif /* def SYSUTIMEH [else] */
3827 #endif /* def V7 [else] */
3828 #endif /* def BSD44 [else] */
3829
3830     if (stat(f,&sb) < 0) {
3831         debug(F111,"setmodtime stat failure",f,errno);
3832         return(-1);
3833     }
3834 #ifdef BSD44
3835     tp[0].tv_sec = sb.st_atime;         /* Access time first */
3836     tp[1].tv_sec = t;                   /* Update time second */
3837     debug(F111,"setmodtime BSD44",f,t);
3838 #else
3839 #ifdef V7
3840     tp.timep[0] = t;                    /* Set modif. time to creation date */
3841     tp.timep[1] = sb.st_atime;          /* Don't change the access time */
3842     debug(F111,"setmodtime V7",f,t);
3843 #else
3844 #ifdef SYSUTIMEH
3845     tp.modtime = t;                     /* Set modif. time to creation date */
3846     tp.actime = sb.st_atime;            /* Don't change the access time */
3847     debug(F111,"setmodtime SYSUTIMEH",f,t);
3848 #else
3849     tp.mtime = t;                       /* Set modif. time to creation date */
3850     tp.atime = sb.st_atime;             /* Don't change the access time */
3851     debug(F111,"setmodtime (other)",f,t);
3852 #endif /* SYSUTIMEH */
3853 #endif /* V7 */
3854 #endif /* BSD44 */
3855
3856     /* Try to set the file date */
3857
3858 #ifdef BSD44
3859     x = utimes(f,tp);
3860     debug(F111,"setmodtime utimes()","BSD44",x);
3861 #else
3862 #ifdef IRIX65
3863     {
3864       /*
3865         The following produces the nonsensical warning:
3866         Argument  of type "const struct utimbuf *" is incompatible with
3867         parameter of type "const struct utimbuf *".  If you can make it
3868         go away, be my guest.
3869       */
3870         const struct utimbuf * t2 = &tp;
3871         x = utime(f,t2);
3872     }
3873 #else
3874     x = utime(f,&tp);
3875     debug(F111,"setmodtime utime()","other",x);
3876 #endif /* IRIX65 */
3877 #endif /* BSD44 */
3878     if (x)
3879       rc = -1;
3880
3881     debug(F101,"setmodtime result","",rc);
3882     return(rc);
3883 }
3884
3885
3886 /*
3887   c h k m o d t i m e  --  Check/Set file modification time.
3888
3889   fc = function code:
3890     0 = Check; returns:
3891       -1 on error,
3892        0 if local older than remote,
3893        1 if modtimes are equal,
3894        2 if local newer than remote.
3895     1 = Set (local file's modtime from remote's); returns:
3896       -1 on error,
3897        0 on success.
3898 */
3899 static int
3900 chkmodtime(local,remote,fc) char * local, * remote; int fc; {
3901 #ifdef NT
3902     struct _stat statbuf;
3903 #else /* NT */
3904     struct stat statbuf;
3905 #endif /* NT */
3906     struct tm * tmlocal = NULL;
3907     struct tm tmremote;
3908     int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0;
3909     char * s, timebuf[64];
3910
3911     debug(F111,"chkmodtime",local,mdtmok);
3912     if (!mdtmok)                        /* Server supports MDTM? */
3913       return(-1);                       /* No don't bother. */
3914
3915 #ifndef NOCSETS
3916     if (ftp_xla) {
3917         lcs = ftp_csl;
3918         if (lcs < 0) lcs = fcharset;
3919         rcs = ftp_csx;
3920         if (rcs < 0) rcs = ftp_csr;
3921     }
3922 #endif /* NOCSETS */
3923
3924     if (fc == 0) {
3925         rc = stat(local,&statbuf);
3926         if (rc == 0) {                  /* Get local file's mod time */
3927             /* Convert to struct tm */
3928             tmlocal = gmtime((time_t *)&statbuf.st_mtime);
3929 #ifdef DEBUG
3930             if (tmlocal) {
3931                 dbtime(local,tmlocal);
3932             }
3933 #endif /* DEBUG */
3934         }
3935     }
3936     /* Get remote file's mod time as yyyymmddhhmmss */
3937
3938     if (havemdtm) {                     /* Already got it from MLSD? */
3939         s = havemdtm;
3940         flag++;
3941     } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) {
3942         char c;
3943         bzero((char *)&tmremote, sizeof(struct tm));
3944         s = ftp_reply_str;
3945         while ((c = *s++)) {            /* Skip past response code */
3946             if (c == SP) {
3947                 flag++;
3948                 break;
3949             }
3950         }
3951     }
3952     if (flag) {
3953         debug(F111,"ftp chkmodtime string",s,flag);
3954         if (fts_sto) {                  /* User gave server time offset? */
3955             char * p;
3956             debug(F110,"ftp chkmodtime offset",fts_sto,0);
3957             ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */
3958             if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */
3959                 ckstrncpy(timebuf,p,64);      /* Convert to MDTM format */
3960                 timebuf[8]  = timebuf[9];  /* h */
3961                 timebuf[9]  = timebuf[10]; /* h */
3962                 timebuf[10] = timebuf[12]; /* m */
3963                 timebuf[11] = timebuf[13]; /* m */
3964                 timebuf[12] = timebuf[12]; /* s */
3965                 timebuf[13] = timebuf[13]; /* s */
3966                 timebuf[14] = NUL;
3967                 s = timebuf;
3968                 debug(F110,"ftp chkmodtime adjust",s,0);
3969             }
3970         }
3971         if (flag) {                     /* Convert to struct tm */
3972             char * pat;
3973             int y2kbug = 0;             /* Seen in Kerberos 4 FTP servers */
3974             if (!ckstrcmp(s,"191",3,0)) {
3975                 pat = "%05d%02d%02d%02d%02d%02d";
3976                 y2kbug++;
3977                 debug(F110,"ftp chkmodtime Y2K BUG detected",s,0);
3978             } else {
3979                 pat = "%04d%02d%02d%02d%02d%02d";
3980             }
3981             if (sscanf(s,               /* Parse into struct tm */
3982                        pat,
3983                        &(tmremote.tm_year),
3984                        &(tmremote.tm_mon),
3985                        &(tmremote.tm_mday),
3986                        &(tmremote.tm_hour),
3987                        &(tmremote.tm_min),
3988                        &(tmremote.tm_sec)
3989                        ) == 6) {
3990                 tmremote.tm_year -= (y2kbug ? 19000 : 1900);
3991                 debug(F101,"ftp chkmodtime year","",tmremote.tm_year);
3992                 tmremote.tm_mon--;
3993
3994 #ifdef DEBUG
3995                 debug(F100,"SERVER TIME FOLLOWS:","",0);
3996                 dbtime(remote,&tmremote);
3997 #endif /* DEBUG */
3998
3999                 if (havedate > -1)
4000                   havedate = 1;
4001             }
4002         }
4003     } else {                            /* Failed */
4004         debug(F101,"ftp chkmodtime ftpcode","",ftpcode);
4005         if (ftpcode == 500 ||           /* Command unrecognized */
4006             ftpcode == 502 ||           /* Command not implemented */
4007             ftpcode == 202)             /* Command superfluous */
4008           mdtmok = 0;                   /* Don't ask this server again */
4009         return(-1);
4010     }
4011     if (fc == 0) {                      /* Compare */
4012         if (havedate == 1) {            /* Only if we have both file dates */
4013             /*
4014               Compare with local file's time.  We don't use
4015               clock time (time_t) here in case of signed/unsigned
4016               confusion, etc.
4017             */
4018             int xx;
4019 #ifdef COMMENT
4020 #ifdef DEBUG        
4021             if (deblog) {
4022                 dbtime("LOCAL",tmlocal);
4023                 dbtime("REMOT",&tmremote);
4024             }
4025 #endif /* DEBUG */
4026 #endif /* COMMENT */
4027             xx = tmcompare(tmlocal,&tmremote);
4028             debug(F101,"chkmodtime tmcompare","",xx);
4029             return(xx + 1);
4030         }
4031     } else if (ftp_dates) {             /* Set */
4032         /*
4033           Here we must convert struct tm to time_t
4034           without applying timezone conversion, for which
4035           there is no portable API.  The method is hidden
4036           in mkutime(), defined above.
4037         */
4038         time_t utc;
4039         utc = mkutime(&tmremote);
4040         debug(F111,"ftp chkmodtime mkutime",remote,utc);
4041         if (utc != (time_t)-1)
4042           return(setmodtime(local,utc));
4043     }
4044     return(-1);
4045 }
4046
4047 /* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */
4048
4049 static int
4050 getfile(remote,local,recover,append,pipename,xlate,fcs,rcs)
4051     char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs;
4052 /* getfile */ {
4053     int rc = -1;
4054     ULONG t0, t1;
4055
4056 #ifdef GFTIMER
4057     CKFLOAT sec;
4058 #else
4059     int sec = 0;
4060 #endif /* GFTIMER */
4061     char fullname[CKMAXPATH+1];
4062
4063     debug(F110,"ftp getfile remote A",remote,0);
4064     debug(F110,"ftp getfile local A",local,0);
4065     debug(F110,"ftp getfile pipename",pipename,0);
4066     if (!remote) remote = "";
4067
4068 #ifdef PATTERNS
4069     /* Automatic type switching? */
4070     if (ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype) {
4071         int x;
4072         x = matchname(remote,0,servertype);
4073         debug(F111,"ftp getfile matchname",remote,x);
4074         switch (x) {
4075           case 0: ftp_typ = FTT_ASC; break;
4076           case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break;
4077           default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ;
4078         }
4079         changetype(ftp_typ,ftp_vbm);
4080         binary = ftp_typ;               /* For file-transfer display */
4081     }
4082 #endif /* PATTERNS */
4083
4084 #ifndef NOCSETS
4085     ftp_csx = -1;                       /* For file-transfer display */
4086     ftp_csl = -1;                       /* ... */
4087
4088     if (rcs > -1)                       /* -1 means no translation */
4089       if (ftp_typ == FTT_ASC)           /* File type is "ascii"? */
4090         if (fcs < 0)                    /* File charset not forced? */
4091           fcs = fcharset;               /* use prevailing FILE CHARACTER-SET */
4092     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4093         debug(F110,"ftp getfile","initxlate",0);
4094         initxlate(rcs,fcs);             /* NB: opposite order of PUT */
4095         ftp_csx = rcs;
4096         ftp_csl = fcs;
4097     } else
4098       xlate = 0;
4099 #endif /* NOCSETS */
4100
4101     if (!local) local = "";
4102     if (!pipename && !*local)
4103       local = remote;
4104
4105     out2screen = !strcmp(local,"-");
4106
4107     fullname[0] = NUL;
4108     if (pipename) {
4109         ckstrncpy(fullname,pipename,CKMAXPATH+1);
4110     } else {
4111         zfnqfp(local,CKMAXPATH,fullname);
4112         if (!fullname[0])
4113           ckstrncpy(fullname,local,CKMAXPATH+1);
4114     }
4115     if (!out2screen && displa && fdispla) { /* Screen */
4116         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,remote);
4117         ftscreen(SCR_AN,0,(CK_OFF_T)0,fullname);
4118         ftscreen(SCR_FS,0,fsize,"");
4119     }
4120     tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0);
4121     tlog(F110," as",fullname,0);
4122     debug(F111,"ftp getfile size",remote,fsize);
4123     debug(F111,"ftp getfile local",local,out2screen);
4124
4125     ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH);
4126
4127     t0 = gmstimer();                    /* Start time */
4128     debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */
4129     rc = recvrequest("RETR",
4130                      local,
4131                      remote,
4132                      append ? "ab" : "wb",
4133                      0,
4134                      recover,
4135                      pipename,
4136                      xlate,
4137                      fcs,
4138                      rcs
4139                      );
4140     t1 = gmstimer();                    /* End time */
4141     debug(F111,"ftp getfile t1",remote,t1);
4142     debug(F111,"ftp getfile sec",remote,(t1-t0)/1000);
4143 #ifdef GFTIMER
4144     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4145     fpxfsecs = sec;                     /* (for doxlog()) */
4146 #else
4147     sec = (t1 - t0) / 1000;
4148     xfsecs = (int)sec;
4149 #endif /* GFTIMER */
4150
4151 #ifdef FTP_TIMEOUT
4152     if (ftp_timed_out)
4153       rc = -4;
4154 #endif  /* FTP_TIMEOUT */
4155
4156     debug(F111,"ftp recvrequest rc",remote,rc);
4157     if (cancelfile || cancelgroup) {
4158         debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup);
4159         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4160     } else if (rc > 0) {
4161         debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup);
4162         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,cmarg);
4163     } else if (rc < 0) {
4164         switch (ftpcode) {
4165           case -4:                      /* Network error */
4166           case -2:                      /* File error */
4167             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,ck_errstr());
4168             break;
4169           case -3:
4170             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,
4171                      "Failure to make data connection");
4172             break;
4173           case -1:                      /* (should be covered above) */
4174             ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4175             break;
4176           default:
4177             ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4178         }
4179     } else {                            /* Tudo bem */
4180         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4181         if (rc == 0) {
4182             ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,""); /* For screen */
4183             makestr(&rrfspec,remote);     /* For WHERE command */
4184             makestr(&rfspec,fullname);
4185         }
4186     }
4187     if (rc > -1) {
4188         if (ftp_dates)                  /* If FTP DATES ON... */
4189           if (!pipename && !out2screen) /* and it's a real file */
4190             if (rc < 1 && rc != -3)     /* and it wasn't skipped */
4191               if (connected)            /* and we still have a connection */
4192                 if (zchki(local) > -1) { /* and the file wasn't discarded */
4193                     chkmodtime(local,remote,1); /* set local file date */
4194                     debug(F110,"ftp get set date",local,0);
4195                 }
4196         filcnt++;                       /* Used by \v(filenum) */
4197     }
4198 #ifdef TLOG
4199     if (tralog) {
4200         if (rc > 0) {
4201             tlog(F100," recovery skipped","",0);
4202         } else if (rc == 0) {
4203             tlog(F101," complete, size", "", fsize);
4204         } else if (cancelfile) {
4205             tlog(F100," canceled by user","",0);
4206 #ifdef FTP_TIMEOUT
4207         } else if (ftp_timed_out) {
4208             tlog(F100," timed out","",0);
4209 #endif  /* FTP_TIMEOUT */
4210         } else {
4211             tlog(F110," failed:",ftp_reply_str,0);
4212         }
4213         if (!tlogfmt)
4214           doxlog(what,local,fsize,ftp_typ,rc,"");
4215     }
4216 #endif /* TLOG */
4217     return(rc);
4218 }
4219
4220 /* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */
4221 /* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */
4222
4223 static int
4224 putfile(cx,
4225     local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg)
4226     char * local, * remote, * mvto, *rnto, *srvrn;
4227     int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg, prm;
4228
4229 /* putfile */ {
4230
4231     char asname[CKMAXPATH+1];
4232     char fullname[CKMAXPATH+1];
4233     int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0;
4234     int xlate = 0, restart = 0, mt = -1;
4235     char * s = NULL, * cmd = NULL;
4236     ULONG t0 = 0, t1 = 0;               /* Times for stats */
4237     int ofcs = 0, orcs = 0;
4238
4239 #ifdef GFTIMER
4240     CKFLOAT sec = 0.0;
4241 #else
4242     int sec = 0;
4243 #endif /* GFTIMER */
4244     debug(F111,"ftp putfile flg",local,flg);
4245     debug(F110,"ftp putfile srv_renam",srvrn,0);
4246     debug(F101,"ftp putfile fcs","",fcs);
4247     debug(F101,"ftp putfile rcs","",rcs);
4248
4249     ofcs = fcs;                         /* Save charset args */
4250     orcs = rcs;
4251
4252     sendstart = (CK_OFF_T)0;
4253     restart = flg & PUT_RES;
4254     if (!remote)
4255       remote = "";
4256
4257     /* FTP protocol command to send to server */
4258     cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR");
4259
4260     if (x_cnv == SET_AUTO) {            /* Name conversion is auto */
4261         if (alike) {                    /* If server & client are alike */
4262             nc = 0;                     /* no conversion */
4263         } else {                        /* If they are different */
4264             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
4265               nc = -1;                  /* only minimal conversions needed */
4266             else                        /* otherwise */
4267               nc = 1;                   /* full conversion */
4268         }
4269     } else                              /* Not auto - do what user said */
4270       nc = x_cnv;
4271
4272     /* If Transfer Mode is Automatic, determine file type */
4273     if (ftp_xfermode == XMODE_A && filepeek && !pipesend) {
4274         if (isdir(local)) {             /* If it's a directory */
4275             k = FT_BIN;                 /* skip the file scan */
4276         } else {
4277             debug(F110,"FTP PUT calling scanfile",local,0);
4278             k = scanfile(local,&o,nscanfile); /* Scan the file */
4279         }
4280         debug(F111,"FTP PUT scanfile",local,k);
4281         if (k > -1 && !forcetype) {
4282             ftp_typ = (k == FT_BIN) ? 1 : 0;
4283             if (xft > -1 && ftp_typ != xft) {
4284                 if (flg & PUT_SIM)
4285                   tlog(F110,"ftp put SKIP (Type):", local, 0);
4286                 return(SKP_TYP);
4287             }
4288             if (ftp_typ == 1 && tenex)  /* User said TENEX? */
4289               ftp_typ = FTT_TEN;
4290         }
4291     }
4292 #ifndef NOCSETS
4293     ftp_csx = -1;                       /* For file-transfer display */
4294     ftp_csl = -1;                       /* ... */
4295
4296     if (rcs > -1) {                     /* -1 means no translation */
4297         if (ftp_typ == 0) {             /* File type is "ascii"? */
4298             if (fcs < 0) {              /* File charset not forced? */
4299                 if (k < 0) {            /* If we didn't scan */
4300                     fcs = fcharset;     /* use prevailing FILE CHARACTER-SET */
4301                 } else {                /* If we did scan, use scan result */
4302                     switch (k) {
4303                       case FT_TEXT:     /* Unknown text */
4304                         fcs = fcharset;
4305                         break;
4306                       case FT_7BIT:     /* 7-bit text */
4307                         fcs = dcset7;
4308                         break;
4309                       case FT_8BIT:     /* 8-bit text */
4310                         fcs = dcset8;
4311                         break;
4312                       case FT_UTF8:     /* UTF-8 */
4313                         fcs = FC_UTF8;
4314                         break;
4315                       case FT_UCS2:     /* UCS-2 */
4316                         fcs = FC_UCS2;
4317                         if (o > -1)     /* Input file byte order */
4318                           fileorder = o;
4319                         break;
4320                       default:
4321                         rcs = -1;
4322                     }
4323                 }
4324             }
4325         }
4326     }
4327     if (fcs > -1 && rcs > -1) {         /* Set up translation functions */
4328         debug(F110,"ftp putfile","initxlate",0);
4329         initxlate(fcs,rcs);
4330         debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs);
4331         xlate = 1;
4332         ftp_csx = rcs;
4333         ftp_csl = fcs;
4334     }
4335 #endif /* NOCSETS */
4336
4337     binary = ftp_typ;                   /* For file-transfer display */
4338     asname[0] = NUL;
4339
4340     if (recursive) {                    /* If sending recursively, */
4341         if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */
4342           return(-1);                   /* Don't PUT if it fails. */
4343         else if (isdir(local))          /* It's a directory */
4344           return(0);                    /* Don't send it! */
4345     }
4346     if (*remote) {                      /* If an as-name template was given */
4347 #ifndef NOSPL
4348         if (cmd_quoting) {              /* and COMMAND QUOTING is ON */
4349             y = CKMAXPATH;              /* evaluate it for this file */
4350             s = asname;
4351             zzstring(remote,&s,&y);
4352         } else
4353 #endif /* NOSPL */
4354           ckstrncpy(asname,remote,CKMAXPATH);   /* (or take it literally) */
4355     } else {                                    /* No as-name */
4356         nzltor(local,asname,nc,0,CKMAXPATH);    /* use local name strip path */
4357         debug(F110,"FTP PUT nzltor",asname,0);
4358     }
4359     /* Preliminary messages and log entries */
4360
4361     fullname[0] = NUL;
4362     zfnqfp(local,CKMAXPATH,fullname);
4363     if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1);
4364     fullname[CKMAXPATH] = NUL;
4365
4366     if (displa && fdispla) {            /* Screen */
4367         ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,local);
4368         ftscreen(SCR_AN,0,(CK_OFF_T)0,asname);
4369         ftscreen(SCR_FS,0,fsize,"");
4370     }
4371 #ifdef DOUPDATE
4372     if (flg & (PUT_UPD|PUT_DIF)) {      /* Date-checking modes... */
4373         mt = chkmodtime(fullname,asname,0);
4374         debug(F111,"ftp putfile chkmodtime",asname,mt);
4375         if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */
4376             tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0);
4377             /* Skip this one */
4378             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_DAT,fullname);
4379             filcnt++;
4380             return(SKP_DAT);
4381         } else if (mt == 1) {           /* Times are equal */
4382             tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0);
4383             ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_EQU,fullname); /* Skip it */
4384             filcnt++;
4385             return(SKP_DAT);
4386         }
4387         /* Local file is newer */
4388         tlog(F110,ftp_typ ? "ftp put /update BINARY:" :
4389              "ftp put /update TEXT:", fullname, 0);
4390     } else if (flg & PUT_RES) {
4391         tlog(F110,ftp_typ ? "ftp put /recover BINARY:" :
4392              "ftp put /recover TEXT:", fullname, 0);
4393     } else {
4394         tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4395     }
4396 #else
4397     tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0);
4398 #endif /* DOUPDATE */
4399     tlog(F110," as",asname,0);
4400
4401 #ifndef NOCSETS
4402     if (xlate) {
4403         debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs);
4404         tlog(F110," file character set:",fcsinfo[fcs].keyword,0);
4405         tlog(F110," server character set:",fcsinfo[rcs].keyword,0);
4406     } else if (!ftp_typ) {
4407         tlog(F110," character sets:","no conversion",0);
4408         fcs = ofcs;                     /* Binary file but we still must */
4409         rcs = orcs;                     /* translate its name */
4410     }
4411 #endif /* NOCSETS */
4412
4413     /* PUT THE FILE */
4414
4415     t0 = gmstimer();                    /* Start time */
4416     if (flg & PUT_SIM) {                /* rc > 0 is a skip reason code */
4417         if (flg & (PUT_UPD|PUT_DIF)) {  /* (see SKP_xxx in ckcker.h) */
4418             rc = (mt < 0) ?             /* Update mode... */
4419               SKP_XNX :                 /* Remote file doesn't exist */
4420                 SKP_XUP;                /* Remote file is older */
4421         } else {
4422             rc = SKP_SIM;               /* "Would be sent", period. */
4423         }
4424     } else {
4425         rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart);
4426     }
4427     t1 = gmstimer();                    /* End time */
4428     filcnt++;                           /* File number */
4429
4430 #ifdef GFTIMER
4431     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4432     fpxfsecs = sec;                     /* (for doxlog()) */
4433 #else
4434     sec = (t1 - t0) / 1000;
4435     xfsecs = (int)sec;
4436 #endif /* GFTIMER */
4437
4438     debug(F111,"ftp sendrequest rc",local,rc);
4439
4440     if (cancelfile || cancelgroup) {
4441         debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup);
4442         ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,"");
4443     } else if (rc > 0) {
4444         debug(F101,"ftp put skipped",local,rc);
4445         ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)rc,fullname);
4446     } else if (rc < 0) {
4447         debug(F111,"ftp put error",local,ftpcode);
4448         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]);
4449     } else {
4450         debug(F111,"ftp put not canceled",ckitoa(displa),fdispla);
4451         ftscreen(SCR_PT,'Z',(CK_OFF_T)0,"");
4452         debug(F111,"ftp put ST_OK",local,rc);
4453         ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,"");
4454         debug(F110,"ftp put old sfspec",sfspec,0);
4455         makestr(&sfspec,fullname);      /* For WHERE command */
4456         debug(F110,"ftp put new sfspec",sfspec,0);
4457         debug(F110,"ftp put old srfspec",srfspec,0);
4458         makestr(&srfspec,asname);
4459         debug(F110,"ftp put new srfspec",srfspec,0);
4460     }
4461
4462     /* Final log entries */
4463
4464 #ifdef TLOG
4465     if (tralog) {
4466         if (rc > 0) {
4467             if (rc == SKP_XNX)
4468               tlog(F100," /simulate: WOULD BE SENT:","no remote file",0);
4469             else if (rc == SKP_XUP)
4470               tlog(F100," /simulate: WOULD BE SENT:","remote file older",0);
4471             else if (rc == SKP_SIM)
4472               tlog(F100," /simulate: WOULD BE SENT","",0);
4473             else
4474               tlog(F110," skipped:",gskreason(rc),0);
4475         } else if (rc == 0) {
4476             tlog(F101," complete, size", "", fsize);
4477         } else if (cancelfile) {
4478             tlog(F100," canceled by user","",0);
4479         } else {
4480             tlog(F110," failed:",ftp_reply_str,0);
4481         }
4482         if (!tlogfmt)
4483           doxlog(what,local,fsize,ftp_typ,rc,"");
4484     }
4485 #endif /* TLOG */
4486
4487     if (rc < 0)                         /* PUT did not succeed */
4488       return(-1);                       /* so done. */
4489
4490     if (flg & PUT_SIM)                  /* Simulating, skip the rest. */
4491       return(SKP_SIM);
4492
4493 #ifdef UNIX
4494     /* Set permissions too? */
4495
4496     if (prm) {                          /* Change permissions? */
4497         s = zgperm(local);              /* Get perms of local file */
4498         if (!s) s = "";
4499         x = strlen(s);
4500         if (x > 3) s += (x - 3);
4501         if (rdigits(s)) {
4502             ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL);
4503             x =
4504               ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE;
4505             tlog(F110, x ? " chmod" : " chmod failed",
4506                  s,
4507                  0
4508                  );
4509             if (!x)
4510               return(-1);
4511         }
4512     }
4513 #endif /* UNIX */
4514
4515     /* Disposition of source file */
4516
4517     if (moving) {
4518         x = zdelet(local);
4519         tlog(F110, (x > -1) ?
4520              " deleted" : " failed to delete",
4521              local,
4522              0
4523              );
4524         if (x < 0)
4525           return(-1);
4526     } else if (mvto) {
4527         x = zrename(local,mvto);
4528         tlog(F110, (x > -1) ?
4529              " moved source to" : " failed to move source to",
4530              mvto,
4531              0
4532              );
4533         if (x < 0)
4534           return(-1);
4535         /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,mvto); */
4536
4537     } else if (rnto) {
4538         char * s = rnto;
4539 #ifndef NOSPL
4540         int y;                          /* Pass it thru the evaluator */
4541         extern int cmd_quoting;         /* for \v(filename) */
4542         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4543             y = CKMAXPATH;
4544             s = (char *)asname;
4545             zzstring(rnto,&s,&y);
4546             s = (char *)asname;
4547         }
4548 #endif /* NOSPL */
4549         if (s) if (*s) {
4550             int x;
4551             x = zrename(local,s);
4552             tlog(F110, (x > -1) ?
4553                  " renamed source file to" :
4554                  " failed to rename source file to",
4555                  s,
4556                  0
4557                  );
4558             if (x < 0)
4559               return(-1);
4560             /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,s); */
4561         }
4562     }
4563
4564     /* Disposition of destination file */
4565
4566     if (srvrn) {                        /* /SERVER-RENAME: */
4567         char * s = srvrn;
4568 #ifndef NOSPL
4569         int y;                          /* Pass it thru the evaluator */
4570         extern int cmd_quoting; /* for \v(filename) */
4571         debug(F111,"ftp putfile srvrn",s,1);
4572
4573         if (cmd_quoting) {              /* But only if cmd_quoting is on */
4574             y = CKMAXPATH;
4575             s = (char *)fullname;       /* We can recycle this buffer now */
4576             zzstring(srvrn,&s,&y);
4577             s = (char *)fullname;
4578         }
4579 #endif /* NOSPL */
4580         debug(F111,"ftp putfile srvrn",s,2);
4581         if (s) if (*s) {
4582             int x;
4583             x = ftp_rename(asname,s);
4584             debug(F111,"ftp putfile ftp_rename",asname,x);
4585             tlog(F110, (x > 0) ?
4586                  " renamed destination file to" :
4587                  " failed to rename destination file to",
4588                  s,
4589                  0
4590                  );
4591             if (x < 1)
4592               return(-1);
4593         }
4594     }
4595     return(0);
4596 }
4597
4598 /* xxout must only be used for ASCII transfers */
4599 static int
4600 #ifdef CK_ANSIC
4601 xxout(char c)
4602 #else
4603 xxout(c) char c;
4604 #endif /* CK_ANSIC */
4605 {
4606 #ifndef OS2
4607 #ifndef VMS
4608 #ifndef MAC
4609 #ifndef OSK
4610     /* For Unix, DG, Stratus, Amiga, Gemdos, other */
4611     if (c == '\012') {
4612         if (zzout(dout,(CHAR)'\015') < 0)
4613           return(-1);
4614         ftpsnd.bytes++;
4615     }
4616 #else /* OSK */
4617     if (c == '\015') {
4618         c = '\012';
4619         if (zzout(dout,(CHAR)'\015') < 0)
4620           return(-1);
4621         ftpsnd.bytes++;
4622     }
4623 #endif /* OSK */
4624 #else /* MAC */
4625     if (c == '\015') {
4626         c = '\012';
4627         if (zzout(dout,(CHAR)'\015') < 0)
4628           return(-1);
4629         ftpsnd.bytes++;
4630     }
4631 #endif /* MAC */
4632 #endif /* VMS */
4633 #endif /* OS2 */
4634     if (zzout(dout,(CHAR)c) < 0)
4635       return(-1);
4636     ftpsnd.bytes++;
4637     return(0);
4638 }
4639
4640 static int
4641 #ifdef CK_ANSIC
4642 scrnout(char c)
4643 #else
4644 scrnout(c) char c;
4645 #endif /* CK_ANSIC */
4646 {
4647     return(putchar(c));
4648 }
4649
4650 static int
4651 #ifdef CK_ANSIC
4652 pipeout(char c)
4653 #else
4654 pipeout(c) char c;
4655 #endif /* CK_ANSIC */
4656 {
4657     return(zmchout(c));
4658 }
4659
4660 static int
4661 ispathsep(c) int c; {
4662     switch (servertype) {
4663       case SYS_VMS:
4664       case SYS_TOPS10:
4665       case SYS_TOPS20:
4666         return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0);
4667       case SYS_OS2:
4668       case SYS_WIN32:
4669       case SYS_DOS:
4670         return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0);
4671       case SYS_VOS:
4672         return((c == '>') ? 1 : 0);
4673       default:
4674         return((c == '/') ? 1 : 0);
4675     }
4676 }
4677
4678 static int
4679 iscanceled() {
4680 #ifdef CK_CURSES
4681     extern int ck_repaint();
4682 #endif /* CK_CURSES */
4683     int x, rc = 0;
4684     char c = 0;
4685     if (cancelfile)
4686       return(1);
4687     x = conchk();                       /* Any chars waiting at console? */
4688     if (x-- > 0) {                      /* Yes...  */
4689         c = coninc(5);                  /* Get one */
4690         switch (c) {
4691           case 032:                     /* Ctrl-X or X */
4692           case 'z':
4693           case 'Z': cancelgroup++;      /* fall thru on purpose */
4694           case 030:                     /* Ctrl-Z or Z */
4695           case 'x':
4696           case 'X': cancelfile++; rc++; break;
4697 #ifdef CK_CURSES
4698           case 'L':
4699           case 'l':
4700           case 014:                     /* Ctrl-L or L or Ctrl-W */
4701           case 027:
4702             ck_repaint();               /* Refresh screen */
4703 #endif /* CK_CURSES */
4704         }
4705     }
4706     while (x-- > 0)                     /* Soak up any rest */
4707       c = coninc(1);
4708     return(rc);
4709 }
4710
4711 #ifdef FTP_TIMEOUT
4712 /* fc = 0 for read; 1 for write */
4713 static int
4714 check_data_connection(fd,fc) int fd, fc; {
4715     int x;
4716     struct timeval tv;
4717     fd_set in, out, err;
4718
4719     if (ftp_timeout < 1L)
4720       return(0);
4721
4722     FD_ZERO(&in);
4723     FD_ZERO(&out);
4724     FD_ZERO(&err);
4725     FD_SET(fd,fc ? &out : &in);
4726     tv.tv_sec = ftp_timeout;            /* Time limit */
4727     tv.tv_usec = 0L;
4728
4729 #ifdef INTSELECT
4730     x = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err,&tv);
4731 #else
4732     x = select(FD_SETSIZE,&in,&out,&err,&tv);
4733 #endif /* INTSELECT */
4734
4735     if (x == 0) {
4736 #ifdef EWOULDBLOCK
4737         errno = EWOULDBLOCK;
4738 #else
4739 #ifdef EAGAIN
4740         errno = EAGAIN;
4741 #else
4742         errno = 11;
4743 #endif  /* EAGAIN */
4744 #endif  /* EWOULDBLOCK */
4745         debug(F100,"ftp check_data_connection TIMOUT","",0);
4746         return(-3);
4747     }
4748     return(0);
4749 }
4750 #endif  /* FTP_TIMEOUT */
4751
4752 /* zzsend - used by buffered output macros. */
4753
4754 static int
4755 #ifdef CK_ANSIC
4756 zzsend(int fd, CHAR c)
4757 #else
4758 zzsend(fd,c) int fd; CHAR c;
4759 #endif /* CK_ANSIC */
4760 {
4761     int rc;
4762
4763     debug(F101,"zzsend ucbufsiz","",ucbufsiz);
4764     debug(F101,"zzsend nout","",nout);
4765     debug(F111,"zzsend","secure?",ftpissecure());
4766
4767     if (iscanceled())                   /* Check for cancellation */
4768       return(-9);
4769
4770 #ifdef FTP_TIMEOUT    
4771     ftp_timed_out = 0;
4772     if (check_data_connection(fd,1) < 0) {
4773         ftp_timed_out = 1;
4774         return(-3);
4775     }
4776 #endif  /* FTP_TIMEOUT */
4777
4778     rc = (!ftpissecure()) ?
4779       send(fd, (SENDARG2TYPE)ucbuf, nout, 0) :
4780         secure_putbuf(fd, ucbuf, nout);
4781     ucbuf[nout] = NUL;
4782     nout = 0;
4783     ucbuf[nout++] = c;
4784     spackets++;
4785     pktnum++;
4786     if (rc > -1 && fdispla != XYFD_B) {
4787         spktl = nout;
4788         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
4789     }
4790     return(rc);
4791 }
4792
4793 /* c m d l i n p u t  --  Command-line PUT */
4794
4795 int
4796 cmdlinput(stay) int stay; {
4797     int x, rc = 0, done = 0, good = 0, status = 0;
4798     ULONG t0, t1;                       /* Times for stats */
4799 #ifdef GFTIMER
4800     CKFLOAT sec;
4801 #else
4802     int sec = 0;
4803 #endif /* GFTIMER */
4804
4805     if (quiet) {                        /* -q really means quiet */
4806         displa = 0;
4807         fdispla = 0;
4808     } else {
4809         displa = 1;
4810         fdispla = XYFD_B;
4811     }
4812     testing = 0;
4813     out2screen = 0;
4814     dpyactive = 0;
4815     what = W_FTP|W_SEND;
4816
4817 #ifndef NOSPL
4818     cmd_quoting = 0;
4819 #endif /* NOSPL */
4820     sndsrc = nfils;
4821
4822     t0 = gmstimer();                    /* Record starting time */
4823
4824     while (!done && !cancelgroup) {     /* Loop for all files */
4825
4826         cancelfile = 0;
4827         x = gnfile();                   /* Get next file from list(s) */
4828         if (x == 0)                     /* (see gnfile() comments...) */
4829           x = gnferror;
4830
4831         switch (x) {
4832           case 1:                       /* File to send */
4833             rc = putfile(FTP_PUT,       /* Function (PUT, APPEND) */
4834                          filnam,        /* Local file to send */
4835                          filnam,        /* Remote name for file */
4836                          forcetype,     /* Text/binary mode forced */
4837                          0,             /* Not moving */
4838                          NULL,          /* No move-to */
4839                          NULL,          /* No rename-to */
4840                          NULL,          /* No server-rename */
4841                          ftp_cnv,       /* Filename conversion */
4842                          0,             /* Unique-server-names */
4843                          -1,            /* All file types */
4844                          0,             /* No permissions */
4845                          -1,            /* No character sets */
4846                          -1,            /* No character sets */
4847                          0              /* No update or restart */
4848                          );
4849             if (rc > -1) {
4850                 good++;
4851                 status = 1;
4852             }
4853             if (cancelfile) {
4854                 continue;               /* Or break? */
4855             }
4856             if (rc < 0) {
4857                 ftp_fai++;
4858             }
4859             continue;                   /* Or break? */
4860
4861           case 0:                       /* No more files, done */
4862             done++;
4863             continue;
4864
4865           case -2:
4866           case -1:
4867             printf("?%s: file not found - \"%s\"\n",
4868                    puterror ? "Fatal" : "Warning",
4869                    filnam
4870                    );
4871             continue;                   /* or break? */
4872           case -3:
4873             printf("?Warning access denied - \"%s\"\n", filnam);
4874             continue;                   /* or break? */
4875           case -5:
4876             printf("?Too many files match\n");
4877             done++;
4878             break;
4879           case -6:
4880             if (good < 1)
4881               printf("?No files selected\n");
4882             done++;
4883             break;
4884           default:
4885             printf("?getnextfile() - unknown failure\n");
4886             done++;
4887         }
4888     }
4889     if (status > 0) {
4890         if (cancelgroup)
4891           status = 0;
4892         else if (cancelfile && good < 1)
4893           status = 0;
4894     }
4895     success = status;
4896     x = success;
4897     if (x > -1) {
4898         lastxfer = W_FTP|W_SEND;
4899         xferstat = success;
4900     }
4901     t1 = gmstimer();                    /* End time */
4902 #ifdef GFTIMER
4903     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
4904     if (!sec) sec = 0.001;
4905     fptsecs = sec;
4906 #else
4907     sec = (t1 - t0) / 1000;
4908     if (!sec) sec = 1;
4909 #endif /* GFTIMER */
4910     tfcps = (long) (tfc / sec);
4911     tsecs = (int)sec;
4912     lastxfer = W_FTP|W_SEND;
4913     xferstat = success;
4914     if (dpyactive)
4915       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
4916     if (!stay)
4917       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
4918     return(success);
4919 }
4920
4921
4922 /*  d o f t p p u t  --  Parse and execute PUT, MPUT, and APPEND  */
4923
4924 int
4925 #ifdef CK_ANSIC
4926 doftpput(int cx, int who)               /* who == 1 for ftp, 0 for kermit */
4927 #else
4928 doftpput(cx,who) int cx, who;
4929 #endif /* CK_ANSIC */
4930 {
4931     struct FDB sf, fl, sw, cm;
4932     int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0;
4933     int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0;
4934     char * s, * s2;
4935
4936     int x_csl, x_csr = -1;              /* Local and remote charsets */
4937     int x_xla = 0;
4938     int x_recurse = 0;
4939     char c, * p;                        /* Workers */
4940 #ifdef PUTARRAY
4941     int range[2];                       /* Array range */
4942     char ** ap = NULL;                  /* Array pointer */
4943     int arrayx = -1;                    /* Array index */
4944 #endif /* PUTARRAY */
4945     ULONG t0 = 0L, t1 = 0L;             /* Times for stats */
4946 #ifdef GFTIMER
4947     CKFLOAT sec;
4948 #else
4949     int sec = 0;
4950 #endif /* GFTIMER */
4951
4952     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
4953     success = 0;                        /* Assume failure */
4954     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
4955     out2screen = 0;                     /* Not outputting file to screen */
4956     putflags = 0;                       /* PUT options */
4957     x_cnv = ftp_cnv;                    /* Filename conversion */
4958     x_usn = ftp_usn;                    /* Unique server names */
4959     x_prm = ftp_prm;                    /* Permissions */
4960     if (x_prm == SET_AUTO)              /* Permissions AUTO */
4961       x_prm = alike;
4962
4963 #ifndef NOCSETS
4964     x_csr = ftp_csr;                    /* Inherit global server charset */
4965     x_csl = ftp_csl;
4966     if (x_csl < 0)
4967       x_csl = fcharset;
4968     x_xla = ftp_xla;
4969 #endif /* NOCSETS */
4970
4971     makestr(&filefile,NULL);            /* No filename list file yet. */
4972     makestr(&srv_renam,NULL);           /* Clear /SERVER-RENAME: */
4973     makestr(&snd_rename,NULL);          /*  PUT /RENAME */
4974     makestr(&snd_move,NULL);            /*  PUT /MOVE */
4975     putpath[0] = NUL;                   /* Initialize for syncdir(). */
4976     puterror = ftp_err;                 /* Inherit global error action. */
4977     what = W_SEND|W_FTP;                /* What we're doing (sending w/FTP) */
4978     asnambuf[0] = NUL;                  /* Clear as-name buffer */
4979
4980     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
4981         ftp_typ = g_ftp_typ;
4982         /* g_ftp_typ = -1; */
4983     }
4984     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
4985         pv[i].sval = NULL;              /* to null pointers */
4986         pv[i].ival = -1;                /* and -1 int values */
4987         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
4988     }
4989     if (who == 0) {                     /* Called with unprefixed command */
4990         switch (cx) {
4991           case XXRSEN:  pv[SND_RES].ival = 1; break;
4992           case XXCSEN:  pv[SND_CMD].ival = 1; break;
4993           case XXMOVE:  pv[SND_DEL].ival = 1; break;
4994           case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */
4995           case XXMSE:   mput++; break;
4996         }
4997     } else {
4998         if (cx == FTP_REP)
4999           pv[SND_RES].ival = 1;
5000         if (cx == FTP_MPU)
5001           mput++;
5002     }
5003     cmfdbi(&sw,                         /* First FDB - command switches */
5004            _CMKEY,                      /* fcode */
5005            "Filename, or switch",       /* hlpmsg */
5006            "",                          /* default */
5007            "",                          /* addtl string data */
5008            nputswi,                     /* addtl numeric data 1: tbl size */
5009            4,                           /* addtl numeric data 2: 4 = cmswi */
5010            xxstring,                    /* Processing function */
5011            putswi,                      /* Keyword table */
5012            &sf                          /* Pointer to next FDB */
5013            );
5014     cmfdbi(&fl,                         /* 3rd FDB - local filespec */
5015            _CMFLD,                      /* fcode */
5016            "",                          /* hlpmsg */
5017            "",                          /* default */
5018            "",                          /* addtl string data */
5019            0,                           /* addtl numeric data 1 */
5020            0,                           /* addtl numeric data 2 */
5021            xxstring,
5022            NULL,
5023            &cm
5024            );
5025     cmfdbi(&cm,                         /* 4th FDB - Confirmation */
5026            _CMCFM,                      /* fcode */
5027            "",                          /* hlpmsg */
5028            "",                          /* default */
5029            "",                          /* addtl string data */
5030            0,                           /* addtl numeric data 1 */
5031            0,                           /* addtl numeric data 2 */
5032            NULL,
5033            NULL,
5034            NULL
5035            );
5036
5037   again:
5038     cmfdbi(&sf,                         /* 2nd FDB - file to send */
5039            _CMIFI,                      /* fcode */
5040            "",                          /* hlpmsg */
5041            "",                          /* default */
5042            "",                          /* addtl string data */
5043            /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */
5044            nolinks | x_recurse,         /* addtl numeric data 1 */
5045            0,                           /* dirflg 0 means "not dirs only" */
5046            xxstring,
5047            NULL,
5048 #ifdef COMMENT
5049            mput ? &cm : &fl
5050 #else
5051            &fl
5052 #endif /* COMMENT */
5053            );
5054
5055     while (1) {                         /* Parse zero or more switches */
5056         x = cmfdb(&sw);                 /* Parse something */
5057         debug(F101,"ftp put cmfdb A","",x);
5058         debug(F101,"ftp put fcode A","",cmresult.fcode);
5059         if (x < 0)                      /* Error */
5060           goto xputx;                   /* or reparse needed */
5061         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
5062           break;
5063         c = cmgbrk();                   /* Get break character */
5064         getval = (c == ':' || c == '='); /* to see how they ended the switch */
5065         if (getval && !(cmresult.kflags & CM_ARG)) {
5066             printf("?This switch does not take arguments\n");
5067             x = -9;
5068             goto xputx;
5069         }
5070         if (!getval && (cmgkwflgs() & CM_ARG)) {
5071             printf("?This switch requires an argument\n");
5072             x = -9;
5073             goto xputx;
5074         }
5075         n = cmresult.nresult;           /* Numeric result = switch value */
5076         debug(F101,"ftp put switch","",n);
5077
5078         switch (n) {                    /* Process the switch */
5079           case SND_AFT:                 /* Send /AFTER:date-time */
5080           case SND_BEF:                 /* Send /BEFORE:date-time */
5081           case SND_NAF:                 /* Send /NOT-AFTER:date-time */
5082           case SND_NBE:                 /* Send /NOT-BEFORE:date-time */
5083             if (!getval) break;
5084             if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) {
5085                 if (x == -3) {
5086                     printf("?Date-time required\n");
5087                     x = -9;
5088                 }
5089                 goto xputx;
5090             }
5091             pv[n].ival = 1;
5092             makestr(&(pv[n].sval),s);
5093             break;
5094
5095           case SND_ASN:                 /* /AS-NAME: */
5096             debug(F101,"ftp put /as-name getval","",getval);
5097             if (!getval) break;
5098             if ((x = cmfld("Name to send under","",&s,NULL)) < 0) {
5099                 if (x == -3) {
5100                     printf("?name required\n");
5101                     x = -9;
5102                 }
5103                 goto xputx;
5104             }
5105             makestr(&(pv[n].sval),brstrip(s));
5106             debug(F110,"ftp put /as-name 1",pv[n].sval,0);
5107             if (pv[n].sval) pv[n].ival = 1;
5108             break;
5109
5110 #ifdef PUTARRAY
5111           case SND_ARR:                 /* /ARRAY */
5112             if (!getval) break;
5113             ap = NULL;
5114             if ((x = cmfld("Array name (a single letter will do)",
5115                            "",
5116                            &s,
5117                            NULL
5118                            )) < 0) {
5119                 if (x == -3)
5120                   break;
5121                 else
5122                   return(x);
5123             }
5124             if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) {
5125                 printf("?Bad array: %s\n",s);
5126                 return(-9);
5127             }
5128             if (!(ap = a_ptr[x])) {
5129                 printf("?No such array: %s\n",s);
5130                 return(-9);
5131             }
5132             pv[n].ival = 1;
5133             pv[SND_CMD].ival = 0;       /* Undo any conflicting ones... */
5134             pv[SND_RES].ival = 0;
5135             pv[SND_FIL].ival = 0;
5136             arrayx = x;
5137             break;
5138 #endif /* PUTARRAY */
5139
5140           case SND_BIN:                 /* /BINARY */
5141           case SND_TXT:                 /* /TEXT or /ASCII */
5142           case SND_TEN:                 /* /TENEX */
5143             pv[SND_BIN].ival = 0;
5144             pv[SND_TXT].ival = 0;
5145             pv[SND_TEN].ival = 0;
5146             pv[n].ival = 1;
5147             break;
5148
5149 #ifdef PUTPIPE
5150           case SND_CMD:                 /* These take no args */
5151             if (nopush) {
5152                 printf("?Sorry, system command access is disabled\n");
5153                 x = -9;
5154                 goto xputx;
5155             }
5156 #ifdef PIPESEND
5157             else if (sndfilter) {
5158                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
5159                 x = -9;
5160                 goto xputx;
5161             }
5162 #endif /* PIPESEND */
5163             sw.hlpmsg = "Command, or switch"; /* Change help message */
5164             pv[n].ival = 1;             /* Just set the flag */
5165             pv[SND_ARR].ival = 0;
5166             break;
5167 #endif /* PUTPIPE */
5168
5169 #ifdef CKSYMLINK
5170           case SND_LNK:
5171             nolinks = 0;
5172             goto again;                 /* Because CMIFI params changed... */
5173           case SND_NLK:
5174             nolinks = 2;
5175             goto again;
5176 #endif /* CKSYMLINK */
5177
5178 #ifdef FTP_RESTART
5179           case SND_RES:                 /* /RECOVER (resend) */
5180             pv[SND_ARR].ival = 0;       /* fall thru on purpose... */
5181 #endif /* FTP_RESTART */
5182
5183           case SND_NOB:
5184           case SND_DEL:                 /* /DELETE */
5185           case SND_SHH:                 /* /QUIET */
5186           case SND_UPD:                 /* /UPDATE */
5187           case SND_SIM:                 /* /UPDATE */
5188           case SND_USN:                 /* /UNIQUE */
5189             pv[n].ival = 1;             /* Just set the flag */
5190             break;
5191
5192           case SND_REC:                 /* /RECURSIVE */
5193             recursive = 2;              /* Must be set before cmifi() */
5194             x_recurse = 1;
5195             goto again;                 /* Because CMIFI params changed... */
5196             break;
5197
5198 #ifdef UNIXOROSK
5199           case SND_DOT:                 /* /DOTFILES */
5200             matchdot = 1;
5201             break;
5202           case SND_NOD:                 /* /NODOTFILES */
5203             matchdot = 0;
5204             break;
5205 #endif /* UNIXOROSK */
5206
5207           case SND_ERR:                 /* /ERROR-ACTION */
5208             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
5209               goto xputx;
5210             pv[n].ival = x;
5211             break;
5212
5213           case SND_EXC:                 /* Excludes */
5214             if (!getval) break;
5215             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
5216                 if (x == -3) {
5217                     printf("?Pattern required\n");
5218                     x = -9;
5219                 }
5220                 goto xputx;
5221             }
5222             if (s) if (!*s) s = NULL;
5223             makestr(&(pv[n].sval),s);
5224             if (pv[n].sval)
5225               pv[n].ival = 1;
5226             break;
5227
5228           case SND_PRM:                 /* /PERMISSIONS */
5229             if (!getval)
5230               x = 1;
5231             else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0)
5232               goto xputx;
5233             pv[SND_PRM].ival = x;
5234             break;
5235
5236 #ifdef PIPESEND
5237           case SND_FLT:                 /* /FILTER */
5238             debug(F101,"ftp put /filter getval","",getval);
5239             if (!getval) break;
5240             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
5241                 if (x == -3)
5242                   s = "";
5243                 else
5244                   goto xputx;
5245             }
5246             if (*s) s = brstrip(s);
5247             y = strlen(s);
5248             for (x = 0; x < y; x++) {   /* Make sure they included "\v(...)" */
5249                 if (s[x] != '\\') continue;
5250                 if (s[x+1] == 'v') break;
5251             }
5252             if (x == y) {
5253                 printf(
5254                 "?Filter must contain a replacement variable for filename.\n"
5255                        );
5256                 x = -9;
5257                 goto xputx;
5258             }
5259             if (s) if (!*s) s = NULL;
5260             makestr(&(pv[n].sval),s);
5261             if (pv[n].sval)
5262               pv[n].ival = 1;
5263             break;
5264 #endif /* PIPESEND */
5265
5266           case SND_NAM:                 /* /FILENAMES */
5267             if (!getval) break;
5268             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
5269               goto xputx;
5270             debug(F101,"ftp put /filenames","",x);
5271             pv[n].ival = x;
5272             break;
5273
5274           case SND_SMA:                 /* Smaller / larger than */
5275           case SND_LAR: {
5276               CK_OFF_T y;
5277               if (!getval) break;
5278               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
5279                 goto xputx;
5280               pv[n].wval = y;
5281               break;
5282           }
5283           case SND_FIL:                 /* Name of file containing filenames */
5284             if (!getval) break;
5285             if ((x = cmifi("Name of file containing list of filenames",
5286                                "",&s,&y,xxstring)) < 0) {
5287                 if (x == -3) {
5288                     printf("?Filename required\n");
5289                     x = -9;
5290                 }
5291                 goto xputx;
5292             } else if (y && iswild(s)) {
5293                 printf("?Wildcards not allowed\n");
5294                 x = -9;
5295                 goto xputx;
5296             }
5297             if (s) if (!*s) s = NULL;
5298             makestr(&(pv[n].sval),s);
5299             if (pv[n].sval) {
5300                 pv[n].ival = 1;
5301                 pv[SND_ARR].ival = 0;
5302             } else {
5303                 pv[n].ival = 0;
5304             }
5305             mput = 0;
5306             break;
5307
5308           case SND_MOV:                 /* MOVE after */
5309           case SND_REN:                 /* RENAME after */
5310           case SND_SRN: {               /* SERVER-RENAME after */
5311               char * m = "";
5312               switch (n) {
5313                 case SND_MOV:
5314                   m = "device and/or directory for source file after sending";
5315                   break;
5316                 case SND_REN:
5317                   m = "new name for source file after sending";
5318                   break;
5319                 case SND_SRN:
5320                   m = "new name for destination file after sending";
5321                   break;
5322               }
5323               if (!getval) break;
5324               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
5325                   if (x == -3) {
5326                       printf("%s\n", n == SND_MOV ?
5327                              "?Destination required" :
5328                              "?New name required"
5329                              );
5330                       x = -9;
5331                   }
5332                   goto xputx;
5333               }
5334               if (s) if (!*s) s = NULL;
5335               makestr(&(pv[n].sval),s ? brstrip(s) : NULL);
5336               pv[n].ival = (pv[n].sval) ? 1 : 0;
5337               break;
5338           }
5339           case SND_STA:                 /* Starting position (= PSEND) */
5340             if (!getval) break;
5341             if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0)
5342               goto xputx;
5343             pv[n].ival = y;
5344             break;
5345
5346           case SND_TYP:                 /* /TYPE */
5347             if (!getval) break;
5348             if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0)
5349               goto xputx;
5350             pv[n].ival = (x == 2) ? -1 : x;
5351             break;
5352
5353 #ifndef NOCSETS
5354           case SND_CSL:                 /* Local character set */
5355           case SND_CSR:                 /* Remote (server) charset */
5356             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) {
5357                 return((x == -3) ? -2 : x);
5358             }
5359             if (n == SND_CSL)
5360               x_csl = x;
5361             else
5362               x_csr = x;
5363             x_xla = 1;                  /* Overrides global OFF setting */
5364             break;
5365
5366           case SND_XPA:                 /* Transparent */
5367             x_xla = 0;
5368             x_csr = -1;
5369             x_csl = -1;
5370             break;
5371 #endif /* NOCSETS */
5372         }
5373     }
5374 #ifdef PIPESEND
5375     if (pv[SND_RES].ival > 0) { /* /RECOVER */
5376         if (sndfilter || pv[SND_FLT].ival > 0) {
5377             printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n");
5378             x = -9;
5379             goto xputx;
5380         }
5381         if (sfttab[0] > 0 && sfttab[SFT_REST] == 0)
5382           printf("WARNING: Server says it doesn't support REST.\n");
5383     }
5384 #endif /* PIPESEND */
5385
5386     cmarg = "";
5387     cmarg2 = asnambuf;
5388     line[0] = NUL;
5389     s = line;
5390     wild = 0;
5391
5392     switch (cmresult.fcode) {           /* How did we get out of switch loop */
5393       case _CMIFI:                      /* Input filename */
5394         if (pv[SND_FIL].ival > 0) {
5395             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5396             x = -9;
5397             goto xputx;
5398         }
5399         ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */
5400         if (pv[SND_ARR].ival > 0)
5401           ckstrncpy(asnambuf,line,CKMAXPATH);
5402         else
5403           wild = cmresult.nresult;      /* Wild flag */
5404         debug(F111,"ftp put wild",line,wild);
5405         if (!wild && !recursive && !mput)
5406           nolinks = 0;
5407         break;
5408       case _CMFLD:                      /* Field */
5409         /* Only allowed with /COMMAND and /ARRAY */
5410         if (pv[SND_FIL].ival > 0) {
5411             printf("?You may not give a PUT filespec and a /LISTFILE\n");
5412             x = -9;
5413             goto xputx;
5414         }
5415         /* For MPUT it's OK to have filespecs that don't match any files */
5416         if (mput)
5417           break;
5418         if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) {
5419 #ifdef CKROOT
5420             if (ckrooterr)
5421               printf("?Off limits: %s\n",cmresult.sresult);
5422             else
5423 #endif /* CKROOT */
5424               printf("?%s - \"%s\"\n",
5425                    iswild(cmresult.sresult) ?
5426                    "No files match" : "File not found",
5427                    cmresult.sresult
5428                    );
5429             x = -9;
5430             goto xputx;
5431         }
5432         ckstrncpy(line,cmresult.sresult,LINBUFSIZ);
5433         if (pv[SND_ARR].ival > 0)
5434           ckstrncpy(asnambuf,line,CKMAXPATH);
5435         break;
5436       case _CMCFM:                      /* Confirmation */
5437         confirmed = 1;
5438         break;
5439       default:
5440         printf("?Unexpected function code: %d\n",cmresult.fcode);
5441         x = -9;
5442         goto xputx;
5443     }
5444     debug(F110,"ftp put string",s,0);
5445     debug(F101,"ftp put confirmed","",confirmed);
5446
5447     /* Save and change protocol and transfer mode */
5448     /* Global values are restored in main parse loop */
5449
5450     g_displa = fdispla;
5451     if (ftp_dis > -1)
5452       fdispla = ftp_dis;
5453     g_skipbup = skipbup;
5454
5455     if (pv[SND_NOB].ival > -1) {        /* /NOBACKUP (skip backup file) */
5456         g_skipbup = skipbup;
5457         skipbup = 1;
5458     }
5459     if (pv[SND_TYP].ival > -1) {        /* /TYPE */
5460         xfiletype = pv[SND_TYP].ival;
5461         if (xfiletype == 2)
5462           xfiletype = -1;
5463     }
5464     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
5465         forcetype = 1;                  /* So skip file scan */
5466         ftp_typ = FTT_BIN;              /* Set binary */
5467     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
5468         forcetype = 1;
5469         ftp_typ = FTT_ASC;
5470     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
5471         forcetype = 1;
5472         ftp_typ = FTT_TEN;
5473     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
5474         forcetype = 1;
5475         ftp_typ = binary;
5476         g_ftp_typ = binary;
5477     }
5478
5479 #ifdef PIPESEND
5480     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
5481         debug(F110,"PUT /COMMAND before stripping",s,0);
5482         s = brstrip(s);
5483         debug(F110,"PUT /COMMAND after stripping",s,0);
5484         if (!*s) {
5485             printf("?Sorry, a command to send from is required\n");
5486             x = -9;
5487             goto xputx;
5488         }
5489         cmarg = s;
5490     }
5491 #endif /* PIPESEND */
5492
5493 /* Set up /MOVE and /RENAME */
5494
5495     if (pv[SND_DEL].ival > 0 &&
5496         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
5497         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
5498         x = -9;
5499         goto xputx;
5500     }
5501 #ifdef CK_TMPDIR
5502     if (pv[SND_MOV].ival > 0) {
5503         int len;
5504         char * p = pv[SND_MOV].sval;
5505         len = strlen(p);
5506         if (!isdir(p)) {                /* Check directory */
5507 #ifdef CK_MKDIR
5508             char * s = NULL;
5509             s = (char *)malloc(len + 4);
5510             if (s) {
5511                 strcpy(s,p);            /* safe */
5512 #ifdef datageneral
5513                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
5514 #else
5515                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
5516 #endif /* datageneral */
5517                 s[len++] = 'X';
5518                 s[len] = NUL;
5519 #ifdef NOMKDIR
5520                 x = -1;
5521 #else
5522                 x = zmkdir(s);
5523 #endif /* NOMKDIR */
5524                 free(s);
5525                 if (x < 0) {
5526                     printf("?Can't create \"%s\"\n",p);
5527                     x = -9;
5528                     goto xputx;
5529                 }
5530             }
5531 #else
5532             printf("?Directory \"%s\" not found\n",p);
5533             x = -9;
5534             goto xputx;
5535 #endif /* CK_MKDIR */
5536         }
5537         makestr(&snd_move,p);
5538     }
5539 #endif /* CK_TMPDIR */
5540
5541     if (pv[SND_REN].ival > 0) {         /* /RENAME */
5542         char * p = pv[SND_REN].sval;
5543         if (!p) p = "";
5544         if (!*p) {
5545             printf("?New name required for /RENAME\n");
5546             x = -9;
5547             goto xputx;
5548         }
5549         p = brstrip(p);
5550 #ifndef NOSPL
5551     /* If name given is wild, rename string must contain variables */
5552         if (wild) {
5553             char * s = tmpbuf;
5554             x = TMPBUFSIZ;
5555             zzstring(p,&s,&x);
5556             if (!strcmp(tmpbuf,p)) {
5557                 printf(
5558     "?/RENAME for file group must contain variables such as \\v(filename)\n"
5559                        );
5560                 x = -9;
5561                 goto xputx;
5562             }
5563         }
5564 #endif /* NOSPL */
5565         makestr(&snd_rename,p);
5566         debug(F110,"FTP snd_rename",snd_rename,0);
5567     }
5568     if (pv[SND_SRN].ival > 0) {         /* /SERVER-RENAME */
5569         char * p = pv[SND_SRN].sval;
5570         if (!p) p = "";
5571         if (!*p) {
5572             printf("?New name required for /SERVER-RENAME\n");
5573             x = -9;
5574             goto xputx;
5575         }
5576         p = brstrip(p);
5577 #ifndef NOSPL
5578         if (wild) {
5579             char * s = tmpbuf;
5580             x = TMPBUFSIZ;
5581             zzstring(p,&s,&x);
5582             if (!strcmp(tmpbuf,p)) {
5583                 printf(
5584 "?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n"
5585                        );
5586                 x = -9;
5587                 goto xputx;
5588             }
5589         }
5590 #endif /* NOSPL */
5591         makestr(&srv_renam,p);
5592         debug(F110,"ftp put srv_renam",srv_renam,0);
5593     }
5594     if (!confirmed) {                   /* CR not typed yet, get more fields */
5595         char * lp;
5596         if (mput) {                     /* MPUT or MMOVE */
5597             nfils = 0;                  /* We already have the first one */
5598 #ifndef NOMSEND
5599             if (cmresult.fcode == _CMIFI) {
5600                 /* First filespec is valid */
5601                 msfiles[nfils++] = line;    /* Store pointer */
5602                 lp = line + (int)strlen(line) + 1; /* Point past it */
5603                 debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1);
5604             } else {
5605                 /* First filespec matches no files */
5606                 debug(F110,"ftp put mput skipping first filespec",
5607                       cmresult.sresult,
5608                       0
5609                       );
5610                 lp = line;
5611             }
5612             /* Parse a filespec, a "field", or confirmation */
5613
5614             cmfdbi(&sf,                 /* 1st FDB - file to send */
5615                    _CMIFI,              /* fcode */
5616                    "",                  /* hlpmsg */
5617                    "",                  /* default */
5618                    "",                  /* addtl string data */
5619                    nolinks | x_recurse, /* addtl numeric data 1 */
5620                    0,                   /* dirflg 0 means "not dirs only" */
5621                    xxstring,
5622                    NULL,
5623                    &fl
5624                    );
5625             cmfdbi(&fl,                 /* 2nd FDB - local filespec */
5626                    _CMFLD,              /* fcode */
5627                    "",                  /* hlpmsg */
5628                    "",                  /* default */
5629                    "",                  /* addtl string data */
5630                    0,                   /* addtl numeric data 1 */
5631                    0,                   /* addtl numeric data 2 */
5632                    xxstring,
5633                    NULL,
5634                    &cm
5635                    );
5636             cmfdbi(&cm,                 /* 3rd FDB - Confirmation */
5637                    _CMCFM,              /* fcode */
5638                    "",
5639                    "",
5640                    "",
5641                    0,
5642                    0,
5643                    NULL,
5644                    NULL,
5645                    NULL
5646                    );
5647
5648             while (!confirmed) {        /* Get more filenames */
5649                 x = cmfdb(&sf);         /* Parse something */
5650                 debug(F101,"ftp put cmfdb B","",x);
5651                 debug(F101,"ftp put fcode B","",cmresult.fcode);
5652                 if (x < 0)              /* Error */
5653                   goto xputx;           /* or reparse needed */
5654                 switch (cmresult.fcode) {
5655                   case _CMCFM:          /* End of command */
5656                     confirmed++;
5657                     if (nfils < 1) {
5658                         debug(F100,"ftp put mput no files match","",0);
5659                         printf("?No files match MPUT list\n");
5660                         x = -9;
5661                         goto xputx;
5662                     }
5663                     break;
5664                   case _CMFLD:          /* No match */
5665                     debug(F110,"ftp put mput skipping",cmresult.sresult,0);
5666                     continue;
5667                   case _CMIFI:          /* Good match */
5668                     s = cmresult.sresult;
5669                     msfiles[nfils++] = lp; /* Got one, count, point to it, */
5670                     p = lp;                /* remember pointer, */
5671                     while ((*lp++ = *s++)) /* and copy it into buffer */
5672                       if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */
5673                           printf("?MPUT list too long\n");
5674                           line[0] = NUL;
5675                           x = -9;
5676                           goto xputx;
5677                       }
5678                     debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1);
5679                     if (nfils == 1)     /* Take care of \v(filespec) */
5680                       fspec[0] = NUL;
5681 #ifdef ZFNQFP
5682                     zfnqfp(p,TMPBUFSIZ,tmpbuf);
5683                     p = tmpbuf;
5684 #endif /* ZFNQFP */
5685                     if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) {
5686                         strcat(fspec,p);    /* safe */
5687                         strcat(fspec," ");  /* safe */
5688                     } else {
5689 #ifdef COMMENT
5690                         printf("WARNING - \\v(filespec) buffer overflow\n");
5691 #else
5692                         debug(F101,"doxput filespec buffer overflow","",0);
5693 #endif /* COMMENT */
5694                     }
5695                 }
5696             }
5697 #endif /* NOMSEND */
5698         } else {                        /* Regular PUT */
5699             nfils = -1;
5700             if ((x = cmtxt(wild ?
5701 "\nOptional as-name template containing replacement variables \
5702 like \\v(filename)" :
5703                            "Optional name to send it with",
5704                            "",&p,NULL)) < 0)
5705               goto xputx;
5706
5707             if (p) if (!*p) p = NULL;
5708             p = brstrip(p);
5709
5710             if (p && *p) {
5711                 makestr(&(pv[SND_ASN].sval),p);
5712                 if (pv[SND_ASN].sval)
5713                   pv[SND_ASN].ival = 1;
5714                 debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0);
5715             }
5716         }
5717     }
5718     /* Set cmarg2 from as-name, however we got it. */
5719
5720     CHECKCONN();
5721     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
5722         char * p;
5723         p = brstrip(pv[SND_ASN].sval);
5724         ckstrncpy(asnambuf,p,CKMAXPATH+1);
5725     }
5726     debug(F110,"ftp put asnambuf",asnambuf,0);
5727
5728     if (pv[SND_FIL].ival > 0) {
5729         if (confirmed) {
5730             if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
5731                 debug(F110,"ftp put can't open",pv[SND_FIL].sval,0);
5732                 printf("?Failure to open %s\n",pv[SND_FIL].sval);
5733                 x = -9;
5734                 goto xputx;
5735             }
5736             makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */
5737             debug(F110,"ftp PUT /LISTFILE opened",filefile,0);
5738             wild = 1;
5739         }
5740     }
5741     if (confirmed && !line[0] && !filefile) {
5742 #ifndef NOMSEND
5743         if (filehead) {                 /* OK if we have a SEND-LIST */
5744             nfils = filesinlist;
5745             sndsrc = nfils;             /* Like MSEND */
5746             addlist = 1;                /* But using a different list... */
5747             filenext = filehead;
5748             goto doput;
5749         }
5750 #endif /* NOMSEND */
5751         printf("?Filename required but not given\n");
5752         x = -9;
5753         goto xputx;
5754     }
5755 #ifndef NOMSEND
5756     addlist = 0;                        /* Don't use SEND-LIST. */
5757 #endif /* NOMSEND */
5758
5759     if (mput) {                         /* MPUT (rather than PUT) */
5760 #ifndef NOMSEND
5761         cmlist = msfiles;               /* List of filespecs */
5762         sndsrc = nfils;                 /* rather filespec and as-name */
5763 #endif /* NOMSEND */
5764         pipesend = 0;
5765     } else if (filefile) {              /* File contains list of filenames */
5766         s = "";
5767         cmarg = "";
5768         line[0] = NUL;
5769         nfils = 1;
5770         sndsrc = 1;
5771
5772     } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) {
5773
5774         /* Not MSEND, MMOVE, /LIST, or /ARRAY */
5775         nfils = sndsrc = -1;
5776         if (!wild) {
5777             if (zchki(s) < 0) {
5778                 printf("?Read access denied - \"%s\"\n", s);
5779                 x = -9;
5780                 goto xputx;
5781             }
5782         }
5783         if (s != line)                  /* We might already have done this. */
5784           ckstrncpy(line,s,LINBUFSIZ);  /* Copy of string just parsed. */
5785 #ifdef DEBUG
5786         else
5787           debug(F110,"doxput line=s",line,0);
5788 #endif /* DEBUG */
5789         cmarg = line;                   /* File to send */
5790     }
5791 #ifndef NOMSEND
5792     zfnqfp(cmarg,fspeclen,fspec);       /* Get full name */
5793 #endif /* NOMSEND */
5794
5795     if (!mput) {                        /* For all but MPUT... */
5796 #ifdef PIPESEND
5797         if (pv[SND_CMD].ival > 0)       /* /COMMAND sets pipesend flag */
5798           pipesend = 1;
5799         debug(F101,"ftp put /COMMAND pipesend","",pipesend);
5800         if (pipesend && filefile) {
5801             printf("?Invalid switch combination\n");
5802             x = -9;
5803             goto xputx;
5804         }
5805 #endif /* PIPESEND */
5806
5807 #ifndef NOSPL
5808     /* If as-name given and filespec is wild, as-name must contain variables */
5809         if ((wild || mput) && asnambuf[0]) {
5810             char * s = tmpbuf;
5811             x = TMPBUFSIZ;
5812             zzstring(asnambuf,&s,&x);
5813             if (!strcmp(tmpbuf,asnambuf)) {
5814                 printf(
5815     "?As-name for file group must contain variables such as \\v(filename)\n"
5816                        );
5817                 x = -9;
5818                 goto xputx;
5819             }
5820         }
5821 #endif /* NOSPL */
5822     }
5823
5824   doput:
5825
5826     if (pv[SND_SHH].ival > 0) {         /* SEND /QUIET... */
5827         fdispla = 0;
5828         debug(F101,"ftp put display","",fdispla);
5829     } else {
5830         displa = 1;
5831         if (ftp_deb)
5832           fdispla = XYFD_B;
5833     }
5834
5835 #ifdef PUTARRAY                         /* SEND /ARRAY... */
5836     if (pv[SND_ARR].ival > 0) {
5837         if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */
5838         if (range[0] == -1)             /* If low end of range not specified */
5839           range[0] = 1;                 /* default to 1 */
5840         if (range[1] == -1)             /* If high not specified */
5841           range[1] = a_dim[arrayx];     /* default to size of array */
5842         if ((range[0] < 0) ||           /* Check range */
5843             (range[0] > a_dim[arrayx]) ||
5844             (range[1] < range[0]) ||
5845             (range[1] > a_dim[arrayx])) {
5846             printf("?Bad array range - [%d:%d]\n",range[0],range[1]);
5847             x = -9;
5848             goto xputx;
5849         }
5850         sndarray = ap;                  /* Array pointer */
5851         sndxin = arrayx;                /* Array index */
5852         sndxlo = range[0];              /* Array range */
5853         sndxhi = range[1];
5854         sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE);
5855         if (!asnambuf[0])
5856           ckstrncpy(asnambuf,sndxnam,CKMAXPATH);
5857         cmarg = "";
5858     }
5859 #endif /* PUTARRAY */
5860
5861     moving = 0;
5862
5863     if (pv[SND_ARR].ival < 1) {         /* File selection & disposition... */
5864         if (pv[SND_DEL].ival > 0)       /* /DELETE was specified */
5865           moving = 1;
5866         if (pv[SND_AFT].ival > 0)       /* Copy SEND criteria */
5867           ckstrncpy(sndafter,pv[SND_AFT].sval,19);
5868         if (pv[SND_BEF].ival > 0)
5869           ckstrncpy(sndbefore,pv[SND_BEF].sval,19);
5870         if (pv[SND_NAF].ival > 0)
5871           ckstrncpy(sndnafter,pv[SND_NAF].sval,19);
5872         if (pv[SND_NBE].ival > 0)
5873           ckstrncpy(sndnbefore,pv[SND_NBE].sval,19);
5874         if (pv[SND_EXC].ival > 0)
5875           makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT);
5876         if (pv[SND_SMA].ival > -1)
5877           sndsmaller = pv[SND_SMA].wval;
5878         if (pv[SND_LAR].ival > -1)
5879           sndlarger = pv[SND_LAR].wval;
5880         if (pv[SND_NAM].ival > -1)
5881           x_cnv = pv[SND_NAM].ival;
5882         if (pv[SND_USN].ival > -1)
5883           x_usn = pv[SND_USN].ival;
5884         if (pv[SND_ERR].ival > -1)
5885           puterror = pv[SND_ERR].ival;
5886
5887 #ifdef DOUPDATE
5888         if (pv[SND_UPD].ival > 0) {
5889             if (x_usn) {
5890                 printf("?Conflicting switches: /UPDATE /UNIQUE\n");
5891                 x = -9;
5892                 goto xputx;
5893             }
5894             putflags |= PUT_UPD;
5895             ftp_dates |= 2;
5896         }
5897 #ifdef COMMENT
5898         /* This works but it's useless, maybe dangerous */
5899         if (pv[SND_DIF].ival > 0) {
5900             if (x_usn) {
5901                 printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n");
5902                 x = -9;
5903                 goto xputx;
5904             }
5905             putflags |= PUT_DIF;
5906             ftp_dates |= 2;
5907         }
5908 #endif /* COMMENT */
5909 #endif /* DOUPDATE */
5910
5911         if (pv[SND_SIM].ival > 0)
5912           putflags |= PUT_SIM;
5913
5914         if (pv[SND_PRM].ival > -1) {
5915 #ifdef UNIX
5916             if (x_usn) {
5917                 printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n");
5918                 x = -9;
5919                 goto xputx;
5920             }
5921             x_prm = pv[SND_PRM].ival;
5922 #else /* UNIX */
5923             printf("?/PERMISSIONS switch is not supported\n");
5924 #endif /* UNIX */
5925         }
5926 #ifdef FTP_RESTART
5927         if (pv[SND_RES].ival > 0) {
5928             if (!sizeok) {
5929                 printf("?PUT /RESTART can't be used because SIZE disabled.\n");
5930                 x = -9;
5931                 goto xputx;
5932             }
5933             if (x_usn || putflags) {
5934                 printf("?Conflicting switches: /RECOVER %s\n",
5935                        x_usn && putflags ? "/UNIQUE /UPDATE" :
5936                        (x_usn ? "/UNIQUE" : "/UPDATE")
5937                        );
5938                 x = -9;
5939                 goto xputx;
5940             }
5941 #ifndef NOCSETS
5942             if (x_xla &&
5943                 (x_csl == FC_UCS2 ||
5944                  x_csl == FC_UTF8 ||
5945                  x_csr == FC_UCS2 ||
5946                  x_csr == FC_UTF8)) {
5947                 printf("?/RECOVER can not be used with Unicode translation\n");
5948                 x = -9;
5949                 goto xputx;
5950             }
5951 #endif /* NOCSETS */
5952             putflags = PUT_RES;
5953         }
5954 #endif /* FTP_RESTART */
5955     }
5956     debug(F101,"ftp PUT restart","",putflags & PUT_RES);
5957     debug(F101,"ftp PUT update","",putflags & PUT_UPD);
5958
5959 #ifdef PIPESEND
5960     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
5961         if (!pv[SND_FLT].sval) {
5962             sndfilter = NULL;
5963         } else {
5964             sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1);
5965             if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */
5966         }
5967         debug(F110,"ftp put /FILTER", sndfilter, 0);
5968     }
5969     if (sndfilter || pipesend)          /* No /UPDATE or /RESTART */
5970       if (putflags)                     /* with pipes or filters */
5971         putflags = 0;
5972 #endif /* PIPESEND */
5973
5974     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
5975     filcnt = 0;
5976     pktnum = 0;
5977     spackets = 0L;
5978
5979     if (wild)                           /* (is this necessary?) */
5980       cx = FTP_MPU;
5981
5982     t0 = gmstimer();                    /* Record starting time */
5983
5984     done = 0;                           /* Loop control */
5985     cancelgroup = 0;
5986
5987     cdlevel = 0;
5988     cdsimlvl = 0;
5989     while (!done && !cancelgroup) {     /* Loop for all files */
5990                                         /* or until canceled. */
5991 #ifdef FTP_PROXY
5992         /*
5993            If we are using a proxy, we don't use the local file list;
5994            instead we use the list on the remote machine which we want
5995            sent to someone else, and we use remglob() to get the names.
5996            But in that case we shouldn't even be executing this routine;
5997            see ftp_mput().
5998         */
5999 #endif /* FTP_PROXY */
6000
6001         cancelfile = 0;
6002         x = gnfile();                   /* Get next file from list(s) */
6003
6004         if (x == 0)                     /* (see gnfile() comments...) */
6005           x = gnferror;
6006         debug(F111,"FTP PUT gnfile",filnam,x);
6007         debug(F111,"FTP PUT binary",filnam,binary);
6008
6009         switch (x) {
6010           case 1:                       /* File to send */
6011             s2 = asnambuf;
6012 #ifndef NOSPL
6013             if (asnambuf[0]) {          /* As-name */
6014                 int n; char *p;         /* to be evaluated... */
6015                 n = TMPBUFSIZ;
6016                 p = tmpbuf;
6017                 zzstring(asnambuf,&p,&n);
6018                 s2 = tmpbuf;
6019                 debug(F110,"ftp put asname",s2,0);
6020             }
6021 #endif /* NOSPL */
6022             rc = putfile(cx,            /* Function (PUT, APPEND) */
6023                     filnam, s2,         /* Name to send, as-name */
6024                     forcetype, moving,  /* Parameters from switches... */
6025                     snd_move, snd_rename, srv_renam,
6026                     x_cnv, x_usn, xfiletype, x_prm,
6027 #ifndef NOCSETS
6028                     x_csl, (!x_xla ? -1 : x_csr),
6029 #else
6030                     -1, -1,
6031 #endif /* NOCSETS */
6032                     putflags
6033                     );
6034             debug(F111,"ftp put putfile rc",filnam,rc);
6035             debug(F111,"ftp put putfile cancelfile",filnam,cancelfile);
6036             debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup);
6037             if (rc > -1) {
6038                 good++;
6039                 status = 1;
6040             }
6041             if (cancelfile)
6042               continue;
6043             if (rc < 0) {
6044                 ftp_fai++;
6045                 if (puterror) {
6046                     status = 0;
6047                     printf("?Fatal upload error: %s\n",filnam);
6048                     done++;
6049                 }
6050             }
6051             continue;
6052           case 0:                       /* No more files, done */
6053             done++;
6054             continue;
6055           case -1:
6056             printf("?%s: file not found - \"%s\"\n",
6057                    puterror ? "Fatal" : "Warning",
6058                    filnam
6059                    );
6060             if (puterror) {
6061                 status = 0;
6062                 done++;
6063                 break;
6064             }
6065             continue;
6066           case -2:
6067             if (puterror) {
6068                 printf("?Fatal: file not found - \"%s\"\n", filnam);
6069                 status = 0;
6070                 done++;
6071                 break;
6072             }
6073             continue;                   /* Not readable, keep going */
6074           case -3:
6075             if (puterror) {
6076                 printf("?Fatal: Read access denied - \"%s\"\n", filnam);
6077                 status = 0;
6078                 done++;
6079                 break;
6080             }
6081             printf("?Warning access denied - \"%s\"\n", filnam);
6082             continue;
6083 #ifdef COMMENT
6084           case -4:                      /* Canceled */
6085             done++;
6086             break;
6087 #endif /* COMMENT */
6088           case -5:
6089             printf("?Too many files match\n");
6090             done++;
6091             break;
6092           case -6:
6093             if (good < 1)
6094               printf("?No files selected\n");
6095             done++;
6096             break;
6097           default:
6098             printf("?getnextfile() - unknown failure\n");
6099             done++;
6100         }
6101     }
6102     if (cdlevel > 0) {
6103         while (cdlevel--) {
6104             if (cdsimlvl) {
6105                 cdsimlvl--;
6106             } else if (!doftpcdup())
6107               break;
6108         }
6109     }
6110     if (status > 0) {
6111         if (cancelgroup)
6112           status = 0;
6113         else if (cancelfile && good < 1)
6114           status = 0;
6115     }
6116     success = status;
6117     x = success;
6118
6119   xputx:
6120     if (x > -1) {
6121 #ifdef GFTIMER
6122         t1 = gmstimer();                /* End time */
6123         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6124         if (!sec) sec = 0.001;
6125         fptsecs = sec;
6126 #else
6127         sec = (t1 - t0) / 1000;
6128         if (!sec) sec = 1;
6129 #endif /* GFTIMER */
6130         tfcps = (long) (tfc / sec);
6131         tsecs = (int)sec;
6132         lastxfer = W_FTP|W_SEND;
6133         xferstat = success;
6134         if (dpyactive)
6135           ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6136     }
6137     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
6138         if (pv[i].sval)
6139           free(pv[i].sval);
6140     }
6141     ftreset();                          /* Undo switch effects */
6142     dpyactive = 0;
6143     return(x);
6144 }
6145
6146
6147 static char ** mgetlist = NULL;         /* For MGET */
6148 static int mgetn = 0, mgetx = 0;
6149 static char xtmpbuf[4096];
6150
6151 /*
6152   c m d l i n g e t
6153
6154   Get files specified by -g command-line option.
6155   File list is set up in cmlist[] by ckuusy.c; nfils is length of list.
6156 */
6157 int
6158 cmdlinget(stay) int stay; {
6159     int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0;
6160     int lcs = -1, rcs = -1, xlate = 0;
6161     int first = 1;
6162     int mget = 1;
6163     int nc;
6164     char * s, * s2, * s3;
6165     ULONG t0, t1;                       /* Times for stats */
6166 #ifdef GFTIMER
6167     CKFLOAT sec;
6168 #else
6169     int sec = 0;
6170 #endif /* GFTIMER */
6171
6172     if (quiet) {                        /* -q really means quiet */
6173         displa = 0;
6174         fdispla = 0;
6175     } else {
6176         displa = 1;
6177         fdispla = XYFD_B;
6178     }
6179     testing = 0;
6180     dpyactive = 0;
6181     out2screen = 0;
6182     what = W_FTP|W_RECV;
6183     mgetmethod = 0;
6184     mgetforced = 0;
6185
6186     havetype = 0;
6187     havesize = (CK_OFF_T)-1;
6188     makestr(&havemdtm,NULL);
6189
6190     if (ftp_fnc < 0)
6191       ftp_fnc = fncact;
6192
6193 #ifndef NOSPL
6194     cmd_quoting = 0;
6195 #endif /* NOSPL */
6196     debug(F101,"ftp cmdlinget nfils","",nfils);
6197
6198     if (ftp_cnv == CNV_AUTO) {          /* Name conversion is auto */
6199         if (alike) {                    /* If server & client are alike */
6200             nc = 0;                     /* no conversion */
6201         } else {                        /* If they are different */
6202             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6203               nc = -1;                  /* only minimal conversions needed */
6204             else                        /* otherwise */
6205               nc = 1;                   /* full conversion */
6206         }
6207     } else                              /* Not auto - do what user said */
6208       nc = ftp_cnv;
6209
6210     if (nfils < 1)
6211       doexit(BAD_EXIT,-1);
6212
6213     t0 = gmstimer();                    /* Starting time for this batch */
6214
6215 #ifndef NOCSETS
6216     if (xlate) {                        /* SET FTP CHARACTER-SET-TRANSLATION */
6217         lcs = ftp_csl;                  /* Local charset */
6218         if (lcs < 0) lcs = fcharset;
6219         if (lcs < 0) xlate = 0;
6220     }
6221     if (xlate) {                        /* Still ON? */
6222         rcs = ftp_csx;                  /* Remote (Server) charset */
6223         if (rcs < 0) rcs = ftp_csr;
6224         if (rcs < 0) xlate = 0;
6225     }
6226 #endif /* NOCSETS */
6227     /*
6228       If we have only one file and it is a directory, then we ask for a
6229       listing of its contents, rather than retrieving the directory file
6230       itself.  This is what (e.g.) Netscape does.
6231     */
6232     if (nfils == 1) {
6233         if (doftpcwd((char *)cmlist[mgetx],-1)) {
6234             /* If we can CD to it, it must be a directory */
6235             if (recursive) {
6236                 cmlist[mgetx] = "*";
6237             } else {
6238                 status =
6239                   (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0);
6240                 done = 1;
6241             }
6242         }
6243     }
6244 /*
6245   The following is to work around UNIX servers which, when given a command
6246   like "NLST path/blah" (not wild) returns the basename without the path.
6247 */
6248     if (!done && servertype == SYS_UNIX && nfils == 1) {
6249         mget = iswild(cmlist[mgetx]);
6250     }
6251     if (!mget && !done) {               /* Invoked by command-line FTP URL */
6252         if (ftp_deb)
6253           printf("DOING GET...\n");
6254         done++;
6255         cancelfile = 0;                 /* This file not canceled yet */
6256         s = cmlist[mgetx];
6257         rc = 0;                         /* Initial return code */
6258         fsize = (CK_OFF_T)-1;
6259         if (sizeok) {
6260             x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */
6261             if (x == REPLY_COMPLETE)
6262               fsize = ckatofs(&ftp_reply_str[4]);
6263         }
6264         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6265         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6266
6267         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6268         s2 = tmpbuf;
6269
6270         /* If local file already exists, take collision action */
6271
6272         if (zchki(s2) > -1) {
6273             switch (ftp_fnc) {
6274               case XYFX_A:              /* Append */
6275                 append = 1;
6276                 break;
6277               case XYFX_R:              /* Rename */
6278               case XYFX_B: {            /* Backup */
6279                   char * p = NULL;
6280                   int x = -1;
6281                   znewn(s2,&p);         /* Make unique name */
6282                   debug(F110,"ftp cmdlinget znewn",p,0);
6283                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6284                       x = zrename(s2,p);
6285                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6286                   } else {              /* Rename incoming file */
6287                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6288                       s2 = tmpbuf;
6289                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6290                   }
6291                   if (x < 0) {
6292                       printf("?Backup/Rename failed\n");
6293                       return(success = 0);
6294                   }
6295                   break;
6296               }
6297               case XYFX_D:              /* Discard */
6298                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6299                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6300                 tlog(F100," refused: name","",0);
6301                 debug(F110,"ftp cmdlinget skip name",s2,0);
6302                 goto xclget;
6303
6304               case XYFX_X:              /* Overwrite */
6305               case XYFX_U:              /* Update (already handled above) */
6306               case XYFX_M:              /* ditto */
6307                 break;
6308             }
6309         }
6310         rc = getfile(s,                 /* Remote name */
6311                      s2,                /* Local name */
6312                      0,                 /* Recover/Restart */
6313                      append,            /* Append */
6314                      NULL,              /* Pipename */
6315                      0,                 /* Translate charsets */
6316                      -1,                /* File charset (none) */
6317                      -1                 /* Server charset (none) */
6318                      );
6319         debug(F111,"ftp cmdlinget rc",s,rc);
6320         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6321         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6322
6323         if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */
6324             rc = getfile(&s[1],         /* Remote name without leading '/' */
6325                          s2,            /* Local name */
6326                          0,             /* Recover/Restart */
6327                          append,        /* Append */
6328                          NULL,          /* Pipename */
6329                          0,             /* Translate charsets */
6330                          -1,            /* File charset (none) */
6331                          -1             /* Server charset (none) */
6332                          );
6333         if (rc > -1) {
6334             good++;
6335             status = 1;
6336         }
6337         if (cancelfile)
6338           goto xclget;
6339         if (rc < 0) {
6340             ftp_fai++;
6341 #ifdef FTP_TIMEOUT
6342             if (ftp_timed_out)
6343               status = 0;
6344 #endif  /* FTP_TIMEOUT */
6345             if (geterror) {
6346                 status = 0;
6347                 done++;
6348             }
6349         }
6350     }
6351     if (ftp_deb && !done)
6352       printf("DOING MGET...\n");
6353     while (!done && !cancelgroup) {
6354         cancelfile = 0;                 /* This file not canceled yet */
6355         s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6356         if (!s) s = "";
6357         if (!*s) {
6358             first = 1;
6359             mgetx++;
6360             if (mgetx < nfils)
6361               s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0);
6362             else
6363               s = NULL;
6364             debug(F111,"ftp cmdlinget remote_files B",s,0);
6365             if (!s) {
6366                 done = 1;
6367                 break;
6368             }
6369         }
6370         /*
6371           The semantics of NLST are ill-defined.  Suppose we have just sent
6372           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
6373           /path/bar, etc.  But some send back only foo and bar, and subsequent
6374           RETR commands based on the pathless names are not going to work.
6375         */
6376         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
6377             if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) {
6378                 int len, left = 4096;
6379                 char * tmp = xtmpbuf;
6380                 len = s3 - cmlist[mgetx] + 1;
6381                 ckstrncpy(tmp,cmlist[mgetx],left);
6382                 tmp += len;
6383                 left -= len;
6384                 ckstrncpy(tmp,s,left);
6385                 s = xtmpbuf;
6386                 debug(F111,"ftp cmdlinget remote_files X",s,0);
6387             }
6388         }
6389         first = 0;                      /* Not first any more */
6390
6391         debug(F111,"ftp cmdlinget havetype",s,havetype);
6392         if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */
6393             debug(F110,"ftp cmdlinget not-a-file",s,0);
6394             continue;
6395         }
6396         rc = 0;                         /* Initial return code */
6397         if (havesize > (CK_OFF_T)-1) {  /* Already have file size? */
6398             fsize = havesize;
6399         } else {                        /* No - must ask server */
6400             /*
6401               Prior to sending the NLST command we necessarily put the
6402               server into ASCII mode.  We must now put it back into the
6403               the requested mode so the upcoming SIZE command returns
6404               right kind of size; this is especially important for
6405               GET /RECOVER; otherwise the server returns the "ASCII" size
6406               of the file, rather than its true size.
6407             */
6408             changetype(ftp_typ,0);      /* Change to requested type */
6409             fsize = (CK_OFF_T)-1;
6410             if (sizeok) {
6411                 x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm);
6412                 if (x == REPLY_COMPLETE)
6413                   fsize = ckatofs(&ftp_reply_str[4]);
6414             }
6415         }
6416         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
6417         debug(F111,"ftp cmdlinget filnam",filnam,fsize);
6418
6419         nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */
6420         s2 = tmpbuf;
6421
6422         /* If local file already exists, take collision action */
6423
6424         if (zchki(s2) > -1) {
6425             switch (ftp_fnc) {
6426               case XYFX_A:              /* Append */
6427                 append = 1;
6428                 break;
6429               case XYFX_R:              /* Rename */
6430               case XYFX_B: {            /* Backup */
6431                   char * p = NULL;
6432                   int x = -1;
6433                   znewn(s2,&p);         /* Make unique name */
6434                   debug(F110,"ftp cmdlinget znewn",p,0);
6435                   if (ftp_fnc == XYFX_B) { /* Backup existing file */
6436                       x = zrename(s2,p);
6437                       debug(F111,"ftp cmdlinget backup zrename",p,x);
6438                   } else {              /* Rename incoming file */
6439                       x = ckstrncpy(tmpbuf,p,CKMAXPATH+1);
6440                       s2 = tmpbuf;
6441                       debug(F111,"ftp cmdlinget rename incoming",p,x);
6442                   }
6443                   if (x < 0) {
6444                       printf("?Backup/Rename failed\n");
6445                       return(success = 0);
6446                   }
6447                   break;
6448               }
6449               case XYFX_D:      /* Discard */
6450                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
6451                 ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s);
6452                 tlog(F100," refused: name","",0);
6453                 debug(F110,"ftp cmdlinget skip name",s2,0);
6454                 continue;
6455               case XYFX_X:              /* Overwrite */
6456               case XYFX_U:              /* Update (already handled above) */
6457               case XYFX_M:              /* ditto */
6458                 break;
6459             }
6460         }
6461                                         /* ^^^ ADD CHARSET STUFF HERE ^^^ */
6462         rc = getfile(s,                 /* Remote name */
6463                      s2,                /* Local name */
6464                      0,                 /* Recover/Restart */
6465                      append,            /* Append */
6466                      NULL,              /* Pipename */
6467                      0,                 /* Translate charsets */
6468                      -1,                /* File charset (none) */
6469                      -1                 /* Server charset (none) */
6470                      );
6471         debug(F111,"ftp cmdlinget rc",s,rc);
6472         debug(F111,"ftp cmdlinget cancelfile",s,cancelfile);
6473         debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup);
6474
6475         if (rc > -1) {
6476             good++;
6477             status = 1;
6478         }
6479         if (cancelfile)
6480           continue;
6481         if (rc < 0) {
6482             ftp_fai++;
6483 #ifdef FTP_TIMEOUT
6484             if (ftp_timed_out)
6485               status = 0;
6486 #endif  /* FTP_TIMEOUT */
6487             if (geterror) {
6488                 status = 0;
6489                 done++;
6490             }
6491         }
6492     }
6493
6494   xclget:
6495     if (cancelgroup)
6496       mlsreset();
6497     if (status > 0) {
6498         if (cancelgroup)
6499           status = 0;
6500         else if (cancelfile && good < 1)
6501           status = 0;
6502     }
6503     success = status;
6504
6505 #ifdef GFTIMER
6506     t1 = gmstimer();                    /* End time */
6507     sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
6508     if (!sec) sec = 0.001;
6509     fptsecs = sec;
6510 #else
6511     sec = (t1 - t0) / 1000;
6512     if (!sec) sec = 1;
6513 #endif /* GFTIMER */
6514
6515     tfcps = (long) (tfc / sec);
6516     tsecs = (int)sec;
6517     lastxfer = W_FTP|W_RECV;
6518     xferstat = success;
6519     if (dpyactive)
6520       ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
6521     if (!stay)
6522       doexit(success ? GOOD_EXIT : BAD_EXIT, -1);
6523     return(success);
6524 }
6525
6526 /*  d o f t p g e t  --  Parse and execute GET, MGET, MDELETE, ...  */
6527
6528 /*
6529   Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use
6530   zstrdat() to convert to UTC-based time_t.  But it doesn't make sense from
6531   the user-interface perspective, since the server's directory listings show
6532   its own local times and since we don't know what timezone it's in, there's
6533   no way to reconcile our local times with the server's.
6534 */
6535 int
6536 doftpget(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
6537     struct FDB fl, sw, cm;
6538     int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0;
6539     int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0;
6540     int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0;
6541     int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0;
6542     int moving = 0, deleting = 0, toscreen = 0, haspath = 0;
6543     int gotsize = 0;
6544     int matchdot = 0;
6545     CK_OFF_T getlarger = (CK_OFF_T)-1;
6546     CK_OFF_T getsmaller = (CK_OFF_T)-1;
6547     char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL;
6548     char * src = "", * local = "";
6549     char * pat = "";
6550
6551     int x_csl = -1, x_csr = -1;         /* Local and remote charsets */
6552     int x_xla = 0;
6553     char c;                             /* Worker char */
6554     ULONG t0 = 0L, t1;                  /* Times for stats */
6555 #ifdef GFTIMER
6556     CKFLOAT sec;
6557 #else
6558     int sec = 0;
6559 #endif /* GFTIMER */
6560
6561     struct stringint pv[SND_MAX+1];    /* Temporary array for switch values */
6562
6563     success = 0;                        /* Assume failure */
6564     forcetype = 0;                      /* No /TEXT or /BINARY given yet */
6565     restart = 0;                        /* No restart yet */
6566     out2screen = 0;                     /* No TO-SCREEN switch given yet */
6567     mgetmethod = 0;                     /* No NLST or MLSD switch yet */
6568     mgetforced = 0;
6569
6570     g_displa = fdispla;
6571     if (ftp_dis > -1)
6572       fdispla = ftp_dis;
6573
6574     x_cnv = ftp_cnv;                    /* Filename conversion */
6575     if (x_cnv == CNV_AUTO) {            /* Name conversion is auto */
6576         if (alike) {                    /* If server & client are alike */
6577             x_cnv = 0;                  /* no conversion */
6578         } else {                        /* If they are different */
6579             if (servertype == SYS_UNIX || servertype == SYS_WIN32)
6580               x_cnv = -1;               /* only minimal conversions needed */
6581             else                        /* otherwise */
6582               x_cnv = 1;                /* full conversion */
6583         }
6584     } else                              /* Not auto - do what user said */
6585       x_cnv = ftp_cnv;
6586
6587     x_prm = ftp_prm;                    /* Permissions */
6588     if (x_prm == SET_AUTO)              /* Permissions AUTO */
6589       x_prm = alike;
6590
6591 #ifndef NOCSETS
6592     x_csr = ftp_csr;                    /* Inherit global server charset */
6593     x_csl = ftp_csl;                    /* Inherit global local charset */
6594     if (x_csl < 0)                      /* If none, use current */
6595       x_csl = fcharset;                 /* file character-set. */
6596     x_xla = ftp_xla;                    /* Translation On/Off */
6597 #endif /* NOCSETS */
6598
6599     geterror = ftp_err;                 /* Inherit global error action. */
6600     asnambuf[0] = NUL;                  /* No as-name yet. */
6601     pipesave = pipesend;
6602     pipesend = 0;
6603
6604     havetype = 0;
6605     havesize = (CK_OFF_T)-1;
6606     makestr(&havemdtm,NULL);
6607
6608     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
6609         ftp_typ = g_ftp_typ;
6610         /* g_ftp_typ = -1; */
6611     }
6612     for (i = 0; i <= SND_MAX; i++) {    /* Initialize switch values */
6613         pv[i].sval = NULL;              /* to null pointers */
6614         pv[i].ival = -1;                /* and -1 int values */
6615         pv[i].wval = (CK_OFF_T)-1;      /* and -1 wide values */
6616     }
6617     zclose(ZMFILE);                     /* In case it was left open */
6618
6619     x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */
6620
6621     if (fp_nml) {                       /* Reset /NAMELIST */
6622         if (fp_nml != stdout)
6623           fclose(fp_nml);
6624         fp_nml = NULL;
6625     }
6626     makestr(&ftp_nml,NULL);
6627
6628     /* Initialize list of remote filespecs */
6629
6630     if (!mgetlist) {
6631         mgetlist = (char **)malloc(MGETMAX * sizeof(char *));
6632         if (!mgetlist) {
6633             printf("?Memory allocation failure - MGET list\n");
6634             return(-9);
6635         }
6636         for (i = 0; i < MGETMAX; i++)
6637           mgetlist[i] = NULL;
6638     }
6639     mgetn = 0;                          /* Number of mget arguments */
6640     mgetx = 0;                          /* Current arg */
6641
6642     if (who == 0) {                     /* Called with unprefixed command */
6643         if (cx == XXGET || cx == XXREGET || cx == XXRETR)
6644           getone++;
6645         switch (cx) {
6646           case XXREGET: pv[SND_RES].ival = 1; break;
6647           case XXRETR:  pv[SND_DEL].ival = 1; break;
6648           case XXGET:
6649           case XXMGET:  mget++; break;
6650         }
6651     } else {                            /* FTP command */
6652         if (cx == FTP_GET || cx == FTP_RGE)
6653           getone++;
6654         switch (cx) {
6655           case FTP_DEL:                 /* (fall thru on purpose) */
6656           case FTP_MDE: mdel++;         /* (ditto) */
6657           case FTP_GET:                 /* (ditto) */
6658           case FTP_MGE: mget++; break;
6659           case FTP_RGE: pv[SND_RES].ival = 1; break;
6660         }
6661     }
6662     cmfdbi(&sw,                         /* First FDB - command switches */
6663            _CMKEY,                      /* fcode */
6664            "Remote filename;\n or switch", /* hlpmsg */
6665            "",                          /* default */
6666            "",                          /* addtl string data */
6667            mdel ? ndelswi : ngetswi,    /* addtl numeric data 1: tbl size */
6668            4,                           /* addtl numeric data 2: 4 = cmswi */
6669            xxstring,                    /* Processing function */
6670            mdel ? delswi : getswi,      /* Keyword table */
6671            &fl                          /* Pointer to next FDB */
6672            );
6673     cmfdbi(&fl,                         /* 2nd FDB - remote filename */
6674            _CMFLD,                      /* fcode */
6675            "",                          /* hlpmsg */
6676            "",                          /* default */
6677            "",                          /* addtl string data */
6678            0,                           /* addtl numeric data 1 */
6679            0,                           /* addtl numeric data 2 */
6680            xxstring,
6681            NULL,
6682            &cm
6683            );
6684     cmfdbi(&cm,                         /* 3rd FDB - Confirmation */
6685            _CMCFM,                      /* fcode */
6686            "",                          /* hlpmsg */
6687            "",                          /* default */
6688            "",                          /* addtl string data */
6689            0,                           /* addtl numeric data 1 */
6690            0,                           /* addtl numeric data 2 */
6691            NULL,
6692            NULL,
6693            NULL
6694            );
6695
6696     while (1) {                         /* Parse 0 or more switches */
6697         x = cmfdb(&sw);                 /* Parse something */
6698         debug(F101,"ftp get cmfdb","",x);
6699         if (x < 0)                      /* Error */
6700           goto xgetx;                   /* or reparse needed */
6701         if (cmresult.fcode != _CMKEY)   /* Break out of loop if not a switch */
6702           break;
6703         c = cmgbrk();                   /* Get break character */
6704         getval = (c == ':' || c == '='); /* to see how they ended the switch */
6705         if (getval && !(cmresult.kflags & CM_ARG)) {
6706             printf("?This switch does not take arguments\n");
6707             x = -9;
6708             goto xgetx;
6709         }
6710         n = cmresult.nresult;           /* Numeric result = switch value */
6711         debug(F101,"ftp get switch","",n);
6712
6713         if (!getval && (cmgkwflgs() & CM_ARG)) {
6714             printf("?This switch requires an argument\n");
6715             x = -9;
6716             goto xgetx;
6717         }
6718         switch (n) {                    /* Process the switch */
6719           case SND_ASN:                 /* /AS-NAME: */
6720             debug(F101,"ftp get /as-name getval","",getval);
6721             if (!getval) break;
6722             if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) {
6723                 if (x == -3) {
6724                     printf("?name required\n");
6725                     x = -9;
6726                 }
6727                 goto xgetx;
6728             }
6729             s = brstrip(s);
6730             if (!*s) s = NULL;
6731             makestr(&(pv[n].sval),s);
6732             pv[n].ival = 1;
6733             break;
6734
6735           case SND_BIN:                 /* /BINARY */
6736           case SND_TXT:                 /* /TEXT or /ASCII */
6737           case SND_TEN:                 /* /TENEX */
6738             pv[SND_BIN].ival = 0;
6739             pv[SND_TXT].ival = 0;
6740             pv[SND_TEN].ival = 0;
6741             pv[n].ival = 1;
6742             break;
6743
6744 #ifdef PUTPIPE
6745           case SND_CMD:                 /* These take no args */
6746             if (nopush) {
6747                 printf("?Sorry, system command access is disabled\n");
6748                 x = -9;
6749                 goto xgetx;
6750             }
6751 #ifdef PIPESEND
6752             else if (rcvfilter) {
6753                 printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n");
6754                 x = -9;
6755                 goto xgetx;
6756             }
6757 #endif /* PIPESEND */
6758             sw.hlpmsg = "Command, or switch"; /* Change help message */
6759             pv[n].ival = 1;             /* Just set the flag */
6760             pv[SND_ARR].ival = 0;
6761             break;
6762 #endif /* PUTPIPE */
6763
6764           case SND_SHH:                 /* /QUIET */
6765           case SND_RES:                 /* /RECOVER (reget) */
6766           case SND_NOB:                 /* /NOBACKUPFILES */
6767           case SND_DEL:                 /* /DELETE */
6768           case SND_UPD:                 /* /UPDATE */
6769           case SND_USN:                 /* /UNIQUE */
6770           case SND_NOD:                 /* /NODOTFILES */
6771           case SND_REC:                 /* /RECOVER */
6772           case SND_MAI:                 /* /TO-SCREEN */
6773             pv[n].ival = 1;             /* Just set the flag */
6774             break;
6775
6776           case SND_DIF:                 /* /DATES-DIFFER */
6777             pv[SND_COL].ival = XYFX_M;  /* Now it's a collision option */
6778             pv[n].ival = 1;
6779             break;
6780
6781           case SND_COL:                 /* /COLLISION: */
6782             if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0)
6783               goto xgetx;
6784             if (x == XYFX_M)
6785               pv[SND_DIF].ival = 1;     /* (phase this out) */
6786             pv[n].ival = x;             /* this should be sufficient */
6787             break;
6788
6789           case SND_ERR:                 /* /ERROR-ACTION */
6790             if ((x = cmkey(qorp,2,"","",xxstring)) < 0)
6791               goto xgetx;
6792             pv[n].ival = x;
6793             break;
6794
6795           case SND_EXC:                 /* Exception list */
6796             if (!getval) break;
6797             if ((x = cmfld("Pattern","",&s,xxstring)) < 0) {
6798                 if (x == -3) {
6799                     printf("?Pattern required\n");
6800                     x = -9;
6801                 }
6802                 goto xgetx;
6803             }
6804             if (s) if (!*s) s = NULL;
6805             makestr(&(pv[n].sval),s);
6806             if (pv[n].sval)
6807               pv[n].ival = 1;
6808             break;
6809
6810 #ifdef PIPESEND
6811           case SND_FLT:
6812             debug(F101,"ftp get /filter getval","",getval);
6813             if (!getval) break;
6814             if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) {
6815                 if (x == -3)
6816                   s = "";
6817                 else
6818                   goto xgetx;
6819             }
6820             s = brstrip(s);
6821             if (pv[SND_MAI].ival < 1) {
6822                 y = strlen(s);
6823                 /* Make sure they included "\v(...)" */
6824                 for (x = 0; x < y; x++) {
6825                     if (s[x] != '\\') continue;
6826                     if (s[x+1] == 'v') break;
6827                 }
6828                 if (x == y) {
6829                     printf(
6830                 "?Filter must contain a replacement variable for filename.\n"
6831                            );
6832                     x = -9;
6833                     goto xgetx;
6834                 }
6835             }
6836             if (*s) {
6837                 pv[n].ival = 1;
6838                 makestr(&(pv[n].sval),s);
6839             } else {
6840                 pv[n].ival = 0;
6841                 makestr(&(pv[n].sval),NULL);
6842             }
6843             break;
6844 #endif /* PIPESEND */
6845
6846           case SND_NAM:
6847             if (!getval) break;
6848             if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0)
6849               goto xgetx;
6850             debug(F101,"ftp get /filenames","",x);
6851             pv[n].ival = x;
6852             break;
6853
6854           case SND_SMA:                 /* Smaller / larger than */
6855           case SND_LAR: {
6856               CK_OFF_T y;
6857               if (!getval) break;
6858               if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0)
6859                 goto xgetx;
6860               pv[n].wval = y;
6861               break;
6862           }
6863           case SND_FIL:                 /* Name of file containing filnames */
6864             if (!getval) break;
6865             if ((x = cmifi("Name of file containing list of filenames",
6866                                "",&s,&y,xxstring)) < 0) {
6867                 if (x == -3) {
6868                     printf("?Filename required\n");
6869                     x = -9;
6870                 }
6871                 goto xgetx;
6872             } else if (y && iswild(s)) {
6873                 printf("?Wildcards not allowed BBB\n");
6874                 x = -9;
6875                 goto xgetx;
6876             }
6877             if (s) if (!*s) s = NULL;
6878             makestr(&(pv[n].sval),s);
6879             if (pv[n].sval)
6880               pv[n].ival = 1;
6881             break;
6882
6883           case SND_MOV:                 /* MOVE after */
6884           case SND_REN:                 /* RENAME after */
6885           case SND_SRN: {               /* SERVER-RENAME */
6886               char * m = "";
6887               switch (n) {
6888                 case SND_MOV:
6889                   m =
6890                    "Device and/or directory for incoming file after reception";
6891                   break;
6892                 case SND_REN:
6893                   m = "New name for incoming file after reception";
6894                   break;
6895                 case SND_SRN:
6896                   m = "New name for source file on server after reception";
6897                   break;
6898               }
6899               if (!getval) break;
6900               if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) {
6901                   if (x == -3) {
6902                       printf("%s\n", n == SND_MOV ?
6903                              "?Destination required" :
6904                              "?New name required"
6905                              );
6906                       x = -9;
6907                   }
6908                   goto xgetx;
6909               }
6910               makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6911               pv[n].ival = (pv[n].sval) ? 1 : 0;
6912               break;
6913           }
6914 #ifndef NOCSETS
6915           case SND_CSL:                 /* Local character set */
6916           case SND_CSR:                 /* Remote (server) charset */
6917             if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0)
6918               return((x == -3) ? -2 : x);
6919             if (n == SND_CSL)
6920               x_csl = x;
6921             else
6922               x_csr = x;
6923             x_xla = 1;                  /* Overrides global OFF setting */
6924             break;
6925
6926           case SND_XPA:                 /* Transparent */
6927             x_xla =  0;
6928             x_csr = -1;
6929             x_csl = -1;
6930             break;
6931 #endif /* NOCSETS */
6932
6933           case SND_NML:
6934             if ((x = cmofi("Local filename","-",&s,xxstring)) < 0)
6935               goto xgetx;
6936             makestr(&ftp_nml,s);
6937             break;
6938
6939           case SND_PAT:                 /* /PATTERN: */
6940             if (!getval) break;
6941             if ((x = cmfld("Pattern","*", &s, xxstring)) < 0)
6942               goto xgetx;
6943             makestr(&(pv[n].sval),*s ? brstrip(s) : NULL);
6944             pv[n].ival = (pv[n].sval) ? 1 : 0;
6945             break;
6946
6947           case SND_NLS:                 /* /NLST */
6948             pv[n].ival = 1;             /* Use NLST */
6949             pv[SND_MLS].ival = 0;       /* Don't use MLSD */
6950             break;
6951
6952           case SND_MLS:                 /* /MLSD */
6953             pv[n].ival = 1;             /* Use MLSD */
6954             pv[SND_NLS].ival = 0;       /* Don't use NLST */
6955             break;
6956
6957           default:                      /* /AFTER, /PERMISSIONS, etc... */
6958             printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf);
6959             x = -9;
6960             goto xgetx;
6961         }
6962     }
6963     line[0] = NUL;
6964     cmarg = line;
6965     cmarg2 = asnambuf;
6966     s = line;
6967 /*
6968   For GET, we want to parse an optional as-name, like with PUT.
6969   For MGET, we must parse a list of names, and then send NLST or MLSD
6970   commands for each name separately.
6971 */
6972     switch (cmresult.fcode) {           /* How did we get out of switch loop */
6973       case _CMFLD:                      /* Field */
6974         if (!getone) {
6975             s = brstrip(cmresult.sresult);
6976             makestr(&(mgetlist[mgetn++]),s);
6977             while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) {
6978                 if (x < 0)
6979                   goto xgetx;
6980                 makestr(&(mgetlist[mgetn++]),brstrip(s));
6981                 if (mgetn >= MGETMAX) {
6982                     printf("?Too many items in MGET list\n");
6983                     goto xgetx;
6984                 }
6985             }
6986             if ((x = cmcfm()) < 0)
6987               goto xgetx;
6988         } else {
6989             s = brstrip(cmresult.sresult);
6990             ckstrncpy(line,s,LINBUFSIZ);
6991             if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0)
6992               if (x != -3)
6993                 goto xgetx;
6994             s = brstrip(s);
6995             ckstrncpy(asnambuf,s,CKMAXPATH+1);
6996             if ((x = cmcfm()) < 0)
6997               goto xgetx;
6998         }
6999         break;
7000       case _CMCFM:                      /* Confirmation */
7001         break;
7002       default:
7003         printf("?Unexpected function code: %d\n",cmresult.fcode);
7004         x = -9;
7005         goto xgetx;
7006     }
7007     if (pv[SND_REC].ival > 0)           /* /RECURSIVE */
7008       recursive = 2;
7009
7010     if (pv[SND_BIN].ival > 0) {         /* /BINARY really means binary... */
7011         forcetype = 1;                  /* So skip the name-pattern match */
7012         ftp_typ = XYFT_B;               /* Set binary */
7013     } else if (pv[SND_TXT].ival > 0) {  /* Similarly for /TEXT... */
7014         forcetype = 1;
7015         ftp_typ = XYFT_T;
7016     } else if (pv[SND_TEN].ival > 0) {  /* and /TENEX*/
7017         forcetype = 1;
7018         ftp_typ = FTT_TEN;
7019     } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) {
7020         forcetype = 1;
7021         ftp_typ = binary;
7022         g_ftp_typ = binary;
7023     }
7024     if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) {
7025         char * p;
7026         p = brstrip(pv[SND_ASN].sval);  /* As-name */
7027         ckstrncpy(asnambuf,p,CKMAXPATH+1);
7028     }
7029     debug(F110,"ftp get asnambuf",asnambuf,0);
7030
7031 #ifdef PIPESEND
7032     if (pv[SND_CMD].ival > 0) {         /* /COMMAND - strip any braces */
7033         char * p;
7034         p = asnambuf;
7035         debug(F110,"GET /COMMAND before stripping",p,0);
7036         p = brstrip(p);
7037         debug(F110,"GET /COMMAND after stripping",p,0);
7038         if (!*p) {
7039             printf("?Sorry, a command to write to is required\n");
7040             x = -9;
7041             goto xgetx;
7042         }
7043         pipename = p;
7044         pipesend = 1;
7045     }
7046 #endif /* PIPESEND */
7047
7048 /* Set up /MOVE and /RENAME */
7049
7050 #ifdef COMMENT
7051     /* Conflict exists only for PUT - removed 13 Mar 2006 - fdc */
7052     if (pv[SND_DEL].ival > 0 &&
7053         (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) {
7054         printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n");
7055         x = -9;
7056         goto xgetx;
7057     }
7058 #endif  /* COMMENT */
7059 #ifdef CK_TMPDIR
7060     if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) {
7061         int len;
7062         char * p = pv[SND_MOV].sval;
7063         len = strlen(p);
7064         if (!isdir(p)) {                /* Check directory */
7065 #ifdef CK_MKDIR
7066             char * s = NULL;
7067             s = (char *)malloc(len + 4);
7068             if (s) {
7069                 strcpy(s,p);            /* safe */
7070 #ifdef datageneral
7071                 if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; }
7072 #else
7073                 if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; }
7074 #endif /* datageneral */
7075                 s[len++] = 'X';
7076                 s[len] = NUL;
7077 #ifdef NOMKDIR
7078                 x = -1;
7079 #else
7080                 x = zmkdir(s);
7081 #endif /* NOMKDIR */
7082                 free(s);
7083                 if (x < 0) {
7084                     printf("?Can't create \"%s\"\n",p);
7085                     x = -9;
7086                     goto xgetx;
7087                 }
7088             }
7089 #else
7090             printf("?Directory \"%s\" not found\n",p);
7091             x = -9;
7092             goto xgetx;
7093 #endif /* CK_MKDIR */
7094         }
7095         makestr(&rcv_move,p);
7096         moving = 1;
7097     }
7098 #endif /* CK_TMPDIR */
7099
7100     if (pv[SND_REN].ival > 0) {         /* /RENAME */
7101         char * p = pv[SND_REN].sval;
7102         if (!p) p = "";
7103         if (!*p) {
7104             printf("?New name required for /RENAME\n");
7105             x = -9;
7106             goto xgetx;
7107         }
7108         p = brstrip(p);
7109 #ifndef NOSPL
7110     /* If name given is wild, rename string must contain variables */
7111         if (mget && !getone) {
7112             char * s = tmpbuf;
7113             x = TMPBUFSIZ;
7114             zzstring(p,&s,&x);
7115             if (!strcmp(tmpbuf,p)) {
7116                 printf(
7117     "?/RENAME for file group must contain variables such as \\v(filename)\n"
7118                        );
7119                 x = -9;
7120                 goto xgetx;
7121             }
7122         }
7123 #endif /* NOSPL */
7124         renaming = 1;
7125         makestr(&rcv_rename,p);
7126         debug(F110,"FTP rcv_rename",rcv_rename,0);
7127     }
7128     if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) {
7129         printf("?Filename required but not given\n");
7130         x = -9;
7131         goto xgetx;
7132     } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) {
7133         printf("?You can't give both /LISTFILE and a remote filename\n");
7134         x = -9;
7135         goto xgetx;
7136     }
7137     CHECKCONN();                        /* Check connection */
7138
7139     if (pv[SND_COL].ival > -1)
7140       x_fnc = pv[SND_COL].ival;
7141
7142 #ifndef NOSPL
7143     /* If as-name given for MGET, as-name must contain variables */
7144     if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) {
7145         char * s = tmpbuf;
7146         x = TMPBUFSIZ;
7147         zzstring(asnambuf,&s,&x);
7148         if (!strcmp(tmpbuf,asnambuf)) {
7149             printf(
7150     "?As-name for MGET must contain variables such as \\v(filename)\n"
7151                    );
7152             x = -9;
7153             goto xgetx;
7154         }
7155     }
7156 #endif /* NOSPL */
7157
7158 /* doget: */
7159
7160     if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */
7161         fdispla = 0;
7162     } else {
7163         displa = 1;
7164         if (mdel || ftp_deb)
7165           fdispla = XYFD_B;
7166     }
7167     deleting = 0;
7168     if (pv[SND_DEL].ival > 0)           /* /DELETE was specified */
7169       deleting = 1;
7170     if (pv[SND_EXC].ival > 0)
7171       makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT);
7172     if (pv[SND_SMA].wval > -1)
7173       getsmaller = pv[SND_SMA].wval;
7174     if (pv[SND_LAR].wval > -1)
7175       getlarger = pv[SND_LAR].wval;
7176     if (pv[SND_NAM].ival > -1)
7177       x_cnv = pv[SND_NAM].ival;
7178     if (pv[SND_ERR].ival > -1)
7179       geterror = pv[SND_ERR].ival;
7180     if (pv[SND_MAI].ival > -1)
7181       toscreen = 1;
7182
7183     if (pv[SND_NLS].ival > 0) {         /* Force NLST or MLSD? */
7184         mgetmethod = SND_NLS;
7185         mgetforced = 1;
7186     } else if (pv[SND_MLS].ival > 0) {
7187         mgetmethod = SND_MLS;
7188         mgetforced = 1;
7189     }
7190
7191 #ifdef FTP_RESTART
7192     if (pv[SND_RES].ival > 0) {
7193         if (!ftp_typ) {
7194             printf("?Sorry, GET /RECOVER requires binary mode\n");
7195             x = -9;
7196             goto xgetx;
7197 #ifdef COMMENT
7198         /* Not true - the fact that the initial REST fails does not mean */
7199         /* it will fail here.  */
7200         } else if (!okrestart) {
7201             printf("WARNING: Server might not support restart...\n");
7202 #endif /* COMMENT */
7203         }
7204         restart = 1;
7205     }
7206 #endif /* FTP_RESTART */
7207
7208 #ifdef PIPESEND
7209     if (pv[SND_FLT].ival > 0) {         /* Have SEND FILTER? */
7210         if (pipesend) {
7211             printf("?Switch conflict: /FILTER and /COMMAND\n");
7212             x = -9;
7213             goto xgetx;
7214         }
7215         makestr(&rcvfilter,pv[SND_FLT].sval);
7216         debug(F110,"ftp get /FILTER", rcvfilter, 0);
7217     }
7218     if (rcvfilter || pipesend) {        /* /RESTART */
7219 #ifdef FTP_RESTART
7220         if (restart) {                  /* with pipes or filters */
7221             printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n");
7222             x = -9;
7223             goto xgetx;
7224         }
7225 #endif /* FTP_RESTART */
7226         if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) {
7227             printf(
7228                 "?Switch conflict: /FILTER or /COMMAND and Date Checking\n");
7229             x = -9;
7230             goto xgetx;
7231         }
7232     }
7233 #endif /* PIPESEND */
7234
7235     tfc = (CK_OFF_T)0;                  /* Initialize stats and counters */
7236     filcnt = 0;
7237     pktnum = 0;
7238     rpackets = 0L;
7239
7240     if (pv[SND_FIL].ival > 0) {
7241         if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) {
7242             debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno);
7243             printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval);
7244             x = -9;
7245             goto xgetx;
7246         }
7247         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */
7248             zclose(ZMFILE);                       /* Failed */
7249             debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0);
7250             printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval);
7251             x = -9;
7252             goto xgetx;
7253         }
7254         listfile = 1;
7255         debug(F110,"ftp get listfile first",tmpbuf,0);
7256         makestr(&(mgetlist[0]),tmpbuf);
7257     }
7258     t0 = gmstimer();                    /* Record starting time */
7259
7260     updating = 0;                       /* Checking dates? */
7261     if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U))
7262       updating = 1;
7263     if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M)
7264       updating = 2;
7265     if (updating)                       /* These switches force FTP DATES ON */
7266       ftp_dates |= 2;
7267
7268     what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */
7269
7270     cancelgroup = 0;                    /* Group not canceled yet */
7271     if (!(ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype))
7272       changetype(ftp_typ,0);            /* Change to requested type */
7273     binary = ftp_typ;                   /* For file-transfer display */
7274     first = 1;                          /* For MGET list */
7275     done = 0;                           /* Loop control */
7276
7277 #ifdef CK_TMPDIR
7278     if (dldir && !f_tmpdir) {           /* If they have a download directory */
7279         if ((s = zgtdir())) {           /* Get current directory */
7280             if (zchdir(dldir)) {        /* Change to download directory */
7281                 ckstrncpy(savdir,s,TMPDIRLEN);
7282                 f_tmpdir = 1;           /* Remember that we did this */
7283             }
7284         }
7285     }
7286 #endif /* CK_TMPDIR */
7287
7288     if (ftp_nml) {                      /* /NAMELIST */
7289         debug(F110,"ftp GET ftp_nml",ftp_nml,0);
7290         if (ftp_nml[0] == '-' && ftp_nml[1] == 0)
7291           fp_nml = stdout;
7292         else
7293           fp_nml = fopen(ftp_nml, "wb");
7294         if (!fp_nml) {
7295             printf("?%s: %s\n",ftp_nml,ck_errstr());
7296             goto xgetx;
7297         }
7298     }
7299     while (!done && !cancelgroup) {     /* Loop for all files */
7300                                         /* or until canceled. */
7301 #ifdef FTP_PROXY
7302         /* do something here if proxy */
7303 #endif /* FTP_PROXY */
7304
7305         rs_len = (CK_OFF_T)0;           /* REGET position */
7306         cancelfile = 0;                 /* This file not canceled yet */
7307         haspath = 0;                    /* Recalculate this each time thru */
7308
7309         if (getone) {                   /* GET */
7310             char * p;
7311             s = line;
7312             src = line;                 /* Server name */
7313             done = 1;
7314             debug(F111,"ftp get file",s,0);
7315         } else if (mget) {              /* MGET */
7316             src = mgetlist[mgetx];
7317             debug(F111,"ftp mget remote_files A",src,first);
7318             s = (char *)remote_files(first,
7319                                      (CHAR *)mgetlist[mgetx],
7320                                      (CHAR *)pv[SND_PAT].sval,
7321                                      0
7322                                      );
7323             debug(F110,"ftp mget remote_files B",s,0);
7324             if (!s) s = "";
7325             if (!*s) {
7326                 first = 1;
7327                 if (listfile) {         /* Names from listfile */
7328                   again:
7329                     tmpbuf[0] = NUL;
7330                     while (!tmpbuf[0]) {
7331                         if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) {
7332                             zclose(ZMFILE);
7333                             debug(F110,"ftp get listfile EOF",
7334                                   pv[SND_FIL].sval,0);
7335                             makestr(&(mgetlist[0]),NULL);
7336                             s = NULL;
7337                             done = 1;
7338                             break;
7339                         }
7340                     }
7341                     if (done)
7342                       continue;
7343
7344                     makestr(&(mgetlist[0]),tmpbuf);
7345                     debug(F110,"ftp get listfile next",tmpbuf,0);
7346                     s = (char *)remote_files(first,
7347                                              (CHAR *)mgetlist[0],
7348                                              (CHAR *)pv[SND_PAT].sval,
7349                                              0
7350                                              );
7351                     debug(F110,"ftp mget remote_files C",s,0);
7352                     if (!s) {
7353                         ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7354                         ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,"File not found");
7355                         tlog(F110,"ftp get file not found:",s,0);
7356                         goto again;
7357                     }
7358                 } else {                /* Names from command line */
7359                     mgetx++;
7360                     if (mgetx < mgetn)
7361                       s = (char *)remote_files(first,
7362                                                (CHAR *)mgetlist[mgetx],
7363                                                (CHAR *)pv[SND_PAT].sval,
7364                                                0
7365                                                );
7366                     else
7367                       s = NULL;
7368                     if (!s) mgetx++;
7369                     debug(F111,"ftp mget remote_files D",s,mgetx);
7370                 }
7371                 if (!s) {
7372                     if (!first || mgetx >= mgetn) {
7373                         done = 1;
7374                         break;
7375                     } else if (geterror) {
7376                         status = 0;
7377                         done = 1;
7378                         break;
7379                     } else {
7380                         continue;
7381                     }
7382                 }
7383             }
7384         }
7385         debug(F111,"ftp mget remote_files E",s,0);
7386         /*
7387           The semantics of NLST are ill-defined.  Suppose we have just sent
7388           NLST /path/[a-z]*.  Most servers send back names like /path/foo,
7389           /path/bar, etc.  But some send back only foo and bar, and subsequent
7390           RETR commands based on the pathless names are not going to work.
7391         */
7392         if (servertype == SYS_UNIX && !ckstrchr(s,'/')) {
7393             char * s3;
7394             if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) {
7395                 int len, left = 4096;
7396                 char * tmp = xtmpbuf;
7397                 len = s3 - mgetlist[mgetx] + 1;
7398                 ckstrncpy(tmp,mgetlist[mgetx],left);
7399                 tmp += len;
7400                 left -= len;
7401                 ckstrncpy(tmp,s,left);
7402                 s = xtmpbuf;
7403                 debug(F111,"ftp mget remote_files F",s,0);
7404             }
7405         }
7406         first = 0;
7407         skipthis = 0;                   /* File selection... */
7408         msg = "";
7409         nam = s;                        /* Filename (without path) */
7410         rc = 0;                         /* Initial return code */
7411         s2 = "";
7412
7413         if (!getone && !skipthis) {     /* For MGET and MDELETE... */
7414             char c, * p = s;
7415             int srvpath = 0;
7416             int usrpath = 0;
7417             int i, k = 0;
7418
7419             debug(F111,"ftp mget havetype",s,havetype);
7420             if (havetype > 0 && havetype != FTYP_FILE) {
7421                 /* Server says it's not file... */
7422                 debug(F110,"ftp mget not-a-file",s,0);
7423                 continue;
7424             }
7425 /*
7426   Explanation: Some ftp servers (such as wu-ftpd) return a recursive list.
7427   But if the client did not ask for a recursive list, we have to ignore any
7428   server files that include a pathname that extends beyond any path that
7429   was included in the user's request.
7430
7431   User's filespec is blah or path/blah (or other non-UNIX syntax).  We need to
7432   get the user's path segment.  Then, for each incoming file, if it begins
7433   with the same path segment, we must strip it (point past it).
7434 */
7435             src = mgetlist[mgetx];      /* In case it moved! */
7436             if (src) {
7437                 for (i = 0; src[i]; i++) { /* Find rightmost path separator */
7438                     if (ispathsep(src[i])) /* in user's pathname */
7439                       k = i + 1;
7440                 }
7441             } else {
7442                 src = "";
7443             }
7444             usrpath = k;                /* User path segment length */
7445             debug(F111,"ftp get usrpath",src,usrpath);
7446
7447             p = s;                      /* Server filename */
7448             while ((c = *p++)) {        /* Look for path in server filename */
7449                 if (ispathsep(c)) {
7450                     /* haspath++; */
7451                     nam = p;            /* Pathless name (for ckmatch) */
7452                     srvpath = p - s;    /* Server path segment length */
7453                 }
7454             }
7455             debug(F111,"ftp get srvpath",s,srvpath);
7456
7457             if (usrpath == 0) {
7458 /*
7459   Here we handle the case where the user said "mget foo" where foo is a
7460   directory name, and the server is sending back names like "foo/file1",
7461   "foo/file2", etc.  This is a nasty trick but it's necessary because the
7462   user can't compensate by typing "mget foo/" because then the server is
7463   likely to send back "foo//file1, foo//file2" etc, and we still won't
7464   get a match...
7465 */
7466                 int srclen = 0, srvlen = 0;
7467                 if (src) srclen = strlen(src);
7468                 if (s) srvlen = strlen(s);
7469                 if (src && (srvlen > srclen)) {
7470                     if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) {
7471                         char * tmpsrc = NULL;
7472                         tmpsrc = (char *)malloc(srclen + 2);
7473                         strncpy(tmpsrc,src,srclen);
7474                         tmpsrc[srclen] = s[srclen];
7475                         tmpsrc[srclen+1] = NUL;
7476                         free(mgetlist[mgetx]);
7477                         mgetlist[mgetx] = tmpsrc;
7478                         tmpsrc = NULL;
7479                         src = mgetlist[mgetx];
7480                         usrpath = srclen+1;
7481                     }                         
7482                 }
7483             }
7484 /*
7485   If as-name not given and server filename includes path that matches
7486   the pathname from the user's file specification, we must trim the common
7487   path prefix from the server's name when constructing the local name.
7488 */
7489             if (src &&                  /* Wed Sep 25 17:27:48 2002 */
7490                 !asnambuf[0] &&
7491                 !recursive &&           /* Thu Sep 19 16:11:59 2002 */
7492                 (srvpath > 0) &&
7493                 !strncmp(src,s,usrpath)) {
7494                 s2 = s + usrpath;       /* Local name skips past remote path */
7495             }
7496 #ifdef COMMENT
7497             /* This doesn't work if the path prefix contains wildcards! */
7498             haspath = (srvpath > usrpath);
7499 #else
7500             {                           /* Count path segments instead */
7501                 int x1 = 0, x2 = 0;
7502                 char *p;
7503                 for (p = s; *p; p++)
7504                   if (ispathsep(*p)) x1++;
7505                 for (p = src; *p; p++) {
7506                     if (ispathsep(*p)) x2++;
7507                 }
7508                 haspath = recursive ? x1 || x2 : x1 > x2;
7509                 debug(F111,"ftp get server path segments",s,x1);
7510                 debug(F111,"ftp get user   path segments",src,x2);
7511             }
7512
7513 #endif /* COMMENT */
7514             debug(F111,"ftp get haspath",s+usrpath,haspath);
7515
7516             if (haspath) {              /* Server file has path segments? */
7517                 if (!recursive) {       /* [M]GET /RECURSIVE? */
7518 /*
7519   We did not ask for a recursive listing, but the server is sending us one
7520   anyway (as wu-ftpd is wont to do).  We get here if the current filename
7521   includes a path segment beyond any path segment we asked for in our
7522   non-recursive [M]GET command.  We MUST skip this file.
7523 */
7524                     debug(F111,"ftp get skipping because of path",s,0);
7525                     continue;
7526                 }
7527             }
7528         } else if (getone && !skipthis) { /* GET (not MGET) */
7529             char * p = nam;
7530             while ((c = *p++)) {        /* Handle path in local name */
7531                 if (ispathsep(c)) {
7532                     if (recursive) {    /* If recursive, keep it */
7533                         haspath = 1;
7534                         break;
7535                     } else {            /* Otherwise lose it. */
7536                       nam = p;
7537                     }
7538                 }
7539             }
7540             s2 = nam;
7541         }
7542         if (!*nam)                      /* Name without path */
7543           nam = s;
7544
7545         if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */
7546             if (nam[0] == '.')
7547               continue;
7548         }
7549         if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */
7550             int xx;
7551             for (i = 0; i < NSNDEXCEPT; i++) {
7552                 if (!rcvexcept[i]) {
7553                     break;
7554                 }
7555                 xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1);
7556                 debug(F111,"ftp mget /except match",rcvexcept[i],xx);
7557                 if (xx) {
7558                     tlog(F100," refused: exception list","",0);
7559                     msg = "Refused: Exception List";
7560                     skipthis++;
7561                     break;
7562                 }
7563             }
7564         }
7565         if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */
7566             if (ckmatch(
7567 #ifdef CKREGEX
7568                         "*.~[0-9]*~"
7569 #else
7570                         "*.~*~"
7571 #endif /* CKREGEX */
7572                         ,nam,0,1) > 0)
7573               continue;
7574         }
7575         if (!x_xla) {                   /* If translation is off */
7576             x_csl = -2;                 /* unset the charsets */
7577             x_csr = -2;
7578         }
7579         ckstrncpy(filnam,s,CKMAXPATH);  /* For \v(filename) */
7580         if (!*s2)                       /* Local name */
7581           s2 = asnambuf;                /* As-name */
7582
7583         if (!*s2)                       /* Sat Nov 16 19:19:39 2002 */
7584           s2 = recursive ? s : nam;     /* Fri Jan 10 13:15:19 2003 */
7585
7586         debug(F110,"ftp get filnam  ",s,0);
7587         debug(F110,"ftp get asname A",s2,0);
7588
7589         /* Receiving to real file */
7590         if (!pipesend &&
7591 #ifdef PIPESEND
7592             !rcvfilter &&
7593 #endif /* PIPESEND */
7594             !toscreen) {
7595 #ifndef NOSPL
7596             /* Do this here so we can decide whether to skip */
7597             if (cmd_quoting && !skipthis && asnambuf[0]) {
7598                 int n; char *p;
7599                 n = TMPBUFSIZ;
7600                 p = tmpbuf;
7601                 zzstring(asnambuf,&p,&n);
7602                 s2 = tmpbuf;
7603                 debug(F111,"ftp get asname B",s2,updating);
7604             }
7605 #endif /* NOSPL */
7606
7607             local = *s2 ? s2 : s;
7608
7609             if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */
7610                 CK_OFF_T x;
7611                 x = zchki(local);
7612                 debug(F111,"ftp get DISCARD zchki",local,x);
7613                 if (x > -1) {
7614                     skipthis++;
7615                     debug(F110,"ftp get skip name",local,0);
7616                     tlog(F100," refused: name","",0);
7617                     msg = "Refused: Name";
7618                 }
7619             }
7620
7621 #ifdef DOUPDATE
7622             if (!skipthis && updating) { /* If updating and not yet skipping */
7623                 if (zchki(local) > -1) {
7624                     x = chkmodtime(local,s,0);
7625 #ifdef DEBUG
7626                     if (deblog) {
7627                         if (updating == 2)
7628                           debug(F111,"ftp get /dates-diff chkmodtime",local,x);
7629                         else
7630                           debug(F111,"ftp get /update chkmodtime",local,x);
7631                     }
7632 #endif /* DEBUG */
7633                     if ((updating == 1 && x > 0) ||  /* /UPDATE */
7634                         (updating == 2 && x == 1)) { /* /DATES-DIFFER */
7635                         skipthis++;
7636                         tlog(F100," refused: date","",0);
7637                         msg = "Refused: Date";
7638                         debug(F110,"ftp get skip date",local,0);
7639                     }
7640                 }
7641             }
7642 #endif /* DOUPDATE */
7643         }
7644         /* Initialize file size to -1 in case server doesn't understand */
7645         /* SIZE command, so xxscreen() will know we don't know the size */
7646
7647         fsize = (CK_OFF_T)-1;
7648
7649         /* Ask for size now only if we need it for selection */
7650         /* because if you're going thru a list 100,000 files to select */
7651         /* a small subset, 100,000 SIZE commands can take hours... */
7652
7653         gotsize = 0;
7654         if (!mdel && !skipthis &&        /* Don't need size for DELE... */
7655             (getsmaller >= (CK_OFF_T)0  || getlarger >= (CK_OFF_T)0)) {
7656             if (havesize >= (CK_OFF_T)0) { /* Already have file size? */
7657                 fsize = havesize;
7658                 gotsize = 1;
7659             } else {                    /* No - must ask server */
7660                 /*
7661                   Prior to sending the NLST command we necessarily put the
7662                   server into ASCII mode.  We must now put it back into the
7663                   the requested mode so the upcoming SIZE command returns
7664                   right kind of size; this is especially important for
7665                   GET /RECOVER; otherwise the server returns the "ASCII" size
7666                   of the file, rather than its true size.
7667                 */
7668                 changetype(ftp_typ,0);  /* Change to requested type */
7669                 fsize = (CK_OFF_T)-1;
7670                 if (sizeok) {
7671                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7672                     if (x == REPLY_COMPLETE) {
7673                         fsize = ckatofs(&ftp_reply_str[4]);
7674                         gotsize = 1;
7675                     }
7676                 }
7677             }
7678             if (gotsize) {
7679                 if (getsmaller >= (CK_OFF_T)0 && fsize >= getsmaller)
7680                   skipthis++;
7681                 if (getlarger >= (CK_OFF_T)0 && fsize <= getlarger)
7682                   skipthis++;
7683                 if (skipthis) {
7684                     debug(F111,"ftp get skip size",s,fsize);
7685                     tlog(F100," refused: size","",0);
7686                     msg = "Refused: Size";
7687                 }
7688 #ifdef COMMENT
7689             } else if (getone) {
7690                 /* SIZE can fail for many reasons.  Does the file exist? */
7691                 x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm);
7692                 if (x != REPLY_COMPLETE) {
7693                     printf(">>> FILE NOT FOUND: %s\n",s);
7694                     break;
7695                 }
7696 #endif /* COMMENT */
7697             }
7698         }
7699         if (skipthis) {                 /* Skipping this file? */
7700             ftscreen(SCR_FN,'F',(CK_OFF_T)0,s);
7701             if (msg)
7702               ftscreen(SCR_ST,ST_ERR,(CK_OFF_T)0,msg);
7703             else
7704               ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,s);
7705             continue;
7706         }
7707         if (fp_nml) {                   /* /NAMELIST only - no transfer */
7708             fprintf(fp_nml,"%s\n",s);
7709             continue;
7710         }
7711         if (recursive && haspath && !pipesend
7712 #ifdef PIPESEND
7713             && !rcvfilter
7714 #endif /* PIPESEND */
7715             ) {
7716             int x;
7717
7718 #ifdef NOMKDIR
7719             x = -1;
7720 #else
7721             x = zmkdir(s);              /* Try to make the directory */
7722 #endif /* NOMKDIR */
7723
7724             if (x < 0) {
7725                 rc = -1;                /* Failure is fatal */
7726                 if (geterror) {
7727                     status = 0;
7728                     ftscreen(SCR_EM,0,(CK_OFF_T)0,
7729                              "Directory creation failure");
7730                     break;
7731                 }
7732             }
7733         }
7734
7735         /* Not skipping */
7736
7737         selected++;                     /* Count this file as selected */
7738         pn = NULL;
7739
7740         if (!gotsize && !mdel) {        /* Didn't get size yet */
7741             if (havesize > (CK_OFF_T)-1) { /* Already have file size? */
7742                 fsize = havesize;
7743                 gotsize = 1;
7744             } else {                    /* No - must ask server */
7745                 fsize = (CK_OFF_T)-1;
7746                 if (sizeok) {
7747                     x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm);
7748                     if (x == REPLY_COMPLETE) {
7749                         fsize = ckatofs(&ftp_reply_str[4]);
7750                         gotsize = 1;
7751                     }
7752                 }
7753             }
7754         }
7755         if (mdel) {                     /* [M]DELETE */
7756             if (displa && !ftp_vbm)
7757               printf(" %s...",s);
7758             rc =
7759              (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1;
7760             if (rc > -1) {
7761                 tlog(F110,"ftp mdelete",s,0);
7762                 if (displa && !ftp_vbm)
7763                   printf("OK\n");
7764             } else {
7765                 tlog(F110,"ftp mdelete failed:",s,0);
7766                 if (displa)
7767                   printf("Failed\n");
7768             }
7769 #ifndef NOSPL
7770 #ifdef PIPESEND
7771         } else if (rcvfilter) {         /* [M]GET with filter */
7772             int n; char * p;
7773             n = CKMAXPATH;
7774             p = tmpbuf;                 /* Safe - no asname with filter */
7775             zzstring(rcvfilter,&p,&n);
7776             if (n > -1)
7777               pn = tmpbuf;
7778             debug(F111,"ftp get rcvfilter",pn,n);
7779 #endif /* PIPESEND */
7780 #endif /* NOSPL */
7781             if (toscreen) s2 = "-";
7782         } else if (pipesend) {          /* [M]GET /COMMAND */
7783             int n; char * p;
7784             n = CKMAXPATH;
7785             p = tmpbuf;                 /* Safe - no asname with filter */
7786             zzstring(pipename,&p,&n);
7787             if (n > -1)
7788               pn = tmpbuf;
7789             debug(F111,"ftp get pipename",pipename,n);
7790             if (toscreen) s2 = "-";
7791         } else {                        /* [M]GET with no pipes or filters */
7792             debug(F111,"ftp get s2 A",s2,x_cnv);
7793             if (toscreen) {
7794                 s2 = "-";               /* (hokey convention for stdout) */
7795             } else if (!*s2) {          /* No asname? */
7796                 if (x_cnv) {            /* If converting */
7797                     nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */
7798                     s2 = tmpbuf;
7799                     debug(F110,"ftp get nzrtol",s2,0);
7800                 } else                  /* otherwise */
7801                   s2 = s;               /* use incoming file's name */
7802             }
7803             debug(F110,"ftp get s2 B",s2,0);
7804
7805             /* If local file already exists, take collision action */
7806
7807             if (!pipesend &&
7808 #ifdef PIPESEND
7809                 !rcvfilter &&
7810 #endif /* PIPESEND */
7811                 !toscreen) {
7812                 CK_OFF_T x;
7813                 x = zchki(s2);
7814                 debug(F111,"ftp get zchki",s2,x);
7815                 debug(F111,"ftp get x_fnc",s2,x_fnc);
7816
7817                 if (x > (CK_OFF_T)-1 && !restart) {
7818                     int x = -1;
7819                     char * newname = NULL;
7820
7821                     switch (x_fnc) {
7822                       case XYFX_A:      /* Append */
7823                         append = 1;
7824                         break;
7825                       case XYFX_R:      /* Rename */
7826                       case XYFX_B:      /* Backup */
7827                         znewn(s2,&newname); /* Make unique name */
7828                         debug(F110,"ftp get znewn",newname,0);
7829                         if (x_fnc == XYFX_B) { /* Backup existing file */
7830                             x = zrename(s2,newname);
7831                             debug(F111,"ftp get backup zrename",newname,x);
7832                         } else {      /* Rename incoming file */
7833                             x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1);
7834                             s2 = tmpbuf;
7835                             debug(F111,"ftp get rename incoming",newname,x);
7836                         }
7837                         if (x < 0) {
7838                             ftscreen(SCR_EM,0,(CK_OFF_T)0,
7839                                      "Backup/Rename failed");
7840                             x = 0;
7841                             goto xgetx;
7842                         }
7843                         break;
7844                       case XYFX_D:      /* Discard (already handled above) */
7845                       case XYFX_U:      /* Update (ditto) */
7846                       case XYFX_M:      /* Update (ditto) */
7847                       case XYFX_X:      /* Overwrite */
7848                         break;
7849                     }
7850                 }
7851             }
7852         }
7853         if (!mdel) {
7854 #ifdef PIPESEND
7855             debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0);
7856 #endif /* PIPESEND */
7857             if (pipesend && !toscreen)
7858               s2 = NULL;
7859 #ifdef DEBUG
7860             if (deblog) {
7861                 debug(F101,"ftp get x_xla","",x_xla);
7862                 debug(F101,"ftp get x_csl","",x_csl);
7863                 debug(F101,"ftp get x_csr","",x_csr);
7864                 debug(F101,"ftp get append","",append);
7865             }
7866 #endif /* DEBUG */
7867
7868             rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr);
7869
7870 #ifdef DEBUG
7871             if (deblog) {
7872                 debug(F111,"ftp get rc",s,rc);
7873                 debug(F111,"ftp get ftp_timed_out",s,ftp_timed_out);
7874                 debug(F111,"ftp get cancelfile",s,cancelfile);
7875                 debug(F111,"ftp get cancelgroup",s,cancelgroup);
7876                 debug(F111,"ftp get renaming",s,renaming);
7877                 debug(F111,"ftp get moving",s,moving);
7878             }
7879 #endif /* DEBUG */
7880         }
7881         if (rc > -1) {
7882             good++;
7883             status = 1;
7884             if (!cancelfile) {
7885                 if (deleting) {         /* GET /DELETE (source file) */
7886                     rc =
7887                       (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE)
7888                         ? 1 : -1;
7889                     tlog(F110, (rc > -1) ?
7890                          " deleted" : " failed to delete", s, 0);
7891                 }
7892                 if (renaming && rcv_rename && !toscreen) {
7893                     char *p;            /* Rename downloaded file */
7894 #ifndef NOSPL
7895                     char tmpbuf[CKMAXPATH+1];
7896                     int n;
7897                     n = CKMAXPATH;
7898                     p = tmpbuf;
7899                     debug(F111,"ftp get /rename",rcv_rename,0);
7900                     zzstring(rcv_rename,&p,&n);
7901                     debug(F111,"ftp get /rename",rcv_rename,0);
7902                     p = tmpbuf;
7903 #else
7904                     p = rcv_rename;
7905 #endif /* NOSPL */
7906                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7907                     debug(F111,"doftpget /RENAME zrename",p,rc);
7908                     tlog(F110, (rc > -1) ?
7909                          " renamed to" :
7910                          " failed to rename to",
7911                          p,
7912                          0
7913                          );
7914                 } else if (moving && rcv_move && !toscreen) {
7915                     char *p;            /* Move downloaded file */
7916 #ifndef NOSPL
7917                     char tmpbuf[CKMAXPATH+1];
7918                     int n;
7919                     n = TMPBUFSIZ;
7920                     p = tmpbuf;
7921                     debug(F111,"ftp get /move-to",rcv_move,0);
7922                     zzstring(rcv_move,&p,&n);
7923                     p = tmpbuf;
7924 #else
7925                     p = rcv_move;
7926 #endif /* NOSPL */
7927                     debug(F111,"ftp get /move-to",p,0);
7928                     rc = (zrename(s2,p) < 0) ? -1 : 1;
7929                     debug(F111,"doftpget /MOVE zrename",p,rc);
7930                     tlog(F110, (rc > -1) ?
7931                          " moved to" : " failed to move to", p, 0);
7932                 }
7933                 if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) {
7934                     char * s = pv[SND_SRN].sval;
7935                     char * srvrn = pv[SND_SRN].sval;
7936                     char tmpbuf[CKMAXPATH+1];
7937 #ifndef NOSPL
7938                     int y;              /* Pass it thru the evaluator */
7939                     extern int cmd_quoting; /* for \v(filename) */
7940                     debug(F111,"ftp get srv_renam",s,1);
7941
7942                     if (cmd_quoting) {
7943                         y = CKMAXPATH;
7944                         s = (char *)tmpbuf;
7945                         zzstring(srvrn,&s,&y);
7946                         s = (char *)tmpbuf;
7947                     }
7948 #endif /* NOSPL */
7949                     debug(F111,"ftp get srv_renam",s,1);
7950                     if (s) if (*s) {
7951                         int x;
7952                         x = ftp_rename(s2,s);
7953                         debug(F111,"ftp get ftp_rename",s2,x);
7954                         tlog(F110, (x > 0) ?
7955                              " renamed source file to" :
7956                              " failed to rename source file to",
7957                              s,
7958                              0
7959                              );
7960                         if (x < 1)
7961                           return(-1);
7962                     }
7963                 }
7964             }
7965         }
7966         if (cancelfile)
7967           continue;
7968         if (rc < 0) {
7969             ftp_fai++;
7970 #ifdef FTP_TIMEOUT
7971             debug(F101,"ftp get ftp_timed_out","",ftp_timed_out);
7972             if (ftp_timed_out) {
7973                 status = 0;
7974                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"GET timed out");
7975             }
7976 #endif  /* FTP_TIMEOUT */
7977             if (geterror) {
7978                 status = 0;
7979                 ftscreen(SCR_EM,0,(CK_OFF_T)0,"Fatal download error");
7980                 done++;
7981             }
7982         }
7983     }
7984 #ifdef DEBUG
7985     if (deblog) {
7986         debug(F101,"ftp get status","",status);
7987         debug(F101,"ftp get cancelgroup","",cancelgroup);
7988         debug(F101,"ftp get cancelfile","",cancelfile);
7989         debug(F101,"ftp get selected","",selected);
7990         debug(F101,"ftp get good","",good);
7991     }
7992 #endif /* DEBUG */
7993
7994     if (selected == 0) {                /* No files met selection criteria */
7995         status = 1;                     /* which is a kind of success. */
7996     } else if (status > 0) {            /* Some files were selected */
7997         if (cancelgroup)                /* but MGET was canceled */
7998           status = 0;                   /* so MGET failed */
7999         else if (cancelfile && good < 1) /* If file was canceled */
8000           status = 0;                   /* MGET failed if it got no files */
8001     }
8002     success = status;
8003     x = success;
8004     debug(F101,"ftp get success","",success);
8005
8006   xgetx:
8007     pipesend = pipesave;                /* Restore global pipe selection */
8008     if (fp_nml) {                       /* Close /NAMELIST */
8009         if (fp_nml != stdout)
8010           fclose(fp_nml);
8011         fp_nml = NULL;
8012     }
8013     if (
8014 #ifdef COMMENT
8015         x > -1
8016 #else
8017         success
8018 #endif  /* COMMENT */
8019         ) {                             /* Download successful */
8020 #ifdef GFTIMER
8021         t1 = gmstimer();                /* End time */
8022         sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */
8023         if (!sec) sec = 0.001;
8024         fptsecs = sec;
8025 #else
8026         sec = (t1 - t0) / 1000;
8027         if (!sec) sec = 1;
8028 #endif /* GFTIMER */
8029         tfcps = (long) (tfc / sec);
8030         tsecs = (int)sec;
8031         lastxfer = W_FTP|W_RECV;
8032         xferstat = success;
8033     }
8034     if (dpyactive)
8035       ftscreen(success > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, "");
8036 #ifdef CK_TMPDIR
8037     if (f_tmpdir) {                     /* If we changed to download dir */
8038         zchdir((char *) savdir);        /* Go back where we came from */
8039         f_tmpdir = 0;
8040     }
8041 #endif /* CK_TMPDIR */
8042
8043     for (i = 0; i <= SND_MAX; i++) {    /* Free malloc'd memory */
8044         if (pv[i].sval)
8045           free(pv[i].sval);
8046     }
8047     for (i = 0; i < mgetn; i++)         /* MGET list too */
8048       makestr(&(mgetlist[i]),NULL);
8049
8050     if (cancelgroup)                    /* Clear temp-file stack */
8051       mlsreset();
8052
8053     ftreset();                          /* Undo switch effects */
8054     dpyactive = 0;
8055     return(x);
8056 }
8057
8058 static struct keytab ftprmt[] = {
8059     { "cd",        XZCWD, 0 },
8060     { "cdup",      XZCDU, 0 },
8061     { "cwd",       XZCWD, CM_INV },
8062     { "delete",    XZDEL, 0 },
8063     { "directory", XZDIR, 0 },
8064     { "exit",      XZXIT, 0 },
8065     { "help",      XZHLP, 0 },
8066     { "login",     XZLGI, 0 },
8067     { "logout",    XZLGO, 0 },
8068     { "mkdir",     XZMKD, 0 },
8069     { "pwd",       XZPWD, 0 },
8070     { "rename",    XZREN, 0 },
8071     { "rmdir",     XZRMD, 0 },
8072     { "type",      XZTYP, 0 },
8073     { "", 0, 0 }
8074 };
8075 static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1;
8076
8077 int
8078 doftpsite() {                           /* Send a SITE command */
8079     int reply;
8080     char * s;
8081     int lcs = -1, rcs = -1;
8082     int save_vbm = ftp_vbm;
8083
8084 #ifndef NOCSETS
8085     if (ftp_xla) {
8086         lcs = ftp_csl;
8087         if (lcs < 0) lcs = fcharset;
8088         rcs = ftp_csx;
8089         if (rcs < 0) rcs = ftp_csr;
8090     }
8091 #endif /* NOCSETS */
8092     if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8093       return(x);
8094     CHECKCONN();
8095     ckstrncpy(line,s,LINBUFSIZ);
8096     if (testing) printf(" ftp site \"%s\"...\n",line);
8097     if (!ftp_vbm)
8098         ftp_vbm = !ckstrcmp("HELP",line,4,0);
8099     if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) {
8100         do {
8101             reply = getreply(0,lcs,rcs,ftp_vbm,0);
8102         } while (reply == REPLY_PRELIM);
8103     }
8104     ftp_vbm = save_vbm;
8105     return(success = (reply == REPLY_COMPLETE));
8106 }
8107
8108
8109 int
8110 dosetftppsv() {                         /* Passive mode */
8111     x = seton(&ftp_psv);
8112     if (x > 0) passivemode = ftp_psv;
8113     return(x);
8114 }
8115
8116 /*  d o f t p r m t  --  Parse and execute REMOTE commands  */
8117
8118 int
8119 doftprmt(cx,who) int cx, who; {         /* who == 1 for ftp, 0 for kermit */
8120     /* cx == 0 means REMOTE */
8121     /* cx != 0 is a XZxxx value */
8122     char * s;
8123
8124     if (who != 0)
8125       return(0);
8126
8127     if (cx == 0) {
8128         if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0)
8129           return(x);
8130         cx = x;
8131     }
8132     switch (cx) {
8133       case XZCDU:                       /* CDUP */
8134         if ((x = cmcfm()) < 0) return(x);
8135         return(doftpcdup());
8136
8137       case XZCWD:                       /* RCD */
8138         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8139           return(x);
8140         ckstrncpy(line,s,LINBUFSIZ);
8141         s = brstrip(line);
8142         return(doftpcwd(s,1));
8143       case XZPWD:                       /* RPWD */
8144         return(doftppwd());
8145       case XZDEL:                       /* RDEL */
8146         return(doftpget(FTP_MDE,1));
8147       case XZDIR:                       /* RDIR */
8148         return(doftpdir(FTP_DIR));
8149       case XZHLP:                       /* RHELP */
8150         return(doftpxhlp());
8151       case XZMKD:                       /* RMKDIR */
8152         return(doftpmkd());
8153       case XZREN:                       /* RRENAME */
8154         return(doftpren());
8155       case XZRMD:                       /* RRMDIR */
8156         return(doftprmd());
8157       case XZLGO:                       /* LOGOUT */
8158         return(doftpres());
8159       case XZXIT:                       /* EXIT */
8160         return(ftpbye());
8161     }
8162     printf("?Not usable with FTP - \"%s\"\n", atmbuf);
8163     return(-9);
8164 }
8165
8166 int
8167 doxftp() {                              /* Command parser for built-in FTP */
8168     int cx, n;
8169     struct FDB kw, fl;
8170     char * s;
8171     int usetls = 0;
8172     int lcs = -1, rcs = -1;
8173
8174 #ifndef NOCSETS
8175     if (ftp_xla) {
8176         lcs = ftp_csl;
8177         if (lcs < 0) lcs = fcharset;
8178         rcs = ftp_csx;
8179         if (rcs < 0) rcs = ftp_csr;
8180     }
8181 #endif /* NOCSETS */
8182
8183     if (inserver)                       /* FTP not allowed in IKSD. */
8184       return(-2);
8185
8186     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8187         ftp_typ = g_ftp_typ;
8188         /* g_ftp_typ = -1; */
8189     }
8190 #ifdef COMMENT
8191 /*
8192   We'll set the collision action locally in doftpget() based on whether
8193   ftp_fnc was ever set to a value.  if not, we'll use the fncact value.
8194 */
8195     if (ftp_fnc < 0)                    /* Inherit global collision action */
8196       ftp_fnc = fncact;                 /* if none specified for FTP */
8197 #endif /* COMMENT */
8198
8199     /* Restore global verbose mode */
8200     if (ftp_deb)
8201       ftp_vbm = 1;
8202     else if (quiet)
8203       ftp_vbm = 0;
8204     else
8205       ftp_vbm = ftp_vbx;
8206
8207     ftp_dates &= 1;                     /* Undo any previous /UPDATE switch */
8208
8209     dpyactive = 0;                      /* Reset global transfer-active flag */
8210     printlines = 0;                     /* Reset printlines */
8211
8212     if (fp_nml) {                       /* Reset /NAMELIST */
8213         if (fp_nml != stdout)
8214           fclose(fp_nml);
8215         fp_nml = NULL;
8216     }
8217     makestr(&ftp_nml,NULL);
8218
8219     cmfdbi(&kw,                         /* First FDB - commands */
8220            _CMKEY,                      /* fcode */
8221            "Hostname; or FTP command",  /* help */
8222            "",                          /* default */
8223            "",                          /* addtl string data */
8224            nftpcmd,                     /* addtl numeric data 1: tbl size */
8225            0,                           /* addtl numeric data 2: none */
8226            xxstring,                    /* Processing function */
8227            ftpcmdtab,                   /* Keyword table */
8228            &fl                          /* Pointer to next FDB */
8229            );
8230     cmfdbi(&fl,                         /* A host name or address */
8231            _CMFLD,                      /* fcode */
8232            "Hostname or address",       /* help */
8233            "",                          /* default */
8234            "",                          /* addtl string data */
8235            0,                           /* addtl numeric data 1 */
8236            0,                           /* addtl numeric data 2 */
8237            xxstring,
8238            NULL,
8239            NULL
8240            );
8241     x = cmfdb(&kw);                     /* Parse a hostname or a keyword */
8242     if (x == -3) {
8243         printf("?ftp what? \"help ftp\" for hints\n");
8244         return(-9);
8245     }
8246     if (x < 0)
8247       return(x);
8248     if (cmresult.fcode == _CMFLD) {     /* If hostname */
8249         return(openftp(cmresult.sresult,0)); /* go open the connection */
8250     } else {
8251         cx = cmresult.nresult;
8252     }
8253     switch (cx) {
8254       case FTP_ACC:                     /* ACCOUNT */
8255         if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0)
8256           return(x);
8257         CHECKCONN();
8258         makestr(&ftp_acc,s);
8259         if (testing)
8260           printf(" ftp account: \"%s\"\n",ftp_acc);
8261         success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE);
8262         return(success);
8263
8264       case FTP_GUP:                     /* Go UP */
8265         if ((x = cmcfm()) < 0) return(x);
8266         CHECKCONN();
8267         if (testing) printf(" ftp cd: \"(up)\"\n");
8268         return(success = doftpcdup());
8269
8270       case FTP_CWD:                     /* CD */
8271         if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0)
8272           return(x);
8273         CHECKCONN();
8274         ckstrncpy(line,s,LINBUFSIZ);
8275         if (testing)
8276           printf(" ftp cd: \"%s\"\n", line);
8277         return(success = doftpcwd(line,1));
8278
8279       case FTP_CHM:                     /* CHMOD */
8280         if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0)
8281           return(x);
8282         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8283         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8284           return(x);
8285         CHECKCONN();
8286         ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL);
8287         if (testing)
8288           printf(" ftp chmod: %s\n",ftpcmdbuf);
8289         success =
8290           (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8291         return(success);
8292
8293       case FTP_CLS:                     /* CLOSE FTP connection */
8294         if ((y = cmcfm()) < 0)
8295           return(y);
8296         CHECKCONN();
8297         if (testing)
8298           printf(" ftp closing...\n");
8299         ftpclose();
8300         return(success = 1);
8301
8302       case FTP_DIR:                     /* DIRECTORY of remote files */
8303       case FTP_VDI:
8304         return(doftpdir(cx));
8305
8306       case FTP_GET:                     /* GET a remote file */
8307       case FTP_RGE:                     /* REGET */
8308       case FTP_MGE:                     /* MGET */
8309       case FTP_MDE:                     /* MDELETE */
8310         return(doftpget(cx,1));
8311
8312       case FTP_IDL:                     /* IDLE */
8313         if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0)
8314           return(x);
8315         if ((y = cmcfm()) < 0)
8316           return(y);
8317         CHECKCONN();
8318         if (z < 0)  {                   /* Display idle timeout */
8319             if (testing)
8320               printf(" ftp query idle timeout...\n");
8321             success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE);
8322         } else {                        /* Set idle timeout */
8323             if (testing)
8324               printf(" ftp idle timeout set: %d...\n",z);
8325             success =
8326               (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE);
8327         }
8328         return(success);
8329
8330       case FTP_MKD:                     /* MKDIR */
8331         return(doftpmkd());
8332
8333       case FTP_MOD:                     /* MODTIME */
8334         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8335           return(x);
8336         CHECKCONN();
8337         ckstrncpy(line,s,LINBUFSIZ);
8338         if (testing)
8339           printf(" ftp modtime \"%s\"...\n",line);
8340         success = 0;
8341         if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) {
8342             success = 1;
8343             mdtmok = 1;
8344             if (!quiet) {
8345                 int flag = 0;
8346                 char c, * s;
8347                 struct tm tmremote;
8348
8349                 bzero((char *)&tmremote, sizeof(struct tm));
8350                 s = ftp_reply_str;
8351                 while ((c = *s++)) {
8352                     if (c == SP) {
8353                         flag++;
8354                         break;
8355                     }
8356                 }
8357                 if (flag) {
8358                     if (sscanf(s, "%04d%02d%02d%02d%02d%02d",
8359                                &tmremote.tm_year,
8360                                &tmremote.tm_mon,
8361                                &tmremote.tm_mday,
8362                                &tmremote.tm_hour,
8363                                &tmremote.tm_min,
8364                                &tmremote.tm_sec
8365                                ) == 6) {
8366                         printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n",
8367                                line,
8368                                tmremote.tm_year,
8369                                tmremote.tm_mon,
8370                                tmremote.tm_mday,
8371                                tmremote.tm_hour,
8372                                tmremote.tm_min,
8373                                tmremote.tm_sec
8374                                );
8375                     } else {
8376                         success = 0;
8377                     }
8378                 }
8379             }
8380         }
8381         return(success);
8382
8383       case FTP_OPN:                     /* OPEN connection */
8384 #ifdef COMMENT
8385         x = cmfld("IP hostname or address","",&s,xxstring);
8386         if (x < 0) {
8387             success = 0;
8388             return(x);
8389         }
8390         ckstrncpy(line,s,LINBUFSIZ);
8391         s = line;
8392         return(openftp(s,0));
8393 #else
8394         {                               /* OPEN connection */
8395             char name[TTNAMLEN+1], *p;
8396             extern int network;
8397             extern char ttname[];
8398             if (network)                /* If we have a current connection */
8399               ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */
8400             else
8401               *name = '\0';             /* as default host */
8402             for (p = name; *p; p++)     /* Remove ":service" from end. */
8403               if (*p == ':') { *p = '\0'; break; }
8404 #ifndef USETLSTAB
8405             x = cmfld("IP hostname or address",name,&s,xxstring);
8406 #else
8407             cmfdbi(&kw,                 /* First FDB - commands */
8408                    _CMKEY,              /* fcode */
8409                    "Hostname or switch", /* help */
8410                    "",                  /* default */
8411                    "",                  /* addtl string data */
8412                    ntlstab,             /* addtl numeric data 1: tbl size */
8413                    0,                   /* addtl numeric data 2: none */
8414                    xxstring,            /* Processing function */
8415                    tlstab,              /* Keyword table */
8416                    &fl                  /* Pointer to next FDB */
8417                    );
8418             cmfdbi(&fl,                 /* A host name or address */
8419                    _CMFLD,              /* fcode */
8420                    "Hostname or address", /* help */
8421                    "",                  /* default */
8422                    "",                  /* addtl string data */
8423                    0,                   /* addtl numeric data 1 */
8424                    0,                   /* addtl numeric data 2 */
8425                    xxstring,
8426                    NULL,
8427                    NULL
8428                    );
8429
8430             for (n = 0;; n++) {
8431                 x = cmfdb(&kw);         /* Parse a hostname or a keyword */
8432                 if (x == -3) {
8433                   printf("?ftp open what? \"help ftp\" for hints\n");
8434                   return(-9);
8435                 }
8436                 if (x < 0)
8437                   break;
8438                 if (cmresult.fcode == _CMFLD) { /* Hostname */
8439                     s = cmresult.sresult;
8440                     break;
8441                 } else if (cmresult.nresult == OPN_TLS) {
8442                     usetls = 1;
8443                 }
8444             }
8445 #endif /* USETLSTAB */
8446             if (x < 0) {
8447                 success = 0;
8448                 return(x);
8449             }
8450             ckstrncpy(line,s,LINBUFSIZ);
8451             s = line;
8452             return(openftp(s,usetls));
8453         }
8454 #endif /* COMMENT */
8455
8456       case FTP_PUT:                     /* PUT */
8457       case FTP_MPU:                     /* MPUT */
8458       case FTP_APP:                     /* APPEND */
8459       case FTP_REP:                     /* REPUT */
8460         return(doftpput(cx,1));
8461
8462       case FTP_PWD:                     /* PWD */
8463         x = doftppwd();
8464         if (x > -1) success = x;
8465         return(x);
8466
8467       case FTP_REN:                     /* RENAME */
8468         return(doftpren());
8469
8470       case FTP_RES:                     /* RESET */
8471         return(doftpres());
8472
8473       case FTP_HLP:                     /* (remote) HELP */
8474         return(doftpxhlp());
8475
8476       case FTP_RMD:                     /* RMDIR */
8477         return(doftprmd());
8478
8479       case FTP_STA:                     /* STATUS */
8480         if ((x = cmtxt("Command", "", &s, xxstring)) < 0)
8481           return(x);
8482         CHECKCONN();
8483         ckstrncpy(line,s,LINBUFSIZ);
8484         if (testing) printf(" ftp status \"%s\"...\n",line);
8485         success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE);
8486         return(success);
8487
8488       case FTP_SIT: {                   /* SITE */
8489           return(doftpsite());
8490       }
8491
8492       case FTP_SIZ:                     /* (ask for) SIZE */
8493         if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0)
8494           return(x);
8495         CHECKCONN();
8496         ckstrncpy(line,s,LINBUFSIZ);
8497         if (testing)
8498           printf(" ftp size \"%s\"...\n",line);
8499         success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE);
8500         if (success)
8501           sizeok = 1;
8502         return(success);
8503
8504       case FTP_SYS:                     /* Ask for server's SYSTEM type */
8505         if ((x = cmcfm()) < 0) return(x);
8506         CHECKCONN();
8507         if (testing)
8508           printf(" ftp system...\n");
8509         success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE);
8510         return(success);
8511
8512       case FTP_UMA:                     /* Set/query UMASK */
8513         if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0)
8514           if (x != -3)
8515             return(x);
8516         ckstrncpy(tmpbuf,s,TMPBUFSIZ);
8517         if ((x = cmcfm()) < 0) return(x);
8518         CHECKCONN();
8519         if (testing) {
8520             if (tmpbuf[0])
8521               printf(" ftp umask \"%s\"...\n",tmpbuf);
8522             else
8523               printf(" ftp query umask...\n");
8524         }
8525         success = ftp_umask(tmpbuf);
8526         return(success);
8527
8528       case FTP_USR:
8529         return(doftpusr());
8530
8531       case FTP_QUO:
8532         if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0)
8533           return(x);
8534         CHECKCONN();
8535         success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
8536         return(success);
8537
8538       case FTP_TYP:                     /* Type */
8539         if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0)
8540           return(x);
8541         if ((y = cmcfm()) < 0) return(y);
8542         CHECKCONN();
8543         ftp_typ = x;
8544         g_ftp_typ = x;
8545         tenex = (ftp_typ == FTT_TEN);
8546         changetype(ftp_typ,ftp_vbm);
8547         return(1);
8548
8549       case FTP_CHK:                     /* Check if remote file(s) exist(s) */
8550         if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0)
8551           return(x);
8552         CHECKCONN();
8553         success = remote_files(1,(CHAR *)s,(CHAR *)s,0) ? 1 : 0;
8554         return(success);
8555
8556       case FTP_FEA:                     /* RFC2389 */
8557         if ((y = cmcfm()) < 0)
8558           return(y);
8559         CHECKCONN();
8560         success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE);
8561         if (success) {
8562             if (sfttab[0] > 0) {
8563                 ftp_aut = sfttab[SFT_AUTH];
8564                 sizeok  = sfttab[SFT_SIZE];
8565                 mdtmok  = sfttab[SFT_MDTM];
8566                 mlstok  = sfttab[SFT_MLST];
8567             }
8568         }
8569         return(success);
8570
8571       case FTP_OPT:                     /* RFC2389 */
8572         /* Perhaps this should be a keyword list... */
8573         if ((x = cmfld("FTP command","",&s,xxstring)) < 0)
8574           return(x);
8575         CHECKCONN();
8576         ckstrncpy(line,s,LINBUFSIZ);
8577         if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0)
8578           return(x);
8579         success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
8580         return(success);
8581
8582       case FTP_ENA:                     /* FTP ENABLE */
8583       case FTP_DIS:                     /* FTP DISABLE */
8584         if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0)
8585           return(x);
8586         if ((y = cmcfm()) < 0) return(y);
8587         switch (x) {
8588           case ENA_AUTH:                /* OK to use autoauthentication */
8589             ftp_aut = (cx == FTP_ENA) ? 1 : 0;
8590             sfttab[SFT_AUTH] = ftp_aut;
8591             break;
8592           case ENA_FEAT:                /* OK to send FEAT command */
8593             featok = (cx == FTP_ENA) ? 1 : 0;
8594             break;
8595           case ENA_MLST:                /* OK to use MLST/MLSD */
8596             mlstok = (cx == FTP_ENA) ? 1 : 0;
8597             sfttab[SFT_MLST] = mlstok;
8598             break;
8599           case ENA_MDTM:                /* OK to use MDTM */
8600             mdtmok = (cx == FTP_ENA) ? 1 : 0;
8601             sfttab[SFT_MDTM] = mdtmok;
8602             break;
8603           case ENA_SIZE:                /* OK to use SIZE */
8604             sizeok = (cx == FTP_ENA) ? 1 : 0;
8605             sfttab[SFT_SIZE] = sizeok;
8606             break;
8607         }
8608         return(success = 1);
8609     }
8610     return(-2);
8611 }
8612
8613 #ifndef NOSHOW
8614 static char *
8615 shopl(x) int x; {
8616     switch (x) {
8617       case FPL_CLR: return("clear");
8618       case FPL_PRV: return("private");
8619       case FPL_SAF: return("safe");
8620       case 0:  return("(not set)");
8621       default: return("(unknown)");
8622     }
8623 }
8624
8625 int
8626 shoftp(brief) int brief; {
8627     char * s = "?";
8628     int n, x;
8629
8630     if (g_ftp_typ > -1) {               /* Restore TYPE if saved */
8631         ftp_typ = g_ftp_typ;
8632         /* g_ftp_typ = -1; */
8633     }
8634     printf("\n");
8635     printf("FTP connection:                 %s\n",connected ?
8636            ftp_host :
8637            "(none)"
8638            );
8639     n = 2;
8640     if (connected) {
8641         n++;
8642         printf("FTP server type:                %s\n",
8643                ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)");
8644     }
8645     if (loggedin)
8646       printf("Logged in as:                   %s\n",
8647              strval(ftp_logname,"(unknown)"));
8648     else
8649       printf("Not logged in\n");
8650     n++;
8651     if (brief) return(0);
8652
8653     printf("\nSET FTP values:\n\n");
8654     n += 3;
8655
8656     printf(" ftp anonymous-password:        %s\n",
8657            ftp_apw ? ftp_apw : "(default)"
8658            );
8659     printf(" ftp auto-login:                %s\n",showoff(ftp_log));
8660     printf(" ftp auto-authentication:       %s\n",showoff(ftp_aut));
8661     switch (ftp_typ) {
8662       case FTT_ASC: s = "text"; break;
8663       case FTT_BIN: s = "binary"; break;
8664       case FTT_TEN: s = "tenex"; break;
8665     }
8666 #ifdef FTP_TIMEOUT
8667     printf(" ftp timeout:                   %ld\n",ftp_timeout);
8668 #endif  /* FTP_TIMEOUT */
8669     printf(" ftp type:                      %s\n",s);
8670     printf(" ftp get-filetype-switching:    %s\n",showoff(get_auto));
8671     printf(" ftp dates:                     %s\n",showoff(ftp_dates));
8672     printf(" ftp error-action:              %s\n",ftp_err ? "quit":"proceed");
8673     printf(" ftp filenames:                 %s\n",
8674            ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal")
8675            );
8676     printf(" ftp debug                      %s\n",showoff(ftp_deb));
8677
8678     printf(" ftp passive-mode:              %s\n",showoff(ftp_psv));
8679     printf(" ftp permissions:               %s\n",showooa(ftp_prm));
8680     printf(" ftp verbose-mode:              %s\n",showoff(ftp_vbx));
8681     printf(" ftp send-port-commands:        %s\n",showoff(ftp_psv));
8682     printf(" ftp unique-server-names:       %s\n",showoff(ftp_usn));
8683 #ifdef COMMENT
8684     /* See note in doxftp() */
8685     if (ftp_fnc < 0)
8686       ftp_fnc = fncact;
8687 #endif /* COMMENT */
8688     printf(" ftp collision:                 %s\n",
8689            fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]);
8690     printf(" ftp server-time-offset:        %s\n",
8691            fts_sto ? fts_sto : "(none)");
8692     n += 15;
8693
8694 #ifndef NOCSETS
8695     printf(" ftp character-set-translation: %s\n",showoff(ftp_xla));
8696     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8697
8698     printf(" ftp server-character-set:      %s\n",fcsinfo[ftp_csr].keyword);
8699     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8700
8701     printf(" file character-set:            %s\n",fcsinfo[fcharset].keyword);
8702     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8703 #endif /* NOCSETS */
8704
8705     x = ftp_dis;
8706     if (x < 0)
8707       x = fdispla;
8708     switch (x) {
8709       case XYFD_N: s = "none"; break;
8710       case XYFD_R: s = "serial"; break;
8711       case XYFD_C: s = "fullscreen"; break;
8712       case XYFD_S: s = "crt"; break;
8713       case XYFD_B: s = "brief"; break;
8714     }
8715     printf(" ftp display:                   %s\n",s);
8716     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8717
8718     if (mlstok || featok || mdtmok || sizeok || ftp_aut) {
8719         printf(" enabled:                      ");
8720         if (ftp_aut) printf(" AUTH");
8721         if (featok)  printf(" FEAT");
8722         if (mdtmok)  printf(" MDTM");
8723         if (mlstok)  printf(" MLST");
8724         if (sizeok)  printf(" SIZE");
8725         printf("\n");
8726         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8727     }
8728     if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) {
8729         printf(" disabled:                     ");
8730         if (!ftp_aut) printf(" AUTH");
8731         if (!featok)  printf(" FEAT");
8732         if (!mdtmok)  printf(" MDTM");
8733         if (!mlstok)  printf(" MLST");
8734         if (!sizeok)  printf(" SIZE");
8735         printf("\n");
8736         if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8737     }
8738     switch (ftpget) {
8739       case 0: s = "kermit"; break;
8740       case 1: s = "ftp"; break;
8741       case 2: s = "auto"; break;
8742       default: s = "?";
8743     }
8744     printf(" get-put-remote:                %s\n",s);
8745     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8746
8747     printf("\n");
8748     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8749
8750 #ifdef FTP_SECURITY
8751     printf("Available security methods:    ");
8752 #ifdef FTP_GSSAPI
8753     printf("GSSAPI ");
8754 #endif /* FTP_GSSAPI */
8755 #ifdef FTP_KRB4
8756     printf("Kerberos4 ");
8757 #endif /* FTP_KRB4 */
8758 #ifdef FTP_SRP
8759     printf("SRP ");
8760 #endif /* FTP_SRP */
8761 #ifdef FTP_SSL
8762     printf("SSL ");
8763 #endif /* FTP_SSL */
8764
8765     n++;
8766     printf("\n\n");
8767     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8768     printf(" ftp authtype:                  %s\n",strval(auth_type,NULL));
8769     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8770     printf(" ftp auto-encryption:           %s\n",showoff(ftp_cry));
8771     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8772     printf(" ftp credential-forwarding:     %s\n",showoff(ftp_cfw));
8773     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8774     printf(" ftp command-protection-level:  %s\n",shopl(ftp_cpl));
8775     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8776     printf(" ftp data-protection-level:     %s\n",shopl(ftp_dpl));
8777     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8778     printf(" ftp secure proxy:              %s\n",shopl(ssl_ftp_proxy));
8779     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8780 #else
8781     printf("Available security methods:     (none)\n");
8782     if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; }
8783 #endif /* FTP_SECURITY */
8784
8785     if (n <= cmd_rows - 3)
8786       printf("\n");
8787     return(0);
8788 }
8789 #endif /* NOSHOW */
8790
8791 #ifndef NOHELP
8792 /* FTP HELP text strings */
8793
8794 static char * fhs_ftp[] = {
8795     "Syntax: FTP subcommand [ operands ]",
8796     "  Makes an FTP connection, or sends a command to the FTP server.",
8797     "  To see a list of available FTP subcommands, type \"ftp ?\".",
8798     "  and then use HELP FTP xxx to get help about subcommand xxx.",
8799     "  Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.",
8800     ""
8801 };
8802
8803 static char * fhs_acc[] = {             /* ACCOUNT */
8804     "Syntax: FTP ACCOUNT text",
8805     "  Sends an account designator to an FTP server that needs one.",
8806     "  Most FTP servers do not use accounts; some use them for other",
8807     "  other purposes, such as disk-access passwords.",
8808     ""
8809 };
8810 static char * fhs_app[] = {             /* APPEND */
8811     "Syntax: FTP APPEND filname",
8812     "  Equivalent to [ FTP ] PUT /APPEND.  See HELP FTP PUT.",
8813     ""
8814 };
8815 static char * fhs_cls[] = {             /* BYE, CLOSE */
8816     "Syntax: [ FTP ] BYE",
8817     "  Logs out from the FTP server and closes the FTP connection.",
8818     "  Also see HELP SET GET-PUT-REMOTE.  Synonym: [ FTP ] CLOSE.",
8819     ""
8820 };
8821 static char * fhs_cwd[] = {             /* CD, CWD */
8822     "Syntax: [ FTP ] CD directory",
8823     "  Asks the FTP server to change to the given directory.",
8824     "  Also see HELP SET GET-PUT-REMOTE.  Synonyms: [ FTP ] CWD, RCD, RCWD.",
8825     ""
8826 };
8827 static char * fhs_gup[] = {             /* CDUP, UP */
8828     "Syntax: FTP CDUP",
8829     "  Asks the FTP server to change to the parent directory of its current",
8830     "  directory.  Also see HELP SET GET-PUT-REMOTE.  Synonym: FTP UP.",
8831     ""
8832 };
8833 static char * fhs_chm[] = {             /* CHMOD */
8834     "Syntax: FTP CHMOD filename permissions",
8835     "  Asks the FTP server to change the permissions, protection, or mode of",
8836     "  the given file.  The given permissions must be in the syntax of the",
8837     "  the server's file system, e.g. an octal number for UNIX.  Also see",
8838     "  FTP PUT /PERMISSIONS",
8839     ""
8840 };
8841 static char * fhs_mde[] = {             /* DELETE */
8842     "Syntax: FTP DELETE [ switches ] filespec",
8843     "  Asks the FTP server to delete the given file or files.",
8844     "  Synonym: MDELETE (Kermit makes no distinction between single and",
8845     "  multiple file deletion).  Optional switches:",
8846     " ",
8847     "  /ERROR-ACTION:{PROCEED,QUIT}",
8848     "  /EXCEPT:pattern",
8849     "  /FILENAMES:{AUTO,CONVERTED,LITERAL}",
8850     "  /LARGER-THAN:number",
8851 #ifdef UNIXOROSK
8852     "  /NODOTFILES",
8853 #endif /* UNIXOROSK */
8854     "  /QUIET",
8855 #ifdef RECURSIVE
8856     "  /RECURSIVE (depends on server)",
8857     "  /SUBDIRECTORIES",
8858 #endif /* RECURSIVE */
8859     "  /SMALLER-THAN:number",
8860     ""
8861 };
8862 static char * fhs_dir[] = {             /* DIRECTORY */
8863     "Syntax: FTP DIRECTORY [ filespec ]",
8864     "  Asks the server to send a directory listing of the files that match",
8865     "  the given filespec, or if none is given, all the files in its current",
8866     "  directory.  The filespec, including any wildcards, must be in the",
8867     "  syntax of the server's file system.  Also see HELP SET GET-PUT-REMOTE.",
8868     "  Synonym: RDIRECTORY.",
8869     ""
8870 };
8871 static char * fhs_vdi[] = {             /* VDIRECTORY */
8872     "Syntax: FTP VDIRECTORY [ filespec ]",
8873     "  Asks the server to send a directory listing of the files that match",
8874     "  the given filespec, or if none is given, all the files in its current",
8875     "  directory.  VDIRECTORY is needed for getting verbose directory",
8876     "  listings from certain FTP servers, such as on TOPS-20.  Try it if",
8877     "  FTP DIRECTORY lists only filenames without details.",
8878     ""
8879 };
8880 static char * fhs_fea[] = {             /* FEATURES */
8881     "Syntax: FTP FEATURES",
8882     "  Asks the FTP server to list its special features.  Most FTP servers",
8883     "  do not recognize this command.",
8884     ""
8885 };
8886 static char * fhs_mge[] = {             /* MGET */
8887     "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]",
8888     "  Download a single file or multiple files.  Asks the FTP server to send",
8889     "  the given file or files.  Also see FTP GET.  Optional switches:",
8890     " ",
8891     "  /AS-NAME:text",
8892     "    Name under which to store incoming file.",
8893     "    Pattern required for for multiple files.",
8894     "  /BINARY",                        /* /IMAGE */
8895     "    Force binary mode.  Synonym: /IMAGE.",
8896     "  /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}",
8897    "    What to do if an incoming file has the same name as an existing file.",
8898
8899 #ifdef PUTPIPE
8900     "  /COMMAND",
8901     "    Specifies that the as-name is a command to which the incoming file",
8902     "    is to be piped as standard input.",
8903 #endif /* PUTPIPE */
8904
8905 #ifdef DOUPDATE
8906     "  /DATES-DIFFER",
8907     "    Download only those files whose modification date-times differ from",
8908     "    those of the corresponding local files, or that do not already",
8909     "    exist on the local computer.",
8910 #endif /* DOUPDATE */
8911
8912     "  /DELETE",
8913     "    Specifies that each file is to be deleted from the server after,",
8914     "    and only if, it is successfully downloaded.",
8915     "  /ERROR-ACTION:{PROCEED,QUIT}",
8916     "    When downloading a group of files, what to do upon failure to",
8917     "    transfer a file: quit or proceed to the next one.",
8918     "  /EXCEPT:pattern",
8919     "    Exception list: don't download any files that match this pattern.",
8920     "    See HELP WILDCARD for pattern syntax.",
8921     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
8922     "    Whether to convert incoming filenames to local syntax.",
8923 #ifdef PIPESEND
8924 #ifndef NOSPL
8925     "  /FILTER:command",
8926     "    Pass incoming files through the given command.",
8927 #endif /* NOSPL */
8928 #endif /* PIPESEND */
8929     "  /LARGER-THAN:number",
8930     "    Only download files that are larger than the given number of bytes.",
8931     "  /LISTFILE:filename",
8932     "    Obtain the list of files to download from the given file.",
8933 #ifndef NOCSETS
8934     "  /LOCAL-CHARACTER-SET:name",
8935     "    When downloading in text mode and character-set conversion is",
8936     "    desired, this specifies the target set.",
8937 #endif /* NOCSETS */
8938     "  /MATCH:pattern",
8939     "    Specifies a pattern to be used to select filenames locally from the",
8940     "    server's list.",
8941     "  /MLSD",
8942     "    Forces sending of MLSD (rather than NLST) to get the file list.",
8943 #ifdef CK_TMPDIR
8944     "  /MOVE-TO:directory",
8945     "    Each file that is downloaded is to be moved to the given local",
8946     "    directory immediately after, and only if, it has been received",
8947     "    successfully.",
8948 #endif /* CK_TMPDIR */
8949     "  /NAMELIST:filename",
8950     "    Instead of downloading the files, stores the list of files that",
8951     "    would be downloaded in the given local file, one filename per line.",
8952     "  /NLST",
8953     "    Forces sending of NLST (rather than MLSD) to get the file list.",
8954     "  /NOBACKUPFILES",
8955     "    Don't download any files whose names end with .~<number>~.",
8956     "  /NODOTFILES",
8957     "    Don't download any files whose names begin with period (.).",
8958     "  /QUIET",
8959     "    Suppress the file-transfer display.",
8960 #ifdef FTP_RESTART
8961     "  /RECOVER",                       /* /RESTART */
8962     "    Resume a download that was previously interrupted from the point of",
8963     "    failure.  Works only in binary mode.  Not supported by all servers.",
8964     "    Synonym: /RESTART.",
8965 #endif /* FTP_RESTART */
8966 #ifdef RECURSIVE
8967     "  /RECURSIVE",                     /* /SUBDIRECTORIES */
8968     "    Create subdirectories automatically if the server sends files",
8969     "    recursively and includes pathnames (most don't).",
8970 #endif /* RECURSIVE */
8971     "  /RENAME-TO:text",
8972     "    Each file that is downloaded is to be renamed as indicated just,",
8973     "    after, and only if, it has arrived successfully.",
8974 #ifndef NOCSETS
8975     "  /SERVER-CHARACTER-SET:name",
8976     "    When downloading in text mode and character-set conversion is desired"
8977 ,   "    this specifies the original file's character set on the server.",
8978 #endif /* NOCSETS */
8979     "  /SERVER-RENAME:text",
8980     "    Each server source file is to be renamed on the server as indicated",
8981     "    immediately after, but only if, it has arrived successfully.",
8982     "  /SMALLER-THAN:number",
8983     "    Download only those files smaller than the given number of bytes.",
8984     "  /TEXT",                          /* /ASCII */
8985     "    Force text mode.  Synonym: /ASCII.",
8986     "  /TENEX",
8987     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
8988 #ifndef NOCSETS
8989     "  /TRANSPARENT",
8990     "    When downloading in text mode, do not convert chracter-sets.",
8991 #endif /* NOCSETS */
8992     "  /TO-SCREEN",
8993     "    The downloaded file is to be displayed on the screen.",
8994 #ifdef DOUPDATE
8995     "  /UPDATE",
8996     "    Equivalent to /COLLISION:UPDATE.  Download only those files that are",
8997     "    newer than than their local counterparts, or that do not exist on",
8998     "    the local computer.",
8999 #endif /* DOUPDATE */
9000     ""
9001 };
9002 static char * fhs_hlp[] = {             /* HELP */
9003     "Syntax: FTP HELP [ command [ subcommand... ] ]",
9004     "  Asks the FTP server for help about the given command.  First use",
9005     "  FTP HELP by itself to get a list of commands, then use HELP FTP xxx",
9006     "  to get help for command \"xxx\".  Synonyms: REMOTE HELP, RHELP.",
9007     ""
9008 };
9009 static char * fhs_idl[] = {             /* IDLE */
9010     "Syntax: FTP IDLE [ number ]",
9011     "  If given without a number, this asks the FTP server to tell its",
9012     "  current idle-time limit.  If given with a number, it asks the server",
9013     "  to change its idle-time limit to the given number of seconds.",
9014     ""
9015 };
9016 static char * fhs_usr[] = {             /* USER, LOGIN */
9017     "Syntax: FTP USER username [ password [ account ] ]",
9018     "  Log in to the FTP server.  To be used when connected but not yet",
9019     "  logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.",
9020     "  If you omit the password, and one is required by the server, you are",
9021     "  prompted for it.  If you omit the account, no account is sent.",
9022     "  Synonym: FTP LOGIN.",
9023     ""
9024 };
9025 static char * fhs_get[] = {             /* GET */
9026     "Syntax: [ FTP ] GET [ options ] filename [ as-name ]",
9027     "  Download a single file.  Asks the FTP server to send the given file.",
9028     "  The optional as-name is the name to store it under when it arrives;",
9029     "  if omitted, the file is stored with the name it arrived with, as",
9030     "  modified according to the FTP FILENAMES setting or /FILENAMES: switch",
9031     "  value.  Aside from the file list and as-name, syntax and options are",
9032     "  the same as for FTP MGET, which is used for downloading multiple files."
9033 ,   ""
9034 };
9035 static char * fhs_mkd[] = {             /* MKDIR */
9036     "Syntax: FTP MKDIR directory",
9037     "  Asks the FTP server to create a directory with the given name,",
9038     "  which must be in the syntax of the server's file system.  Synonyms:",
9039     "  REMOTE MKDIR, RMKDIR.",
9040     ""
9041 };
9042 static char * fhs_mod[] = {             /* MODTIME */
9043     "Syntax: FTP MODTIME filename",
9044     "  Asks the FTP server to send the modification time of the given file,",
9045     "  to be displayed on the screen.  The date-time format is all numeric:",
9046     "  yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating",
9047     "  fractions of seconds).",
9048     ""
9049 };
9050 static char * fhs_mpu[] = {             /* MPUT */
9051     "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]",
9052     "  Uploads files.  Sends the given file or files to the FTP server.",
9053     "  Also see FTP PUT.  Optional switches are:",
9054     " ",
9055     "  /AFTER:date-time",
9056     "    Uploads only those files newer than the given date-time.",
9057     "    HELP DATE for info about date-time formats.  Synonym: /SINCE.",
9058 #ifdef PUTARRAY
9059     "  /ARRAY:array-designator",
9060     "    Tells Kermit to upload the contents of the given array, rather than",
9061     "    a file.",
9062 #endif /* PUTARRAY */
9063     "  /AS-NAME:text",
9064     "    Name under which to send files.",
9065     "    Pattern required for for multiple files.",
9066     "  /BEFORE:date-time",
9067     "    Upload only those files older than the given date-time.",
9068     "  /BINARY",
9069     "    Force binary mode.  Synonym: /IMAGE.",
9070 #ifdef PUTPIPE
9071     "  /COMMAND",
9072     "    Specifies that the filespec is a command whose standard output is",
9073     "    to be sent.",
9074 #endif /* PUTPIPE */
9075
9076 #ifdef COMMENT
9077 #ifdef DOUPDATE
9078     "  /DATES-DIFFER",
9079     "    Upload only those files whose modification date-times differ from",
9080     "    those on the server, or that don't exist on the server at all.",
9081 #endif /* DOUPDATE */
9082 #endif /* COMMENT */
9083
9084     "  /DELETE",
9085     "    Specifies that each source file is to be deleted after, and only if,",
9086     "    it is successfully uploaded.",
9087     "  /DOTFILES",
9088     "    Include files whose names begin with period (.).",
9089     "  /ERROR-ACTION:{PROCEED,QUIT}",
9090     "    When uploading a group of files, what to do upon failure to",
9091     "    transfer a file: quit or proceed to the next one.",
9092     "  /EXCEPT:pattern",
9093     "    Exception list: don't upload any files that match this pattern.",
9094     "    See HELP WILDCARD for pattern syntax.",
9095     "  /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}",
9096     "    Whether to convert outbound filenames to common syntax.",
9097 #ifdef PIPESEND
9098 #ifndef NOSPL
9099     "  /FILTER:command",
9100     "    Pass outbound files through the given command.",
9101 #endif /* NOSPL */
9102 #endif /* PIPESEND */
9103 #ifdef CKSYMLINK
9104     "  /FOLLOWINKS",
9105     "    Send files that are pointed to by symbolic links.",
9106     "  /NOFOLLOWINKS",
9107     "    Skip over symbolic links (default).",
9108 #endif /* CKSYMLINK */
9109     "  /LARGER-THAN:number",
9110     "    Only upload files that are larger than the given number of bytes.",
9111     "  /LISTFILE:filename",
9112     "    Obtain the list of files to upload from the given file.",
9113 #ifndef NOCSETS
9114     "  /LOCAL-CHARACTER-SET:name",
9115     "    When uploading in text mode and character-set conversion is",
9116     "    desired, this specifies the source-file character set.",
9117 #endif /* NOCSETS */
9118 #ifdef CK_TMPDIR
9119     "  /MOVE-TO:directory",
9120     "    Each source file that is uploaded is to be moved to the given local",
9121     "    directory when, and only if, the transfer is successful.",
9122 #endif /* CK_TMPDIR */
9123     "  /NOBACKUPFILES",
9124     "    Don't upload any files whose names end with .~<number>~.",
9125 #ifdef UNIXOROSK
9126     "  /NODOTFILES",
9127     "    Don't upload any files whose names begin with period (.).",
9128 #endif /* UNIXOROSK */
9129     "  /NOT-AFTER:date-time",
9130     "    Upload only files that are not newer than the given date-time",
9131     "  /NOT-BEFORE:date-time",
9132     "    Upload only files that are not older than the given date-time",
9133 #ifdef UNIX
9134     "  /PERMISSIONS",
9135     "    Ask the server to set the permissions of each file it receives",
9136     "    according to the source file's permissions.",
9137 #endif /* UNIX */
9138     "  /QUIET",
9139     "    Suppress the file-transfer display.",
9140 #ifdef FTP_RESTART
9141     "  /RECOVER",
9142     "    Resume an upload that was previously interrupted from the point of",
9143     "    failure.  Synonym: /RESTART.",
9144 #endif /* FTP_RESTART */
9145 #ifdef RECURSIVE
9146     "  /RECURSIVE",
9147     "    Send files from the given directory and all the directories beneath",
9148     "    it.  Synonym: /SUBDIRECTORIES.",
9149 #endif /* RECURSIVE */
9150     "  /RENAME-TO:text",
9151     "    Each source file that is uploaded is to be renamed on the local",
9152     "    local computer as indicated when and only if, the transfer completes",
9153     "    successfully.",
9154 #ifndef NOCSETS
9155     "  /SERVER-CHARACTER-SET:name",
9156     "    When uploading in text mode and character-set conversion is desired,",
9157     "    this specifies the character set to which the file should be",
9158     "    converted for storage on the server.",
9159 #endif /* NOCSETS */
9160     "  /SERVER-RENAME:text",
9161     "    Each file that is uploaded is to be renamed as indicated on the",
9162     "    server after, and only if, if arrives successfully.",
9163     "  /SIMULATE",
9164     "    Show which files would be sent without actually sending them.",
9165     "  /SMALLER-THAN:number",
9166     "    Upload only those files smaller than the given number of bytes.",
9167     "  /TEXT",
9168     "    Force text mode.  Synonym: /ASCII.",
9169     "  /TENEX",
9170     "    Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).",
9171 #ifndef NOCSETS
9172     "  /TRANSPARENT",
9173     "    When uploading in text mode, do not convert chracter-sets.",
9174 #endif /* NOCSETS */
9175     "  /TYPE:{TEXT,BINARY}",
9176     "    Upload only files of the given type.",
9177 #ifdef DOUPDATE
9178     "  /UPDATE",
9179     "    If a file of the same name exists on the server, upload only if",
9180     "    the local file is newer.",
9181 #endif /* DOUPDATE */
9182     "  /UNIQUE-SERVER-NAMES",
9183     "    Ask the server to compute new names for any incoming file that has",
9184     "    the same name as an existing file.",
9185     ""
9186 };
9187 static char * fhs_opn[] = {             /* OPEN */
9188 #ifdef CK_SSL
9189     "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]",
9190     "  Opens a connection to the FTP server on the given host.  The default",
9191     "  TCP port is 21 (990 if SSL/TLS is used), but a different port number",
9192     "  can be supplied if necessary.  Optional switches are:",
9193 #else /* CK_SSL */
9194     "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]",
9195     "  Opens a connection to the FTP server on the given host.  The default",
9196     "  TCP port is 21, but a different port number can be supplied if",
9197     "  necessary.  Optional switches are:",
9198 #endif /* CK_SSL */
9199     " ",
9200     "  /ANONYMOUS",
9201     "    Logs you in anonymously.",
9202     "  /USER:text",
9203     "    Supplies the given text as your username.",
9204     "  /PASSWORD:text",
9205     "    Supplies the given text as your password.  If you include a username",
9206     "    but omit this switch and the server requires a password, you are",
9207     "    prompted for it.",
9208     "  /ACCOUNT:text",
9209     "    Supplies the given text as your account, if required by the server.",
9210     "  /ACTIVE",
9211     "    Forces an active (rather than passive) connection.",
9212     "  /PASSIVE",
9213     "    Forces a passive (rather than active) connection.",
9214     "  /NOINIT",
9215     "    Inhibits sending initial REST, STRU, and MODE commands, which are",
9216     "    well-known standard commands, but to which some servers react badly.",
9217     "  /NOLOGIN",
9218     "    Inhibits autologin for this connection only.",
9219     ""
9220 };
9221 static char * fhs_opt[] = {             /* OPTS, OPTIONS */
9222     "Syntax: FTP OPTIONS",
9223     "  Asks the FTP server to list its current options.  Advanced, new,",
9224     "  not supported by most FTP servers.",
9225     ""
9226 };
9227 static char * fhs_put[] = {             /* PUT, SEND */
9228     "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]",
9229     "  Like FTP MPUT, but only one filespec is allowed, and if it is followed",
9230     "  by an additional field, this is interpreted as the name under which",
9231     "  to send the file or files.  See HELP FTP MPUT.",
9232     ""
9233 };
9234 static char * fhs_reput[] = {           /* REPUT, RESEND */
9235     "Syntax: [ FTP ] REPUT [ switches ] filespec [ as-name ]",
9236     "  Synonym for FTP PUT /RECOVER.  Recovers an interrupted binary-mode",
9237     "  upload from the point of failure if the FTP server supports recovery.",
9238     "  Synonym: [ FTP ] RESEND.  For details see HELP FTP MPUT.",
9239     ""
9240 };
9241 static char * fhs_pwd[] = {             /* PWD */
9242     "Syntax: FTP PWD",
9243     "  Asks the FTP server to reveal its current working directory.",
9244     "  Synonyms: REMOTE PWD, RPWD.",
9245     ""
9246 };
9247 static char * fhs_quo[] = {             /* QUOTE */
9248     "Syntax: FTP QUOTE text",
9249     "  Sends an FTP protocol command to the FTP server.  Use this command",
9250     "  for sending commands that Kermit might not support.",
9251     ""
9252 };
9253 static char * fhs_rge[] = {             /* REGET */
9254     "Syntax: FTP REGET",
9255     "  Synonym for FTP GET /RECOVER.",
9256     ""
9257 };
9258 static char * fhs_ren[] = {             /* RENAME */
9259     "Syntax: FTP RENAME name1 name1",
9260     "  Asks the FTP server to change the name of the file whose name is name1",
9261     "  and which resides in the FTP server's file system, to name2.  Works",
9262     "  only for single files; wildcards are not accepted.",
9263     ""
9264 };
9265 static char * fhs_res[] = {             /* RESET */
9266     "Syntax: FTP RESET",
9267     "  Asks the server to log out your session, terminating your access",
9268     "  rights, without closing the connection.",
9269     ""
9270 };
9271 static char * fhs_rmd[] = {             /* RMDIR */
9272     "Syntax: FTP RMDIR directory",
9273     "  Asks the FTP server to remove the directory whose name is given.",
9274     "  This usually requires the directory to be empty.  Synonyms: REMOTE",
9275     "  RMDIR, RRMDIR.",
9276     ""
9277 };
9278 static char * fhs_sit[] = {             /* SITE */
9279     "Syntax: FTP SITE text",
9280     "  Sends a site-specific command to the FTP server.",
9281     ""
9282 };
9283 static char * fhs_siz[] = {             /* SIZE */
9284     "Syntax: FTP SIZE filename",
9285     "  Asks the FTP server to send a numeric string representing the size",
9286     "  of the given file.",
9287     ""
9288 };
9289 static char * fhs_sta[] = {             /* STATUS */
9290     "Syntax: FTP STATUS [ filename ]",
9291     "  Asks the FTP server to report its status.  If a filename is given,",
9292     "  the FTP server should report details about the file.",
9293     ""
9294 };
9295 static char * fhs_sys[] = {             /* SYSTEM */
9296     "Syntax: FTP SYSTEM",
9297     "  Asks the FTP server to report its operating system type.",
9298     ""
9299 };
9300 static char * fhs_typ[] = {             /* TYPE */
9301     "Syntax: FTP TYPE { TEXT, BINARY, TENEX }",
9302     "  Puts the client and server in the indicated transfer mode.",
9303     "  ASCII is a synonym for TEXT.  TENEX is used only for uploading 8-bit",
9304     "  binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or",
9305     "  downloading files from TENEX or TOPS-20 that have been uploaded in",
9306     "  TENEX mode.",
9307     ""
9308 };
9309 static char * fhs_uma[] = {             /* UMASK */
9310     "Syntax: FTP UMASK number",
9311     "  Asks the FTP server to set its file creation mode mask.  Applies",
9312     "  only (or mainly) to UNIX-based FTP servers.",
9313     ""
9314 };
9315 static char * fhs_chk[] = {             /* CHECK */
9316     "Syntax: FTP CHECK remote-filespec",
9317     "  Asks the FTP server if the given file or files exist.  If the",
9318     "  remote-filespec contains wildcards, this command fails if no server",
9319     "  files match, and succeeds if at least one file matches.  If the",
9320     "  remote-filespec does not contain wildcards, this command succeeds if",
9321     "  the given file exists and fails if it does not.",
9322     ""
9323 };
9324 static char * fhs_ena[] = {             /* ENABLE */
9325     "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9326     "  Enables the use of the given FTP protocol command in case it has been",
9327     "  disabled (but this is no guarantee that the FTP server understands it)."
9328 ,
9329     "  Use SHOW FTP to see which of these commands is enabled and disabled.",
9330     "  Also see FTP DISABLE.",
9331     ""
9332 };
9333 static char * fhs_dis[] = {             /* DISABLE */
9334     "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }",
9335     "  Disables the use of the given FTP protocol command.",
9336     "  Also see FTP ENABLE.",
9337     ""
9338 };
9339
9340 #endif /* NOHELP */
9341
9342 int
9343 doftphlp() {
9344     int cx;
9345     if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0)
9346       if (cx != -3)
9347         return(cx);
9348     if ((x = cmcfm()) < 0)
9349       return(x);
9350
9351 #ifdef NOHELP
9352     printf("Sorry, no help available\n");
9353 #else
9354     switch (cx) {
9355       case -3:
9356         return(hmsga(fhs_ftp));
9357       case FTP_ACC:                     /* ACCOUNT */
9358         return(hmsga(fhs_acc));
9359       case FTP_APP:                     /* APPEND */
9360         return(hmsga(fhs_app));
9361       case FTP_CLS:                     /* BYE, CLOSE */
9362         return(hmsga(fhs_cls));
9363       case FTP_CWD:                     /* CD, CWD */
9364         return(hmsga(fhs_cwd));
9365       case FTP_GUP:                     /* CDUP, UP */
9366         return(hmsga(fhs_gup));
9367       case FTP_CHM:                     /* CHMOD */
9368         return(hmsga(fhs_chm));
9369       case FTP_MDE:                     /* DELETE, MDELETE */
9370         return(hmsga(fhs_mde));
9371       case FTP_DIR:                     /* DIRECTORY */
9372         return(hmsga(fhs_dir));
9373       case FTP_VDI:                     /* VDIRECTORY */
9374         return(hmsga(fhs_vdi));
9375       case FTP_FEA:                     /* FEATURES */
9376         return(hmsga(fhs_fea));
9377       case FTP_GET:                     /* GET */
9378         return(hmsga(fhs_get));
9379       case FTP_HLP:                     /* HELP */
9380         return(hmsga(fhs_hlp));
9381       case FTP_IDL:                     /* IDLE */
9382         return(hmsga(fhs_idl));
9383       case FTP_USR:                     /* USER, LOGIN */
9384         return(hmsga(fhs_usr));
9385       case FTP_MGE:                     /* MGET */
9386         return(hmsga(fhs_mge));
9387       case FTP_MKD:                     /* MKDIR */
9388         return(hmsga(fhs_mkd));
9389       case FTP_MOD:                     /* MODTIME */
9390         return(hmsga(fhs_mod));
9391       case FTP_MPU:                     /* MPUT */
9392         return(hmsga(fhs_mpu));
9393       case FTP_OPN:                     /* OPEN */
9394         return(hmsga(fhs_opn));
9395       case FTP_OPT:                     /* OPTS, OPTIONS */
9396         return(hmsga(fhs_opt));
9397       case FTP_PUT:                     /* PUT, SEND */
9398         return(hmsga(fhs_put));
9399       case FTP_REP:                     /* REPUT, RESEND */
9400         return(hmsga(fhs_reput));
9401       case FTP_PWD:                     /* PWD */
9402         return(hmsga(fhs_pwd));
9403       case FTP_QUO:                     /* QUOTE */
9404         return(hmsga(fhs_quo));
9405       case FTP_RGE:                     /* REGET */
9406         return(hmsga(fhs_rge));
9407       case FTP_REN:                     /* RENAME */
9408         return(hmsga(fhs_ren));
9409       case FTP_RES:                     /* RESET */
9410         return(hmsga(fhs_res));
9411       case FTP_RMD:                     /* RMDIR */
9412         return(hmsga(fhs_rmd));
9413       case FTP_SIT:                     /* SITE */
9414         return(hmsga(fhs_sit));
9415       case FTP_SIZ:                     /* SIZE */
9416         return(hmsga(fhs_siz));
9417       case FTP_STA:                     /* STATUS */
9418         return(hmsga(fhs_sta));
9419       case FTP_SYS:                     /* SYSTEM */
9420         return(hmsga(fhs_sys));
9421       case FTP_TYP:                     /* TYPE */
9422         return(hmsga(fhs_typ));
9423       case FTP_UMA:                     /* UMASK */
9424         return(hmsga(fhs_uma));
9425       case FTP_CHK:                     /* CHECK */
9426         return(hmsga(fhs_chk));
9427       case FTP_ENA:
9428         return(hmsga(fhs_ena));
9429       case FTP_DIS:
9430         return(hmsga(fhs_dis));
9431       default:
9432         printf("Sorry, help available for this command.\n");
9433         break;
9434     }
9435 #endif /* NOHELP */
9436     return(success = 0);
9437 }
9438
9439 int
9440 dosetftphlp() {
9441     int cx;
9442     if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0)
9443       if (cx != -3)
9444         return(cx);
9445     if (cx != -3)
9446       ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ);
9447     if ((x = cmcfm()) < 0)
9448       return(x);
9449
9450 #ifdef NOHELP
9451     printf("Sorry, no help available\n");
9452 #else
9453     switch (cx) {
9454       case -3:
9455         printf("\nSyntax: SET FTP parameter value\n");
9456         printf("  Type \"help set ftp ?\" for a list of parameters.\n");
9457         printf("  Type \"help set ftp xxx\" for information about setting\n");
9458         printf("  parameter xxx.  Type \"show ftp\" for current values.\n\n");
9459         return(0);
9460
9461       case FTS_BUG:
9462         printf("\nSyntax: SET FTP BUG <name> {ON, OFF}\n");
9463         printf(
9464             "  Activates a workaround for the named bug in the FTP server.\n");
9465         printf("  Type SET FTP BUG ? for a list of names.\n");
9466         printf("  For each bug, the default is OFF\n\n");
9467         return(0);
9468
9469 #ifdef FTP_SECURITY
9470       case FTS_ATP:                     /* "authtype" */
9471         printf("\nSyntax: SET FTP AUTHTYPE list\n");
9472         printf("  Specifies an ordered list of authentication methods to be\n"
9473                );
9474         printf("  when FTP AUTOAUTHENTICATION is ON.  The default list is:\n");
9475         printf("  GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n");
9476         return(0);
9477
9478       case FTS_AUT:                     /* "autoauthentication" */
9479         printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n");
9480         printf("  Tells whether authentication should be negotiated by the\n");
9481         printf("  FTP OPEN command.  Default is ON.\n\n");
9482         break;
9483
9484       case FTS_CRY:                     /* "autoencryption" */
9485         printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n");
9486         printf("  Tells whether encryption (privacy) should be negotiated\n");
9487         printf("  by the FTP OPEN command.  Default is ON.\n\n");
9488         break;
9489 #endif /* FTP_SECURITY */
9490
9491       case FTS_LOG:                     /* "autologin" */
9492         printf("\nSET FTP AUTOLOGIN { ON, OFF }\n");
9493         printf("  Tells Kermit whether to try to log you in automatically\n");
9494         printf("  as part of the connection process.\n\n");
9495         break;
9496
9497       case FTS_DIS:
9498         printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n");
9499         printf("  Chooses the file-transfer display style for FTP.\n");
9500         printf("  Like SET TRANSFER DISPLAY but applies only to FTP.\n\n");
9501         break;
9502
9503 #ifndef NOCSETS
9504       case FTS_XLA:                     /* "character-set-translation" */
9505         printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n");
9506         printf("  Whether to translate character sets when transferring\n");
9507         printf("  text files with FTP.  OFF by default.\n\n");
9508         break;
9509
9510 #endif /* NOCSETS */
9511       case FTS_FNC:                     /* "collision" */
9512         printf("\n");
9513         printf(
9514 "Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n"
9515                );
9516         printf("  Tells what do when an incoming file has the same name as\n");
9517         printf("  an existing file when downloading with FTP.\n\n");
9518         break;
9519
9520 #ifdef FTP_SECURITY
9521       case FTS_CPL:                     /* "command-protection-level" */
9522         printf("\n");
9523         printf(
9524 "Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9525                );
9526         printf("\n");
9527         printf(
9528 "  Tells what level of protection is applied to the FTP command channel.\n\n");
9529         break;
9530       case FTS_CFW:                     /* "credential-forwarding" */
9531         printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n");
9532         printf("  Tells whether end-user credentials are to be forwarded\n");
9533         printf("  to the server if supported by the authentication method\n");
9534         printf("  (GSSAPI-KRB5 only).\n\n");
9535         break;
9536       case FTS_DPL:                     /* "data-protection-level" */
9537         printf("\n");
9538         printf(
9539 "Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }"
9540                );
9541         printf("\n");
9542         printf(
9543 "  Tells what level of protection is applied to the FTP data channel.\n\n");
9544         break;
9545 #endif /* FTP_SECURITY */
9546
9547       case FTS_DBG:                     /* "debug" */
9548         printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n");
9549         printf("  Whether to print FTP protocol messages.\n\n");
9550         return(0);
9551
9552       case FTS_ERR:                     /* "error-action" */
9553         printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n");
9554         printf("  What to do when an error occurs when transferring a group\n")
9555           ;
9556         printf("  of files: quit and fail, or proceed to the next file.\n\n");
9557         return(0);
9558
9559       case FTS_CNV:                     /* "filenames" */
9560         printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n");
9561         printf("  What to do with filenames: convert them, take and use them\n"
9562                );
9563         printf("  literally; or choose what to do automatically based on the\n"
9564                );
9565         printf("  OS type of the server.  The default is AUTO.\n\n");
9566         return(0);
9567
9568       case FTS_PSV:                     /* "passive-mode" */
9569         printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n");
9570         printf("  Whether to use passive mode, which helps to get through\n");
9571         printf("  firewalls.  ON by default.\n\n");
9572         return(0);
9573
9574       case FTS_PRM:                     /* "permissions" */
9575         printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n");
9576         printf("  Whether to try to send file permissions when uploading.\n");
9577         printf("  OFF by default.  AUTO means only if client and server\n");
9578         printf("  have the same OS type.\n\n");
9579         return(0);
9580
9581       case FTS_TST:                     /* "progress-messages" */
9582         printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n");
9583         printf("  Whether Kermit should print locally-generated feedback\n");
9584         printf("  messages for each non-file-transfer command.");
9585         printf("  ON by default.\n\n");
9586         return(0);
9587
9588       case FTS_SPC:                     /* "send-port-commands" */
9589         printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n");
9590         printf("  Whether Kermit should send a new PORT command for each");
9591         printf("  task.\n\n");
9592         return(0);
9593
9594 #ifndef NOCSETS
9595       case FTS_CSR:                     /* "server-character-set" */
9596         printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n");
9597         printf("  The name of the character set used for text files on the\n");
9598         printf("  server.  Enter a name of '?' for a menu.\n\n");
9599         return(0);
9600 #endif /* NOCSETS */
9601
9602       case FTS_STO:                     /* "server-time-offset */
9603         printf(
9604 "\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n");
9605         printf(
9606 "  Specifies an offset to apply to the server's file timestamps.\n");
9607         printf(
9608 "  Use this to correct for misconfigured server time or timezone.\n");
9609         printf(
9610 "  Format: must begin with + or - sign.  Hours must be given; minutes\n");
9611         printf(
9612 "  and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n");
9613         return(0);
9614
9615       case FTS_TYP:                     /* "type" */
9616         printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n");
9617         printf("  Establishes the default transfer mode.\n");
9618         printf("  TENEX is used for uploading 8-bit binary files to 36-bit\n");
9619         printf("  platforms such as TENEX and TOPS-20 and for downloading\n");
9620         printf("  them again.  ASCII is a synonym for TEXT.  Normally each\n");
9621         printf("  file's type is determined automatically from its contents\n"
9622                );
9623         printf("  or its name; SET FTP TYPE does not prevent that, it only\n");
9624         printf("  tells which mode to use when the type can't be determined\n"
9625                );
9626         printf("  automatically.  To completely disable automatic transfer-\n"
9627                );
9628         printf("  mode switching and force either text or binary mode, give\n"
9629                );
9630         printf("  the top-level command ASCII or BINARY, as in traditional\n");
9631         printf("  FTP clients.\n\n");
9632         return(0);
9633
9634 #ifdef FTP_TIMEOUT
9635       case FTS_TMO:
9636        printf("\nSyntax: SET FTP TIMEOUT number-of-seconds\n");
9637        printf("  Establishes a timeout for FTP transfers.\n");
9638        printf("  The timeout applies per network read or write on the data\n");
9639        printf("  connection, not to the whole transfer.  By default the\n");
9640        printf("  timeout value is 0, meaning no timeout.  Use a positive\n");
9641        printf("  number to escape gracefully from hung data connections or\n");
9642        printf("  directory listings.\n\n");
9643         return(0);
9644 #endif  /* FTP_TIMEOUT */
9645
9646 #ifdef PATTERNS
9647       case FTS_GFT:
9648         printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n");
9649         printf("  Tells whether GET and MGET should automatically switch\n");
9650         printf("  the appropriate file type, TEXT, BINARY, or TENEX, by\n");
9651         printf("  matching the name of each incoming file with its list of\n");
9652         printf("  FILE TEXT-PATTERNS and FILE BINARY-PATTERNS.  ON by\n");
9653         printf("  default.  SHOW PATTERNS displays the current pattern\n");
9654         printf("  list.  HELP SET FILE to see how to change it.\n");
9655         break;
9656 #endif /* PATTERNS */
9657
9658       case FTS_USN:                     /* "unique-server-names" */
9659         printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n");
9660         printf("  Tells whether to ask the server to create unique names\n");
9661         printf("  for any uploaded file that has the same name as an\n");
9662         printf("  existing file.  Default is OFF.\n\n");
9663         return(0);
9664
9665       case FTS_VBM:                     /* "verbose-mode" */
9666         printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n");
9667         printf("  Whether to display all responses from the FTP server.\n");
9668         printf("  OFF by default.\n\n");
9669         return(0);
9670
9671       case FTS_DAT:
9672         printf("\nSyntax: SET FTP DATES { ON, OFF }\n");
9673         printf("  Whether to set date of incoming files from the file date\n");
9674         printf("  on the server.  ON by default.  Note: there is no way to\n")
9675           ;
9676         printf("  set the date on files uploaded to the server.  Also note\n");
9677         printf("  that not all servers support this feature.\n\n");
9678         return(0);
9679
9680       case FTS_APW:
9681         printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n");
9682         printf("  Password to supply automatically on anonymous FTP\n");
9683         printf("  connections instead of the default user@host.\n");
9684         printf("  Omit optional text to restore default.\n\n");
9685         return(0);
9686
9687       default:
9688         printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf);
9689     }
9690 #endif /* NOHELP */
9691     return(0);
9692 }
9693
9694 #ifndef L_SET
9695 #define L_SET 0
9696 #endif /* L_SET */
9697 #ifndef L_INCR
9698 #define L_INCR 1
9699 #endif /* L_INCR */
9700
9701 #ifdef FTP_SRP
9702 char srp_user[BUFSIZ];                  /* where is BUFSIZ defined? */
9703 char *srp_pass;
9704 char *srp_acct;
9705 #endif /* FTP_SRP */
9706
9707 static int kerror;                      /* Needed for all auth types */
9708
9709 static struct   sockaddr_in hisctladdr;
9710 static struct   sockaddr_in hisdataaddr;
9711 static struct   sockaddr_in data_addr;
9712 static int      data = -1;
9713 static int      ptflag = 0;
9714 static struct   sockaddr_in myctladdr;
9715
9716 #ifdef COMMENT
9717 #ifndef OS2
9718 UID_T getuid();
9719 #endif /* OS2 */
9720 #endif /* COMMENT */
9721
9722
9723 static int cpend = 0;                   /* No pending replies */
9724
9725 #ifdef CK_SSL
9726 extern SSL *ssl_ftp_con;
9727 extern SSL_CTX *ssl_ftp_ctx;
9728 extern SSL *ssl_ftp_data_con;
9729 extern int ssl_ftp_active_flag;
9730 extern int ssl_ftp_data_active_flag;
9731 #endif /* CK_SSL */
9732
9733 /*  f t p c m d  --  Send a command to the FTP server  */
9734 /*
9735   Call with:
9736     char * cmd: The command to send.
9737     char * arg: The argument (e.g. a filename).
9738     int lcs: The local character set index.
9739     int rcs: The remote (server) character set index.
9740     int vbm: Verbose mode:
9741       0 = force verbosity off
9742      >0 = force verbosity on
9743
9744   If arg is given (not NULL or empty) and lcs != rcs and both are > -1,
9745   and neither lcs or rcs is UCS-2, the arg is translated from the local
9746   character set to the remote one before sending the result to the server.
9747
9748    Returns:
9749     0 on failure with ftpcode = -1
9750     >= 0 on success (getreply() result) with ftpcode = 0.
9751 */
9752 static char xcmdbuf[RFNBUFSIZ];
9753
9754 static int
9755 ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; {
9756     char * s = NULL;
9757     int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1;
9758     sig_t oldintr;
9759
9760     if (ftp_deb)                        /* DEBUG */
9761       vbm = 1;
9762     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
9763       vbm = 0;
9764     else if (vbm < 0)                   /* VERBOSE */
9765       vbm = ftp_vbm;
9766
9767     cancelfile = 0;
9768     if (!cmd) cmd = "";
9769     if (!arg) arg = "";
9770     cmdlen = (int)strlen(cmd);
9771     len = cmdlen + (int)strlen(arg) + 1;
9772
9773     if (ftp_deb /* && !dpyactive */ ) {
9774 #ifdef FTP_PROXY
9775         if (ftp_prx) printf("%s ", ftp_host);
9776 #endif /* FTP_PROXY */
9777         printf("---> ");
9778         if (!anonymous && strcmp("PASS",cmd) == 0)
9779           printf("PASS XXXX");
9780         else
9781           printf("%s %s",cmd,arg);
9782         printf("\n");
9783     }
9784     /* bzero(xcmdbuf,RFNBUFSIZ); */
9785     ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL);
9786
9787 #ifdef DEBUG
9788     if (deblog) {
9789         debug(F110,"ftpcmd cmd",cmd,0);
9790         debug(F110,"ftpcmd arg",arg,0);
9791         debug(F101,"ftpcmd lcs","",lcs);
9792         debug(F101,"ftpcmd rcs","",rcs);
9793     }
9794 #endif /* DEBUG */
9795
9796     if (csocket == -1) {
9797         perror("No control connection for command");
9798         ftpcode = -1;
9799         return(0);
9800     }
9801     havesigint = 0;
9802     oldintr = signal(SIGINT, cmdcancel);
9803
9804 #ifndef NOCSETS
9805     if (*arg &&                         /* If an arg was given */
9806         lcs > -1 &&                     /* and a local charset */
9807         rcs > -1 &&                     /* and a remote charset */
9808         lcs != rcs &&                   /* and the two are not the same */
9809         lcs != FC_UCS2 &&               /* and neither one is UCS-2 */
9810         rcs != FC_UCS2                  /* ... */
9811         ) {
9812         initxlate(lcs,rcs);             /* Translate arg from lcs to rcs */
9813         xgnbp = arg;                    /* Global pointer to input string */
9814         rfnptr = rfnbuf;                /* Global pointer to output buffer */
9815
9816         while (1) {
9817             if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break;
9818             if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break;
9819         }
9820         /*
9821           We have to copy here instead of translating directly into
9822           xcmdbuf[] so strputc() can check length.  Alternatively we could
9823           write yet another xpnbyte() output function.
9824         */
9825         if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) {
9826             printf("?FTP command too long: %s + arg\n",cmd);
9827             ftpcode = -1;
9828             return(0);
9829         }
9830         x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1));
9831     }
9832 #endif /* NOCSETS */
9833
9834     s = xcmdbuf;                        /* Command to send to server */
9835
9836 #ifdef DEBUG
9837     if (deblog) {                       /* Log it */
9838         if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) {
9839             /* But don't log passwords */
9840             debug(F110,"FTP SENT ","PASS XXXX",0);
9841         } else {
9842             debug(F110,"FTP SENT ",s,0);
9843         }
9844     }
9845 #endif /* DEBUG */
9846
9847 #ifdef CK_ENCRYPTION
9848   again:
9849 #endif /* CK_ENCRYPTION */
9850     if (scommand(s) == 0) {              /* Send it. */
9851       signal(SIGINT, oldintr);
9852       return(0);
9853     }
9854     cpend = 1;
9855     x = !strcmp(cmd,"QUIT");            /* Is it the QUIT command? */
9856     if (x)                              /* In case we're interrupted */
9857       connected = 0;                    /* while waiting for the reply... */
9858
9859     fc = 0;                             /* Function code for getreply() */
9860     if (!strncmp(cmd,"AUTH ",5)         /* Must parse AUTH reply */
9861 #ifdef FTPHOST
9862         && strncmp(cmd, "HOST ",5)
9863 #endif /* FTPHOST */
9864         ) {
9865         fc = GRF_AUTH;
9866     } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */
9867         fc = GRF_FEAT;                  /* But FEAT not widely understood */
9868         if (!ftp_deb)                   /* So suppress error messages */
9869           vbm = 9;
9870     }
9871     r = getreply(x,                     /* Expect connection to close */
9872                  lcs,rcs,               /* Charsets */
9873                  vbm,                   /* Verbosity */
9874                  fc                     /* Function code */
9875                  );
9876     if (q > -1)
9877       quiet = q;
9878
9879 #ifdef CK_ENCRYPTION
9880     if (ftpcode == 533 && ftp_cpl == FPL_PRV) {
9881         fprintf(stderr,
9882                "ENC command not supported at server; retrying under MIC...\n");
9883         ftp_cpl = FPL_SAF;
9884         goto again;
9885     }
9886 #endif /* CK_ENCRYPTION */
9887 #ifdef COMMENT
9888     if (cancelfile && oldintr != SIG_IGN)
9889       (*oldintr)(SIGINT);
9890 #endif /* COMMENT */
9891     signal(SIGINT, oldintr);
9892     return(r);
9893 }
9894
9895 static VOID
9896 lostpeer() {
9897     debug(F100,"lostpeer","",0);
9898     if (connected) {
9899         if (csocket != -1) {
9900 #ifdef CK_SSL
9901             if (ssl_ftp_active_flag) {
9902                 SSL_shutdown(ssl_ftp_con);
9903                 SSL_free(ssl_ftp_con);
9904                 ssl_ftp_proxy = 0;
9905                 ssl_ftp_active_flag = 0;
9906                 ssl_ftp_con = NULL;
9907             }
9908 #endif /* CK_SSL */
9909 #ifdef TCPIPLIB
9910             socket_close(csocket);
9911 #else /* TCPIPLIB */
9912 #ifdef USE_SHUTDOWN
9913             shutdown(csocket, 1+1);
9914 #endif /* USE_SHUTDOWN */
9915             close(csocket);
9916 #endif /* TCPIPLIB */
9917             csocket = -1;
9918         }
9919         if (data != -1) {
9920 #ifdef CK_SSL
9921             if (ssl_ftp_data_active_flag) {
9922                 SSL_shutdown(ssl_ftp_data_con);
9923                 SSL_free(ssl_ftp_data_con);
9924                 ssl_ftp_data_active_flag = 0;
9925                 ssl_ftp_data_con = NULL;
9926             }
9927 #endif /* CK_SSL */
9928 #ifdef TCPIPLIB
9929             socket_close(data);
9930 #else /* TCPIPLIB */
9931 #ifdef USE_SHUTDOWN
9932             shutdown(data, 1+1);
9933 #endif /* USE_SHUTDOWN */
9934             close(data);
9935 #endif /* TCPIPLIB */
9936             data = -1;
9937             globaldin = -1;
9938         }
9939         connected = 0;
9940         anonymous = 0;
9941         loggedin = 0;
9942         auth_type = NULL;
9943         ftp_cpl = ftp_dpl = FPL_CLR;
9944 #ifdef CKLOGDIAL
9945         ftplogend();
9946 #endif /* CKLOGDIAL */
9947
9948 #ifdef LOCUS
9949         if (autolocus)                  /* Auotomatic locus switching... */
9950           setlocus(1,1);                /* Switch locus to local. */
9951 #endif /* LOCUS */
9952 #ifdef OS2
9953         DialerSend(OPT_KERMIT_HANGUP, 0);
9954 #endif /* OS2 */
9955     }
9956 #ifdef FTP_PROXY
9957     pswitch(1);
9958     if (connected) {
9959         if (csocket != -1) {
9960 #ifdef TCPIPLIB
9961             socket_close(csocket);
9962 #else /* TCPIPLIB */
9963 #ifdef USE_SHUTDOWN
9964             shutdown(csocket, 1+1);
9965 #endif /* USE_SHUTDOWN */
9966             close(csocket);
9967 #endif /* TCPIPLIB */
9968             csocket = -1;
9969         }
9970         connected = 0;
9971         anonymous = 0;
9972         loggedin = 0;
9973         auth_type = NULL;
9974         ftp_cpl = ftp_dpl = FPL_CLR;
9975     }
9976     proxflag = 0;
9977     pswitch(0);
9978 #endif /* FTP_PROXY */
9979 }
9980
9981 int
9982 ftpisopen() {
9983     return(connected);
9984 }
9985
9986 static int
9987 ftpclose() {
9988     extern int quitting;
9989     if (!connected)
9990       return(0);
9991     ftp_xfermode = xfermode;
9992     if (!ftp_vbm && !quiet)
9993       printlines = 1;
9994     ftpcmd("QUIT",NULL,0,0,ftp_vbm);
9995     if (csocket) {
9996 #ifdef CK_SSL
9997         if (ssl_ftp_active_flag) {
9998             SSL_shutdown(ssl_ftp_con);
9999             SSL_free(ssl_ftp_con);
10000             ssl_ftp_proxy = 0;
10001             ssl_ftp_active_flag = 0;
10002             ssl_ftp_con = NULL;
10003         }
10004 #endif /* CK_SSL */
10005 #ifdef TCPIPLIB
10006         socket_close(csocket);
10007 #else /* TCPIPLIB */
10008 #ifdef USE_SHUTDOWN
10009         shutdown(csocket, 1+1);
10010 #endif /* USE_SHUTDOWN */
10011         close(csocket);
10012 #endif /* TCPIPLIB */
10013     }
10014     csocket = -1;
10015     connected = 0;
10016     anonymous = 0;
10017     loggedin = 0;
10018     mdtmok = 1;
10019     sizeok = 1;
10020     featok = 1;
10021     stouarg = 1;
10022     typesent = 0;
10023     data = -1;
10024     globaldin = -1;
10025 #ifdef FTP_PROXY
10026     if (!proxy)
10027       macnum = 0;
10028 #endif /* FTP_PROXY */
10029     auth_type = NULL;
10030     ftp_dpl = FPL_CLR;
10031 #ifdef CKLOGDIAL
10032     ftplogend();
10033 #endif /* CKLOGDIAL */
10034 #ifdef LOCUS
10035     /* Unprefixed file management commands are executed locally */
10036     if (autolocus && !ftp_cmdlin && !quitting) {
10037         setlocus(1,1);
10038     }
10039 #endif /* LOCUS */
10040 #ifdef OS2
10041     DialerSend(OPT_KERMIT_HANGUP, 0);
10042 #endif /* OS2 */
10043     return(0);
10044 }
10045
10046 int
10047 ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; {
10048     char * host;
10049
10050     if (connected) {
10051         printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host);
10052         ftpcode = -1;
10053         return(0);
10054     }
10055 #ifdef FTPHOST
10056     hostcmd = 0;
10057 #endif /* FTPHOST */
10058     alike = 0;
10059     ftp_srvtyp[0] = NUL;
10060     if (!service) service = "";
10061     if (!*service) service = use_tls ? "ftps" : "ftp";
10062
10063     if (!isdigit(service[0])) {
10064         struct servent *destsp;
10065         destsp = getservbyname(service, "tcp");
10066         if (!destsp) {
10067             if (!ckstrcmp(service,"ftp",-1,0)) {
10068                 ftp_port = 21;
10069             } else if (!ckstrcmp(service,"ftps",-1,0)) {
10070                 ftp_port = 990;
10071             } else {
10072                 printf("?Bad port name - \"%s\"\n", service);
10073                 ftpcode = -1;
10074                 return(0);
10075             }
10076         } else {
10077             ftp_port = destsp->s_port;
10078             ftp_port = ntohs((unsigned short)ftp_port); /* SMS 2007/02/15 */
10079         }
10080     } else
10081         ftp_port = atoi(service);
10082     if (ftp_port <= 0) {
10083         printf("?Bad port name - \"%s\"\n", service);
10084         ftpcode = -1;
10085         return(0);
10086     }
10087     host = ftp_hookup(remote, ftp_port, use_tls);
10088     if (host) {
10089         ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN);
10090         connected = 1;                  /* Set FTP defaults */
10091         ftp_cpl = ftp_dpl = FPL_CLR;
10092         curtype = FTT_ASC;              /* Server uses ASCII mode */
10093         form = FORM_N;
10094         mode = MODE_S;
10095         stru = STRU_F;
10096         strcpy(bytename, "8");
10097         bytesize = 8;
10098
10099 #ifdef FTP_SECURITY
10100         if (ftp_aut) {
10101             if (ftp_auth()) {
10102                 if (ftp_cry 
10103 #ifdef OS2
10104                      && ck_crypt_is_installed()
10105 #endif /* OS2 */
10106                      ) {
10107                     if (!quiet)
10108                       printf("FTP Command channel is Private (encrypted)\n");
10109                     ftp_cpl = FPL_PRV;
10110                     if (setpbsz(DEFAULT_PBSZ) < 0) {
10111                         /* a failure here is most likely caused by a mixup */
10112                         /* in the session key used by client and server    */
10113                         printf("?Protection buffer size negotiation failed\n");
10114                         return(0);
10115                     }
10116                     if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) {
10117                         if (!quiet)
10118                           printf("FTP Data channel is Private (encrypted)\n");
10119                         ftp_dpl = FPL_PRV;
10120                     } else
10121                       printf("?Unable to enable encryption on data channel\n");
10122                 } else {
10123                     ftp_cpl = FPL_SAF;
10124                 }
10125             }
10126             if (!connected)
10127               goto fail;
10128         }
10129 #endif /* FTP_SECURITY */
10130         if (ftp_log)                    /* ^^^ */
10131           ftp_login(remote);
10132
10133         if (!connected)
10134           goto fail;
10135
10136         ftp_xfermode = xfermode;
10137
10138 #ifdef CKLOGDIAL
10139         dologftp();
10140 #endif /* CKLOGDIAL */
10141 #ifdef OS2
10142         DialerSend(OPT_KERMIT_CONNECT, 0);
10143 #endif /* OS2 */
10144         passivemode = ftp_psv;
10145         sendport = ftp_spc;
10146         mdtmok = 1;
10147         sizeok = 1;
10148         stouarg = 1;
10149         typesent = 0;
10150
10151         if (ucbuf == NULL) {
10152             actualbuf = DEFAULT_PBSZ;
10153             while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL)
10154               actualbuf >>= 2;
10155         }
10156         if (!maxbuf)
10157           ucbufsiz = actualbuf - FUDGE_FACTOR;
10158         debug(F101,"ftpopen ucbufsiz","",ucbufsiz);
10159         return(1);
10160     }
10161   fail:
10162     printf("?Can't FTP connect to %s:%s\n",remote,service);
10163     ftpcode = -1;
10164     return(0);
10165 }
10166
10167 #ifdef CK_SSL
10168 int
10169 ssl_auth() {
10170     int i;
10171     char* p;
10172
10173     if (ssl_debug_flag) {
10174         fprintf(stderr,"SSL DEBUG ACTIVE\n");
10175         fflush(stderr);
10176         /* for the moment I want the output on screen */
10177     }
10178     if (ssl_ftp_data_con != NULL) {
10179         SSL_free(ssl_ftp_data_con);
10180         ssl_ftp_data_con = NULL;
10181     }
10182     if (ssl_ftp_con != NULL) {
10183         SSL_free(ssl_ftp_con);
10184         ssl_ftp_con=NULL;
10185     }
10186     if (ssl_ftp_ctx != NULL) {
10187         SSL_CTX_free(ssl_ftp_ctx);
10188         ssl_ftp_ctx = NULL;
10189     }
10190
10191     /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 
10192      * was added to OpenSSL 0.9.6e and 0.9.7.  It does not exist in previous
10193      * versions
10194      */
10195 #ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
10196 #define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L
10197 #endif
10198     if (auth_type && !strcmp(auth_type,"TLS")) {
10199         ssl_ftp_ctx=SSL_CTX_new(SSLv3_client_method());
10200         if (!ssl_ftp_ctx)
10201           return(0);
10202         SSL_CTX_set_options(ssl_ftp_ctx,
10203                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10204                             );
10205     } else {
10206         ssl_ftp_ctx = SSL_CTX_new(ftp_bug_use_ssl_v2 ? SSLv23_client_method() : 
10207                                   SSLv3_client_method());
10208         if (!ssl_ftp_ctx)
10209           return(0);
10210         SSL_CTX_set_options(ssl_ftp_ctx,
10211                             (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)|
10212                             SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA
10213                             );
10214     }
10215     SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx,
10216                                   (pem_password_cb *)ssl_passwd_callback);
10217     SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback);
10218     SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT);
10219
10220 #ifdef OS2
10221 #ifdef NT
10222     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10223     {
10224         char path[CKMAXPATH];
10225         extern char exedir[];
10226
10227         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10228         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10229             debug(F110,"ftp ssl_auth unable to load path",path,0);
10230             if (ssl_debug_flag)
10231                 printf("?Unable to load verify-dir: %s\r\n",path);
10232         }
10233
10234         ckmakmsg(path,CKMAXPATH,
10235                  (char *)GetAppData(1),"kermit 95/certs",NULL,NULL);
10236         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10237             debug(F110,"ftp ssl_auth unable to load path",path,0);
10238             if (ssl_debug_flag)
10239                 printf("?Unable to load verify-dir: %s\r\n",path);
10240         }
10241
10242         ckmakmsg(path,CKMAXPATH,
10243                  (char *)GetAppData(0),"kermit 95/certs",NULL,NULL);
10244         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10245             debug(F110,"ftp ssl_auth unable to load path",path,0);
10246             if (ssl_debug_flag)
10247                 printf("?Unable to load verify-dir: %s\r\n",path);
10248         }
10249
10250         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10251         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10252             debug(F110,"ftp ssl_auth unable to load path",path,0);
10253             if (ssl_debug_flag)
10254                 printf("?Unable to load verify-file: %s\r\n",path);
10255         }
10256
10257         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10258                  "kermit 95/ca_certs.pem",NULL,NULL);
10259         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10260             debug(F110,"ftp ssl_auth unable to load path",path,0);
10261             if (ssl_debug_flag)
10262                 printf("?Unable to load verify-file: %s\r\n",path);
10263         }
10264
10265         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10266                  "kermit 95/ca_certs.pem",NULL,NULL);
10267         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10268             debug(F110,"ftp ssl_auth unable to load path",path,0);
10269             if (ssl_debug_flag)
10270                 printf("?Unable to load verify-file: %s\r\n",path);
10271         }
10272     }
10273 #else /* NT */
10274     /* The defaults in the SSL crypto library are not appropriate for OS/2 */
10275     {
10276
10277         char path[CKMAXPATH];
10278         extern char exedir[];
10279
10280         ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL);
10281         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0)  {
10282             debug(F110,"ftp ssl_auth unable to load path",path,0);
10283             if (ssl_debug_flag)
10284                 printf("?Unable to load verify-dir: %s\r\n",path);
10285         }
10286         ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL);
10287         if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) {
10288             debug(F110,"ftp ssl_auth unable to load path",path,0);
10289             if (ssl_debug_flag)
10290                 printf("?Unable to load verify-file: %s\r\n",path);
10291         }
10292     }
10293 #endif /* NT */
10294 #else /* OS2 */
10295     SSL_CTX_set_default_verify_paths(ssl_ftp_ctx);
10296 #endif /* OS2 */
10297
10298     if (ssl_verify_file &&
10299         SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) {
10300         debug(F110,
10301               "ftp ssl auth unable to load ssl_verify_file",
10302               ssl_verify_file,
10303               0
10304               );
10305         if (ssl_debug_flag)
10306           printf("?Unable to load verify-file: %s\r\n",ssl_verify_file);
10307     }
10308     if (ssl_verify_dir &&
10309         SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) {
10310         debug(F110,
10311               "ftp ssl auth unable to load ssl_verify_dir",
10312               ssl_verify_dir,
10313               0
10314               );
10315         if (ssl_debug_flag)
10316           printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir);
10317     }
10318
10319     /* set up the new CRL Store */
10320     crl_store = (X509_STORE *)X509_STORE_new();
10321     if (crl_store) {
10322 #ifdef OS2
10323         char path[CKMAXPATH];
10324         extern char exedir[];
10325
10326         ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL);
10327         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10328             debug(F110,"ftp ssl auth unable to load dir",path,0);
10329             if (ssl_debug_flag)
10330                 printf("?Unable to load crl-dir: %s\r\n",path);
10331         }
10332 #ifdef NT
10333         ckmakmsg(path,CKMAXPATH,
10334                  (char *)GetAppData(1),"kermit 95/crls",NULL,NULL);
10335         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10336             debug(F110,"ftp ssl auth unable to load dir",path,0);
10337             if (ssl_debug_flag)
10338                 printf("?Unable to load crl-dir: %s\r\n",path);
10339         }
10340         ckmakmsg(path,CKMAXPATH,
10341                  (char *)GetAppData(0),"kermit 95/crls",NULL,NULL);
10342         if (X509_STORE_load_locations(crl_store,NULL,path) == 0) {
10343             debug(F110,"ftp ssl auth unable to load dir",path,0);
10344             if (ssl_debug_flag)
10345                 printf("?Unable to load crl-dir: %s\r\n",path);
10346         }
10347 #endif /* NT */
10348         
10349         ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL);
10350         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10351             debug(F110,"ftp ssl auth unable to load file",path,0);
10352             if (ssl_debug_flag)
10353                 printf("?Unable to load crl-file: %s\r\n",path);
10354         }
10355 #ifdef NT
10356         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1),
10357                  "kermit 95/ca_crls.pem",NULL,NULL);
10358         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10359             debug(F110,"ftp ssl auth unable to load file",path,0);
10360             if (ssl_debug_flag)
10361                 printf("?Unable to load crl-file: %s\r\n",path);
10362         }
10363         ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0),
10364                  "kermit 95/ca_crls.pem",NULL,NULL);
10365         if (X509_STORE_load_locations(crl_store,path,NULL) == 0) {
10366             debug(F110,"ftp ssl auth unable to load file",path,0);
10367             if (ssl_debug_flag)
10368                 printf("?Unable to load crl-file: %s\r\n",path);
10369         }
10370 #endif /* NT */
10371 #endif /* OS2 */
10372
10373         if (ssl_crl_file || ssl_crl_dir) {
10374             if (ssl_crl_file &&
10375                 X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) {
10376                 debug(F110,
10377                       "ftp ssl auth unable to load ssl_crl_file",
10378                       ssl_crl_file,
10379                       0
10380                       );
10381                 if (ssl_debug_flag)
10382                   printf("?Unable to load crl-file: %s\r\n",ssl_crl_file);
10383             }
10384             if (ssl_crl_dir &&
10385                 X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) {
10386                 debug(F110,
10387                       "ftp ssl auth unable to load ssl_crl_dir",
10388                       ssl_crl_dir,
10389                       0
10390                       );
10391                 if (ssl_debug_flag)
10392                   printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir);
10393             }
10394         } else {
10395             X509_STORE_set_default_paths(crl_store);
10396         }
10397     }
10398     SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag,
10399                        ssl_client_verify_callback);
10400     ssl_verify_depth = -1;
10401     ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx);
10402     tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0);
10403     SSL_set_fd(ssl_ftp_con,csocket);
10404     SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL);
10405     if (ssl_cipher_list) {
10406         SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list);
10407     } else {
10408         char * p;
10409         if (p = getenv("SSL_CIPHER")) {
10410             SSL_set_cipher_list(ssl_ftp_con,p);
10411         } else {
10412             SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST);
10413         }
10414     }
10415     if (ssl_debug_flag) {
10416         fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n");
10417         fflush(stderr);
10418     }
10419     if (SSL_connect(ssl_ftp_con) <= 0) {
10420         static char errbuf[1024];
10421         ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ",
10422                  ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
10423         fprintf(stderr,"%s\n", errbuf);
10424         fflush(stderr);
10425         ssl_ftp_active_flag=0;
10426         SSL_free(ssl_ftp_con);
10427         ssl_ftp_con = NULL;
10428     } else {
10429         ssl_ftp_active_flag = 1;
10430
10431         if (!ssl_certsok_flag && !tls_is_krb5(1)) {
10432             char *subject = ssl_get_subject_name(ssl_ftp_con);
10433
10434             if (!subject) {
10435                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
10436                     debug(F110,"ssl_auth","[SSL - FAILED]",0);
10437                     return(ssl_ftp_active_flag = 0);
10438                 } else {
10439                     if (uq_ok("Warning: Server didn't provide a certificate\n",
10440                                "Continue? (Y/N)",3,NULL,0) <= 0) {
10441                         debug(F110, "ssl_auth","[SSL - FAILED]",0);
10442                         return(ssl_ftp_active_flag = 0);
10443                     }
10444                 }
10445             } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) {
10446                 debug(F110,"ssl_auth","[SSL - FAILED]",0);
10447                 return(ssl_ftp_active_flag = 0);
10448             }
10449         }
10450         debug(F110,"ssl_auth","[SSL - OK]",0);
10451         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
10452     }
10453     if (ssl_debug_flag) {
10454         fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n");
10455         fflush(stderr);
10456     }
10457     return(ssl_ftp_active_flag);
10458 }
10459 #endif /* CK_SSL */
10460
10461 static sigtype
10462 cmdcancel(sig) int sig; {
10463 #ifdef OS2
10464     /* In Unix we "chain" to trap(), which prints this */
10465     printf("^C...\n");
10466 #endif /* OS2 */
10467     debug(F100,"ftp cmdcancel caught SIGINT ","",0);
10468     fflush(stdout);
10469     secure_getc(0,1);                   /* Initialize net input buffers */
10470     cancelfile++;
10471     cancelgroup++;
10472     mlsreset();
10473 #ifndef OS2
10474 #ifdef FTP_PROXY
10475     if (ptflag)                         /* proxy... */
10476       longjmp(ptcancel,1);
10477 #endif /* FTP_PROXY */
10478     debug(F100,"ftp cmdcancel chain to trap()...","",0);
10479     trap(SIGINT);
10480     /* NOTREACHED */
10481     debug(F100,"ftp cmdcancel return from trap()...","",0);
10482 #else
10483     debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0);
10484     PostCtrlCSem();
10485 #endif /* OS2 */
10486 }
10487
10488 static int
10489 #ifdef CK_ANSIC
10490 scommand(char * s)                      /* Was secure_command() */
10491 #else
10492 scommand(s) char * s;
10493 #endif /* CK_ANSIC */
10494 {
10495     int length = 0, len2;
10496     char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
10497 #ifdef CK_SSL
10498     if (ssl_ftp_active_flag) {
10499         int error, rc;
10500         length = strlen(s) + 2;
10501         length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10502         rc = SSL_write(ssl_ftp_con,out,length);
10503         error = SSL_get_error(ssl_ftp_con,rc);
10504         switch (error) {
10505           case SSL_ERROR_NONE:
10506             return(1);
10507           case SSL_ERROR_WANT_WRITE:
10508           case SSL_ERROR_WANT_READ:
10509           case SSL_ERROR_SYSCALL:
10510 #ifdef NT
10511             {
10512                 int gle = GetLastError();
10513             }
10514 #endif /* NT */
10515           case SSL_ERROR_WANT_X509_LOOKUP:
10516           case SSL_ERROR_SSL:
10517           case SSL_ERROR_ZERO_RETURN:
10518           default:
10519             lostpeer();
10520         }
10521         return(0);
10522     }
10523 #endif /* CK_SSL */
10524
10525     if (auth_type && ftp_cpl != FPL_CLR) {
10526 #ifdef FTP_SRP
10527         if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0))
10528           if ((length = srp_encode(ftp_cpl == FPL_PRV,
10529                                    (CHAR *)s,
10530                                    (CHAR *)out,
10531                                    strlen(s))) < 0) {
10532               fprintf(stderr, "SRP failed to encode message\n");
10533               return(0);
10534           }
10535 #endif /* FTP_SRP */
10536 #ifdef FTP_KRB4
10537         if (ck_krb4_is_installed() &&
10538             (strcmp(auth_type, "KERBEROS_V4") == 0)) {
10539             if (ftp_cpl == FPL_PRV) {
10540                 length =
10541                   krb_mk_priv((CHAR *)s, (CHAR *)out,
10542                               strlen(s), ftp_sched,
10543 #ifdef KRB524
10544                               ftp_cred.session,
10545 #else /* KRB524 */
10546                               &ftp_cred.session,
10547 #endif /* KRB524 */
10548                               &myctladdr, &hisctladdr);
10549             } else {
10550                 length =
10551                   krb_mk_safe((CHAR *)s,
10552                               (CHAR *)out,
10553                               strlen(s),
10554 #ifdef KRB524
10555                               ftp_cred.session,
10556 #else /* KRB524 */
10557                               &ftp_cred.session,
10558 #endif /* KRB524 */
10559                               &myctladdr, &hisctladdr);
10560             }
10561             if (length == -1) {
10562                 fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
10563                         ftp_cpl == FPL_PRV ? "priv" : "safe");
10564                 return(0);
10565             }
10566         }
10567 #endif /* FTP_KRB4 */
10568 #ifdef FTP_GSSAPI
10569         /* Scommand (based on level) */
10570         if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
10571             gss_buffer_desc in_buf, out_buf;
10572             OM_uint32 maj_stat, min_stat;
10573             int conf_state;
10574             in_buf.value = s;
10575             in_buf.length = strlen(s) + 1;
10576             maj_stat = gss_seal(&min_stat, gcontext,
10577                                 (ftp_cpl==FPL_PRV), /* private */
10578                                 GSS_C_QOP_DEFAULT,
10579                                 &in_buf, &conf_state,
10580                                 &out_buf);
10581             if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */
10582                 user_gss_error(maj_stat, min_stat,
10583                                (ftp_cpl==FPL_PRV)?
10584                                "gss_seal ENC didn't complete":
10585                                "gss_seal MIC didn't complete");
10586             } else if ((ftp_cpl == FPL_PRV) && !conf_state) {
10587                 fprintf(stderr, "GSSAPI didn't encrypt message");
10588             } else {
10589                 if (ftp_deb)
10590                   fprintf(stderr, "sealed (%s) %d bytes\n",
10591                           ftp_cpl==FPL_PRV?"ENC":"MIC",
10592                           out_buf.length);
10593                 memcpy(out, out_buf.value,
10594                        length=out_buf.length);
10595                 gss_release_buffer(&min_stat, &out_buf);
10596             }
10597         }
10598 #endif /* FTP_GSSAPI */
10599         /* Other auth types go here ... */
10600
10601         len2 = FTP_BUFSIZ;
10602         if ((kerror = radix_encode((CHAR *)out, (CHAR *)in,
10603                                    length, &len2, RADIX_ENCODE))
10604             ) {
10605             fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
10606                     radix_error(kerror));
10607             return(0);
10608         }
10609         if (ftp_deb)
10610           fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length);
10611         len2 = ckmakmsg(out,
10612                         FTP_BUFSIZ,
10613                         ftp_cpl == FPL_PRV ? "ENC " : "MIC ",
10614                         in,
10615                         "\r\n",
10616                         NULL
10617                         );
10618         send(csocket,(SENDARG2TYPE)out,len2,0);
10619     } else {
10620         char out[FTP_BUFSIZ];
10621         int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL);
10622         send(csocket,(SENDARG2TYPE)out,len,0);
10623     }
10624     return(1);
10625 }
10626
10627 static int
10628 mygetc() {
10629     static char inbuf[4096];
10630     static int bp = 0, ep = 0;
10631     int rc;
10632
10633     if (bp == ep) {
10634         bp = ep = 0;
10635 #ifdef CK_SSL
10636         if (ssl_ftp_active_flag) {
10637             int error;
10638             rc = SSL_read(ssl_ftp_con,inbuf,4096);
10639             error = SSL_get_error(ssl_ftp_con,rc);
10640             switch (error) {
10641               case SSL_ERROR_NONE:
10642                 break;
10643               case SSL_ERROR_WANT_WRITE:
10644               case SSL_ERROR_WANT_READ:
10645                 return(0);
10646               case SSL_ERROR_SYSCALL:
10647                 if (rc == 0) {          /* EOF */
10648                     break;
10649                 } else {
10650 #ifdef NT
10651                     int gle = GetLastError();
10652 #endif /* NT */
10653                     break;
10654                 }
10655               case SSL_ERROR_WANT_X509_LOOKUP:
10656               case SSL_ERROR_SSL:
10657               case SSL_ERROR_ZERO_RETURN:
10658               default:
10659                 break;
10660             }
10661         } else
10662 #endif /* CK_SSL */
10663           rc = recv(csocket,(char *)inbuf,4096,0);
10664         if (rc <= 0)
10665           return(EOF);
10666         ep = rc;
10667     }
10668     return(inbuf[bp++]);
10669 }
10670
10671 /*  x l a t e c  --  Translate a character  */
10672 /*
10673     Call with:
10674       fc    = Function code: 0 = translate, 1 = initialize.
10675       c     = Character (as int).
10676       incs  = Index of charset to translate from.
10677       outcs = Index of charset to translate to.
10678
10679     Returns:
10680       0: OK
10681      -1: Error
10682 */
10683 static int
10684 xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; {
10685 #ifdef NOCSETS
10686     return(c);
10687 #else
10688     static char buf[128];
10689     static int cx;
10690     int c0, c1;
10691
10692     if (fc == 1) {                      /* Initialize */
10693         cx = 0;                         /* Catch-up buffer write index */
10694         xgnbp = buf;                    /* Catch-up buffer read pointer */
10695         buf[0] = NUL;                   /* Buffer is empty */
10696         return(0);
10697     }
10698     if (cx >= 127) {                    /* Catch-up buffer full */
10699         debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */
10700         printf("?Translation buffer overflow\n");
10701         return(-1);
10702     }
10703     /* Add char to buffer. */
10704     /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */
10705
10706     debug(F000,"xlatec buf",ckitoa(cx),c);
10707     buf[cx++] = c;
10708     buf[cx] = NUL;
10709
10710     while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) {
10711         if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */
10712           return(-1);
10713     }
10714     /* If we're caught up, reinitialize the buffer */
10715     return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0);
10716 #endif /* NOCSETS */
10717 }
10718
10719
10720 /*  p a r s e f e a t  */
10721
10722 /* Note: for convenience we align keyword values with table indices */
10723 /* If you need to insert a new keyword, adjust the SFT_xxx definitions */
10724
10725 static struct keytab feattab[] = {
10726     { "$$$$", 0,        0 },            /* Dummy for sfttab[0] */
10727     { "AUTH", SFT_AUTH, 0 },
10728     { "LANG", SFT_LANG, 0 },
10729     { "MDTM", SFT_MDTM, 0 },
10730     { "MLST", SFT_MLST, 0 },
10731     { "PBSZ", SFT_PBSZ, 0 },
10732     { "PROT", SFT_PROT, 0 },
10733     { "REST", SFT_REST, 0 },
10734     { "SIZE", SFT_SIZE, 0 },
10735     { "TVFS", SFT_TVFS, 0 },
10736     { "UTF8", SFT_UTF8, 0 }
10737 };
10738 static int nfeattab = (sizeof(feattab) / sizeof(struct keytab));
10739
10740 #define FACT_CSET  1
10741 #define FACT_CREA  2
10742 #define FACT_LANG  3
10743 #define FACT_MTYP  4
10744 #define FACT_MDTM  5
10745 #define FACT_PERM  6
10746 #define FACT_SIZE  7
10747 #define FACT_TYPE  8
10748 #define FACT_UNIQ  9
10749
10750 static struct keytab facttab[] = {
10751     { "CHARSET",    FACT_CSET, 0 },
10752     { "CREATE",     FACT_CREA, 0 },
10753     { "LANG",       FACT_LANG, 0 },
10754     { "MEDIA-TYPE", FACT_MTYP, 0 },
10755     { "MODIFY",     FACT_MDTM, 0 },
10756     { "PERM",       FACT_PERM, 0 },
10757     { "SIZE",       FACT_SIZE, 0 },
10758     { "TYPE",       FACT_TYPE, 0 },
10759     { "UNIQUE",     FACT_UNIQ, 0 }
10760 };
10761 static int nfacttab = (sizeof(facttab) / sizeof(struct keytab));
10762
10763 static struct keytab ftyptab[] = {
10764     { "CDIR", FTYP_CDIR, 0 },
10765     { "DIR",  FTYP_DIR,  0 },
10766     { "FILE", FTYP_FILE, 0 },
10767     { "PDIR", FTYP_PDIR, 0 }
10768 };
10769 static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab));
10770
10771 static VOID
10772 parsefeat(s) char * s; {                /* Parse a FEATURE response */
10773     char kwbuf[8];
10774     int i, x;
10775     if (!s) return;
10776     if (!*s) return;
10777     while (*s < '!')
10778       s++;
10779     for (i = 0; i < 4; i++) {
10780         if (s[i] < '!')
10781           break;
10782         kwbuf[i] = s[i];
10783     }
10784     if (s[i] && s[i] != SP && s[i] != CR && s[i] != LF)
10785       return;
10786     kwbuf[i] = NUL;
10787     /* xlookup requires a full (but case independent) match */
10788     i = xlookup(feattab,kwbuf,nfeattab,&x);
10789     debug(F111,"ftp parsefeat",s,i);
10790     if (i < 0 || i > 15)
10791       return;
10792
10793     switch (i) {
10794       case SFT_MDTM:                    /* Controlled by ENABLE/DISABLE */
10795         sfttab[i] = mdtmok;
10796         if (mdtmok) sfttab[0]++;
10797         break;
10798       case SFT_MLST:                    /* ditto */
10799         sfttab[i] = mlstok;
10800         if (mlstok) sfttab[0]++;
10801         break;
10802       case SFT_SIZE:                    /* ditto */
10803         sfttab[i] = sizeok;
10804         if (sizeok) sfttab[0]++;
10805         break;
10806       case SFT_AUTH:                    /* ditto */
10807         sfttab[i] = ftp_aut;
10808         if (ftp_aut) sfttab[0]++;
10809         break;
10810       default:                          /* Others */
10811         sfttab[0]++;
10812         sfttab[i]++;
10813     }
10814 }
10815
10816 static char *
10817 parsefacts(s) char * s; {               /* Parse MLS[DT] File Facts */
10818     char * p;
10819     int i, j, x;
10820     if (!s) return(NULL);
10821     if (!*s) return(NULL);
10822
10823     /* Maybe we should make a copy of s so we can poke it... */
10824
10825     while ((p = ckstrchr(s,'='))) {
10826         *p = NUL;                       /* s points to fact */
10827         i = xlookup(facttab,s,nfacttab,&x); 
10828         debug(F111,"ftp parsefact fact",s,i);
10829         *p = '=';
10830         s = p+1;                        /* Now s points to arg */
10831         p = ckstrchr(s,';');
10832         if (!p)
10833           p = ckstrchr(s,SP);
10834         if (!p) {
10835             debug(F110,"ftp parsefact end-of-val search fail",s,0);
10836             break;
10837         }
10838         *p = NUL;
10839         debug(F110,"ftp parsefact valu",s,0);
10840         switch (i) {
10841           case FACT_CSET:               /* Ignore these for now */
10842           case FACT_CREA:
10843           case FACT_LANG:
10844           case FACT_PERM:
10845           case FACT_MTYP:
10846           case FACT_UNIQ:
10847             break;
10848           case FACT_MDTM:               /* Modtime */
10849             makestr(&havemdtm,s);
10850             debug(F110,"ftp parsefact mdtm",havemdtm,0);
10851             break;
10852           case FACT_SIZE:               /* Size */
10853             havesize = ckatofs(s);
10854             debug(F101,"ftp parsefact size","",havesize);
10855             break;
10856           case FACT_TYPE:               /* Type */
10857             j = xlookup(ftyptab,s,nftyptab,NULL);
10858             debug(F111,"ftp parsefact type",s,j);
10859             havetype = (j < 1) ? 0 : j;
10860             break;
10861         }
10862         *p = ';';
10863         s = p+1;                        /* s points next fact or name */
10864     }
10865     while (*s == SP)                    /* Skip past spaces. */
10866       s++;
10867     if (!*s)                            /* Make sure we still have a name */
10868       s = NULL;
10869     debug(F110,"ftp parsefact name",s,0);
10870     return(s);
10871 }
10872
10873 /*  g e t r e p l y  --  (to an FTP command sent to server)  */
10874
10875 /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */
10876
10877 static int
10878 getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; {
10879     /* lcs, rcs, vbm parameters as in ftpcmd() */
10880     register int i, c, n;
10881     register int dig;
10882     register char *cp;
10883     int xlate = 0;
10884     int count = 0;
10885     int auth = 0;
10886     int originalcode = 0, continuation = 0;
10887     sig_t oldintr;
10888     int pflag = 0;
10889     char *pt = pasv;
10890     char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */
10891     int safe = 0;
10892     int xquiet = 0;
10893
10894     auth = (fc == GRF_AUTH);
10895
10896 #ifndef NOCSETS
10897     debug(F101,"ftp getreply lcs","",lcs);
10898     debug(F101,"ftp getreply rcs","",rcs);
10899     if (lcs > -1 && rcs > -1 && lcs != rcs) {
10900         xlate = 1;
10901         initxlate(rcs,lcs);
10902         xlatec(1,0,rcs,lcs);
10903     }
10904 #endif /* NOCSETS */
10905     debug(F101,"ftp getreply fc","",fc);
10906
10907     if (quiet)
10908       xquiet = 1;
10909     if (vbm == 9) {
10910         xquiet = 1;
10911         vbm = 0;
10912     }
10913     if (ftp_deb)                        /* DEBUG */
10914       vbm = 1;
10915     else if (quiet || dpyactive)        /* QUIET or File Transfer Active */
10916       vbm = 0;
10917     else if (vbm < 0)                   /* VERBOSE */
10918       vbm = ftp_vbm;
10919
10920     ibuf[0] = '\0';
10921     if (reply_parse)
10922       reply_ptr = reply_buf;
10923     havesigint = 0;
10924     oldintr = signal(SIGINT, cmdcancel);
10925     for (count = 0;; count++) {
10926         obuf[0] = '\0';
10927         dig = n = ftpcode = i = 0;
10928         cp = ftp_reply_str;
10929         while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') {
10930             if (c == IAC) {             /* Handle telnet commands */
10931                 switch (c = mygetc()) {
10932                   case WILL:
10933                   case WONT:
10934                     c = mygetc();
10935                     obuf[0] = IAC;
10936                     obuf[1] = DONT;
10937                     obuf[2] = c;
10938                     obuf[3] = NUL;
10939 #ifdef CK_SSL
10940                     if (ssl_ftp_active_flag) {
10941                         int error, rc;
10942                         rc = SSL_write(ssl_ftp_con,obuf,3);
10943                         error = SSL_get_error(ssl_ftp_con,rc);
10944                         switch (error) {
10945                           case SSL_ERROR_NONE:
10946                             break;
10947                           case SSL_ERROR_WANT_WRITE:
10948                           case SSL_ERROR_WANT_READ:
10949                             return(0);
10950                           case SSL_ERROR_SYSCALL:
10951                             if (rc == 0) { /* EOF */
10952                                 break;
10953                             } else {
10954 #ifdef NT
10955                                 int gle = GetLastError();
10956 #endif /* NT */
10957                                 break;
10958                             }
10959                           case SSL_ERROR_WANT_X509_LOOKUP:
10960                           case SSL_ERROR_SSL:
10961                           case SSL_ERROR_ZERO_RETURN:
10962                           default:
10963                             break;
10964                         }
10965                     } else
10966 #endif /* CK_SSL */
10967                       send(csocket,(SENDARG2TYPE)obuf,3,0);
10968                     break;
10969                   case DO:
10970                   case DONT:
10971                     c = mygetc();
10972                     obuf[0] = IAC;
10973                     obuf[1] = WONT;
10974                     obuf[2] = c;
10975                     obuf[3] = NUL;
10976 #ifdef CK_SSL
10977                     if (ssl_ftp_active_flag) {
10978                         int error, rc;
10979                         rc = SSL_write(ssl_ftp_con,obuf,3);
10980                         error = SSL_get_error(ssl_ftp_con,rc);
10981                         switch (error) {
10982                           case SSL_ERROR_NONE:
10983                             break;
10984                           case SSL_ERROR_WANT_WRITE:
10985                           case SSL_ERROR_WANT_READ:
10986                               signal(SIGINT,oldintr);
10987                               return(0);
10988                           case SSL_ERROR_SYSCALL:
10989                             if (rc == 0) { /* EOF */
10990                                 break;
10991                             } else {
10992 #ifdef NT
10993                                 int gle = GetLastError();
10994 #endif /* NT */
10995                                 break;
10996                             }
10997                           case SSL_ERROR_WANT_X509_LOOKUP:
10998                           case SSL_ERROR_SSL:
10999                           case SSL_ERROR_ZERO_RETURN:
11000                           default:
11001                             break;
11002                         }
11003                     } else
11004 #endif /* CK_SSL */
11005                       send(csocket,(SENDARG2TYPE)obuf,3,0);
11006                     break;
11007                   default:
11008                     break;
11009                 }
11010                 continue;
11011             }
11012             dig++;
11013             if (c == EOF) {
11014                 if (expecteof) {
11015                     signal(SIGINT,oldintr);
11016                     ftpcode = 221;
11017                     debug(F101,"ftp getreply EOF","",ftpcode);
11018                     return(0);
11019                 }
11020                 lostpeer();
11021                 if (!xquiet) {
11022                     if (ftp_deb)
11023                       printf("421 ");
11024                     printf(
11025                       "Service not available, connection closed by server\n");
11026                     fflush(stdout);
11027                 }
11028                 signal(SIGINT,oldintr);
11029                 ftpcode = 421;
11030                 debug(F101,"ftp getreply EOF","",ftpcode);
11031                 return(4);
11032             }
11033             if (n == 0) {               /* First digit */
11034                 n = c;                  /* Save it */
11035             }
11036             if (auth_type &&
11037 #ifdef CK_SSL
11038                 !ssl_ftp_active_flag &&
11039 #endif /* CK_SSL */
11040                 !ibuf[0] && (n == '6' || continuation)) {
11041                 if (c != '\r' && dig > 4)
11042                   obuf[i++] = c;
11043             } else {
11044                 if (auth_type &&
11045 #ifdef CK_SSL
11046                     !ssl_ftp_active_flag &&
11047 #endif /* CK_SSL */
11048                     !ibuf[0] && dig == 1 && vbm)
11049                   printf("Unauthenticated reply received from server:\n");
11050                 if (reply_parse) {
11051                     *reply_ptr++ = c;
11052                     *reply_ptr = NUL;
11053                 }
11054                 if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */
11055                     ftp_cmdlin < 2) {
11056                     if ((c != '\r') &&
11057                         (ftp_deb || ((vbm || (!auth && n == '5')) &&
11058                         (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0
11059                         )))))
11060                     {
11061 #ifdef FTP_PROXY
11062                         if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0)))
11063                           printf("%s:",ftp_host);
11064 #endif /* FTP_PROXY */
11065
11066                         if (!xquiet) {
11067 #ifdef NOCSETS
11068                             printf("%c",c);
11069 #else
11070                             if (xlate) {
11071                                 xlatec(0,c,rcs,lcs);
11072                             } else {
11073                                 printf("%c",c);
11074                             }
11075 #endif /* NOCSETS */
11076                         }
11077                     }
11078                 }
11079             }
11080             if (auth_type &&
11081 #ifdef CK_SSL
11082                 !ssl_ftp_active_flag &&
11083 #endif /* CK_SSL */
11084                 !ibuf[0] && n != '6')
11085               continue;
11086             if (dig < 4 && isdigit(c))
11087               ftpcode = ftpcode * 10 + (c - '0');
11088             if (!pflag && ftpcode == 227)
11089               pflag = 1;
11090             if (dig > 4 && pflag == 1 && isdigit(c))
11091               pflag = 2;
11092             if (pflag == 2) {
11093                 if (c != '\r' && c != ')')
11094                   *pt++ = c;
11095                 else {
11096                     *pt = '\0';
11097                     pflag = 3;
11098                 }
11099             }
11100             if (dig == 4 && c == '-' && n != '6') {
11101                 if (continuation)
11102                   ftpcode = 0;
11103                 continuation++;
11104             }
11105             if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) {
11106                 *cp++ = c;
11107                 *cp = NUL;
11108             }
11109         }
11110         if (deblog ||
11111 #ifdef COMMENT
11112 /*
11113   Sometimes we need to print the server reply.  printlines is nonzero for any
11114   command where the results are sent back on the control connection rather
11115   than the data connection, e.g. STAT.  In the TOPS-20 case, each file line
11116   has ftpcode 213.  But if you do this with a UNIX server, it sends "213-Start
11117   STAT", <line with ftpcode == 0>, "213-End" or somesuch.  So when printlines
11118   is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213
11119   lines from UNIX.  Further experimentation needed with other servers.  Of
11120   course RFC959 is mute as to the format of the server reply.
11121
11122   'printlines' is also true for PWD and BYE.
11123 */
11124             (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20)))
11125 #else
11126 /* No, we can't be that clever -- it breaks other things like RPWD... */
11127             (printlines &&
11128              (ftpcode != 631 && ftpcode != 632 && ftpcode != 633))
11129 #endif /* COMMENT */
11130             ) {
11131             char * q = cp;
11132             char *r = ftp_reply_str;
11133             *q-- = NUL;                 /* NUL-terminate */
11134             while (*q < '!' && q > r)   /* Strip CR, etc */
11135               *q-- = NUL;
11136             if (!ftp_deb && printlines) { /* If printing */
11137                 if (ftpcode != 0)       /* strip ftpcode if any */
11138                   r += 4;
11139 #ifdef NOCSETS
11140                 printf("%s\n",r);       /* and print */
11141 #else
11142                 if (!xlate) {
11143                     printf("%s\n",r);
11144                 } else {                /* Translating */
11145                     xgnbp = r;          /* Set up strgetc() */
11146                     while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) {
11147                         if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */
11148                             signal(SIGINT,oldintr);
11149                             return(-1);
11150                         }
11151                     }
11152                     printf("\n");
11153                 }
11154 #endif /* NOCSETS */
11155             }
11156         }
11157         debug(F110,"FTP RCVD ",ftp_reply_str,0);
11158
11159         if (fc == GRF_FEAT) {           /* Parsing FEAT command response? */
11160             if (count == 0 && n == '2') {
11161                 int i;                  /* (Re)-init server FEATure table */
11162                 debug(F100,"ftp getreply clearing feature table","",0);
11163                 for (i = 0; i < 16; i++)
11164                   sfttab[i] = 0;
11165             } else {
11166                 parsefeat((char *)ftp_reply_str);
11167             }
11168         }
11169         if (auth_type &&
11170 #ifdef CK_SSL
11171             !ssl_ftp_active_flag &&
11172 #endif /* CK_SSL */
11173              !ibuf[0] && n != '6') {
11174             signal(SIGINT,oldintr);
11175             return(getreply(expecteof,lcs,rcs,vbm,auth));
11176         }
11177         ibuf[0] = obuf[i] = '\0';
11178         if (ftpcode && n == '6')
11179           if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) {
11180               printf("Unknown reply: %d %s\n", ftpcode, obuf);
11181               n = '5';
11182           } else safe = (ftpcode == 631);
11183         if (obuf[0]                     /* if there is a string to decode */
11184 #ifdef CK_SSL
11185             && !ssl_ftp_active_flag     /* and not SSL/TLS */
11186 #endif /* CK_SSL */
11187             ) {
11188             if (!auth_type) {
11189                 printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf);
11190                 n = '5';
11191             }
11192 #ifndef CK_ENCRYPTION
11193             else if (ftpcode == 632) {
11194                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11195                 n = '5';
11196             }
11197 #endif /* CK_ENCRYPTION */
11198 #ifdef NOCONFIDENTIAL
11199             else if (ftpcode == 633) {
11200                 printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf);
11201                 n = '5';
11202             }
11203 #endif /* NOCONFIDENTIAL */
11204             else {
11205                 int len = FTP_BUFSIZ;
11206                 if ((kerror = radix_encode((CHAR *)obuf,
11207                                            (CHAR *)ibuf,
11208                                            0,
11209                                            &len,
11210                                            RADIX_DECODE))
11211                     ) {
11212                     printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n",
11213                            ftpcode, radix_error(kerror), obuf);
11214                     n = '5';
11215                 }
11216 #ifdef FTP_SRP
11217                 else if (strcmp(auth_type, "SRP") == 0) {
11218                     int outlen;
11219                     outlen = srp_decode(!safe, (CHAR *)ibuf,
11220                                         (CHAR *) ibuf, len);
11221                     if (outlen < 0) {
11222                         printf("Warning: %d reply %s!\n",
11223                                ftpcode, safe ? "modified" : "garbled");
11224                         n = '5';
11225                     } else {
11226                         ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen);
11227                         if (ftp_deb)
11228                           printf("%c:", safe ? 'S' : 'P');
11229                         continue;
11230                     }
11231                 }
11232 #endif /* FTP_SRP */
11233 #ifdef FTP_KRB4
11234                 else if (strcmp(auth_type, "KERBEROS_V4") == 0) {
11235                     if (safe) {
11236                         kerror = krb_rd_safe((CHAR *)ibuf, len,
11237 #ifdef KRB524
11238                                              ftp_cred.session,
11239 #else /* KRB524 */
11240                                              &ftp_cred.session,
11241 #endif /* KRB524 */
11242                                              &hisctladdr,
11243                                              &myctladdr,
11244                                              &ftp_msg_data
11245                                              );
11246                     } else {
11247                         kerror = krb_rd_priv((CHAR *)ibuf, len,
11248                                              ftp_sched,
11249 #ifdef KRB524
11250                                              ftp_cred.session,
11251 #else /* KRB524 */
11252                                              &ftp_cred.session,
11253 #endif /* KRB524 */
11254                                              &hisctladdr,
11255                                              &myctladdr,
11256                                              &ftp_msg_data
11257                                              );
11258                     }
11259                     if (kerror != KSUCCESS) {
11260                         printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode,
11261                                safe ? "modified" : "garbled",
11262                                safe ? "safe" : "priv",
11263                                krb_get_err_text(kerror));
11264                         n = '5';
11265                     } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) {
11266                         kerror = KFAILURE;
11267                         n = '5';
11268                         printf("reply data too large for buffer\n");
11269                     } else {
11270                         if (ftp_deb)
11271                           printf("%c:", safe ? 'S' : 'P');
11272                         memcpy(ibuf,ftp_msg_data.app_data,
11273                                ftp_msg_data.app_length);
11274                         ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n",
11275                                   FTP_BUFSIZ - ftp_msg_data.app_length);
11276                         continue;
11277                     }
11278                 }
11279 #endif /* FTP_KRB4 */
11280 #ifdef FTP_GSSAPI
11281                 else if (strcmp(auth_type, "GSSAPI") == 0) {
11282                     gss_buffer_desc xmit_buf, msg_buf;
11283                     OM_uint32 maj_stat, min_stat;
11284                     int conf_state;
11285                     xmit_buf.value = ibuf;
11286                     xmit_buf.length = len;
11287                     /* decrypt/verify the message */
11288                     conf_state = safe;
11289                     maj_stat = gss_unseal(&min_stat, gcontext,
11290                                           &xmit_buf, &msg_buf,
11291                                           &conf_state, NULL);
11292                     if (maj_stat != GSS_S_COMPLETE) {
11293                         user_gss_error(maj_stat, min_stat,
11294                                        "failed unsealing reply");
11295                         n = '5';
11296                     } else {
11297                         memcpy(ibuf, msg_buf.value, msg_buf.length);
11298                         ckstrncpy(&ibuf[msg_buf.length], "\r\n",
11299                                   FTP_BUFSIZ-msg_buf.length);
11300                         gss_release_buffer(&min_stat,&msg_buf);
11301                         if (ftp_deb)
11302                           printf("%c:", safe ? 'S' : 'P');
11303                         continue;
11304                     }
11305                 }
11306 #endif /* FTP_GSSAPI */
11307                 /* Other auth types go here... */
11308             }
11309         } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 &&
11310                    !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) {
11311 #ifdef NOCSETS
11312             printf("%c",c);
11313 #else
11314             if (xlate) {
11315                 xlatec(0,c,rcs,lcs);
11316             } else {
11317                 printf("%c",c);
11318             }
11319 #endif /* NOCSETS */
11320             fflush (stdout);
11321         }
11322         if (continuation && ftpcode != originalcode) {
11323             if (originalcode == 0)
11324               originalcode = ftpcode;
11325             continue;
11326         }
11327         *cp = '\0';
11328         if (n != '1')
11329           cpend = 0;
11330         signal(SIGINT,oldintr);
11331         if (ftpcode == 421 || originalcode == 421) {
11332             lostpeer();
11333             if (!xquiet && !ftp_deb)
11334               printf("%s\n",reply_buf);
11335         }
11336         if ((cancelfile != 0) &&
11337 #ifndef ULTRIX3
11338             /* Ultrix 3.0 cc objects violently to this clause */
11339             (oldintr != cmdcancel) &&
11340 #endif /* ULTRIX3 */
11341             (oldintr != SIG_IGN)) {
11342             if (oldintr)
11343               (*oldintr)(SIGINT);
11344         }
11345         if (reply_parse) {
11346             *reply_ptr = '\0';
11347             if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) {
11348                 reply_parse = reply_ptr + strlen(reply_parse);
11349                 if ((reply_ptr = ckstrpbrk(reply_parse, " \r")))
11350                   *reply_ptr = '\0';
11351             } else
11352               reply_parse = reply_ptr;
11353         }
11354         while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */
11355           *cp-- = NUL;
11356         debug(F111,"ftp getreply",ftp_reply_str,n - '0');
11357         return(n - '0');
11358     } /* for (;;) */
11359 }
11360
11361 #ifdef BSDSELECT
11362 static int
11363 #ifdef CK_ANSIC
11364 empty(fd_set * mask, int sec)
11365 #else
11366 empty(mask, sec) fd_set * mask; int sec;
11367 #endif /* CK_ANSIC */
11368 {
11369     struct timeval t;
11370     t.tv_sec = (long) sec;
11371     t.tv_usec = 0L;
11372     debug(F100,"ftp empty calling select...","",0);
11373 #ifdef INTSELECT
11374     x = select(32, (int *)mask, NULL, NULL, &t);
11375 #else
11376     x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t);
11377 #endif /* INTSELECT */
11378     debug(F101,"ftp empty select","",x);
11379     return(x);
11380 }
11381 #else /* BSDSELECT */
11382 #ifdef IBMSELECT
11383 static int
11384 empty(mask, cnt, sec) int * mask, sec;
11385                       int   cnt;
11386 {
11387     return(select(mask,cnt,0,0,sec*1000));
11388 }
11389 #endif /* IBMSELECT */
11390 #endif /* BSDSELECT */
11391
11392 static sigtype
11393 cancelsend(sig) int sig; {
11394     havesigint++;
11395     cancelgroup++;
11396     cancelfile = 0;
11397     printf(" Canceled...\n");
11398     secure_getc(0,1);                   /* Initialize net input buffers */
11399     debug(F100,"ftp cancelsend caught SIGINT ","",0);
11400     fflush(stdout);
11401 #ifndef OS2
11402     longjmp(sendcancel, 1);
11403 #else
11404     PostCtrlCSem();
11405 #endif /* OS2 */
11406 }
11407
11408 static VOID
11409 #ifdef CK_ANSIC
11410 secure_error(char *fmt, ...)
11411 #else
11412 /* VARARGS1 */
11413 secure_error(fmt, p1, p2, p3, p4, p5)
11414    char *fmt; int p1, p2, p3, p4, p5;
11415 #endif /* CK_ANSIC */
11416 {
11417 #ifdef CK_ANSIC
11418     va_list ap;
11419
11420     va_start(ap, fmt);
11421     vfprintf(stderr, fmt, ap);
11422     va_end(ap);
11423 #else
11424     fprintf(stderr, fmt, p1, p2, p3, p4, p5);
11425 #endif
11426     fprintf(stderr, "\n");
11427 }
11428
11429 /*
11430  * Internal form of settype; changes current type in use with server
11431  * without changing our notion of the type for data transfers.
11432  * Used to change to and from ascii for listings.
11433  */
11434 static VOID
11435 changetype(newtype, show) int newtype, show; {
11436     int rc;
11437     char * s;
11438
11439     if ((newtype == curtype) && typesent++)
11440       return;
11441     switch (newtype) {
11442       case FTT_ASC:
11443         s = "A";
11444         break;
11445       case FTT_BIN:
11446         s = "I";
11447         break;
11448       case FTT_TEN:
11449         s = "L 8";
11450         break;
11451       default:
11452         s = "I";
11453         break;
11454     }
11455     rc = ftpcmd("TYPE",s,-1,-1,show);
11456     if (rc == REPLY_COMPLETE)
11457       curtype = newtype;
11458 }
11459
11460 /* PUT a file.  Returns -1 on error, 0 on success, 1 if file skipped */
11461
11462 static VOID
11463 #ifdef CK_ANSIC
11464 doftpsend(void * threadinfo)
11465 #else
11466 doftpsend(threadinfo) VOID * threadinfo;
11467 #endif
11468 {
11469 #ifdef NTSIG
11470     if (threadinfo) {                   /* Thread local storage... */
11471         TlsSetValue(TlsIndex,threadinfo);
11472         debug(F100, "doftpsend called with threadinfo block","", 0);
11473     } else debug(F100, "doftpsend - threadinfo is NULL", "", 0);
11474 #endif /* NTSIG */
11475 #ifdef CK_LOGIN
11476 #ifdef IKSD
11477 #ifdef NT
11478     if (inserver)
11479       setntcreds();
11480 #endif /* NT */
11481 #endif /* IKSD */
11482 #endif /* CK_LOGIN */
11483
11484     if (initconn()) {
11485 #ifndef NOHTTP
11486         int y = -1;
11487         /* debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy); */
11488
11489        /*  If the connection failed and we are using an HTTP Proxy
11490         *  and the reason for the failure was an authentication
11491         *  error, then we need to give the user to ability to
11492         *  enter a username and password, just like a browser.
11493         *
11494         *  I tried to do all of this within the netopen() call
11495         *  but it is much too much work.
11496         */
11497         while (y != 0 && tcp_http_proxy != NULL ) {
11498
11499             if (tcp_http_proxy_errno == 401 ||
11500                  tcp_http_proxy_errno == 407 ) {
11501                 char uid[UIDBUFLEN];
11502                 char pwd[PWDSIZ];
11503                 struct txtbox tb[2];
11504                 int ok;
11505
11506                 tb[0].t_buf = uid;
11507                 tb[0].t_len = UIDBUFLEN;
11508                 tb[0].t_lbl = "Proxy Userid: ";
11509                 tb[0].t_dflt = NULL;
11510                 tb[0].t_echo = 1;
11511                 tb[1].t_buf = pwd;
11512                 tb[1].t_len = 256;
11513                 tb[1].t_lbl = "Proxy Passphrase: ";
11514                 tb[1].t_dflt = NULL;
11515                 tb[1].t_echo = 2;
11516
11517                 ok = uq_mtxt("Proxy Server Authentication Required\n",
11518                               NULL, 2, tb);
11519                 if (ok && uid[0]) {
11520                     char * proxy_user, * proxy_pwd;
11521
11522                     proxy_user = tcp_http_proxy_user;
11523                     proxy_pwd  = tcp_http_proxy_pwd;
11524
11525                     tcp_http_proxy_user = uid;
11526                     tcp_http_proxy_pwd = pwd;
11527
11528                     y = initconn();
11529
11530                     debug(F101,"doftpsend","initconn",y);
11531                     memset(pwd,0,PWDSIZ);
11532                     tcp_http_proxy_user = proxy_user;
11533                     tcp_http_proxy_pwd = proxy_pwd;
11534                 } else
11535                     break;
11536             } else
11537                 break;
11538         }
11539
11540         if ( y != 0 ) {
11541 #endif /* NOHTTP */
11542             signal(SIGINT, ftpsnd.oldintr);
11543 #ifdef SIGPIPE
11544             if (ftpsnd.oldintp)
11545               signal(SIGPIPE, ftpsnd.oldintp);
11546 #endif /* SIGPIPE */
11547             ftpcode = -1;
11548             zclose(ZIFILE);
11549             ftpsndret = -1;
11550 #ifdef NTSIG
11551             ckThreadEnd(threadinfo);
11552 #endif /* NTSIG */
11553             return;
11554 #ifndef NOHTTP
11555         }
11556 #endif /* NOHTTP */
11557     }
11558     ftpsndret = 0;
11559 #ifdef NTSIG
11560      ckThreadEnd(threadinfo);
11561 #endif /* NTSIG */
11562 }
11563
11564 static VOID
11565 #ifdef CK_ANSIC
11566 failftpsend(void * threadinfo)
11567 #else
11568 failftpsend(threadinfo) VOID * threadinfo;
11569 #endif /* CK_ANSIC */
11570 {
11571 #ifdef NTSIG
11572     if (threadinfo) {                   /* Thread local storage... */
11573         TlsSetValue(TlsIndex,threadinfo);
11574         debug(F100, "docmdfile called with threadinfo block","", 0);
11575     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11576 #endif /* NTSIG */
11577 #ifdef CK_LOGIN
11578 #ifdef IKSD
11579 #ifdef NT
11580     if (inserver)
11581       setntcreds();
11582 #endif /* NT */
11583 #endif /* IKSD */
11584 #endif /* CK_LOGIN */
11585
11586     while (cpend) {
11587         ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11588         debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply);
11589     }
11590     if (data >= 0) {
11591 #ifdef CK_SSL
11592         if (ssl_ftp_data_active_flag) {
11593             SSL_shutdown(ssl_ftp_data_con);
11594             SSL_free(ssl_ftp_data_con);
11595             ssl_ftp_data_active_flag = 0;
11596             ssl_ftp_data_con = NULL;
11597         }
11598 #endif /* CK_SSL */
11599 #ifdef TCPIPLIB
11600         socket_close(data);
11601 #else /* TCPIPLIB */
11602 #ifdef USE_SHUTDOWN
11603         shutdown(data, 1+1);
11604 #endif /* USE_SHUTDOWN */
11605         close(data);
11606 #endif /* TCPIPLIB */
11607         data = -1;
11608         globaldin = -1;
11609     }
11610     if (ftpsnd.oldintr)
11611         signal(SIGINT,ftpsnd.oldintr);
11612 #ifdef SIGPIPE
11613     if (ftpsnd.oldintp)
11614         signal(SIGPIPE,ftpsnd.oldintp);
11615 #endif /* SIGPIPE */
11616     ftpcode = -1;
11617 #ifndef OS2
11618     /* TEST ME IN K95 */
11619     if (havesigint) {
11620         havesigint = 0;
11621         debug(F100,"ftp failftpsend chain to trap()...","",0);
11622         if (ftpsnd.oldintr != SIG_IGN)
11623           (*ftpsnd.oldintr)(SIGINT);
11624         /* NOTREACHED (I hope!) */
11625         debug(F100,"ftp failftpsend return from trap()...","",0);
11626     }
11627 #endif /* OS2 */
11628 }
11629
11630 static VOID
11631 #ifdef CK_ANSIC
11632 failftpsend2(void * threadinfo)
11633 #else
11634 failftpsend2(threadinfo) VOID * threadinfo;
11635 #endif /* CK_ANSIC */
11636 {
11637 #ifdef NTSIG
11638     if (threadinfo) {                   /* Thread local storage... */
11639         TlsSetValue(TlsIndex,threadinfo);
11640         debug(F100, "docmdfile called with threadinfo block","", 0);
11641     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
11642 #endif /* NTSIG */
11643 #ifdef CK_LOGIN
11644 #ifdef IKSD
11645 #ifdef NT
11646     if (inserver)
11647       setntcreds();
11648 #endif /* NT */
11649 #endif /* IKSD */
11650 #endif /* CK_LOGIN */
11651
11652     debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes);
11653     tfc += ffc;
11654 #ifdef GFTIMER
11655     fpfsecs = gftimer();
11656 #endif /* GFTIMER */
11657     zclose(ZIFILE);
11658 #ifdef PIPESEND
11659     if (sndfilter)
11660       pipesend = 0;
11661 #endif /* PIPESEND */
11662     signal(SIGINT, ftpsnd.oldintr);
11663 #ifdef SIGPIPE
11664     if (ftpsnd.oldintp)
11665       signal(SIGPIPE, ftpsnd.oldintp);
11666 #endif /* SIGPIPE */
11667     if (!cpend) {
11668         ftpcode = -1;
11669         ftpsndret = -1;
11670 #ifdef NTSIG
11671         ckThreadEnd(threadinfo);
11672 #endif /* NTSIG */
11673         return;
11674     }
11675     if (data >= 0) {
11676 #ifdef CK_SSL
11677         if (ssl_ftp_data_active_flag) {
11678             SSL_shutdown(ssl_ftp_data_con);
11679             SSL_free(ssl_ftp_data_con);
11680             ssl_ftp_data_active_flag = 0;
11681             ssl_ftp_data_con = NULL;
11682         }
11683 #endif /* CK_SSL */
11684 #ifdef TCPIPLIB
11685         socket_close(data);
11686 #else /* TCPIPLIB */
11687 #ifdef USE_SHUTDOWN
11688         shutdown(data, 1+1);
11689 #endif /* USE_SHUTDOWN */
11690         close(data);
11691 #endif /* TCPIPLIB */
11692         data = -1;
11693         globaldin = -1;
11694     }
11695     if (dout) {
11696 #ifdef TCPIPLIB
11697         socket_close(dout);
11698 #else /* TCPIPLIB */
11699 #ifdef USE_SHUTDOWN
11700         shutdown(dout, 1+1);
11701 #endif /* USE_SHUTDOWN */
11702         close(dout);
11703 #endif /* TCPIPLIB */
11704     }
11705     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
11706     ftpcode = -1;
11707     ftpsndret = -1;
11708
11709 #ifndef OS2
11710     /* TEST ME IN K95 */
11711     if (havesigint) {
11712         havesigint = 0;
11713         debug(F100,"ftp failftpsend2 chain to trap()...","",0);
11714         if (ftpsnd.oldintr != SIG_IGN)
11715           (*ftpsnd.oldintr)(SIGINT);
11716         /* NOTREACHED (I hope!) */
11717         debug(F100,"ftp failftpsend2 return from trap()...","",0);
11718     }
11719 #endif /* OS2 */
11720 }
11721
11722 static VOID
11723 #ifdef CK_ANSIC
11724 doftpsend2(void * threadinfo)
11725 #else
11726 doftpsend2(threadinfo) VOID * threadinfo;
11727 #endif
11728 {
11729     register int c, d = 0;
11730     int n, t, x, notafile, unique = 0;
11731     char *buf, *bufp;
11732     
11733 #ifdef NTSIG
11734     if (threadinfo) {                   /* Thread local storage... */
11735         TlsSetValue(TlsIndex,threadinfo);
11736         debug(F100, "doftpsend2 called with threadinfo block","", 0);
11737     } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0);
11738 #endif /* NTSIG */
11739 #ifdef CK_LOGIN
11740 #ifdef IKSD
11741 #ifdef NT
11742     if (inserver)
11743       setntcreds();
11744 #endif /* NT */
11745 #endif /* IKSD */
11746 #endif /* CK_LOGIN */
11747
11748     buf = ftpsndbuf;                    /* (not on stack) */
11749
11750     unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1;
11751     notafile = sndarray || pipesend;
11752
11753 #ifdef FTP_RESTART
11754     if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) {
11755         char * p;
11756         changetype(FTT_BIN,0);          /* Change to binary */
11757
11758         /* Ask for remote file's size */
11759         x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm);
11760
11761         if (x == REPLY_COMPLETE) {      /* Have ftpsnd.reply */
11762             p = &ftp_reply_str[4];      /* Parse it */
11763             while (isdigit(*p)) {
11764                 sendstart = sendstart * 10 + (int)(*p - '0');
11765                 p++;
11766             }
11767             if (*p && *p != CR) {       /* Bad number */
11768                 debug(F110,"doftpsend2 bad size",ftp_reply_str,0);
11769                 sendstart = (CK_OFF_T)0;
11770             } else if (sendstart > fsize) { /* Remote file bigger than local */
11771                 debug(F110,"doftpsend2 big size",ckfstoa(fsize),sendstart);
11772                 sendstart = (CK_OFF_T)0;
11773             }
11774             /* Local is newer */
11775             debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart);
11776             if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) {
11777                 debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0);
11778                 sendstart = (CK_OFF_T)0; /* Send the whole file */
11779             }
11780         }
11781         changetype(ftp_typ,0);          /* Change back to appropriate type */
11782         if (sendstart > (CK_OFF_T)0) {  /* Still restarting? */
11783             if (sendstart == fsize) {   /* Same size - no need to send */
11784                 debug(F111,"doftpsend2 /restart SKIP",
11785                       ckfstoa(fsize),sendstart);
11786                 zclose(ZIFILE);
11787                 ftpsndret = SKP_RES;
11788 #ifdef NTSIG
11789                 ckThreadEnd(threadinfo);
11790 #endif /* NTSIG */
11791                 return;
11792             }
11793             errno = 0;                  /* Restart needed, seek to the spot */
11794             if (zfseek((long)sendstart) < 0) {
11795                 debug(F111,"doftpsend2 zfseek fails",
11796                       ftpsnd.local,sendstart);
11797                 fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr());
11798                 sendstart = 0;
11799                 zclose(ZIFILE);
11800                 ftpsndret = -1;
11801 #ifdef NTSIG
11802                 ckThreadEnd(threadinfo);
11803 #endif /* NTSIG */
11804                 return;
11805             }
11806 #ifdef COMMENT
11807             debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart);
11808             x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm);
11809             if (x != REPLY_CONTINUE) {
11810                 sendstart = 0;
11811                 zclose(ZIFILE);
11812                 ftpsndret = -1;
11813 #ifdef NTSIG
11814                 ckThreadEnd(threadinfo);
11815 #endif /* NTSIG */
11816                 return;
11817             } else {
11818                 ftpsnd.cmd = "STOR";
11819             }
11820 #else
11821             sendmode = SM_RESEND;
11822             ftpsnd.cmd = "APPE";
11823 #endif /* COMMENT */
11824             /* sendstart = (CK_OFF_T)0; */
11825         }
11826     }
11827 #endif /* FTP_RESTART */
11828
11829     if (unique && !stouarg)             /* If we know STOU accepts no arg */
11830       ftpsnd.remote = NULL;             /* don't include one. */
11831
11832     x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm);
11833     debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode);
11834     debug(F101,"doftpsend2 ftpcmd","",x);
11835
11836     if (x != REPLY_PRELIM && unique) {
11837         /*
11838           RFC959 says STOU does not take an argument.  But every FTP server
11839           I've encountered but one accepts the arg and constructs the unique
11840           name from it, which is better than making up a totally random name
11841           for the file, which is what RFC959 calls for.  Especially because
11842           there is no way for the client to find out the name chosen by the
11843           server.  So we try STOU with the argument first, which works with
11844           most servers, and if it fails we retry it without the arg, for
11845           the benefit of the one picky server that is not "liberal in what
11846           it accepts" UNLESS the first STOU got a 502 code ("not implemented")
11847           which means STOU is not accepted, period.
11848         */
11849         if ((x == 5) && stouarg && (ftpcode != 502)) {
11850             x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); 
11851             if (x == REPLY_PRELIM)      /* If accepted */
11852               stouarg = 0;              /* flag no STOU arg for this server */
11853         }
11854     }
11855     if (x != REPLY_PRELIM) {
11856         signal(SIGINT, ftpsnd.oldintr);
11857 #ifdef SIGPIPE
11858         if (ftpsnd.oldintp)
11859           signal(SIGPIPE, ftpsnd.oldintp);
11860 #endif /* SIGPIPE */
11861         debug(F101,"doftpsend2 not REPLY_PRELIM","",x);
11862         zclose(ZIFILE);
11863 #ifdef PIPESEND
11864         if (sndfilter)
11865           pipesend = 0;
11866 #endif /* PIPESEND */
11867         ftpsndret = -1;
11868 #ifdef NTSIG
11869         ckThreadEnd(threadinfo);
11870 #endif /* NTSIG */
11871         return;
11872     }
11873     debug(F100,"doftpsend2 getting data connection...","",0);
11874     dout = dataconn(ftpsnd.lmode);             /* Get data connection */
11875     debug(F101,"doftpsend2 dataconn","",dout);
11876     if (dout == -1) {
11877         failftpsend2(threadinfo);
11878 #ifdef NTSIG
11879         ckThreadEnd(threadinfo);
11880 #endif /* NTSIG */
11881         return;
11882     }
11883     /* Initialize per-file stats */
11884     ffc = (CK_OFF_T)0;                  /* Character counter */
11885     cps = oldcps = 0L;                  /* Thruput */
11886     n = 0;
11887 #ifdef GFTIMER
11888     rftimer();                          /* reset f.p. timer */
11889 #endif /* GFTIMER */
11890
11891 #ifdef SIGPIPE
11892     ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN);
11893 #endif /* SIGPIPE */
11894     debug(F101,"doftpsend2 curtype","",curtype);
11895     switch (curtype) {
11896       case FTT_BIN:                     /* Binary mode */
11897       case FTT_TEN:
11898         errno = d = 0;
11899 #ifdef VMS
11900         /*
11901           This is because VMS zxin() is C-Library fread() 
11902           but the file was opened with zopeni(), which is RMS.
11903         */
11904         while (((c = zminchar()) > -1) && !cancelfile) {
11905             ffc++;
11906             if (zzout(dout,c) < 0)
11907               break;
11908         }
11909 #else  /* VMS */
11910         while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) {
11911             ftpsnd.bytes += n;
11912             ffc += n;
11913             debug(F111,"doftpsend2 zxin",ckltoa(n),ffc);
11914             ckhexdump("doftpsend2 zxin",buf,16);
11915 #ifdef CK_SSL
11916             if (ssl_ftp_data_active_flag) {
11917                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11918                     if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0)
11919                       break;
11920                     spackets++;
11921                     pktnum++;
11922                     if (fdispla != XYFD_B) {
11923                         spktl = d;
11924                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11925                     }
11926                 }
11927             } else {
11928 #endif /* CK_SSL */
11929                 for (bufp = buf; n > 0; n -= d, bufp += d) {
11930                     if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0)
11931                         || iscanceled())
11932                       break;
11933                     spackets++;
11934                     pktnum++;
11935                     if (fdispla != XYFD_B) {
11936                         spktl = d;
11937                         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
11938                     }
11939                 }
11940 #ifdef CK_SSL
11941             }
11942 #endif /* CK_SSL */
11943             if (d <= 0)
11944               break;
11945         }
11946 #endif  /* VMS */
11947
11948         debug(F111,"doftpsend2 XX zxin",ckltoa(n),ffc);
11949         if (n < 0)
11950           fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr());
11951         if (d < 0 || (d = secure_flush(dout)) < 0) {
11952             if (d == -1 && errno && errno != EPIPE)
11953               perror("netout");
11954             ftpsnd.bytes = -1;
11955         }
11956         break;
11957
11958       case FTT_ASC:                     /* Text mode */
11959 #ifndef NOCSETS
11960         if (ftpsnd.xlate) {             /* With translation */
11961             initxlate(ftpsnd.incs,ftpsnd.outcs);
11962             while (!cancelfile) {
11963                 if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break;
11964                 if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break;
11965             }
11966         } else {
11967 #endif /* NOCSETS */
11968             /* Text mode, no translation */
11969             while (((c = zminchar()) > -1) && !cancelfile) {
11970                 ffc++;
11971                 if (xxout(c) < 0)
11972                   break;
11973             }
11974             d = 0;
11975 #ifndef NOCSETS
11976         }
11977 #endif /* NOCSETS */
11978         if (dout == -1 || (d = secure_flush(dout)) < 0) {
11979             if (d == -1 && errno && errno != EPIPE)
11980               perror("netout");
11981             ftpsnd.bytes = -1;
11982         }
11983         break;
11984     }
11985     tfc += ffc;                         /* Total file chars */
11986 #ifdef GFTIMER
11987     fpfsecs = gftimer();
11988 #endif /* GFTIMER */
11989     zclose(ZIFILE);                     /* Close input file */
11990 #ifdef PIPESEND
11991     if (sndfilter)                      /* Undo this (it's per file) */
11992       pipesend = 0;
11993 #endif /* PIPESEND */
11994
11995 #ifdef CK_SSL
11996         if (ssl_ftp_data_active_flag) {
11997             SSL_shutdown(ssl_ftp_data_con);
11998             SSL_free(ssl_ftp_data_con);
11999             ssl_ftp_data_active_flag = 0;
12000             ssl_ftp_data_con = NULL;
12001         }
12002 #endif /* CK_SSL */
12003
12004 #ifdef TCPIPLIB
12005     socket_close(dout);                 /* Close data connection */
12006 #else /* TCPIPLIB */
12007 #ifdef USE_SHUTDOWN
12008     shutdown(dout, 1+1);
12009 #endif /* USE_SHUTDOWN */
12010     close(dout);
12011 #endif /* TCPIPLIB */
12012     ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0);
12013     signal(SIGINT, ftpsnd.oldintr);            /* Put back interrupts */
12014 #ifdef SIGPIPE
12015     if (ftpsnd.oldintp)
12016       signal(SIGPIPE, ftpsnd.oldintp);
12017 #endif /* SIGPIPE */
12018     if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) {
12019         debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply);
12020         ftpsndret = -1;
12021 #ifdef NTSIG
12022         ckThreadEnd(threadinfo);
12023 #endif /* NTSIG */
12024         return;
12025     } else if (cancelfile) {
12026         debug(F101,"doftpsend2 canceled","",ftpsnd.bytes);
12027         ftpsndret = -1;
12028 #ifdef NTSIG
12029         ckThreadEnd(threadinfo);
12030 #endif /* NTSIG */
12031         return;
12032     }
12033     debug(F101,"doftpsend2 ok","",ftpsnd.bytes);
12034     ftpsndret = 0;
12035 #ifdef NTSIG
12036      ckThreadEnd(threadinfo);
12037 #endif /* NTSIG */
12038 }
12039
12040 static int
12041 sendrequest(cmd, local, remote, xlate, incs, outcs, restart)
12042     char *cmd, *local, *remote; int xlate, incs, outcs, restart;
12043 {
12044     if (!remote) remote = "";           /* Check args */
12045     if (!*remote) remote = local;
12046     if (!local) local = "";
12047     if (!*local) return(-1);
12048     if (!cmd) cmd = "";
12049     if (!*cmd) cmd = "STOR";
12050
12051     debug(F111,"ftp sendrequest restart",local,restart);
12052
12053     nout = 0;                           /* Init output buffer count */
12054     ftpsnd.bytes = 0;                   /* File input byte count */
12055     dout = -1;
12056
12057 #ifdef FTP_PROXY
12058     if (proxy) {
12059         proxtrans(cmd, local, remote, !strcmp(cmd,"STOU"));
12060         return(0);
12061     }
12062 #endif /* FTP_PROXY */
12063
12064     changetype(ftp_typ,0);              /* Change type for this file */
12065
12066     ftpsnd.oldintr = NULL;              /* Set up interrupt handler */
12067     ftpsnd.oldintp = NULL;
12068     ftpsnd.restart = restart;
12069     ftpsnd.xlate = xlate;
12070     ftpsnd.lmode = "wb";
12071
12072 #ifdef PIPESEND                         /* Use Kermit API for file i/o... */
12073     if (sndfilter) {
12074         char * p = NULL, * q;
12075 #ifndef NOSPL
12076         int n = CKMAXPATH;
12077         if (cmd_quoting && (p = (char *) malloc(n + 1))) {
12078             q = p;
12079             debug(F110,"sendrequest pipesend filter",sndfilter,0);
12080             zzstring(sndfilter,&p,&n);
12081             debug(F111,"sendrequest pipename",q,n);
12082             if (n <= 0) {
12083                 printf("?Sorry, send filter + filename too long, %d max.\n",
12084                        CKMAXPATH
12085                        );
12086                 free(q);
12087                 return(-1);
12088             }
12089             ckstrncpy(filnam,q,CKMAXPATH+1);
12090             free(q);
12091             local = filnam;
12092         }
12093 #endif /* NOSPL */
12094     }
12095
12096     if (sndfilter)                      /* If sending thru a filter */
12097       pipesend = 1;                     /* set this for open and i/o */
12098 #endif /* PIPESEND */
12099     
12100 #ifdef VMS
12101     debug(F101,"XXX before openi binary","",binary);
12102     debug(F101,"XXX before openi ftp_typ","",ftp_typ);
12103 #endif  /* VMS */
12104
12105     if (openi(local) == 0)              /* Try to open the input file */
12106       return(-1);
12107
12108 #ifdef VMS
12109     debug(F101,"XXX after openi binary","",binary);
12110     debug(F101,"XXX after openi ftp_typ","",ftp_typ);
12111     if (!forcetype) {
12112         if (binary != ftp_typ) {        /* VMS zopeni() sets binary */
12113             debug(F101,"XXX changing type","",binary);
12114             doftptyp(binary);
12115             debug(F101,"XXX after doftptyp","",ftp_typ);
12116
12117             /* **** */
12118             if (displa && fdispla) {    /* Update file type display */
12119                 ftscreen(SCR_FN,'F',(CK_OFF_T)0,local);
12120             }
12121         }
12122     }
12123 #endif  /* VMS */
12124     ftpsndret = 0;
12125     ftpsnd.incs = incs;
12126     ftpsnd.outcs = outcs;
12127     ftpsnd.cmd = cmd;
12128     ftpsnd.local = local;
12129     ftpsnd.remote = remote;
12130     ftpsnd.oldintr = signal(SIGINT, cancelsend);
12131     havesigint = 0;
12132
12133     if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0)
12134       return(-1);
12135     if (ftpsndret < 0)
12136       return(-1);
12137     if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0)
12138       return(-1);
12139
12140     return(ftpsndret);
12141 }
12142
12143 static sigtype
12144 cancelrecv(sig) int sig; {
12145     havesigint++;
12146     cancelfile = 0;
12147     cancelgroup++;
12148     secure_getc(0,1);                   /* Initialize net input buffers */
12149     printf(" Canceling...\n");
12150     debug(F100,"ftp cancelrecv caught SIGINT","",0);
12151     fflush(stdout);
12152     if (fp_nml) {
12153         if (fp_nml != stdout)
12154           fclose(fp_nml);
12155         fp_nml = NULL;
12156     }
12157 #ifndef OS2
12158     longjmp(recvcancel, 1);
12159 #else
12160     PostCtrlCSem();
12161 #endif /* OS2 */
12162 }
12163
12164 /* Argumentless front-end for secure_getc() */
12165
12166 static int
12167 netgetc() {
12168     return(secure_getc(globaldin,0));
12169 }
12170
12171 /* Returns -1 on failure, 0 on success, 1 if file skipped */
12172
12173 /*
12174   Sets ftpcode < 0 on failure if failure reason is not server reply code:
12175     -1: interrupted by user.
12176     -2: error opening or writing output file (reason in errno).
12177     -3: failure to make data connection.
12178     -4: network read error (reason in errno).
12179 */
12180
12181 struct xx_ftprecv {
12182     int reply;
12183     int fcs;
12184     int rcs;
12185     int recover;
12186     int xlate;
12187     int din;
12188     int is_retr;
12189     sig_t oldintr, oldintp;
12190     char * cmd;
12191     char * local;
12192     char * remote;
12193     char * lmode;
12194     char * pipename;
12195     int    tcrflag;
12196     CK_OFF_T localsize;
12197 };
12198 static struct xx_ftprecv ftprecv;
12199
12200 static int ftprecvret = 0;
12201
12202 static VOID
12203 #ifdef CK_ANSIC
12204 failftprecv(VOID * threadinfo)
12205 #else
12206 failftprecv(threadinfo) VOID * threadinfo;
12207 #endif /* CK_ANSIC */
12208 {
12209 #ifdef NTSIG
12210     if (threadinfo) {                   /* Thread local storage... */
12211         TlsSetValue(TlsIndex,threadinfo);
12212         debug(F100, "docmdfile called with threadinfo block","", 0);
12213     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12214 #endif /* NTSIG */
12215
12216 #ifdef CK_LOGIN
12217 #ifdef IKSD
12218 #ifdef NT
12219     if (inserver)
12220       setntcreds();
12221 #endif /* NT */
12222 #endif /* IKSD */
12223 #endif /* CK_LOGIN */
12224
12225     while (cpend) {
12226         ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12227     }
12228     if (data >= 0) {
12229 #ifdef CK_SSL
12230         if (ssl_ftp_data_active_flag) {
12231             SSL_shutdown(ssl_ftp_data_con);
12232             SSL_free(ssl_ftp_data_con);
12233             ssl_ftp_data_active_flag = 0;
12234             ssl_ftp_data_con = NULL;
12235         }
12236 #endif /* CK_SSL */
12237 #ifdef TCPIPLIB
12238         socket_close(data);
12239 #else /* TCPIPLIB */
12240 #ifdef USE_SHUTDOWN
12241         shutdown(data, 1+1);
12242 #endif /* USE_SHUTDOWN */
12243         close(data);
12244 #endif /* TCPIPLIB */
12245         data = -1;
12246         globaldin = -1;
12247     }
12248     if (ftprecv.oldintr)
12249       signal(SIGINT, ftprecv.oldintr);
12250     ftpcode = -1;
12251     ftprecvret = -1;
12252
12253 #ifndef OS2
12254     /* TEST ME IN K95 */
12255     if (havesigint) {
12256         havesigint = 0;
12257         debug(F100,"ftp failftprecv chain to trap()...","",0);
12258         if (ftprecv.oldintr != SIG_IGN)
12259           (*ftprecv.oldintr)(SIGINT);
12260         /* NOTREACHED (I hope!) */
12261         debug(F100,"ftp failftprecv return from trap()...","",0);
12262     }
12263 #endif /* OS2 */
12264     return;
12265 }
12266
12267 static VOID
12268 #ifdef CK_ANSIC
12269 doftprecv(VOID * threadinfo)
12270 #else
12271 doftprecv(threadinfo) VOID * threadinfo;
12272 #endif /* CK_ANSIC */
12273 {
12274 #ifdef NTSIG
12275     if (threadinfo) {                   /* Thread local storage... */
12276         TlsSetValue(TlsIndex,threadinfo);
12277         debug(F100, "docmdfile called with threadinfo block","", 0);
12278     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12279 #endif /* NTSIG */
12280 #ifdef CK_LOGIN
12281 #ifdef IKSD
12282 #ifdef NT
12283     if (inserver)
12284       setntcreds();
12285 #endif /* NT */
12286 #endif /* IKSD */
12287 #endif /* CK_LOGIN */
12288
12289 #ifndef COMMENT
12290     if (!out2screen && !ftprecv.pipename) {
12291         int x;
12292         char * local;
12293         local = ftprecv.local;
12294         x = zchko(local);
12295         if (x < 0) {
12296             if ((!dpyactive || ftp_deb))
12297               fprintf(stderr,
12298                       "Temporary file %s: %s\n", ftprecv.local, ck_errstr());
12299             signal(SIGINT, ftprecv.oldintr);
12300             ftpcode = -2;
12301             ftprecvret = -1;
12302 #ifdef NTSIG
12303             ckThreadEnd(threadinfo);
12304 #endif /* NTSIG */
12305             return;
12306         }
12307     }
12308 #endif /* COMMENT */
12309     changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0);
12310     if (initconn()) {                   /* Initialize the data connection */
12311         signal(SIGINT, ftprecv.oldintr);
12312         ftpcode = -1;
12313         ftprecvret = -3;
12314 #ifdef NTSIG
12315         ckThreadEnd(threadinfo);
12316 #endif /* NTSIG */
12317         return;
12318     }
12319     secure_getc(0,1);                   /* Initialize net input buffers */
12320     ftprecvret = 0;
12321
12322 #ifdef NTSIG
12323     ckThreadEnd(threadinfo);
12324 #endif /* NTSIG */
12325 }
12326
12327 static VOID
12328 #ifdef CK_ANSIC
12329 failftprecv2(VOID * threadinfo)
12330 #else
12331 failftprecv2(threadinfo) VOID * threadinfo;
12332 #endif /* CK_ANSIC */
12333 {
12334 #ifdef NTSIG
12335     if (threadinfo) {                   /* Thread local storage... */
12336         TlsSetValue(TlsIndex,threadinfo);
12337         debug(F100, "docmdfile called with threadinfo block","", 0);
12338     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12339 #endif /* NTSIG */
12340 #ifdef CK_LOGIN
12341 #ifdef IKSD
12342 #ifdef NT
12343     if (inserver)
12344       setntcreds();
12345 #endif /* NT */
12346 #endif /* IKSD */
12347 #endif /* CK_LOGIN */
12348
12349     /* Cancel using RFC959 recommended IP,SYNC sequence  */
12350
12351     debug(F100,"ftp recvrequest CANCEL","",0);
12352 #ifdef GFTIMER
12353     fpfsecs = gftimer();
12354 #endif /* GFTIMER */
12355 #ifdef SIGPIPE
12356     if (ftprecv.oldintp)
12357       signal(SIGPIPE, ftprecv.oldintr);
12358 #endif /* SIGPIPE */
12359     signal(SIGINT, SIG_IGN);
12360     if (!cpend) {
12361         ftpcode = -1;
12362         signal(SIGINT, ftprecv.oldintr);
12363         ftprecvret = -1;
12364 #ifdef NTSIG
12365         ckThreadEnd(threadinfo);
12366 #endif /* NTSIG */
12367         return;
12368     }
12369     cancel_remote(ftprecv.din);
12370
12371 #ifdef FTP_TIMEOUT
12372     if (ftp_timed_out && out2screen && !quiet)
12373       printf("\n?Timed out.\n");
12374 #endif  /* FTP_TIMEOUT */
12375
12376     if (ftpcode > -1)
12377       ftpcode = -1;
12378     if (data >= 0) {
12379 #ifdef CK_SSL
12380         if (ssl_ftp_data_active_flag) {
12381             SSL_shutdown(ssl_ftp_data_con);
12382             SSL_free(ssl_ftp_data_con);
12383             ssl_ftp_data_active_flag = 0;
12384             ssl_ftp_data_con = NULL;
12385         }
12386 #endif /* CK_SSL */
12387 #ifdef TCPIPLIB
12388         socket_close(data);
12389 #else /* TCPIPLIB */
12390 #ifdef USE_SHUTDOWN
12391         shutdown(data, 1+1);
12392 #endif /* USE_SHUTDOWN */
12393         close(data);
12394 #endif /* TCPIPLIB */
12395         data = -1;
12396         globaldin = -1;
12397     }
12398     if (!out2screen) {
12399         int x = 0;
12400         debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep);
12401         zclose(ZOFILE);
12402         switch (keep) {                 /* which is... */
12403           case SET_AUTO:                /* AUTO */
12404             if (curtype == FTT_ASC)     /* Delete file if TYPE A. */
12405               x = 1;
12406             break;
12407           case SET_OFF:                 /* DISCARD */
12408             x = 1;                      /* Delete file, period. */
12409             break;
12410           default:                      /* KEEP */
12411             break;
12412         }
12413         if (x) {
12414             x = zdelet(ftprecv.local);
12415             debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x);
12416         }
12417     }
12418     if (ftprecv.din) {
12419 #ifdef TCPIPLIB
12420         socket_close(ftprecv.din);
12421 #else /* TCPIPLIB */
12422 #ifdef USE_SHUTDOWN
12423         shutdown(ftprecv.din, 1+1);
12424 #endif /* USE_SHUTDOWN */
12425         close(ftprecv.din);
12426 #endif /* TCPIPLIB */
12427     }
12428     signal(SIGINT, ftprecv.oldintr);
12429     ftprecvret = -1;
12430
12431     if (havesigint) {
12432         havesigint = 0;
12433         debug(F100,"FTP failftprecv2 chain to trap()...","",0);
12434 #ifdef OS2
12435         debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0);
12436         PostCtrlCSem();
12437 #else /* OS2 */
12438         if (ftprecv.oldintr != SIG_IGN)
12439           (*ftprecv.oldintr)(SIGINT);
12440         /* NOTREACHED (I hope!) */
12441         debug(F100,"ftp failftprecv2 return from trap()...","",0);
12442 #endif /* OS2 */
12443     }
12444 }
12445
12446 static VOID
12447 #ifdef CK_ANSIC
12448 doftprecv2(VOID * threadinfo)
12449 #else
12450 doftprecv2(threadinfo) VOID * threadinfo;
12451 #endif /* CK_ANSIC */
12452 {
12453     register int c, d;
12454     CK_OFF_T bytes = (CK_OFF_T)0;
12455     int bare_lfs = 0;
12456     int blksize = 0;
12457     ULONG start = 0L, stop;
12458     char * p;
12459     static char * rcvbuf = NULL;
12460     static int rcvbufsiz = 0;
12461 #ifdef CK_URL
12462     char newname[CKMAXPATH+1];          /* For file dialog */
12463 #endif /* CK_URL */
12464     extern int adl_ask;
12465
12466 #ifdef FTP_TIMEOUT
12467     ftp_timed_out = 0;
12468 #endif  /* FTP_TIMEOUT */
12469
12470     ftprecv.din = -1;
12471 #ifdef NTSIG
12472     if (threadinfo) {                   /* Thread local storage... */
12473         TlsSetValue(TlsIndex,threadinfo);
12474         debug(F100, "docmdfile called with threadinfo block","", 0);
12475     } else debug(F100, "docmdfile - threadinfo is NULL", "", 0);
12476 #endif /* NTSIG */
12477 #ifdef CK_LOGIN
12478 #ifdef IKSD
12479 #ifdef NT
12480     if (inserver)
12481       setntcreds();
12482 #endif /* NT */
12483 #endif /* IKSD */
12484 #endif /* CK_LOGIN */
12485
12486     if (ftprecv.recover) {                      /* Initiate recovery */
12487         x = ftpcmd("REST",ckfstoa(ftprecv.localsize),-1,-1,ftp_vbm);
12488         debug(F111,"ftp reply","REST",x);
12489         if (x == REPLY_CONTINUE) {
12490             ftprecv.lmode = "ab";
12491             rs_len = ftprecv.localsize;
12492         } else {
12493             ftprecv.recover = 0;
12494         }
12495     }
12496     /* IMPORTANT: No FTP commands can come between REST and RETR! */
12497
12498     debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover);
12499
12500     /* Send the command and get reply */
12501     debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0);
12502     debug(F110,"ftp recvrequest remote",ftprecv.remote,0);
12503
12504     if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm)
12505         != REPLY_PRELIM) {
12506         signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */
12507         ftprecvret = -1;                /* ftpcode is set by ftpcmd() */
12508 #ifdef NTSIG
12509         ckThreadEnd(threadinfo);
12510 #endif /* NTSIG */
12511         return;
12512     }
12513     ftprecv.din = dataconn("r");        /* Good reply, open data connection */
12514     globaldin = ftprecv.din;            /* Global copy of file descriptor */
12515     if (ftprecv.din == -1) {            /* Check for failure */
12516         ftpcode = -3;                   /* Code for no data connection */
12517         ftprecvret = -1;
12518 #ifdef NTSIG
12519         ckThreadEnd(threadinfo);
12520 #endif /* NTSIG */
12521         return;
12522     }
12523 #ifdef CK_URL
12524     /* In K95 GUI put up a file box */
12525     if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */
12526         int x;
12527         char * preface =
12528 "\r\nIncoming file from FTP server...\r\n\
12529 Please confirm output file specification or supply an alternative:";
12530
12531         x = uq_file(preface,            /* K95 GUI: Put up file box. */
12532                     NULL,
12533                     4,
12534                     NULL,
12535                     ftprecv.local ? ftprecv.local : ftprecv.remote,
12536                     newname,
12537                     CKMAXPATH+1
12538                     );
12539         if (x > 0) {
12540             ftprecv.local = newname;    /* Substitute user's file name */
12541             if (x == 2)                 /* And append if user said to */
12542               ftprecv.lmode = "ab";
12543         }
12544     }
12545 #endif /* CK_URL */
12546     x = 1;                              /* Output file open OK? */
12547     if (ftprecv.pipename) {             /* Command */
12548         x = zxcmd(ZOFILE,ftprecv.pipename);
12549         debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x);
12550     } else if (!out2screen) {           /* File */
12551         struct filinfo xx;
12552         xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0;
12553         xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0;
12554         /* Append or New */
12555         xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N;
12556         x = zopeno(ZOFILE,ftprecv.local,NULL,&xx);
12557         debug(F111,"ftp recvrequest zopeno",ftprecv.local,x);
12558     }
12559     if (x < 1) {                        /* Failure to open output file */
12560         if ((!dpyactive || ftp_deb))
12561           fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr());
12562         ftprecvret = -1;
12563 #ifdef NTSIG
12564         ckThreadEnd(threadinfo);
12565 #endif /* NTSIG */
12566         return;
12567     }
12568     blksize = FTP_BUFSIZ;               /* Allocate input buffer */
12569
12570     debug(F101,"ftp recvrequest blksize","",blksize);
12571     debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz);
12572
12573     if (rcvbufsiz < blksize) {          /* if necessary */
12574         if (rcvbuf) {
12575             free(rcvbuf);
12576             rcvbuf = NULL;
12577         }
12578         rcvbuf = (char *)malloc((unsigned)blksize);
12579         if (!rcvbuf) {
12580             debug(F100,"ftp get rcvbuf malloc failed","",0);
12581             ftpcode = -2;
12582 #ifdef ENOMEM
12583             errno = ENOMEM;
12584 #endif /* ENOMEM */
12585             if ((!dpyactive || ftp_deb))
12586               perror("malloc");
12587             rcvbufsiz = 0;
12588             ftprecvret = -1;
12589 #ifdef NTSIG
12590             ckThreadEnd(threadinfo);
12591 #endif /* NTSIG */
12592             return;
12593         }
12594         debug(F101,"ftp get rcvbuf malloc ok","",blksize);
12595         rcvbufsiz = blksize;
12596     }
12597     debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz);
12598
12599     ffc = (CK_OFF_T)0;                  /* Character counter */
12600     cps = oldcps = 0L;                  /* Thruput */
12601     start = gmstimer();                 /* Start time (msecs) */
12602 #ifdef GFTIMER
12603     rftimer();                          /* Start time (float) */
12604 #endif /* GFTIMER */
12605
12606     debug(F111,"ftp get type",ftprecv.local,curtype);
12607     debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl);
12608     switch (curtype) {
12609       case FTT_BIN:                     /* Binary mode */
12610       case FTT_TEN:                     /* TENEX mode */
12611         d = 0;
12612         while (1) {
12613             errno = 0;
12614             c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz);
12615             if (cancelfile) {
12616                 failftprecv2(threadinfo);
12617 #ifdef NTSIG
12618                 ckThreadEnd(threadinfo);
12619 #endif /* NTSIG */
12620                 return;
12621             }
12622             if (c < 1)
12623               break;
12624 #ifdef printf                           /* (What if it isn't?) */
12625             if (out2screen && !ftprecv.pipename) {
12626                 int i;
12627                 for (i = 0; i < c; i++)
12628                   printf("%c",rcvbuf[i]);
12629             } else
12630 #endif /* printf */
12631               {
12632                 register int i;
12633                 i = 0;
12634                 errno = 0;
12635                 while (i < c) {
12636                     if (zmchout(rcvbuf[i++]) < 0) {
12637                         d = i;
12638                         break;
12639                     }
12640                 }
12641             }
12642             bytes += c;
12643             ffc += c;
12644         }
12645 #ifdef FTP_TIMEOUT
12646         if (c == -3) {
12647             debug(F100,"ftp recvrequest timeout","",0); 
12648             bytes = (CK_OFF_T)-1;
12649             ftp_timed_out = 1;
12650             ftpcode = -3;
12651         } else
12652 #endif  /* FTP_TIMEOUT */
12653         if (c < 0) {
12654             debug(F111,"ftp recvrequest errno",ckitoa(c),errno);
12655             if (c == -1 && errno != EPIPE)
12656               if ((!dpyactive || ftp_deb))
12657                 perror("netin");
12658             bytes = (CK_OFF_T)-1;
12659             ftpcode = -4;
12660         }
12661         if (d < c) {
12662             ftpcode = -2;
12663             if ((!dpyactive || ftp_deb)) {
12664                 char * p;
12665                 p = ftprecv.local ? ftprecv.local : ftprecv.pipename;
12666                 if (d < 0)
12667                   fprintf(stderr,
12668                           "local(3): %s: %s\n", ftprecv.local, ck_errstr());
12669                 else
12670                   fprintf(stderr,
12671                           "%s: short write\n", ftprecv.local);
12672             }
12673         }
12674         break;
12675
12676       case FTT_ASC:                     /* Text mode */
12677         debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate);
12678 #ifndef NOCSETS
12679         if (ftprecv.xlate) {
12680             int t;
12681 #ifdef CK_ANSIC
12682             int (*fn)(char);
12683 #else
12684             int (*fn)();
12685 #endif /* CK_ANSIC */
12686             debug(F110,"ftp recvrequest (data)","initxlate",0);
12687             initxlate(ftprecv.rcs,ftprecv.fcs);         /* (From,To) */
12688             if (ftprecv.pipename) {
12689                 fn = pipeout;
12690                 debug(F110,"ftp recvrequest ASCII","pipeout",0);
12691             } else {
12692                 fn = out2screen ? scrnout : putfil;
12693                 debug(F110,"ftp recvrequest ASCII",
12694                       out2screen ? "scrnout" : "putfil",0);
12695             }
12696             while (1) {
12697                 /* Get byte from net */
12698                 c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12699                 if (cancelfile) {
12700                     failftprecv2(threadinfo);
12701 #ifdef NTSIG
12702                     ckThreadEnd(threadinfo);
12703 #endif /* NTSIG */
12704                     return;
12705                 }
12706                 if (c0 < 0)
12707                   break;
12708                 /* Second byte from net */
12709                 c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc);
12710                 if (cancelfile) {
12711                     failftprecv2(threadinfo);
12712 #ifdef NTSIG
12713                     ckThreadEnd(threadinfo);
12714 #endif /* NTSIG */
12715                     return;
12716                 }
12717                 if (c1 < 0)
12718                   break;
12719 #ifdef COMMENT
12720                 /* K95: Check whether we need this */
12721                 if (fileorder > 0)      /* Little Endian */
12722                   bytswap(&c0,&c1);     /* swap bytes*/
12723 #endif /* COMMENT */
12724
12725 #ifdef OS2
12726                 if ( out2screen &&            /* we're translating to UCS-2 */ 
12727                      !k95stdout && !inserver) /* for the real screen... */     
12728                 {
12729                     union {
12730                         USHORT ucs2;
12731                         UCHAR  bytes[2];
12732                     } output;
12733
12734                     output.bytes[0] = c1;
12735                     output.bytes[1] = c0;
12736
12737                     VscrnWrtUCS2StrAtt(VCMD,
12738                                        &output.ucs2,
12739                                        1,
12740                                        wherey[VCMD],
12741                                        wherex[VCMD],
12742                                        &colorcmd
12743                                        );
12744
12745                 } else 
12746 #endif /* OS2 */
12747                 {
12748                     if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12749                     if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break;
12750                 }
12751             }
12752         } else {
12753 #endif /* NOCSETS */
12754             while (1) {
12755                 c = secure_getc(ftprecv.din,0);
12756                 if (cancelfile
12757 #ifdef FTP_TIMEOUT
12758                     || ftp_timed_out
12759 #endif  /* FTP_TIMEOUT */
12760                     ) {
12761                     failftprecv2(threadinfo);
12762 #ifdef NTSIG
12763                     ckThreadEnd(threadinfo);
12764 #endif /* NTSIG */
12765                     return;
12766                 }
12767                 if (c < 0 || c == EOF)
12768                   break;
12769 #ifdef UNIX
12770                 /* Record format conversion for Unix */
12771                 /* SKIP THIS FOR WINDOWS! */
12772                 if (c == '\n')
12773                   bare_lfs++;
12774                 while (c == '\r') {
12775                     bytes++;
12776                     if ((c = secure_getc(ftprecv.din,0)) != '\n' ||
12777                         ftprecv.tcrflag) {
12778                         if (cancelfile) {
12779                             failftprecv2(threadinfo);
12780 #ifdef NTSIG
12781                             ckThreadEnd(threadinfo);
12782 #endif /* NTSIG */
12783                             return;
12784                         }
12785                         if (c < 0 || c == EOF)
12786                           goto break2;
12787                         if (c == '\0') {
12788                             bytes++;
12789                             goto contin2;
12790                         }
12791                     }
12792                 }
12793                 if (c < 0)
12794                   break;
12795 #endif /* UNX */
12796
12797                 if (out2screen && !ftprecv.pipename)
12798 #ifdef printf
12799                   printf("%c",(char)c);
12800 #else
12801                   putchar((char)c);
12802 #endif /* printf */
12803                 else
12804                   if ((d = zmchout(c)) < 0)
12805                     break;
12806                 bytes++;
12807                 ffc++;
12808               contin2:
12809                 ;
12810             }
12811           break2:
12812             if (bare_lfs && (!dpyactive || ftp_deb)) {
12813                 printf("WARNING! %d bare linefeeds received in ASCII mode\n",
12814                        bare_lfs);
12815                 printf("File might not have transferred correctly.\n");
12816             }
12817             if (ftprecv.din == -1) {
12818                 bytes = (CK_OFF_T)-1;
12819             }
12820             if (c == -2)
12821               bytes = (CK_OFF_T)-1;
12822             break;
12823 #ifndef NOCSETS
12824         }
12825 #endif /* NOCSETS */
12826     }
12827     if (ftprecv.pipename || !out2screen) {
12828         zclose(ZOFILE);                 /* Close the file */
12829         debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode);
12830         if (ftpcode < 0) {              /* If download failed */
12831             int x = 0;
12832             switch (keep) {             /* which is... */
12833               case SET_AUTO:            /* AUTO */
12834                 if (curtype == FTT_ASC) /* Delete file if TYPE A. */
12835                   x = 1;
12836                 break;
12837               case SET_OFF:             /* DISCARD */
12838                 x = 1;                  /* Delete file, period. */
12839                 break;
12840               default:                  /* KEEP */
12841                 break;
12842             }
12843             if (x) {
12844                 x = zdelet(ftprecv.local);
12845                 debug(F111,"ftp get delete incomplete",ftprecv.local,x);
12846             }
12847         }
12848     }
12849     signal(SIGINT, ftprecv.oldintr);
12850 #ifdef SIGPIPE
12851     if (ftprecv.oldintp)
12852       signal(SIGPIPE, ftprecv.oldintp);
12853 #endif /* SIGPIPE */
12854     stop = gmstimer();
12855 #ifdef GFTIMER
12856     fpfsecs = gftimer();
12857 #endif /* GFTIMER */
12858     tfc += ffc;
12859
12860 #ifdef TCPIPLIB
12861     socket_close(ftprecv.din);
12862 #else /* TCPIPLIB */
12863 #ifdef USE_SHUTDOWN
12864     shutdown(ftprecv.din, 1+1);
12865 #endif /* USE_SHUTDOWN */
12866     close(ftprecv.din);
12867 #endif /* TCPIPLIB */
12868     ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0);
12869     ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || 
12870                    ftprecv.reply == REPLY_ERROR) ? -1 : 0);
12871 #ifdef NTSIG
12872      ckThreadEnd(threadinfo);
12873 #endif /* NTSIG */
12874 }
12875
12876 static int
12877 recvrequest(cmd, local, remote, lmode, printnames, recover, pipename,
12878             xlate, fcs, rcs)
12879     char *cmd, *local, *remote, *lmode, *pipename;
12880     int printnames, recover, xlate, fcs, rcs;
12881 {
12882 #ifdef NT
12883     struct _stat stbuf;
12884 #else /* NT */
12885     struct stat stbuf;
12886 #endif /* NT */
12887
12888 #ifdef DEBUG
12889     if (deblog) {
12890         debug(F111,"ftp recvrequest cmd",cmd,recover);
12891         debug(F110,"ftp recvrequest local ",local,0);
12892         debug(F111,"ftp recvrequest remote",remote,ftp_typ);
12893         debug(F110,"ftp recvrequest pipename ",pipename,0);
12894         debug(F101,"ftp recvrequest xlate","",xlate);
12895         debug(F101,"ftp recvrequest fcs","",fcs);
12896         debug(F101,"ftp recvrequest rcs","",rcs);
12897     }
12898 #endif /* DEBUG */
12899
12900     ftprecv.localsize = (CK_OFF_T)0;
12901
12902     if (remfile) {                      /* See remcfm(), remtxt() */
12903         if (rempipe) {
12904             pipename = remdest;
12905         } else {
12906             local = remdest;
12907             if (remappd) lmode = "ab";
12908         }
12909     }
12910     out2screen = 0;
12911     if (!cmd) cmd = "";                 /* Core dump prevention */
12912     if (!remote) remote = "";
12913     if (!lmode) lmode = "";
12914
12915     if (pipename) {                     /* No recovery for pipes. */
12916         recover = 0;
12917         if (!local)
12918           local = pipename;
12919     } else {
12920         if (!local)                     /* Output to screen? */
12921           local = "-";
12922         out2screen = !strcmp(local,"-");
12923     }
12924     debug(F101,"ftp recvrequest out2screen","",out2screen);
12925
12926 #ifdef OS2
12927     if ( ftp_xla && out2screen && !k95stdout && !inserver )
12928         fcs = FC_UCS2;
12929 #endif /* OS2 */
12930
12931     if (out2screen)                     /* No recovery to screen */
12932       recover = 0;
12933     if (!ftp_typ)                       /* No recovery in text mode */
12934       recover = 0;
12935     ftprecv.is_retr = (strcmp(cmd, "RETR") == 0);
12936
12937     if (!ftprecv.is_retr)               /* No recovery except for RETRieve */
12938       recover = 0;
12939
12940 #ifdef COMMENT
12941     if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */
12942         if (recursive && ckstrchr(local,'/')) {
12943             
12944         }
12945     }
12946 #endif /* COMMENT */
12947
12948     ftprecv.localsize = (CK_OFF_T)0;    /* Local file size */
12949     rs_len = (CK_OFF_T)0;               /* Recovery point */
12950
12951     debug(F101,"ftp recvrequest recover","",recover);
12952     if (recover) {                      /* Recovering... */
12953         if (stat(local, &stbuf) < 0) {  /* Can't stat local file */
12954             debug(F101,"ftp recvrequest recover stat failed","",errno);
12955             recover = 0;                /* So cancel recovery */
12956         } else {                        /* Have local file info */
12957             ftprecv.localsize = stbuf.st_size;  /* Get size */
12958             /* Remote file smaller than local */
12959             if (fsize < ftprecv.localsize) {
12960                 debug(F101,"ftp recvrequest recover remote smaller","",fsize);
12961                 recover = 0;            /* Recovery can't work */
12962             } else if (fsize == ftprecv.localsize) { /* Sizes are equal */
12963                 debug(F111,"ftp recvrequest recover equal size",
12964                       remote,ftprecv.localsize);
12965                 return(1);
12966             }
12967 #ifdef COMMENT
12968 /*
12969   The problem here is that the original partial file never got its date
12970   set, either because FTP DATES was OFF, or because the partial file was
12971   downloaded by some other program that doesn't set local file dates, or
12972   because Kermit only sets the file's date when the download was complete
12973   and successful.  In all these cases, the local file has a later time
12974   than the remote.
12975 */
12976             if (recover) {              /* Remote is bigger */
12977                 x = chkmodtime(local,remote,0); /* Check file dates */
12978                 debug(F111,"ftp recvrequest chkmodtime",remote,x);
12979                 if (x != 1)             /* Dates must be equal! */
12980                   recover = 0;          /* If not, get whole file */
12981             }
12982 #endif /* COMMENT */
12983         }
12984         debug(F111,"ftp recvrequest recover",remote,recover);
12985     }
12986
12987 #ifdef FTP_PROXY
12988     if (proxy && ftprecv.is_retr)
12989       return(proxtrans(cmd, local ? local : remote, remote));
12990 #endif /* FTP_PROXY */
12991
12992     ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr;
12993
12994     ftprecv.reply = 0;
12995     ftprecv.fcs = fcs;
12996     ftprecv.rcs = rcs;
12997     ftprecv.recover = recover;
12998     ftprecv.xlate = xlate;
12999     ftprecv.cmd = cmd;
13000     ftprecv.local = local;
13001     ftprecv.remote = remote;
13002     ftprecv.lmode = lmode;
13003     ftprecv.pipename = pipename;
13004     ftprecv.oldintp = NULL;
13005     ftpcode = 0;
13006
13007     havesigint = 0;
13008     ftprecv.oldintr = signal(SIGINT, cancelrecv);
13009     if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0)
13010       return -1;
13011
13012 #ifdef FTP_TIMEOUT
13013     debug(F111,"ftp recvrequest ftprecvret",remote,ftprecvret);
13014     debug(F111,"ftp recvrequest ftp_timed_out",remote,ftp_timed_out);
13015     if (ftp_timed_out)
13016       ftprecvret = -1;
13017 #endif  /* FTP_TIMEOUT */
13018
13019     if (ftprecvret < 0)
13020       return -1;
13021
13022     if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0)
13023       return -1;
13024     return ftprecvret;
13025 }
13026
13027 /*
13028  * Need to start a listen on the data channel before we send the command,
13029  * otherwise the server's connect may fail.
13030  */
13031 static int
13032 initconn() {
13033     register char *p, *a;
13034     int result, tmpno = 0;
13035     int on = 1;
13036     GSOCKNAME_T len;
13037
13038 #ifndef NO_PASSIVE_MODE
13039     int a1,a2,a3,a4,p1,p2;
13040
13041     if (passivemode) {
13042         data = socket(AF_INET, SOCK_STREAM, 0);
13043         globaldin = data;
13044         if (data < 0) {
13045             perror("ftp: socket");
13046             return(-1);
13047         }
13048         if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13049             printf("Passive mode refused\n");
13050             passivemode = 0;
13051             return(initconn());
13052         }
13053 /*
13054   Now we have a string of comma-separated one-byte unsigned integer values,
13055   The first four are the an IP address.  The fifth is the MSB of the port
13056   number, the sixth is the LSB.  From that we can make a sockaddr_in.
13057 */
13058         if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
13059             printf("Passive mode address scan failure\n");
13060             return(-1);
13061         };
13062 #ifndef NOHTTP
13063         if (tcp_http_proxy) {
13064 #ifdef OS2
13065             char * agent = "Kermit 95"; /* Default user agent */
13066 #else
13067             char * agent = "C-Kermit";
13068 #endif /* OS2 */
13069             register struct hostent *hp = 0;
13070             struct servent *destsp;
13071             char host[512], *p, *q;
13072 #ifdef IP_TOS
13073 #ifdef IPTOS_THROUGHPUT
13074             int tos;
13075 #endif /* IPTOS_THROUGHPUT */
13076 #endif /* IP_TOS */
13077             int s;
13078 #ifdef DEBUG
13079             extern int debtim;
13080             int xdebtim;
13081             xdebtim = debtim;
13082             debtim = 1;
13083 #endif /* DEBUG */
13084
13085             ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2),
13086                       ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2),
13087                       NULL,NULL,NULL
13088                       );
13089             memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
13090             for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++)
13091               *q = *p;
13092             *q = '\0';
13093
13094             hisctladdr.sin_addr.s_addr = inet_addr(host);
13095             if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
13096             {
13097                 debug(F110,"initconn A",host,0);
13098                 hisctladdr.sin_family = AF_INET;
13099             } else {
13100                 debug(F110,"initconn B",host,0);
13101                 hp = gethostbyname(host);
13102 #ifdef HADDRLIST
13103                 hp = ck_copyhostent(hp); /* make safe copy that won't change */
13104 #endif /* HADDRLIST */
13105                 if (hp == NULL) {
13106                     fprintf(stderr, "ftp: %s: Unknown host\n", host);
13107                     ftpcode = -1;
13108 #ifdef DEBUG
13109                     debtim = xdebtim;
13110 #endif /* DEBUG */
13111                     return(0);
13112                 }
13113                 hisctladdr.sin_family = hp->h_addrtype;
13114 #ifdef HADDRLIST
13115                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
13116                        sizeof(hisctladdr.sin_addr));
13117 #else /* HADDRLIST */
13118                 memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
13119                        sizeof(hisctladdr.sin_addr));
13120 #endif /* HADDRLIST */
13121             }
13122             data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13123             debug(F101,"initconn socket","",data);
13124             if (data < 0) {
13125                 perror("ftp: socket");
13126                 ftpcode = -1;
13127 #ifdef DEBUG
13128                 debtim = xdebtim;
13129 #endif /* DEBUG */
13130                 return(0);
13131             }
13132             if (*p == ':')
13133               p++;
13134             else
13135               p = "http";
13136
13137             destsp = getservbyname(p,"tcp");
13138             if (destsp)
13139               hisctladdr.sin_port = destsp->s_port;
13140             else if (p)
13141               hisctladdr.sin_port = htons(atoi(p));
13142             else
13143               hisctladdr.sin_port = htons(80);
13144             errno = 0;
13145 #ifdef HADDRLIST
13146             debug(F100,"initconn HADDRLIST","",0);
13147             while
13148 #else
13149             debug(F100,"initconn no HADDRLIST","",0);
13150             if
13151 #endif /* HADDRLIST */
13152               (connect(data, (struct sockaddr *)&hisctladdr,
13153                        sizeof (hisctladdr)) < 0) {
13154                   debug(F101,"initconn connect failed","",errno);
13155 #ifdef HADDRLIST
13156                   if (hp && hp->h_addr_list[1]) {
13157                       int oerrno = errno;
13158
13159                       fprintf(stderr,
13160                               "ftp: connect to address %s: ",
13161                               inet_ntoa(hisctladdr.sin_addr)
13162                               );
13163                       errno = oerrno;
13164                       perror((char *)0);
13165                       hp->h_addr_list++;
13166                       memcpy((char *)&hisctladdr.sin_addr,
13167                              hp->h_addr_list[0],
13168                              sizeof(hisctladdr.sin_addr));
13169                       fprintf(stdout, "Trying %s...\n",
13170                               inet_ntoa(hisctladdr.sin_addr));
13171 #ifdef TCPIPLIB
13172                       socket_close(data);
13173 #else /* TCPIPLIB */
13174                       close(data);
13175 #endif /* TCPIPLIB */
13176                       data = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
13177                       if (data < 0) {
13178                           perror("ftp: socket");
13179                           ftpcode = -1;
13180 #ifdef DEBUG
13181                           debtim = xdebtim;
13182 #endif /* DEBUG */
13183                           return(0);
13184                       }
13185                       continue;
13186                   }
13187 #endif /* HADDRLIST */
13188                   perror("ftp: connect");
13189                   ftpcode = -1;
13190                   goto bad;
13191               }
13192             if (http_connect(data,
13193                              tcp_http_proxy_agent ?
13194                                tcp_http_proxy_agent :
13195                                  agent,
13196                              NULL,
13197                              tcp_http_proxy_user,
13198                              tcp_http_proxy_pwd,
13199                              0,
13200                              proxyhost
13201                              ) < 0) {
13202 #ifdef TCPIPLIB
13203                 socket_close(data);
13204 #else /* TCPIPLIB */
13205                 close(data);
13206 #endif /* TCPIPLIB */
13207                 perror("ftp: connect");
13208                 ftpcode = -1;
13209                 goto bad;
13210             }
13211         } else
13212 #endif /* NOHTTP */
13213         {
13214             data_addr.sin_family = AF_INET;
13215             data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
13216             data_addr.sin_port = htons((p1<<8)|p2);
13217
13218             if (connect(data,
13219                         (struct sockaddr *)&data_addr,
13220                         sizeof(data_addr)) < 0
13221                 ) {
13222                 perror("ftp: connect");
13223                 return(-1);
13224             }
13225         }
13226         debug(F100,"initconn connect ok","",0);
13227 #ifdef IP_TOS
13228 #ifdef IPTOS_THROUGHPUT
13229         on = IPTOS_THROUGHPUT;
13230         if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13231           perror("ftp: setsockopt TOS (ignored)");
13232 #endif /* IPTOS_THROUGHPUT */
13233 #endif /* IP_TOS */
13234         memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in));
13235         return(0);
13236     }
13237 #endif /* NO_PASSIVE_MODE */
13238
13239   noport:
13240     memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in));
13241     if (sendport)
13242       data_addr.sin_port = 0;   /* let system pick one */
13243     if (data != -1) {
13244 #ifdef TCPIPLIB
13245         socket_close(data);
13246 #else /* TCPIPLIB */
13247 #ifdef USE_SHUTDOWN
13248         shutdown(data, 1+1);
13249 #endif /* USE_SHUTDOWN */
13250         close(data);
13251 #endif /* TCPIPLIB */
13252     }
13253     data = socket(AF_INET, SOCK_STREAM, 0);
13254     globaldin = data;
13255     if (data < 0) {
13256         perror("ftp: socket");
13257         if (tmpno)
13258           sendport = 1;
13259         return(-1);
13260     }
13261     if (!sendport) {
13262         if (setsockopt(data,
13263                        SOL_SOCKET,
13264                        SO_REUSEADDR,
13265                        (char *)&on,
13266                        sizeof (on)
13267                        ) < 0
13268             ) {
13269             perror("ftp: setsockopt (reuse address)");
13270             goto bad;
13271         }
13272     }
13273     if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
13274         perror("ftp: bind");
13275         goto bad;
13276     }
13277     len = sizeof (data_addr);
13278     if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
13279         perror("ftp: getsockname");
13280         goto bad;
13281     }
13282     if (listen(data, 1) < 0) {
13283         perror("ftp: listen");
13284         goto bad;
13285     }
13286     if (sendport) {
13287         a = (char *)&data_addr.sin_addr;
13288         p = (char *)&data_addr.sin_port;
13289         ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ",
13290                   UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",",
13291                   UC(p[0]),",", UC(p[1]));
13292         result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm);
13293         if (result == REPLY_ERROR && sendport) {
13294             sendport = 0;
13295             tmpno = 1;
13296             goto noport;
13297         }
13298         return(result != REPLY_COMPLETE);
13299     }
13300     if (tmpno)
13301       sendport = 1;
13302 #ifdef IP_TOS
13303 #ifdef IPTOS_THROUGHPUT
13304     on = IPTOS_THROUGHPUT;
13305     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
13306       perror("ftp: setsockopt TOS (ignored)");
13307 #endif
13308 #endif
13309     return(0);
13310   bad:
13311 #ifdef TCPIPLIB
13312     socket_close(data);
13313 #else /* TCPIPLIB */
13314 #ifdef USE_SHUTDOWN
13315     shutdown(data, 1+1);
13316 #endif /* USE_SHUTDOWN */
13317     close(data);
13318 #endif /* TCPIPLIB */
13319     data = -1;
13320     globaldin = data;
13321     if (tmpno)
13322       sendport = 1;
13323     return(-1);
13324 }
13325
13326 #ifdef CK_SSL
13327 static int
13328 ssl_dataconn() {
13329     if (ssl_ftp_data_con!=NULL) {       /* Do SSL */
13330         SSL_free(ssl_ftp_data_con);
13331         ssl_ftp_data_con=NULL;
13332     }
13333     ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx);
13334
13335     SSL_set_fd(ssl_ftp_data_con,data);
13336     SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL);
13337
13338     SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con);
13339
13340     if (ssl_debug_flag) {
13341         fprintf(stderr,"=>START SSL connect on DATA\n");
13342         fflush(stderr);
13343     }
13344     if (SSL_connect(ssl_ftp_data_con) <= 0) {
13345         static char errbuf[1024];
13346         ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ",
13347                   ERR_error_string(ERR_get_error(),NULL),NULL,NULL);
13348         fprintf(stderr,"%s\n", errbuf);
13349         fflush(stderr);
13350 #ifdef TCPIPLIB
13351         socket_close(data);
13352 #else /* TCPIPLIB */
13353 #ifdef USE_SHUTDOWN
13354         shutdown(data, 1+1);
13355 #endif /* USE_SHUTDOWN */
13356         close(data);
13357 #endif /* TCPIPLIB */
13358         data = -1;
13359         globaldin = data;
13360         return(-1);
13361     } else {
13362         ssl_ftp_data_active_flag=1;
13363
13364         if (!ssl_certsok_flag && !tls_is_krb5(2)) {
13365             char *subject = ssl_get_subject_name(ssl_ftp_data_con);
13366
13367             if (!subject) {
13368                 if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
13369                     debug(F110,"dataconn","[SSL _- FAILED]",0);
13370
13371                     ssl_ftp_data_active_flag = 0;
13372 #ifdef TCPIPLIB
13373                     socket_close(data);
13374 #else /* TCPIPLIB */
13375 #ifdef USE_SHUTDOWN
13376                     shutdown(data, 1+1);
13377 #endif /* USE_SHUTDOWN */
13378                     close(data);
13379 #endif /* TCPIPLIB */
13380                     data = -1;
13381                     globaldin = data;
13382                     return(-1);
13383                 } else {
13384                     if (!out2screen && displa && fdispla) {
13385                         ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13386                         /* fdispla = XYFD_B; */
13387                     }
13388
13389                     if (uq_ok(
13390           "Warning: Server didn't provide a certificate on data connection\n",
13391                                "Continue with file transfer? (Y/N)",
13392                               3,NULL,0) <= 0) {
13393                         debug(F110, "dataconn","[SSL - FAILED]",0);
13394                         ssl_ftp_data_active_flag = 0;
13395 #ifdef TCPIPLIB
13396                         socket_close(data);
13397 #else /* TCPIPLIB */
13398 #ifdef USE_SHUTDOWN
13399                         shutdown(data, 1+1);
13400 #endif /* USE_SHUTDOWN */
13401                         close(data);
13402 #endif /* TCPIPLIB */
13403                         data = -1;
13404                         globaldin = data;
13405                         return(-1);
13406                     }
13407                 }
13408             } else {
13409                 if (!out2screen && displa && fdispla == XYFD_C) {
13410                     ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled");
13411                     /* fdispla = XYFD_B; */
13412                 }
13413
13414                 if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) {
13415                     debug(F110,"dataconn","[SSL - FAILED]",0);
13416                     ssl_ftp_data_active_flag = 0;
13417 #ifdef TCPIPLIB
13418                     socket_close(data);
13419 #else /* TCPIPLIB */
13420 #ifdef USE_SHUTDOWN
13421                     shutdown(data, 1+1);
13422 #endif /* USE_SHUTDOWN */
13423                     close(data);
13424 #endif /* TCPIPLIB */
13425                     data = -1;
13426                     globaldin = data;
13427                     return(-1);
13428                 }
13429             }
13430         }
13431         debug(F110,"dataconn","[SSL - OK]",0);
13432 #ifdef COMMENT
13433         /* This messes up the full screen file transfer display */
13434         ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag);
13435 #endif /* COMMENT */
13436     }
13437     if (ssl_debug_flag) {
13438         fprintf(stderr,"=>DONE SSL connect on DATA\n");
13439         fflush(stderr);
13440     }
13441     return(data);
13442 }
13443 #endif /* CK_SSL */
13444
13445 static int
13446 dataconn(lmode) char *lmode; {
13447     int s;
13448 #ifdef IP_TOS
13449     int tos;
13450 #endif /* IP_TOS */
13451 #ifdef UCX50
13452     static u_int fromlen;
13453 #else
13454     static SOCKOPT_T fromlen;
13455 #endif /* UCX50 */
13456
13457     fromlen = sizeof(hisdataaddr);
13458
13459 #ifndef NO_PASSIVE_MODE
13460     if (passivemode) {
13461 #ifdef CK_SSL
13462         ssl_ftp_data_active_flag=0;
13463         if (ssl_ftp_active_flag &&
13464             (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13465           return(ssl_dataconn());
13466 #endif /* CK_SSL */
13467         return(data);
13468     }
13469 #endif /* NO_PASSIVE_MODE */
13470
13471     s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
13472     if (s < 0) {
13473         perror("ftp: accept");
13474 #ifdef TCPIPLIB
13475         socket_close(data);
13476 #else /* TCPIPLIB */
13477 #ifdef USE_SHUTDOWN
13478         shutdown(data, 1+1);
13479 #endif /* USE_SHUTDOWN */
13480         close(data);
13481 #endif /* TCPIPLIB */
13482         data = -1;
13483         globaldin = data;
13484         return(-1);
13485     }
13486 #ifdef TCPIPLIB
13487     socket_close(data);
13488 #else /* TCPIPLIB */
13489 #ifdef USE_SHUTDOWN
13490     shutdown(data, 1+1);
13491 #endif /* USE_SHUTDOWN */
13492     close(data);
13493 #endif /* TCPIPLIB */
13494     data = s;
13495     globaldin = data;
13496 #ifdef IP_TOS
13497 #ifdef IPTOS_THROUGHPUT
13498     tos = IPTOS_THROUGHPUT;
13499     if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
13500       perror("ftp: setsockopt TOS (ignored)");
13501 #endif /* IPTOS_THROUGHPUT */
13502 #endif /* IP_TOS */
13503
13504 #ifdef CK_SSL
13505     ssl_ftp_data_active_flag=0;
13506     if (ssl_ftp_active_flag &&
13507         (ssl_ftp_proxy || ftp_dpl == FPL_PRV))
13508       return(ssl_dataconn());
13509 #endif /* CK_SSL */
13510     return(data);
13511 }
13512
13513 #ifdef FTP_PROXY
13514 static sigtype
13515 pscancel(sig) int sig; {
13516     cancelfile++;
13517 }
13518
13519 static VOID
13520 pswitch(flag) int flag; {
13521     extern int proxy;
13522     sig_t oldintr;
13523     static struct comvars {
13524         int connect;
13525         char name[MAXHOSTNAMELEN];
13526         struct sockaddr_in mctl;
13527         struct sockaddr_in hctl;
13528         FILE *in;
13529         FILE *out;
13530         int tpe;
13531         int curtpe;
13532         int cpnd;
13533         int sunqe;
13534         int runqe;
13535         int mcse;
13536         int ntflg;
13537         char nti[17];
13538         char nto[17];
13539         int mapflg;
13540         char mi[CKMAXPATH];
13541         char mo[CKMAXPATH];
13542         char *authtype;
13543         int clvl;
13544         int dlvl;
13545 #ifdef FTP_KRB4
13546         des_cblock session;
13547         des_key_schedule ftp_sched;
13548 #endif /* FTP_KRB4 */
13549 #ifdef FTP_GSSAPI
13550         gss_ctx_id_t gcontext;
13551 #endif /* GSSAPI */
13552     } proxstruct, tmpstruct;
13553     struct comvars *ip, *op;
13554
13555     cancelfile = 0;
13556     oldintr = signal(SIGINT, pscancel);
13557     if (flag) {
13558         if (proxy)
13559           return;
13560         ip = &tmpstruct;
13561         op = &proxstruct;
13562         proxy++;
13563     } else {
13564         if (!proxy)
13565           return;
13566         ip = &proxstruct;
13567         op = &tmpstruct;
13568         proxy = 0;
13569     }
13570     ip->connect = connected;
13571     connected = op->connect;
13572     if (ftp_host) {
13573         strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1);
13574         ip->name[MAXHOSTNAMELEN - 1] = '\0';
13575         ip->name[strlen(ip->name)] = '\0';
13576     } else
13577       ip->name[0] = 0;
13578     ftp_host = op->name;
13579     ip->hctl = hisctladdr;
13580     hisctladdr = op->hctl;
13581     ip->mctl = myctladdr;
13582     myctladdr = op->mctl;
13583     ip->in = csocket;
13584     csocket = op->in;
13585     ip->out = csocket;
13586     csocket = op->out;
13587     ip->tpe = ftp_typ;
13588     ftp_typ = op->tpe;
13589     ip->curtpe = curtype;
13590     curtype = op->curtpe;
13591     ip->cpnd = cpend;
13592     cpend = op->cpnd;
13593     ip->sunqe = ftp_usn;
13594     ftp_usn = op->sunqe;
13595     ip->mcse = mcase;
13596     mcase = op->mcse;
13597     ip->ntflg = ntflag;
13598     ntflag = op->ntflg;
13599     strncpy(ip->nti, ntin, 16);
13600     (ip->nti)[strlen(ip->nti)] = '\0';
13601     strcpy(ntin, op->nti);
13602     strncpy(ip->nto, ntout, 16);
13603     (ip->nto)[strlen(ip->nto)] = '\0';
13604     strcpy(ntout, op->nto);
13605     ip->mapflg = mapflag;
13606     mapflag = op->mapflg;
13607     strncpy(ip->mi, mapin, CKMAXPATH - 1);
13608     (ip->mi)[strlen(ip->mi)] = '\0';
13609     strcpy(mapin, op->mi);
13610     strncpy(ip->mo, mapout, CKMAXPATH - 1);
13611     (ip->mo)[strlen(ip->mo)] = '\0';
13612     strcpy(mapout, op->mo);
13613     ip->authtype = auth_type;
13614     auth_type = op->authtype;
13615     ip->clvl = ftp_cpl;
13616     ftp_cpl = op->clvl;
13617     ip->dlvl = ftp_dpl;
13618     ftp_dpl = op->dlvl;
13619     if (!ftp_cpl)
13620       ftp_cpl = FPL_CLR;
13621     if (!ftp_dpl)
13622       ftp_dpl = FPL_CLR;
13623 #ifdef FTP_KRB4
13624     memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session));
13625     memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session));
13626     memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched));
13627     memcpy(ftp_sched, op->schedule, sizeof(ftp_sched));
13628 #endif /* FTP_KRB4 */
13629 #ifdef FTP_GSSAPI
13630     ip->gcontext = gcontext;
13631     gcontext = op->gcontext;
13632 #endif /* GSSAPI */
13633     signal(SIGINT, oldintr);
13634     if (cancelfile) {
13635         cancelfile = 0;
13636         debug(F101,"pswitch cancelfile B","",cancelfile);
13637         (*oldintr)(SIGINT);
13638     }
13639 }
13640
13641 static sigtype
13642 cancelpt(sig) int sig; {
13643     printf("\n");
13644     fflush(stdout);
13645     ptabflg++;
13646     cancelfile = 0;
13647 #ifndef OS2
13648     longjmp(ptcancel, 1);
13649 #else
13650     PostCtrlCSem();
13651 #endif /* OS2 */
13652 }
13653
13654 void
13655 proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; {
13656     sig_t oldintr;
13657     int secndflag = 0, prox_type, nfnd;
13658     char *cmd2;
13659 #ifdef BSDSELECT
13660     fd_set mask;
13661 #endif /* BSDSELECT */
13662     sigtype cancelpt();
13663
13664     if (strcmp(cmd, "RETR"))
13665       cmd2 = "RETR";
13666     else
13667       cmd2 = unique ? "STOU" : "STOR";
13668     if ((prox_type = type) == 0) {
13669         if (servertype == SYS_UNIX && unix_proxy)
13670           prox_type = FTT_BIN;
13671         else
13672           prox_type = FTT_ASC;
13673     }
13674     if (curtype != prox_type)
13675       changetype(prox_type, 1);
13676     if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) {
13677         printf("Proxy server does not support third party transfers.\n");
13678         return;
13679     }
13680     pswitch(0);
13681     if (!connected) {
13682         printf("No primary connection\n");
13683         pswitch(1);
13684         ftpcode = -1;
13685         return;
13686     }
13687     if (curtype != prox_type)
13688       changetype(prox_type, 1);
13689
13690     if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) {
13691         pswitch(1);
13692         return;
13693     }
13694
13695     /* Replace with calls to cc_execute() */
13696     if (setjmp(ptcancel))
13697       goto cancel;
13698     oldintr = signal(SIGINT, cancelpt);
13699     if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) {
13700         signal(SIGINT, oldintr);
13701         pswitch(1);
13702         return;
13703     }
13704     sleep(2000);
13705     pswitch(1);
13706     secndflag++;
13707     if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM)
13708       goto cancel;
13709     ptflag++;
13710     getreply(0,-1,-1,ftp_vbm,0);
13711     pswitch(0);
13712     getreply(0,-1,-1,ftp_vbm,0);
13713     signal(SIGINT, oldintr);
13714     pswitch(1);
13715     ptflag = 0;
13716     return;
13717
13718   cancel:
13719     signal(SIGINT, SIG_IGN);
13720     ptflag = 0;
13721     if (strcmp(cmd, "RETR") && !proxy)
13722       pswitch(1);
13723     else if (!strcmp(cmd, "RETR") && proxy)
13724       pswitch(0);
13725     if (!cpend && !secndflag) {  /* only here if cmd = "STOR" (proxy=1) */
13726         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13727             pswitch(0);
13728             if (cpend)
13729               cancel_remote(0);
13730         }
13731         pswitch(1);
13732         if (ptabflg)
13733           ftpcode = -1;
13734         signal(SIGINT, oldintr);
13735         return;
13736     }
13737     if (cpend)
13738       cancel_remote(0);
13739     pswitch(!proxy);
13740     if (!cpend && !secndflag) {  /* only if cmd = "RETR" (proxy=1) */
13741         if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) {
13742             pswitch(0);
13743             if (cpend)
13744               cancel_remote(0);
13745             pswitch(1);
13746             if (ptabflg)
13747               ftpcode = -1;
13748             signal(SIGINT, oldintr);
13749             return;
13750         }
13751     }
13752     if (cpend)
13753       cancel_remote(0);
13754     pswitch(!proxy);
13755     if (cpend) {
13756 #ifdef BSDSELECT
13757         FD_ZERO(&mask);
13758         FD_SET(csocket, &mask);
13759         if ((nfnd = empty(&mask, 10)) <= 0) {
13760             if (nfnd < 0) {
13761                 perror("cancel");
13762             }
13763             if (ptabflg)
13764               ftpcode = -1;
13765             lostpeer();
13766         }
13767 #else /* BSDSELECT */
13768 #ifdef IBMSELECT
13769         if ((nfnd = empty(&csocket, 1, 10)) <= 0) {
13770             if (nfnd < 0) {
13771                 perror("cancel");
13772             }
13773             if (ptabflg)
13774               ftpcode = -1;
13775             lostpeer();
13776         }
13777 #endif /* IBMSELECT */
13778 #endif /* BSDSELECT */
13779         getreply(0,-1,-1,ftp_vbm,0);
13780         getreply(0,-1,-1,ftp_vbm,0);
13781     }
13782     if (proxy)
13783       pswitch(0);
13784     pswitch(1);
13785     if (ptabflg)
13786       ftpcode = -1;
13787     signal(SIGINT, oldintr);
13788 }
13789 #endif /* FTP_PROXY */
13790
13791 #ifdef FTP_SECURITY
13792 #ifdef FTP_GSSAPI
13793
13794 #ifdef COMMENT
13795 /* ck_gss_mech_krb5 is not declared anywhere */
13796 struct {
13797     CONST gss_OID_desc * CONST * mech_type;
13798     char *service_name;
13799 } gss_trials[] = {
13800     { &ck_gss_mech_krb5, "ftp" },
13801     { &ck_gss_mech_krb5, "host" },
13802 };
13803 #else
13804 /* This matches what is declared above */
13805 struct {
13806     CONST gss_OID_desc * CONST * mech_type;
13807     char *service_name;
13808 } gss_trials[] = {
13809     { &gss_mech_krb5, "ftp" },
13810     { &gss_mech_krb5, "host" },
13811 };
13812 #endif  /* COMMENT */
13813
13814
13815 int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]);
13816 #endif /* FTP_GSSAPI */
13817
13818 static int
13819 ftp_auth() {
13820     extern int setsafe();
13821     int j = 0, n;
13822 #ifdef FTP_KRB4
13823     char *service, inst[INST_SZ];
13824     ULONG cksum;
13825     ULONG checksum = (ULONG) getpid();
13826     CHAR out_buf[FTP_BUFSIZ];
13827     int i;
13828 #else /* FTP_KRB4 */
13829 #ifdef FTP_GSSAPI
13830     CHAR out_buf[FTP_BUFSIZ];
13831     int i;
13832 #endif /* FTP_GSSAPI */
13833 #endif /* FTP_KRB4 */
13834
13835     if (ssl_ftp_proxy)                  /* Do not allow AUTH over SSL proxy */
13836         return(0);
13837
13838     if (auth_type)
13839       return(1);                        /* auth already succeeded */
13840
13841     /* Try each auth type as specified by the end user */
13842     for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) {
13843 #ifdef FTP_GSSAPI
13844         if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) {
13845             n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm);
13846             if (n == REPLY_CONTINUE) {
13847                 OM_uint32 maj_stat, min_stat;
13848                 gss_name_t target_name;
13849                 gss_buffer_desc send_tok, recv_tok, *token_ptr;
13850                 char stbuf[FTP_BUFSIZ];
13851                 int comcode, trial;
13852                 struct gss_channel_bindings_struct chan;
13853                 char * realm = NULL;
13854                 char tgt[256];
13855
13856                 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
13857                 chan.initiator_address.length = 4;
13858                 chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
13859                 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
13860                 chan.acceptor_address.length = 4;
13861                 chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
13862                 chan.application_data.length = 0;
13863                 chan.application_data.value = 0;
13864
13865                 if (!quiet)
13866                   printf("GSSAPI accepted as authentication type\n");
13867
13868                 realm = ck_krb5_realmofhost(ftp_user_host);
13869                 if (realm) {
13870                     ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm);
13871                     debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0);
13872                     if ( krb5_autoget &&
13873                          !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) ||
13874                             (ck_krb5_is_tgt_valid() > 0)) )
13875                         ck_krb5_autoget_TGT(realm);
13876                 }
13877
13878                 /* Blob from gss-client */
13879                 for (trial = 0; trial < n_gss_trials; trial++) {
13880                     /* ftp@hostname first, the host@hostname */
13881                     /* the V5 GSSAPI binding canonicalizes this for us... */
13882                     ckmakmsg(stbuf,FTP_BUFSIZ,
13883                              gss_trials[trial].service_name,
13884                              "@",
13885                              ftp_user_host,
13886                              NULL
13887                              );
13888                     if (ftp_deb)
13889                       fprintf(stderr,
13890                               "Authenticating to <%s>...\n", stbuf);
13891                     send_tok.value = stbuf;
13892                     send_tok.length = strlen(stbuf);
13893                     maj_stat = gss_import_name(&min_stat, &send_tok,
13894                                                gss_nt_service_name,
13895                                                &target_name
13896                                                );
13897                     if (maj_stat != GSS_S_COMPLETE) {
13898                         user_gss_error(maj_stat, min_stat, "parsing name");
13899                         secure_error("name parsed <%s>\n", stbuf);
13900                         continue;
13901                     }
13902                     token_ptr = GSS_C_NO_BUFFER;
13903                     gcontext = GSS_C_NO_CONTEXT; /* structure copy */
13904
13905                     do {
13906                         if (ftp_deb)
13907                           fprintf(stderr, "calling gss_init_sec_context\n");
13908                         maj_stat =
13909                           gss_init_sec_context(&min_stat,
13910                                                GSS_C_NO_CREDENTIAL,
13911                                                &gcontext,
13912                                                target_name,
13913                                                (gss_OID) *
13914                                                  gss_trials[trial].mech_type,
13915                                                GSS_C_MUTUAL_FLAG |
13916                                                GSS_C_REPLAY_FLAG |
13917                                                (ftp_cfw ?
13918                                                 GSS_C_DELEG_FLAG : 0),
13919                                                0,
13920                                                 /* channel bindings */
13921                                                 (krb5_d_no_addresses ?
13922                                                   GSS_C_NO_CHANNEL_BINDINGS :
13923                                                   &chan),
13924                                                 token_ptr,
13925                                                NULL,    /* ignore mech type */
13926                                                &send_tok,
13927                                                NULL,    /* ignore ret_flags */
13928                                                NULL
13929                                                );       /* ignore time_rec */
13930
13931                         if (maj_stat != GSS_S_COMPLETE &&
13932                             maj_stat != GSS_S_CONTINUE_NEEDED) {
13933                             if (trial == n_gss_trials-1)
13934                               user_gss_error(maj_stat,
13935                                              min_stat,
13936                                              "initializing context"
13937                                              );
13938                             gss_release_name(&min_stat, &target_name);
13939                             /* maybe we missed on the service name */
13940                             goto outer_loop;
13941                         }
13942                         if (send_tok.length != 0) {
13943                             int len;
13944                             reply_parse = "ADAT="; /* for ftpcmd() later */
13945                             len = FTP_BUFSIZ;
13946                             kerror =
13947                               radix_encode(send_tok.value,
13948                                            out_buf,
13949                                            send_tok.length,
13950                                            &len,
13951                                            RADIX_ENCODE
13952                                            );
13953                             if (kerror)  {
13954                                 fprintf(stderr,
13955                                         "Base 64 encoding failed: %s\n",
13956                                         radix_error(kerror)
13957                                         );
13958                                 goto gss_complete_loop;
13959                             }
13960                             comcode = ftpcmd("ADAT",out_buf,-1,-1,0);
13961                             if (comcode != REPLY_COMPLETE
13962                                 && comcode != REPLY_CONTINUE /* (335) */
13963                                 ) {
13964                                 if (trial == n_gss_trials-1) {
13965                                     fprintf(stderr, "GSSAPI ADAT failed\n");
13966                                     /* force out of loop */
13967                                     maj_stat = GSS_S_FAILURE;
13968                                 }
13969                                 /*
13970                                   Backoff to the v1 gssapi is still possible.
13971                                   Send a new AUTH command.  If that fails,
13972                                   terminate the loop.
13973                                 */
13974                                 if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm)
13975                                     != REPLY_CONTINUE) {
13976                                     fprintf(stderr,
13977                                 "GSSAPI ADAT failed, AUTH restart failed\n");
13978                                     /* force out of loop */
13979                                     maj_stat = GSS_S_FAILURE;
13980                                 }
13981                                 goto outer_loop;
13982                             }
13983                             if (!reply_parse) {
13984                                 fprintf(stderr,
13985                               "No authentication data received from server\n");
13986                                 if (maj_stat == GSS_S_COMPLETE) {
13987                                     fprintf(stderr,
13988                                             "...but no more was needed\n");
13989                                     goto gss_complete_loop;
13990                                 } else {
13991                                     user_gss_error(maj_stat,
13992                                                    min_stat,
13993                                                    "no reply, huh?"
13994                                                    );
13995                                     goto gss_complete_loop;
13996                                 }
13997                             }
13998                             len = FTP_BUFSIZ;
13999                             kerror = radix_encode(reply_parse,out_buf,i,&len,
14000                                                   RADIX_DECODE);
14001                             if (kerror) {
14002                                 fprintf(stderr,
14003                                         "Base 64 decoding failed: %s\n",
14004                                         radix_error(kerror));
14005                                 goto gss_complete_loop;
14006                             }
14007
14008                             /* everything worked */
14009                             token_ptr = &recv_tok;
14010                             recv_tok.value = out_buf;
14011                             recv_tok.length = len;
14012                             continue;
14013
14014                             /* get out of loop clean */
14015                           gss_complete_loop:
14016                             trial = n_gss_trials-1;
14017                             gss_release_buffer(&min_stat, &send_tok);
14018                             gss_release_name(&min_stat, &target_name);
14019                             goto outer_loop;
14020                         }
14021                     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
14022
14023                   outer_loop:
14024                     if (maj_stat == GSS_S_COMPLETE)
14025                       break;
14026                 }
14027                 if (maj_stat == GSS_S_COMPLETE) {
14028                     printf("GSSAPI authentication succeeded\n");
14029                     reply_parse = NULL;
14030                     auth_type = "GSSAPI";
14031                     return(1);
14032                 } else {
14033                     fprintf(stderr, "GSSAPI authentication failed\n");
14034                     reply_parse = NULL;
14035                 }
14036             } else {
14037                 if (ftp_deb)
14038                 fprintf(stderr, "GSSAPI rejected as an authentication type\n");
14039                 if (ftpcode == 500 || ftpcode == 502)
14040                     return(0);
14041             }
14042         }
14043 #endif /* FTP_GSSAPI */
14044 #ifdef FTP_SRP
14045         if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) {
14046             if (srp_ftp_auth(ftp_user_host,NULL,NULL))
14047               return(1);
14048             else if (ftpcode == 500 || ftpcode == 502)
14049               return(0);
14050         }
14051 #endif /* FTP_SRP */
14052 #ifdef FTP_KRB4
14053         if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) {
14054             n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm);
14055             if (n == REPLY_CONTINUE) {
14056                 char tgt[4*REALM_SZ+1];
14057                 int rc;
14058
14059                 if (!quiet)
14060                   printf("KERBEROS_V4 accepted as authentication type\n");
14061                 ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ);
14062                 ckstrncpy(ftp_realm,
14063                           (char *)ck_krb4_realmofhost(ftp_user_host),
14064                           REALM_SZ
14065                           );
14066
14067                 ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm);
14068                 rc = ck_krb4_tkt_isvalid(tgt);
14069
14070                 if (rc <= 0 && krb4_autoget)
14071                   ck_krb4_autoget_TGT(ftp_realm);
14072
14073                 service = "ftp";
14074                 kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum);
14075                 if (kerror == KDC_PR_UNKNOWN) {
14076                     service = "rcmd";
14077                     kerror = krb_mk_req(&ftp_tkt,
14078                                         service,
14079                                         inst,
14080                                         ftp_realm,
14081                                         checksum
14082                                         );
14083                 }
14084                 if (kerror)
14085                   fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
14086                           krb_get_err_text(kerror));
14087                 if (!kerror) {
14088                     kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred);
14089                     if (kerror)
14090                       fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
14091                               krb_get_err_text(kerror));
14092                 }
14093                 if (!kerror) {
14094                     int rc;
14095                     rc = des_key_sched(ftp_cred.session, ftp_sched);
14096                     if (rc == -1) {
14097                        printf("?Invalid DES key specified in credentials\r\n");
14098                        debug(F110,"ftp_auth",
14099                              "invalid DES Key specified in credentials",0);
14100                     } else if ( rc == -2 ) {
14101                         printf("?Weak DES key specified in credentials\r\n");
14102                         debug(F110,"ftp_auth",
14103                               "weak DES Key specified in credentials",0);
14104                     } else if ( rc != 0 ) {
14105                         printf("?DES Key Schedule not set by credentials\r\n");
14106                         debug(F110,"ftp_auth",
14107                               "DES Key Schedule not set by credentials",0);
14108                     }
14109                     reply_parse = "ADAT=";
14110                     i = FTP_BUFSIZ;
14111                     kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length,
14112                                           &i, RADIX_ENCODE);
14113                     if (kerror) {
14114                         fprintf(stderr, "Base 64 encoding failed: %s\n",
14115                                 radix_error(kerror));
14116                         goto krb4_err;
14117                     }
14118                     if (i > FTP_BUFSIZ - 6)
14119                       printf("?ADAT data too long\n");
14120                     if (ftpcmd("ADAT",out_buf,-1,-1,0) !=
14121                         REPLY_COMPLETE) {
14122                         fprintf(stderr, "Kerberos V4 authentication failed\n");
14123                         goto krb4_err;
14124                     }
14125                     if (!reply_parse) {
14126                         fprintf(stderr,
14127                              "No authentication data received from server\n");
14128                         goto krb4_err;
14129                     }
14130                     i = sizeof(out_buf);
14131                     kerror =
14132                       radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE);
14133                     if (kerror) {
14134                         fprintf(stderr, "Base 64 decoding failed: %s\n",
14135                                 radix_error(kerror));
14136                         goto krb4_err;
14137                     }
14138                     kerror = krb_rd_safe(out_buf, i,
14139 #ifdef KRB524
14140                                          ftp_cred.session,
14141 #else /* KRB524 */
14142                                          &ftp_cred.session,
14143 #endif /* KRB524 */
14144                                          &hisctladdr,
14145                                          &myctladdr,
14146                                          &ftp_msg_data
14147                                          );
14148                     if (kerror) {
14149                         fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
14150                                 krb_get_err_text(kerror));
14151                         goto krb4_err;
14152                     }
14153
14154                     /* fetch the (modified) checksum */
14155                     memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum));
14156                     if (ntohl(cksum) == checksum + 1) {
14157                         if (ftp_vbm)
14158                           printf("Kerberos V4 authentication succeeded\n");
14159                         reply_parse = NULL;
14160                         auth_type = "KERBEROS_V4";
14161                         return(1);
14162                     } else
14163                       fprintf(stderr,
14164                               "Kerberos V4 mutual authentication failed\n");
14165                   krb4_err:
14166                     reply_parse = NULL;
14167                 }
14168             } else {
14169                 if (ftp_deb)
14170                   fprintf(stderr,
14171                       "KERBEROS_V4 rejected as an authentication type\n");
14172                 if (ftpcode == 500 || ftpcode == 502)
14173                     return(0);
14174             }
14175         }
14176 #endif /* FTP_KRB4 */
14177 #ifdef CK_SSL
14178         if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) {
14179 #ifdef FTPHOST
14180             if (!hostcmd) {
14181                 ftpcmd("HOST",ftp_user_host,0,0,0);
14182                 hostcmd = 1;
14183             }
14184 #endif /* FTPHOST */
14185             n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm);
14186             if (n != REPLY_COMPLETE)
14187               n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm);
14188             if (n == REPLY_COMPLETE) {
14189                 if (!quiet)
14190                   printf("TLS accepted as authentication type\n");
14191
14192                 auth_type = "TLS";
14193                 ssl_auth();
14194                 if (ssl_ftp_active_flag ) {
14195                     ftp_dpl = FPL_CLR;
14196                     ftp_cpl = FPL_PRV;
14197                     return(1);
14198                 } else {
14199                     fprintf(stderr,"TLS authentication failed\n");
14200                     auth_type = NULL;
14201 #ifdef TCPIPLIB
14202                     socket_close(csocket);
14203 #else /* TCPIPLIB */
14204 #ifdef USE_SHUTDOWN
14205                     shutdown(csocket, 1+1);
14206 #endif /* USE_SHUTDOWN */
14207                     close(csocket);
14208 #endif /* TCPIPLIB */
14209                     csocket = -1;
14210                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14211                       return(0);
14212                 }
14213             } else {
14214                 if (ftp_deb)
14215                   fprintf(stderr,"TLS rejected as an authentication type\n");
14216                 if (ftpcode == 500 || ftpcode == 502)
14217                     return(0);
14218             }
14219         }
14220         if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) {
14221 #ifdef FTPHOST
14222             if (!hostcmd) {
14223                 ftpcmd("HOST",ftp_user_host,0,0,0);
14224                 hostcmd = 1;
14225             }
14226 #endif /* FTPHOST */
14227             n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm);
14228             if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) {
14229                 if (!quiet)
14230                   printf("SSL accepted as authentication type\n");
14231                 auth_type = "SSL";
14232                 ssl_auth();
14233                 if (ssl_ftp_active_flag) {
14234                     ftp_dpl = FPL_PRV;
14235                     ftp_cpl = FPL_PRV;
14236                     setprotbuf(1<<20);
14237                     return(1);
14238                 } else {
14239                     fprintf(stderr,"SSL authentication failed\n");
14240                     auth_type = NULL;
14241 #ifdef TCPIPLIB
14242                     socket_close(csocket);
14243 #else /* TCPIPLIB */
14244 #ifdef USE_SHUTDOWN
14245                     shutdown(csocket, 1+1);
14246 #endif /* USE_SHUTDOWN */
14247                     close(csocket);
14248 #endif /* TCPIPLIB */
14249                     csocket = -1;
14250                     if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL)
14251                       return(0);
14252                 }
14253             } else {
14254                 if (ftp_deb)
14255                   fprintf(stderr, "SSL rejected as an authentication type\n");
14256                 if (ftpcode == 500 || ftpcode == 502)
14257                   return(0);
14258             }
14259         }
14260 #endif /* CK_SSL */
14261         /* Other auth types go here ... */
14262     } /* for (j;;) */
14263     return(0);
14264 }
14265 #endif /* FTP_SECURITY */
14266
14267 static int
14268 #ifdef CK_ANSIC
14269 setprotbuf(unsigned int size)
14270 #else
14271 setprotbuf(size) unsigned int size;
14272 #endif /* CK_ANSIC */
14273 /* setprotbuf */ {
14274     if (ucbuf)
14275       free(ucbuf);
14276     ucbuf = NULL;
14277     ucbufsiz = 0;
14278     actualbuf = size;
14279     while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) {
14280         if (actualbuf)
14281           actualbuf /= 2;
14282         else
14283           return(0);
14284     }
14285     ucbufsiz = actualbuf - FUDGE_FACTOR;
14286     debug(F101,"setprotbuf ucbufsiz","",ucbufsiz);
14287     if (ucbufsiz < 128) {
14288         printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz);
14289     } else if (ucbufsiz < 0) {
14290         printf("ERROR: ucbuf allocation failure\n");
14291         return(-1);
14292     }
14293     maxbuf = actualbuf;
14294     return(1);
14295 }
14296
14297 static int
14298 #ifdef CK_ANSIC
14299 setpbsz(unsigned int size)
14300 #else
14301 setpbsz(size) unsigned int size;
14302 #endif /* CK_ANSIC */
14303 /* setpbsz */ {
14304     if (!setprotbuf(size)) {
14305         perror("?Error while trying to malloc PROT buffer:");
14306 #ifdef FTP_SRP
14307         srp_reset();
14308 #endif /* FTP_SRP */
14309         ftpclose();
14310         return(-1);
14311     }
14312     reply_parse = "PBSZ=";
14313     ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ",
14314 #ifdef CK_SSL
14315              ssl_ftp_active_flag ? "0" :
14316 #endif /* CK_SSL */
14317              ckuitoa(actualbuf),NULL,NULL);
14318     if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) {
14319         if (connected) {
14320             printf("?Unable to negotiate PROT buffer size with FTP server\n");
14321             ftpclose();
14322         }
14323         return(-1);
14324     }
14325     if (reply_parse) {
14326         if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
14327           maxbuf = actualbuf;
14328     } else
14329       maxbuf = actualbuf;
14330     ucbufsiz = maxbuf - FUDGE_FACTOR;
14331     debug(F101,"setpbsz ucbufsiz","",ucbufsiz);    
14332     reply_parse = NULL;
14333     return(0);
14334 }
14335
14336 static VOID
14337 cancel_remote(din) int din; {
14338     CHAR buf[FTP_BUFSIZ];
14339     int x, nfnd;
14340 #ifdef BSDSELECT
14341     fd_set mask;
14342 #endif /* BSDSELECT */
14343 #ifdef IBMSELECT
14344     int fds[2], fdcnt = 0;
14345 #endif /* IBMSELECT */
14346 #ifdef DEBUG
14347     extern int debtim;
14348     int xdebtim;
14349     xdebtim = debtim;
14350     debtim = 1;
14351 #endif /* DEBUG */
14352     debug(F100,"ftp cancel_remote entry","",0);
14353 #ifdef CK_SSL
14354     if (ssl_ftp_active_flag) {
14355         /*
14356          * Send Telnet IP, Telnet DM but do so inline and within the
14357          * TLS channel
14358          */
14359         int count, error;
14360
14361         buf[0] = IAC;
14362         buf[1] = TN_IP;
14363         buf[2] = IAC;
14364         buf[3] = TN_DM;
14365         buf[4] = NUL;
14366
14367         count = SSL_write(ssl_ftp_con, buf, 4);
14368         debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count);
14369         error = SSL_get_error(ssl_ftp_con,count);
14370         debug(F111,"ftp cancel_remote","SSL_get_error()",error);
14371         switch (error) {
14372           case SSL_ERROR_NONE:
14373             break;
14374           case SSL_ERROR_WANT_WRITE:
14375           case SSL_ERROR_WANT_READ:
14376           case SSL_ERROR_SYSCALL:
14377 #ifdef NT
14378             {
14379                 int gle = GetLastError();
14380             }
14381 #endif /* NT */
14382           case SSL_ERROR_WANT_X509_LOOKUP:
14383           case SSL_ERROR_SSL:
14384           case SSL_ERROR_ZERO_RETURN:
14385           default:
14386             lostpeer();
14387             return;
14388         }
14389     } else
14390 #endif /* CK_SSL */
14391     {
14392         /*
14393          * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
14394          * after urgent byte rather than before as is protocol now.
14395          */
14396         buf[0] = IAC;
14397         buf[1] = TN_IP;
14398         buf[2] = IAC;
14399         buf[3] = NUL;
14400         if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3)
14401           perror("cancel");
14402         debug(F101,"ftp cancel_remote send 1","",x);
14403         buf[0] = TN_DM;
14404         x = send(csocket,(SENDARG2TYPE)buf,1,0);
14405         debug(F101,"ftp cancel_remote send 2","",x);
14406     }
14407     x = scommand("ABOR");
14408     debug(F101,"ftp cancel_remote scommand","",x);
14409 #ifdef BSDSELECT
14410     FD_ZERO(&mask);
14411     FD_SET(csocket, &mask);
14412     if (din) {
14413         FD_SET(din, &mask);
14414     }
14415     nfnd = empty(&mask, 10);
14416     debug(F101,"ftp cancel_remote empty","",nfnd);
14417     if ((nfnd) <= 0) {
14418         if (nfnd < 0) {
14419             perror("cancel");
14420         }
14421 #ifdef FTP_PROXY
14422         if (ptabflg)
14423           ftpcode = -1;
14424 #endif /* FTP_PROXY */
14425         lostpeer();
14426     }
14427     debug(F110,"ftp cancel_remote","D",0);
14428     if (din && FD_ISSET(din, &mask)) {
14429         /* Security: No threat associated with this read. */
14430         /* But you can't simply read the TLS data stream  */
14431 #ifdef CK_SSL
14432         if (ssl_ftp_data_active_flag) {
14433             int count, error;
14434             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14435                     /* LOOP */ ;
14436         } else
14437 #endif /* CK_SSL */
14438         {
14439             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14440                 /* LOOP */ ;
14441         }
14442     }
14443     debug(F110,"ftp cancel_remote","E",0);
14444 #else /* BSDSELECT */
14445 #ifdef IBMSELECT
14446     fds[0] = csocket;
14447     fdcnt++;
14448     if (din) {
14449         fds[1] = din;
14450         fdcnt++;
14451     }
14452     nfnd = empty(fds, fdcnt, 10);
14453     debug(F101,"ftp cancel_remote empty","",nfnd);
14454     if ((nfnd) <= 0) {
14455         if (nfnd < 0) {
14456             perror("cancel");
14457         }
14458 #ifdef FTP_PROXY
14459         if (ptabflg)
14460           ftpcode = -1;
14461 #endif /* FTP_PROXY */
14462         lostpeer();
14463     }
14464     debug(F110,"ftp cancel_remote","D",0);
14465     if (din && select(&din, 1,0,0,1) ) {
14466 #ifdef CK_SSL
14467         if (ssl_ftp_data_active_flag) {
14468             int count, error;
14469             while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0)
14470                     /* LOOP */ ;
14471         } else
14472 #endif /* CK_SSL */
14473         {
14474             while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0)
14475                 /* LOOP */ ;
14476         }
14477     }
14478     debug(F110,"ftp cancel_remote","E",0);
14479 #else /* IBMSELECT */
14480     Some form of select is required.
14481 #endif /* IBMSELECT */
14482 #endif /* BSDSELECT */
14483     if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) {
14484         debug(F110,"ftp cancel_remote","F",0);
14485         /* 552 needed for NIC style cancel */
14486         getreply(0,-1,-1,ftp_vbm,0);
14487         debug(F110,"ftp cancel_remote","G",0);
14488     }
14489     debug(F110,"ftp cancel_remote","H",0);
14490     getreply(0,-1,-1,ftp_vbm,0);
14491     debug(F110,"ftp cancel_remote","I",0);
14492 #ifdef DEBUG
14493     debtim = xdebtim;
14494 #endif /* DEBUG */
14495 }
14496
14497 static int
14498 fts_dpl(x) int x; {
14499     if (!auth_type
14500 #ifdef OS2
14501          || !ck_crypt_is_installed()
14502 #endif /* OS2 */
14503          ) {
14504         switch ( x ) {
14505           case FPL_PRV:
14506             printf("?Cannot set protection level to PRIVATE\n");
14507             return(0);
14508           case FPL_SAF:
14509             printf("?Cannot set protection level to SAFE\n");
14510             return(0);
14511         }
14512         ftp_dpl = x;
14513         return(1);
14514     }
14515
14516 #ifdef CK_SSL
14517     if (x == FPL_SAF &&
14518         (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) {
14519         printf("Cannot set protection level to safe\n");
14520         return(0);
14521     }
14522 #endif /* CK_SSL */
14523     /* Start with a PBSZ of 1 meg */
14524     if (x != FPL_CLR) {
14525         if (setpbsz(DEFAULT_PBSZ) < 0)
14526           return(0);
14527     }
14528     y = ftpcmd(x == FPL_CLR ? "PROT C" :
14529                (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm);
14530     if (y == REPLY_COMPLETE) {
14531         ftp_dpl = x;
14532         return(1);
14533     }
14534     return(0);
14535 }
14536
14537 static int
14538 fts_cpl(x) int x; {
14539     if (!auth_type 
14540 #ifdef OS2
14541          || !ck_crypt_is_installed()
14542 #endif /* OS2 */
14543          ) {
14544         switch ( x ) {
14545           case FPL_PRV:
14546             printf("?Cannot set protection level to PRIVATE\n");
14547             return(0);
14548           case FPL_SAF:
14549             printf("?Cannot set protection level to SAFE\n");
14550             return(0);
14551         }
14552         ftp_cpl = x;
14553         return(1);
14554     }
14555     if (x == FPL_CLR) {
14556         y = ftpcmd("CCC",NULL,0,0,ftp_vbm);
14557         if (y == REPLY_COMPLETE) {
14558             ftp_cpl = x;
14559             return(1);
14560         }
14561         return(0);
14562     }
14563     ftp_cpl = x;
14564     return(1);
14565 }
14566
14567 #ifdef FTP_GSSAPI
14568 static VOID
14569 user_gss_error(maj_stat, min_stat, s)
14570     OM_uint32 maj_stat, min_stat;
14571     char *s;
14572 {
14573     /* a lot of work just to report the error */
14574     OM_uint32 gmaj_stat, gmin_stat, msg_ctx;
14575     gss_buffer_desc msg;
14576     msg_ctx = 0;
14577     while (!msg_ctx) {
14578         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
14579                                        GSS_C_GSS_CODE,
14580                                        GSS_C_NULL_OID,
14581                                        &msg_ctx,
14582                                        &msg
14583                                        );
14584         if ((gmaj_stat == GSS_S_COMPLETE)||
14585             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14586             fprintf(stderr, "GSSAPI error major: %s\n",
14587                     (char*)msg.value);
14588             gss_release_buffer(&gmin_stat, &msg);
14589         }
14590         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14591           break;
14592     }
14593     msg_ctx = 0;
14594     while (!msg_ctx) {
14595         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
14596                                        GSS_C_MECH_CODE,
14597                                        GSS_C_NULL_OID,
14598                                        &msg_ctx,
14599                                        &msg
14600                                        );
14601         if ((gmaj_stat == GSS_S_COMPLETE)||
14602             (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
14603             fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value);
14604             gss_release_buffer(&gmin_stat, &msg);
14605         }
14606         if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
14607           break;
14608     }
14609     fprintf(stderr, "GSSAPI error: %s\n", s);
14610 }
14611 #endif /* FTP_GSSAPI */
14612
14613 #ifndef NOMHHOST
14614 #ifdef datageneral
14615 #define NOMHHOST
14616 #else
14617 #ifdef HPUX5WINTCP
14618 #define NOMHHOST
14619 #endif /* HPUX5WINTCP */
14620 #endif /* datageneral */
14621 #endif /* NOMHHOST */
14622
14623 #ifdef INADDRX
14624 static struct in_addr inaddrx;
14625 #endif /* INADDRX */
14626
14627 static char *
14628 ftp_hookup(host, port, tls) char * host; int port; int tls; {
14629     register struct hostent *hp = 0;
14630 #ifdef IP_TOS
14631 #ifdef IPTOS_THROUGHPUT
14632     int tos;
14633 #endif /* IPTOS_THROUGHPUT */
14634 #endif /* IP_TOS */
14635     int s;
14636     GSOCKNAME_T len;
14637     static char hostnamebuf[MAXHOSTNAMELEN];
14638     char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ;
14639     int  cport;
14640 #ifdef DEBUG
14641     extern int debtim;
14642     int xdebtim;
14643     xdebtim = debtim;
14644     debtim = 1;
14645 #endif /* DEBUG */
14646
14647     debug(F111,"ftp_hookup",host,port);
14648
14649 #ifndef NOHTTP
14650     if (tcp_http_proxy) {
14651         struct servent *destsp;
14652         char *p, *q;
14653
14654         ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL);
14655         for (p = tcp_http_proxy, q = hostname;
14656              *p != '\0' && *p != ':';
14657              p++, q++
14658              )
14659           *q = *p;
14660         *q = '\0';
14661
14662         if (*p == ':')
14663           p++;
14664         else
14665           p = "http";
14666
14667         destsp = getservbyname(p,"tcp");
14668         if (destsp)
14669           cport = ntohs(destsp->s_port);
14670         else if (p) {
14671           cport = atoi(p);
14672         } else
14673           cport = 80;
14674     } else
14675 #endif /* NOHTTP */
14676     {
14677         ckstrncpy(hostname,host,MAXHOSTNAMELEN);
14678         cport = port;
14679     }
14680     memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
14681     hisctladdr.sin_addr.s_addr = inet_addr(host);
14682     if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */
14683     {
14684         debug(F110,"ftp hookup A",hostname,0);
14685         hisctladdr.sin_family = AF_INET;
14686         ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN);
14687     } else {
14688         debug(F110,"ftp hookup B",hostname,0);
14689         hp = gethostbyname(hostname);
14690 #ifdef HADDRLIST
14691         hp = ck_copyhostent(hp);        /* make safe copy that won't change */
14692 #endif /* HADDRLIST */
14693         if (hp == NULL) {
14694             fprintf(stderr, "ftp: %s: Unknown host\n", host);
14695             ftpcode = -1;
14696 #ifdef DEBUG
14697             debtim = xdebtim;
14698 #endif /* DEBUG */
14699             return((char *) 0);
14700         }
14701         hisctladdr.sin_family = hp->h_addrtype;
14702 #ifdef HADDRLIST
14703         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0],
14704                sizeof(hisctladdr.sin_addr));
14705 #else /* HADDRLIST */
14706         memcpy((char *)&hisctladdr.sin_addr, hp->h_addr,
14707                sizeof(hisctladdr.sin_addr));
14708 #endif /* HADDRLIST */
14709         ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN);
14710     }
14711     debug(F110,"ftp hookup C",hostnamebuf,0);
14712     ftp_host = hostnamebuf;
14713     s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14714     debug(F101,"ftp hookup socket","",s);
14715     if (s < 0) {
14716         perror("ftp: socket");
14717         ftpcode = -1;
14718 #ifdef DEBUG
14719         debtim = xdebtim;
14720 #endif /* DEBUG */
14721         return(0);
14722     }
14723     hisctladdr.sin_port = htons(cport);
14724     errno = 0;
14725
14726 #ifdef COMMENT
14727   printf("hisctladdr=%d\n",sizeof(hisctladdr));
14728   printf("hisctladdr.sin_addr=%d\n",sizeof(hisctladdr.sin_addr));
14729   printf("sockaddr_in=%d\n",sizeof(struct sockaddr_in));
14730   printf("hisctladdr.sin_addr.s_addr=%d\n",sizeof(hisctladdr.sin_addr.s_addr));
14731 #endif  /* COMMENT */
14732
14733 #ifdef HADDRLIST
14734     debug(F100,"ftp hookup HADDRLIST","",0);
14735     while
14736 #else
14737     debug(F100,"ftp hookup no HADDRLIST","",0);
14738     if
14739 #endif /* HADDRLIST */
14740       (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
14741           debug(F101,"ftp hookup connect failed","",errno);
14742 #ifdef HADDRLIST
14743           if (hp && hp->h_addr_list[1]) {
14744               int oerrno = errno;
14745
14746               fprintf(stderr, "ftp: connect to address %s: ",
14747                       inet_ntoa(hisctladdr.sin_addr));
14748               errno = oerrno;
14749               perror((char *) 0);
14750               hp->h_addr_list++;
14751               memcpy((char *)&hisctladdr.sin_addr,
14752                      hp->h_addr_list[0],
14753                      sizeof(hisctladdr.sin_addr));
14754               fprintf(stdout, "Trying %s...\n",
14755                       inet_ntoa(hisctladdr.sin_addr));
14756 #ifdef TCPIPLIB
14757               socket_close(s);
14758 #else /* TCPIPLIB */
14759               close(s);
14760 #endif /* TCPIPLIB */
14761               s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
14762               if (s < 0) {
14763                   perror("ftp: socket");
14764                   ftpcode = -1;
14765 #ifdef DEBUG
14766                   debtim = xdebtim;
14767 #endif /* DEBUG */
14768                   return(0);
14769               }
14770               continue;
14771           }
14772 #endif /* HADDRLIST */
14773           perror("ftp: connect");
14774           ftpcode = -1;
14775           goto bad;
14776       }
14777     debug(F100,"ftp hookup connect ok","",0);
14778
14779     len = sizeof (myctladdr);
14780     errno = 0;
14781     if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
14782         debug(F101,"ftp hookup getsockname failed","",errno);
14783         perror("ftp: getsockname");
14784         ftpcode = -1;
14785         goto bad;
14786     }
14787     debug(F100,"ftp hookup getsockname ok","",0);
14788
14789 #ifndef NOHTTP
14790     if (tcp_http_proxy) {
14791 #ifdef OS2
14792         char * agent = "Kermit 95";     /* Default user agent */
14793 #else
14794         char * agent = "C-Kermit";
14795 #endif /* OS2 */
14796
14797         if (http_connect(s,agent,NULL,
14798                          tcp_http_proxy_user,
14799                          tcp_http_proxy_pwd,
14800                          0,
14801                          proxyhost
14802                          ) < 0) {
14803             char * foo = NULL;
14804 #ifdef TCPIPLIB
14805             socket_close(s);
14806 #else /* TCPIPLIB */
14807             close(s);
14808 #endif /* TCPIPLIB */
14809
14810             while (foo == NULL && tcp_http_proxy != NULL ) {
14811
14812                 if (tcp_http_proxy_errno == 401 ||
14813                      tcp_http_proxy_errno == 407 ) {
14814                     char uid[UIDBUFLEN];
14815                     char pwd[PWDSIZ];
14816                     struct txtbox tb[2];
14817                     int ok;
14818
14819                     tb[0].t_buf = uid;
14820                     tb[0].t_len = UIDBUFLEN;
14821                     tb[0].t_lbl = "Proxy Userid: ";
14822                     tb[0].t_dflt = NULL;
14823                     tb[0].t_echo = 1;
14824                     tb[1].t_buf = pwd;
14825                     tb[1].t_len = 256;
14826                     tb[1].t_lbl = "Proxy Passphrase: ";
14827                     tb[1].t_dflt = NULL;
14828                     tb[1].t_echo = 2;
14829
14830                     ok = uq_mtxt("Proxy Server Authentication Required\n",
14831                                   NULL, 2, tb);
14832
14833                     if (ok && uid[0]) {
14834                         char * proxy_user, * proxy_pwd;
14835
14836                         proxy_user = tcp_http_proxy_user;
14837                         proxy_pwd  = tcp_http_proxy_pwd;
14838
14839                         tcp_http_proxy_user = uid;
14840                         tcp_http_proxy_pwd = pwd;
14841
14842                         foo = ftp_hookup(host, port, 0);
14843
14844                         debug(F110,"ftp_hookup()",foo,0);
14845                         memset(pwd,0,PWDSIZ);
14846                         tcp_http_proxy_user = proxy_user;
14847                         tcp_http_proxy_pwd = proxy_pwd;
14848                     } else
14849                         break;
14850                 } else
14851                     break;
14852             }
14853             if (foo != NULL)
14854               return(foo);
14855             perror("ftp: connect");
14856             ftpcode = -1;
14857             goto bad;
14858         }
14859         ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN);
14860     }
14861 #endif /* NOHTTP */
14862
14863     csocket = s;
14864
14865 #ifdef CK_SSL
14866     if (tls) {
14867         /* FTP over SSL
14868          * If the connection is over an SSL proxy then the
14869          * auth_type will be NULL.  However, I'm not sure
14870          * whether we should protect the data channel in
14871          * that case or not.
14872          */
14873
14874         debug(F100,"ftp hookup use_tls","",0);
14875         if (!ssl_auth()) {
14876             debug(F100,"ftp hookup ssl_auth failed","",0);
14877             auth_type = NULL;
14878             ftpcode = -1;
14879             csocket = -1;
14880             goto bad;
14881         }
14882         ssl_ftp_proxy = 1;
14883     }
14884 #endif /* CK_SSL */
14885
14886 #ifdef IP_TOS
14887 #ifdef IPTOS_LOWDELAY
14888     tos = IPTOS_LOWDELAY;
14889     if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
14890       perror("ftp: setsockopt TOS (ignored)");
14891 #endif
14892 #endif
14893     if (!quiet)
14894       printf("Connected to %s.\n", host);
14895
14896     /* Read greeting from server */
14897     if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) {
14898         debug(F100,"ftp hookup bad reply","",0);
14899 #ifdef TCPIPLIB
14900         socket_close(csocket);
14901 #else /* TCPIPLIB */
14902         close(csocket);
14903 #endif /* TCPIPLIB */
14904         ftpcode = -1;
14905         goto bad;
14906     }
14907 #ifdef SO_OOBINLINE
14908     {
14909         int on = 1;
14910         errno = 0;
14911         if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
14912                        sizeof(on)) < 0) {
14913             perror("ftp: setsockopt");
14914             debug(F101,"ftp hookup setsockopt failed","",errno);
14915         }
14916 #ifdef DEBUG
14917         else
14918           debug(F100,"ftp hookup setsockopt ok","",0);
14919 #endif /* DEBUG */
14920     }
14921 #endif /* SO_OOBINLINE */
14922
14923 #ifdef DEBUG
14924     debtim = xdebtim;
14925 #endif /* DEBUG */
14926     return(ftp_host);
14927
14928   bad:
14929     debug(F100,"ftp hookup bad","",0);
14930 #ifdef TCPIPLIB
14931     socket_close(s);
14932 #else /* TCPIPLIB */
14933     close(s);
14934 #endif /* TCPIPLIB */
14935 #ifdef DEBUG
14936     debtim = xdebtim;
14937 #endif /* DEBUG */
14938     csocket = -1;
14939     return((char *)0);
14940 }
14941
14942 static VOID
14943 ftp_init() {
14944     int i, n;
14945
14946     /* The purpose of the initial REST 0 is not clear, but other FTP */
14947     /* clients do it.  In any case, failure of this command is not a */
14948     /* reliable indication that the server does not support Restart. */
14949
14950     okrestart = 0;
14951     if (!noinit) {
14952         n = ftpcmd("REST 0",NULL,0,0,0);
14953         if (n == REPLY_COMPLETE)
14954           okrestart = 1;
14955 #ifdef COMMENT
14956         else if (ftp_deb)
14957           printf("WARNING: Unable to restore file pointer.\n");
14958 #endif /* COMMENT */
14959     }
14960     n = ftpcmd("SYST",NULL,0,0,0);      /* Get server system type */
14961     if (n == REPLY_COMPLETE) {
14962         register char *cp, c = NUL;
14963         cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */
14964         if (cp == NULL)
14965           cp = ckstrchr(ftp_reply_str+4,'\r');
14966         if (cp) {
14967             if (cp[-1] == '.')
14968               cp--;
14969             c = *cp;                    /* Save this char */
14970             *cp = '\0';                 /* Replace it with NUL */
14971         }
14972         if (!quiet)
14973           printf("Remote system type is %s.\n",ftp_reply_str+4);
14974         ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN);
14975         if (cp)                         /* Put back saved char */
14976           *cp = c;
14977     }
14978     alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0);
14979
14980     if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX;
14981     else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32;
14982     else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32;
14983     else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS;
14984     else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS;
14985     else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20;
14986     else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10;
14987
14988 #ifdef FTP_PROXY
14989     unix_proxy = 0;
14990     if (servertype == SYS_UNIX && proxy) unix_proxy = 1;
14991 #endif /* FTP_PROXY */
14992
14993     if (ftp_cmdlin && ftp_xfermode == XMODE_M)
14994       ftp_typ = binary;                 /* Type given on command line */
14995     else                                /* Otherwise set it automatically */
14996       ftp_typ = alike ? FTT_BIN : FTT_ASC;
14997     changetype(ftp_typ,0);              /* Change to this type */
14998     g_ftp_typ = ftp_typ;                /* Make it the global type */
14999     if (!quiet)
15000       printf("Default transfer mode is %s\n",
15001              ftp_typ ? "BINARY" : "TEXT (\"ASCII\")"
15002              );
15003     for (i = 0; i < 16; i++)            /* Init server FEATure table */
15004       sfttab[i] = 0;
15005     if (!noinit) {
15006         n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */
15007 #ifdef COMMENT
15008         if (n != REPLY_COMPLETE)
15009           printf("WARNING: Server does not accept MODE S(TREAM)\n");
15010 #endif /* COMMENT */
15011         n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */
15012 #ifdef COMMENT
15013         if (n != REPLY_COMPLETE)
15014           printf("WARNING: Server does not accept STRU F(ILE)\n");
15015 #endif /* COMMENT */
15016         if (featok) {
15017             n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */
15018             if (n == REPLY_COMPLETE) {
15019                 debug(F101,"ftp_init FEAT","",sfttab[0]);
15020                 if (deblog || ftp_deb) {
15021                     int i;
15022                     for (i = 1; i < 16 && i < nfeattab; i++) {
15023                         debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]);
15024                         if (ftp_deb)
15025                           printf("  Server %s %s\n",
15026                                  sfttab[i] ? "supports" : "does not support",
15027                                  feattab[i].kwd
15028                                  );
15029                     }
15030                     /* Deal with disabled MLST opts here if necessary */
15031                     /* But why would it be? */
15032                 }
15033             }
15034         }
15035     }
15036 }
15037
15038 static int
15039 ftp_login(host) char * host; {          /* (also called from ckuusy.c) */
15040     static char ftppass[PASSBUFSIZ]="";
15041     char tmp[PASSBUFSIZ];
15042     char *user = NULL, *pass = NULL, *acct = NULL;
15043     int n, aflag = 0;
15044     extern char uidbuf[];
15045     extern char pwbuf[];
15046     extern int  pwflg, pwcrypt;
15047
15048     debug(F111,"ftp_login",ftp_logname,ftp_log);
15049
15050     if (!ckstrcmp(ftp_logname,"anonymous",-1,0))
15051       anonymous = 1;
15052     if (!ckstrcmp(ftp_logname,"ftp",-1,0))
15053       anonymous = 1;
15054
15055 #ifdef FTP_SRP
15056     if (auth_type && !strcmp(auth_type, "SRP")) {
15057         user = srp_user;
15058         pass = srp_pass;
15059         acct = srp_acct;
15060     } else
15061 #endif /* FTP_SRP */
15062       if (anonymous) {
15063           user = "anonymous";
15064           if (ftp_tmp) {                /* They gave a password */
15065               pass = ftp_tmp;
15066           } else if (ftp_apw) {         /* SET FTP ANONYMOUS-PASSWORD */
15067               pass = ftp_apw;
15068           } else {                      /* Supply user@host */
15069               ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL);
15070               pass = tmp;
15071           }
15072           debug(F110,"ftp anonymous",pass,0);
15073       } else {
15074 #ifdef USE_RUSERPASS
15075           if (ruserpass(host, &user, &pass, &acct) < 0) {
15076               ftpcode = -1;
15077               return(0);
15078           }
15079 #endif /* USE_RUSERPASS */
15080           if (ftp_logname) {
15081               user = ftp_logname;
15082               pass = ftp_tmp;
15083           } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) {
15084               user = uidbuf;
15085               if (ftp_tmp) {
15086                   pass = ftp_tmp;
15087               } else if (pwbuf[0] && pwflg) {
15088                   ckstrncpy(ftppass,pwbuf,PASSBUFSIZ);
15089 #ifdef OS2
15090                   if ( pwcrypt )
15091                       ck_encrypt((char *)ftppass);
15092 #endif /* OS2 */
15093                   pass = ftppass;
15094               }
15095           }
15096           acct = ftp_acc;
15097           while (user == NULL) {
15098               char *myname, prompt[PROMPTSIZ];
15099               int ok;
15100
15101               myname = whoami();
15102               if (myname)
15103                 ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
15104                           NULL,NULL,NULL,NULL,NULL,NULL,NULL);
15105               else
15106                 ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
15107               tmp[0] = '\0';
15108               
15109               ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL,
15110                           DEFAULT_UQ_TIMEOUT);
15111               if (!ok || *tmp == '\0')
15112                 user = myname;
15113               else
15114                 user = brstrip(tmp);
15115           }
15116       }
15117     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15118     if (n == REPLY_COMPLETE) {
15119         /* determine if we need to send a dummy password */
15120         if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE)
15121           ftpcmd("PASS dummy",NULL,0,0,1);
15122     } else if (n == REPLY_CONTINUE) {
15123 #ifdef CK_ENCRYPTION
15124         int oldftp_cpl;
15125 #endif /* CK_ENCRYPTION */
15126
15127         if (pass == NULL) {
15128             int ok;
15129             setint();
15130             ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
15131                         DEFAULT_UQ_TIMEOUT);
15132             if (ok)
15133                 pass = brstrip(ftppass);
15134         }
15135
15136 #ifdef CK_ENCRYPTION
15137         oldftp_cpl = ftp_cpl;
15138         ftp_cpl = FPL_PRV;
15139 #endif /* CK_ENCRYPTION */
15140         n = ftpcmd("PASS",pass,-1,-1,1);
15141         if (!anonymous && pass) {
15142             char * p = pass;
15143             while (*p++) *(p-1) = NUL;
15144             makestr(&ftp_tmp,NULL);
15145         }
15146 #ifdef CK_ENCRYPTION
15147         /* level may have changed */
15148         if (ftp_cpl == FPL_PRV)
15149           ftp_cpl = oldftp_cpl;
15150 #endif /* CK_ENCRYPTION */
15151     }
15152     if (n == REPLY_CONTINUE) {
15153         aflag++;
15154         if (acct == NULL) {
15155             static char ftpacct[80];
15156             int ok;
15157             setint();
15158             ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL,
15159                         DEFAULT_UQ_TIMEOUT);
15160             if (ok)
15161                 acct = brstrip(ftpacct);
15162         }
15163         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15164     }
15165     if (n != REPLY_COMPLETE) {
15166         fprintf(stderr, "FTP login failed.\n");
15167         if (haveurl)
15168           doexit(BAD_EXIT,-1);
15169         return(0);
15170     }
15171     if (!aflag && acct != NULL) {
15172         ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15173     }
15174     makestr(&ftp_logname,user);
15175     loggedin = 1;
15176 #ifdef LOCUS
15177     /* Unprefixed file management commands go to server */
15178     if (autolocus && !ftp_cmdlin) {
15179         setlocus(0,1);
15180     }
15181 #endif /* LOCUS */
15182     ftp_init();
15183
15184     if (anonymous && !quiet) {
15185         printf(" Logged in as anonymous (%s)\n",pass);
15186         memset(pass, 0, strlen(pass));
15187     }
15188     if (ftp_rdir) {
15189         if (doftpcwd(ftp_rdir,-1) < 1)
15190           doexit(BAD_EXIT,-1);
15191     }
15192
15193 #ifdef FTP_PROXY
15194     if (proxy)
15195       return(1);
15196 #endif /* FTP_PROXY */
15197     return(1);
15198 }
15199
15200 static int
15201 ftp_reset() {
15202     int rc;
15203 #ifdef BSDSELECT
15204     int nfnd = 1;
15205     fd_set mask;
15206     FD_ZERO(&mask);
15207     while (nfnd > 0) {
15208         FD_SET(csocket, &mask);
15209         if ((nfnd = empty(&mask,0)) < 0) {
15210             perror("reset");
15211             ftpcode = -1;
15212             lostpeer();
15213             return(0);
15214         } else if (nfnd) {
15215             getreply(0,-1,-1,ftp_vbm,0);
15216         }
15217     }
15218 #else /* BSDSELECT */
15219 #ifdef IBMSELECT
15220     int nfnd = 1;
15221     while (nfnd > 0) {
15222         if ((nfnd = empty(&csocket,1,0)) < 0) {
15223             perror("reset");
15224             ftpcode = -1;
15225             lostpeer();
15226             return(0);
15227         } else if (nfnd) {
15228             getreply(0,-1,-1,ftp_vbm,0);
15229         }
15230     }
15231 #endif /* IBMSELECT */
15232 #endif /* BSDSELECT */
15233     rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE);
15234     if (rc > 0)
15235       loggedin = 0;
15236     return(rc);
15237 }
15238
15239 static int
15240 ftp_rename(from, to) char * from, * to; {
15241     int lcs = -1, rcs = -1;
15242 #ifndef NOCSETS
15243     if (ftp_xla) {
15244         lcs = ftp_csl;
15245         if (lcs < 0) lcs = fcharset;
15246         rcs = ftp_csx;
15247         if (rcs < 0) rcs = ftp_csr;
15248     }
15249 #endif /* NOCSETS */
15250     if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) {
15251         return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE);
15252     }
15253     return(0);                          /* Failure */
15254 }
15255
15256 static int
15257 ftp_umask(mask) char * mask; {
15258     int rc;
15259     rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE);
15260     return(rc);
15261 }
15262
15263 static int
15264 ftp_user(user,pass,acct) char * user, * pass, * acct; {
15265     int n = 0, aflag = 0;
15266     char pwd[PWDSIZ];
15267
15268     if (!auth_type && ftp_aut) {
15269 #ifdef FTP_SRP
15270         if (ck_srp_is_installed()) {
15271             if (srp_ftp_auth( NULL, user, pass)) {
15272                 makestr(&pass,srp_pass);
15273             }
15274         }
15275 #endif /* FTP_SRP */
15276     }
15277     n = ftpcmd("USER",user,-1,-1,ftp_vbm);
15278     if (n == REPLY_COMPLETE)
15279       n = ftpcmd("PASS dummy",NULL,0,0,1);
15280     else if (n == REPLY_CONTINUE) {
15281 #ifdef CK_ENCRYPTION
15282         int oldftp_cpl;
15283 #endif /* CK_ENCRYPTION */
15284         if (pass == NULL || !pass[0]) {
15285             int ok;
15286             pwd[0] = '\0';
15287             setint();
15288             ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL,
15289                         DEFAULT_UQ_TIMEOUT);
15290             if (ok)
15291                 pass = brstrip(pwd);
15292         }
15293
15294 #ifdef CK_ENCRYPTION
15295         if ((oldftp_cpl = ftp_cpl) == PROT_S)
15296           ftp_cpl = PROT_P;
15297 #endif /* CK_ENCRYPTION */
15298         n = ftpcmd("PASS",pass,-1,-1,1);
15299         memset(pass, 0, strlen(pass));
15300 #ifdef CK_ENCRYPTION
15301         /* level may have changed */
15302         if (ftp_cpl == PROT_P)
15303           ftp_cpl = oldftp_cpl;
15304 #endif /* CK_ENCRYPTION */
15305     }
15306     if (n == REPLY_CONTINUE) {
15307         if (acct == NULL || !acct[0]) {
15308             int ok;
15309             pwd[0] = '\0';
15310             setint();
15311             ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL,
15312                         DEFAULT_UQ_TIMEOUT);
15313             if (ok)
15314                 acct = pwd;
15315         }
15316         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15317         aflag++;
15318     }
15319     if (n != REPLY_COMPLETE) {
15320         printf("Login failed.\n");
15321         return(0);
15322     }
15323     if (!aflag && acct != NULL && acct[0]) {
15324         n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm);
15325     }
15326     if (n == REPLY_COMPLETE) {
15327         makestr(&ftp_logname,user);
15328         loggedin = 1;
15329         ftp_init();
15330         return(1);
15331     }
15332     return(0);
15333 }
15334
15335 char *
15336 ftp_authtype() {
15337     if (!connected)
15338       return("NULL");
15339     return(auth_type ? auth_type : "NULL");
15340 }
15341
15342 char *
15343 ftp_cpl_mode() {
15344     switch (ftp_cpl) {
15345       case FPL_CLR:
15346         return("clear");
15347       case FPL_SAF:
15348         return("safe");
15349       case FPL_PRV:
15350         return("private");
15351       case FPL_CON:
15352         return("confidential");
15353       default:
15354         return("(error)");
15355     }
15356 }
15357
15358 char *
15359 ftp_dpl_mode() {
15360     switch (ftp_dpl) {
15361       case FPL_CLR:
15362         return("clear");
15363       case FPL_SAF:
15364         return("safe");
15365       case FPL_PRV:
15366         return("private");
15367       case FPL_CON:
15368         return("confidential");
15369       default:
15370         return("(error)");
15371     }
15372 }
15373
15374
15375 /* remote_files() */
15376 /*
15377    Returns next remote filename on success;
15378    NULL on error or no more files with global rfrc set to:
15379      -1: Bad argument
15380      -2: Server error response to NLST, e.g. file not found
15381      -3: No more files
15382      -9: Internal error
15383 */
15384 #define FTPNAMBUFLEN CKMAXPATH+1024
15385
15386 /* Check: ckmaxfiles CKMAXOPEN */
15387
15388 #define MLSDEPTH 128                    /* Stack of open temp files */
15389 static int mlsdepth = 0;                /* Temp file stack depth */
15390 static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */
15391 static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */
15392
15393 static VOID
15394 mlsreset() {                            /* Reset MGET temp-file stack */
15395     int i;
15396     for (i = 0; i <= mlsdepth; i++) {
15397         if (tmpfilptr[i]) {
15398             fclose(tmpfilptr[i]);
15399             tmpfilptr[i] = NULL;
15400             if (tmpfilnam[i]) {
15401 #ifdef OS2
15402                 unlink(tmpfilnam[i]);
15403 #endif /* OS2 */
15404                 free(tmpfilnam[i]);
15405             }
15406         }
15407     }
15408     mlsdepth = 0;
15409 }
15410
15411 static CHAR *
15412 #ifdef CK_ANSIC
15413 remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch)
15414 #else /* CK_ANSIC */
15415 remote_files(new_query, arg, pattern, proxy_switch)
15416     int new_query;
15417     CHAR * arg;                         /* That we send to the server */
15418     CHAR * pattern;                     /* That we use locally */
15419     int proxy_switch;
15420 #endif /* CK_ANSIC */
15421 /* remote_files */ {
15422     static CHAR buf[FTPNAMBUFLEN];
15423     CHAR *cp, *whicharg;
15424     char * cdto = NULL;
15425     char * p;
15426     int i, x, forced = 0;
15427     int lcs = 0, rcs = 0, xlate = 0;
15428
15429     debug(F101,"ftp remote_files new_query","",new_query);
15430     debug(F110,"ftp remote_files arg",arg,0);
15431     debug(F110,"ftp remote_files pattern",pattern,0);
15432
15433     rfrc = -1;
15434     if (pattern)                        /* Treat empty pattern same as NULL */
15435       if (!*pattern)
15436         pattern = NULL;
15437     if (arg)                            /* Ditto for arg */
15438       if (!*arg)
15439         arg = NULL;
15440
15441   again:
15442
15443     if (new_query) {
15444         if (tmpfilptr[mlsdepth]) {
15445             fclose(tmpfilptr[mlsdepth]);
15446             tmpfilptr[mlsdepth] = NULL;
15447 #ifdef OS2
15448             if (!ftp_deb && !deblog)
15449               unlink(tmpfilnam[mlsdepth]);
15450 #endif /* OS2 */
15451         }
15452     }
15453     if (tmpfilptr[mlsdepth] == NULL) {
15454         extern char * tempdir;
15455         char * p;
15456         debug(F110,"ftp remote_files tempdir",tempdir,0);
15457         if (tempdir) {
15458             p = tempdir;
15459         } else {
15460 #ifdef OS2
15461 #ifdef NT
15462             p = getenv("K95TMP");
15463 #else
15464             p = getenv("K2TMP");
15465 #endif /* NT */
15466             if (!p)
15467 #endif /* OS2 */
15468               p = getenv("CK_TMP");
15469             if (!p)
15470               p = getenv("TMPDIR");
15471             if (!p) p = getenv("TEMP");
15472             if (!p) p = getenv("TMP");
15473 #ifdef OS2ORUNIX
15474             if (p) {
15475                 int len = strlen(p);
15476                 if (p[len-1] != '/'
15477 #ifdef OS2
15478                     && p[len-1] != '\\'
15479 #endif /* OS2 */
15480                      ) {
15481                     static char foo[CKMAXPATH];
15482                     ckstrncpy(foo,p,CKMAXPATH);
15483                     ckstrncat(foo,"/",CKMAXPATH);
15484                     p = foo;
15485                 }
15486             } else
15487 #else /* OS2ORUNIX */
15488             if (!p)
15489 #endif /* OS2ORUNIX */
15490 #ifdef UNIX                             /* Systems that have a standard */
15491                 p = "/tmp/";            /* temporary directory... */
15492 #else
15493 #ifdef datageneral
15494             p = ":TMP:";
15495 #else
15496             p = "";
15497 #endif /* datageneral */
15498 #endif /* UNIX */
15499         }
15500         debug(F110,"ftp remote_files p",p,0);
15501
15502         /* Get temp file */
15503
15504         if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) {
15505             ckmakmsg((char *)tmpfilnam[mlsdepth],
15506                      CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL);
15507         } else {
15508             printf("?Malloc failure: remote_files()\n");
15509             return(NULL);
15510         }
15511
15512 #ifdef NT
15513         {
15514             char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]);
15515             if ( tmpfil )
15516                 ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1);
15517         }
15518 #else /* NT */
15519 #ifdef MKTEMP
15520 #ifdef MKSTEMP
15521         x = mkstemp((char *)tmpfilnam[mlsdepth]);
15522         if (x > -1) close(x);           /* We just want the name. */
15523 #else
15524         mktemp((char *)tmpfilnam[mlsdepth]);
15525 #endif /* MKSTEMP */
15526         /* if no mktmpnam() the name will just be "ckXXXXXX"... */
15527 #endif /* MKTEMP */
15528 #endif /* NT */
15529
15530         debug(F111,"ftp remote_files tmpfilnam[mlsdepth]",
15531               tmpfilnam[mlsdepth],mlsdepth);
15532
15533 #ifdef FTP_PROXY
15534         if (proxy_switch) {
15535             pswitch(!proxy);
15536         }
15537 #endif /* FTP_PROXY */
15538
15539         debug(F101,"ftp remote_files ftp_xla","",ftp_xla);
15540         debug(F101,"ftp remote_files ftp_csl","",ftp_csl);
15541         debug(F101,"ftp remote_files ftp_csr","",ftp_csr);
15542
15543 #ifndef NOCSETS
15544         xlate = ftp_xla;                /* SET FTP CHARACTER-SET-TRANSLATION */
15545         if (xlate) {                    /* ON? */
15546             lcs = ftp_csl;              /* Local charset */
15547             if (lcs < 0) lcs = fcharset;
15548             if (lcs < 0) xlate = 0;
15549         }
15550         if (xlate) {                    /* Still ON? */
15551             rcs = ftp_csx;              /* Remote (Server) charset */
15552             if (rcs < 0) rcs = ftp_csr;
15553             if (rcs < 0) xlate = 0;
15554         }
15555 #endif /* NOCSETS */
15556
15557         forced = mgetforced;            /* MGET method forced? */
15558         if (!forced || !mgetmethod)     /* Not forced... */
15559           mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */
15560               SND_MLS :
15561               SND_NLS; 
15562 /*                                           
15563   User's Command:                 Result:
15564     mget /nlst                     NLST (NULL)
15565     mget /nlst foo                 NLST foo
15566     mget /nlst *.txt               NLST *.txt 
15567     mget /nlst /match:*.txt        NLST (NULL)
15568     mget /nlst /match:*.txt  foo   NLST foo   
15569     mget /mlsd                     MLSD (NULL)
15570     mget /mlsd foo                 MLSD foo
15571     mget /mlsd *.txt               MLSD (NULL)
15572     mget /mlsd /match:*.txt        MLSD (NULL)
15573     mget /mlsd /match:*.txt  foo   MLSD foo
15574 */
15575         x = -1;
15576         while (x < 0) {
15577             if (pattern) {              /* Don't simplify this! */
15578                 whicharg = arg;
15579             } else if (mgetmethod == SND_MLS) {
15580                 if (arg)
15581                   whicharg = iswild((char *)arg) ? NULL : arg;
15582                 else
15583                   whicharg = NULL;
15584             } else {
15585                 whicharg = arg;
15586             }
15587             debug(F110,"ftp remote_files mgetmethod",
15588                   mgetmethod == SND_MLS ? "MLSD" : "NLST", 0);
15589             debug(F110,"ftp remote_files whicharg",whicharg,0);
15590
15591             x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST",
15592                             (char *)tmpfilnam[mlsdepth],
15593                             (char *)whicharg,
15594                             "wb",
15595                             0,
15596                             0,
15597                             NULL,
15598                             xlate,
15599                             lcs,
15600                             rcs
15601                             );
15602             if (x < 0) {                /* Chosen method wasn't accepted */
15603                 if (forced) {
15604                     if (ftpcode > 500 && ftpcode < 505 && !quiet)
15605                       printf("?%s: Not supported by server\n",
15606                              mgetmethod == SND_MLS ? "MLSD" : "NLST"
15607                              );
15608                     rfrc = -2;          /* Fail */
15609                     return(NULL);
15610                 }
15611                 /* Not forced - if MLSD failed, try NLST */
15612                 if (mgetmethod == SND_MLS) {  /* Server lied about MLST */
15613                     sfttab[SFT_MLST] = 0;     /* So disable it */
15614                     mlstok = 0;               /* and */
15615                     mgetmethod = SND_NLS;     /* try NLST */
15616                     continue;
15617                 }
15618                 rfrc = -2;
15619                 return(NULL);
15620             }
15621         }
15622 #ifdef FTP_PROXY
15623         if (proxy_switch) {
15624             pswitch(!proxy);
15625         }
15626 #endif /* FTP_PROXY */
15627         tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r");
15628 #ifndef OS2
15629         if (tmpfilptr[mlsdepth]) {
15630             if (!ftp_deb && !deblog)
15631               unlink(tmpfilnam[mlsdepth]);
15632         }
15633 #endif /* OS2 */
15634       notemp:
15635         if (!tmpfilptr[mlsdepth]) {
15636             debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0);
15637             if ((!dpyactive || ftp_deb))
15638               printf("?Can't find list of remote files, oops\n");
15639             rfrc = -9;
15640             return(NULL);
15641         }
15642         if (ftp_deb)
15643           printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]);
15644     }
15645     buf[0] = NUL;
15646     buf[FTPNAMBUFLEN-1] = NUL;
15647     buf[FTPNAMBUFLEN-2] = NUL;
15648
15649     /* We have to redo all this because the first time was only for */
15650     /* for getting the file list, now it's for getting each file */
15651
15652     if (arg && mgetmethod == SND_MLS) { /* MLSD */
15653         if (!pattern && iswild((char *)arg)) {
15654             pattern = arg;              /* Wild arg is really a pattern */
15655             if (pattern)
15656               if (!*pattern)
15657                 pattern = NULL;
15658             arg = NULL;                 /* and not an arg */
15659         }
15660         if (new_query) {                /* Initial query? */
15661             cdto = (char *)arg;         /* (nonwild) arg given? */
15662             if (cdto)
15663               if (!*cdto)
15664                 cdto = NULL;
15665             if (cdto)                   /* If so, then CD to it */
15666               doftpcwd(cdto,0);
15667         }
15668     }
15669     new_query = 0;
15670
15671     if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) {
15672         fclose(tmpfilptr[mlsdepth]);
15673         tmpfilptr[mlsdepth] = NULL;
15674
15675 #ifdef OS2
15676         if (!ftp_deb && !deblog)
15677           unlink(tmpfilnam[mlsdepth]);
15678 #endif /* OS2 */
15679         if (ftp_deb && !deblog) {
15680             printf("(Temporary file %s NOT deleted)\n",
15681                    (char *)tmpfilnam[mlsdepth]);
15682         }
15683         if (mlsdepth <= 0) {            /* EOF at depth 0 */
15684             rfrc = -3;                  /* means we're done */
15685             return(NULL);
15686         }
15687         printf("POPPING(%d)...\n",mlsdepth-1); 
15688         if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]);
15689         mlsdepth--;
15690         doftpcdup();
15691         zchdir("..");                   /* <-- Not portable */
15692         goto again;
15693     }
15694     if (buf[FTPNAMBUFLEN-1]) {
15695         printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n",
15696                FTPNAMBUFLEN
15697                );
15698         debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN);
15699         return(NULL);
15700     }
15701     /* debug(F110,"ftp remote_files buf 1",buf,0); */
15702     if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL)
15703       *cp = '\0';
15704     if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL)
15705       *cp = '\0';
15706     debug(F110,"ftp remote_files buf",buf,0);
15707     rfrc = 0;
15708
15709     if (ftp_deb)
15710       printf("[%s]\n",(char *)buf);
15711
15712     havesize = (CK_OFF_T)-1;            /* Initialize file facts... */
15713     havetype = 0;
15714     makestr(&havemdtm,NULL);
15715     p = (char *)buf;
15716
15717     if (mgetmethod == SND_NLS) {        /* NLST... */
15718         if (pattern) {
15719             if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15720               goto again;
15721         }
15722     } else {                            /* MLSD... */
15723         p = parsefacts((char *)buf);
15724         switch (havetype) {
15725           case FTYP_FILE:               /* File: Get it if it matches */
15726             if (pattern) {
15727                 if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1))
15728                   goto again;
15729             }
15730             break;
15731           case FTYP_CDIR:               /* Current directory */
15732           case FTYP_PDIR:               /* Parent directory */
15733             goto again;                 /* Skip */
15734           case FTYP_DIR:                /* (Sub)Directory */
15735             if (!recursive)             /* If not /RECURSIVE */
15736               goto again;               /* Skip */
15737             if (mlsdepth < MLSDEPTH) {
15738                 char * p2 = NULL;
15739                 mlsdepth++;
15740                 printf("RECURSING [%s](%d)...\n",p,mlsdepth); 
15741                 if (doftpcwd(p,0) > 0) {
15742                     int x;
15743                     if (!ckstrchr(p,'/')) {
15744                         /* zmkdir() needs dirsep */
15745                         if ((p2 = (char *)malloc((int)strlen(p) + 2))) {
15746                             strcpy(p2,p);       /* SAFE */
15747                             strcat(p2,"/");     /* SAFE */
15748                             p = p2;
15749                         }
15750                     }
15751 #ifdef NOMKDIR
15752                     x = -1;
15753 #else
15754                     x = zmkdir(p);
15755 #endif /* NOMKDIR */
15756                     if (x > -1) {
15757                         zchdir(p);
15758                         p = (char *)remote_files(1,arg,pattern,0);
15759                         if (p2) free(p2);
15760                     } else {
15761                         printf("?mkdir failed: [%s] Depth=%d\n",
15762                                p,
15763                                mlsdepth
15764                                );
15765                         mlsreset();
15766                         if (p2) free(p2);
15767                         return(NULL);
15768                     }
15769                 } else {
15770                     printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth);
15771                     mlsreset();
15772                     return(NULL);
15773                 }
15774             } else {
15775                 printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n",
15776                        mlsdepth
15777                        );
15778                 mlsreset();
15779                 return(NULL);
15780             }
15781         }
15782     }
15783
15784 #ifdef DEBUG
15785     if (deblog) {
15786         debug(F101,"remote_files havesize","",havesize);
15787         debug(F101,"remote_files havetype","",havetype);
15788         debug(F110,"remote_files havemdtm",havemdtm,0); 
15789         debug(F110,"remote_files name",p,0);    
15790     }
15791 #endif /* DEBUG */
15792     return((CHAR *)p);
15793 }
15794
15795 /* N O T  P O R T A B L E !!! */
15796
15797 #if (SIZEOF_SHORT == 4)
15798 typedef unsigned short ftp_uint32;
15799 typedef short ftp_int32;
15800 #else
15801 #if (SIZEOF_INT == 4)
15802 typedef unsigned int ftp_uint32;
15803 typedef int ftp_int32;
15804 #else
15805 #if (SIZEOF_LONG == 4)
15806 typedef ULONG ftp_uint32;
15807 typedef long ftp_int32;
15808 #endif
15809 #endif
15810 #endif
15811
15812 /* Perhaps use these in general, certainly use them for GSSAPI */
15813
15814 #ifndef looping_write
15815 #define ftp_int32 int
15816 #define ftp_uint32 unsigned int
15817 static int
15818 looping_write(fd, buf, len)
15819     int fd;
15820     register CONST char *buf;
15821     int len;
15822 {
15823     int cc;
15824     register int wrlen = len;
15825     do {
15826         cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0);
15827         if (cc < 0) {
15828             if (errno == EINTR)
15829               continue;
15830             return(cc);
15831         } else {
15832             buf += cc;
15833             wrlen -= cc;
15834         }
15835     } while (wrlen > 0);
15836     return(len);
15837 }
15838 #endif
15839 #ifndef looping_read
15840 static int
15841 looping_read(fd, buf, len)
15842     int fd;
15843     register char *buf;
15844     register int len;
15845 {
15846     int cc, len2 = 0;
15847
15848     do {
15849         cc = recv(fd, (char *)buf, len,0);
15850         if (cc < 0) {
15851             if (errno == EINTR)
15852               continue;
15853             return(cc);                 /* errno is already set */
15854         } else if (cc == 0) {
15855             return(len2);
15856         } else {
15857             buf += cc;
15858             len2 += cc;
15859             len -= cc;
15860         }
15861     } while (len > 0);
15862     return(len2);
15863 }
15864 #endif /* looping_read */
15865
15866 #define ERR -2
15867
15868 #ifdef COMMENT
15869 static
15870 secure_putbyte(fd, c) int fd; CHAR c; {
15871     int ret;
15872
15873     ucbuf[nout++] = c;
15874     if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) {
15875         nout = 0;
15876         if (!ftpissecure())
15877           ret = send(fd, (SENDARG2TYPE)ucbuf,
15878                      (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0);
15879         else
15880           ret = secure_putbuf(fd,
15881                               ucbuf,
15882                               (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR
15883                               );
15884         return(ret?ret:c);
15885     }
15886     return(c);
15887 }
15888 #endif /* COMMENT */
15889
15890 /* returns:
15891  *       0  on success
15892  *      -1  on error (errno set)
15893  *      -2  on security error
15894  */
15895 static int
15896 secure_flush(fd) int fd; {
15897     int rc = 0;
15898     int len = 0;
15899
15900     if (nout > 0) {
15901         len = nout;
15902         if (!ftpissecure()) {
15903             rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0);
15904             nout = 0;
15905             goto xflush;
15906         } else {
15907             rc = secure_putbuf(fd, ucbuf, nout);
15908             if (rc)
15909               goto xflush;
15910         }
15911     }
15912     rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0);
15913
15914   xflush:
15915     if (rc > -1 && len > 0 && fdispla != XYFD_B) {
15916         spackets++;
15917         spktl = len;
15918         ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL);
15919     }
15920     return(rc);
15921 }
15922
15923 #ifdef COMMENT                          /* (not used) */
15924 /* returns:
15925  *      c>=0  on success
15926  *      -1    on error
15927  *      -2    on security error
15928  */
15929 static int
15930 #ifdef CK_ANSIC
15931 secure_putc(char c, int fd)
15932 #else
15933 secure_putc(c, fd) char c; int fd;
15934 #endif /* CK_ANSIC */
15935 /* secure_putc */ {
15936     return(secure_putbyte(fd, (CHAR) c));
15937 }
15938 #endif /* COMMENT */
15939
15940 /* returns:
15941  *      nbyte on success
15942  *      -1  on error (errno set)
15943  *      -2  on security error
15944  */
15945 static int
15946 #ifdef CK_ANSIC
15947 secure_write(int fd, CHAR * buf, unsigned int nbyte)
15948 #else
15949 secure_write(fd, buf, nbyte)
15950     int fd;
15951     CHAR * buf;
15952     unsigned int nbyte;
15953 #endif /* CK_ANSIC */
15954 {
15955     int ret;
15956
15957 #ifdef FTP_TIMEOUT    
15958     ftp_timed_out = 0;
15959     if (check_data_connection(fd,1) < 0) {
15960         ftp_timed_out = 1;
15961         return(-3);
15962     }
15963 #endif  /* FTP_TIMEOUT */
15964
15965     if (!ftpissecure()) {
15966         if (nout > 0) {
15967             if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0)
15968               return(ret);
15969             nout = 0;
15970         }
15971         return(send(fd,(SENDARG2TYPE)buf,nbyte,0));
15972     } else {
15973         int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR;
15974         int bsent = 0;
15975
15976         while (bsent < nbyte) {
15977             int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ?
15978                         (ucbuflen - nout) : (nbyte - bsent));
15979 #ifdef DEBUG
15980             if (deblog) {
15981                 debug(F101,"secure_write ucbuflen","",ucbuflen);
15982                 debug(F101,"secure_write ucbufsiz","",ucbufsiz);
15983                 debug(F101,"secure_write bsent","",bsent);
15984                 debug(F101,"secure_write b2cp","",b2cp);
15985             }
15986 #endif /* DEBUG */
15987             memcpy(&ucbuf[nout],&buf[bsent],b2cp);
15988             nout += b2cp;
15989             bsent += b2cp;
15990
15991             if (nout == ucbuflen) {
15992                 nout = 0;
15993                 ret = secure_putbuf(fd, ucbuf, ucbuflen);
15994                 if (ret < 0)
15995                   return(ret);
15996             }
15997         }
15998         return(bsent);
15999     }
16000 }
16001
16002 /* returns:
16003  *       0  on success
16004  *      -1  on error (errno set)
16005  *      -2  on security error
16006  */
16007 static int
16008 #ifdef CK_ANSIC
16009 secure_putbuf(int fd, CHAR * buf, unsigned int nbyte)
16010 #else
16011 secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte;
16012 #endif /* CK_ANSIC */
16013 {
16014     static char *outbuf = NULL;         /* output ciphertext */
16015 #ifdef FTP_SECURITY
16016     static unsigned int bufsize = 0;    /* size of outbuf */
16017 #endif /* FTP_SECURITY */
16018     ftp_int32 length   = 0;
16019     ftp_uint32 net_len = 0;
16020
16021     /* Other auth types go here ... */
16022 #ifdef CK_SSL
16023     if (ssl_ftp_data_active_flag) {
16024         int count, error;
16025
16026         /* there is no need to send an empty buffer when using SSL/TLS */
16027         if ( nbyte == 0 )
16028           return(0);
16029
16030         count = SSL_write(ssl_ftp_data_con, buf, nbyte);
16031         error = SSL_get_error(ssl_ftp_data_con,count);
16032         switch (error) {
16033           case SSL_ERROR_NONE:
16034             return(0);
16035           case SSL_ERROR_WANT_WRITE:
16036           case SSL_ERROR_WANT_READ:
16037           case SSL_ERROR_SYSCALL:
16038 #ifdef NT
16039             {
16040                 int gle = GetLastError();
16041                 if (gle == 0)
16042                   return(0);
16043                 debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle);
16044             }
16045 #endif /* NT */
16046           case SSL_ERROR_WANT_X509_LOOKUP:
16047           case SSL_ERROR_SSL:
16048           case SSL_ERROR_ZERO_RETURN:
16049           default:
16050             SSL_shutdown(ssl_ftp_data_con);
16051             SSL_free(ssl_ftp_data_con);
16052             ssl_ftp_data_active_flag = 0;
16053             ssl_ftp_data_con = NULL;
16054 #ifdef TCPIPLIB
16055             socket_close(data);
16056 #else /* TCPIPLIB */
16057 #ifdef USE_SHUTDOWN
16058             shutdown(data, 1+1);
16059 #endif /* USE_SHUTDOWN */
16060             close(data);
16061 #endif /* TCPIPLIB */
16062             data = -1;
16063             globaldin = data;
16064             return(-1);
16065         }
16066         return(-1);
16067     }
16068 #endif /* CK_SSL */
16069
16070 #ifdef FTP_SRP
16071     if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) {
16072         if (bufsize < nbyte + FUDGE_FACTOR) {
16073             if (outbuf?
16074                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16075                 (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16076                 bufsize = nbyte + FUDGE_FACTOR;
16077             } else {
16078                 bufsize = 0;
16079                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16080                 return(ERR);
16081             }
16082         }
16083         if ((length =
16084              srp_encode(ftp_dpl == FPL_PRV,
16085                         (CHAR *) buf,
16086                         (CHAR *) outbuf,
16087                         nbyte
16088                         )
16089              ) < 0) {
16090             secure_error ("srp_encode failed");
16091             return ERR;
16092         }
16093     }
16094 #endif /* FTP_SRP */
16095 #ifdef FTP_KRB4
16096     if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) {
16097         struct sockaddr_in myaddr, hisaddr;
16098         GSOCKNAME_T len;
16099         len = sizeof(myaddr);
16100         if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16101             secure_error("secure_putbuf: getsockname failed");
16102             return(ERR);
16103         }
16104         len = sizeof(hisaddr);
16105         if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16106             secure_error("secure_putbuf: getpeername failed");
16107             return(ERR);
16108         }
16109         if (bufsize < nbyte + FUDGE_FACTOR) {
16110             if (outbuf ?
16111                 (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))):
16112                  (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) {
16113                 bufsize = nbyte + FUDGE_FACTOR;
16114             } else {
16115                 bufsize = 0;
16116                 secure_error("%s (in malloc of PROT buffer)", ck_errstr());
16117                 return(ERR);
16118             }
16119         }
16120         if (ftp_dpl == FPL_PRV) {
16121             length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte,
16122                                  ftp_sched,
16123 #ifdef KRB524
16124                                  ftp_cred.session,
16125 #else /* KRB524 */
16126                                  &ftp_cred.session,
16127 #endif /* KRB524 */
16128                                  &myaddr,
16129                                  &hisaddr
16130                                  );
16131         } else {
16132             length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte,
16133 #ifdef KRB524
16134                                  ftp_cred.session,
16135 #else /* KRB524 */
16136                                  &ftp_cred.session,
16137 #endif /* KRB524 */
16138                                  &myaddr,
16139                                  &hisaddr
16140                                  );
16141         }
16142         if (length == -1) {
16143             secure_error("krb_mk_%s failed for KERBEROS_V4",
16144                          ftp_dpl == FPL_PRV ? "priv" : "safe");
16145             return(ERR);
16146         }
16147     }
16148 #endif /* FTP_KRB4 */
16149 #ifdef FTP_GSSAPI
16150     if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) {
16151         gss_buffer_desc in_buf, out_buf;
16152         OM_uint32 maj_stat, min_stat;
16153         int conf_state;
16154
16155         in_buf.value = buf;
16156         in_buf.length = nbyte;
16157         maj_stat = gss_seal(&min_stat, gcontext,
16158                             (ftp_dpl == FPL_PRV), /* confidential */
16159                             GSS_C_QOP_DEFAULT,
16160                             &in_buf,
16161                             &conf_state,
16162                             &out_buf
16163                             );
16164         if (maj_stat != GSS_S_COMPLETE) {
16165             /* generally need to deal */
16166             /* ie. should loop, but for now just fail */
16167             user_gss_error(maj_stat, min_stat,
16168                            ftp_dpl == FPL_PRV?
16169                            "GSSAPI seal failed":
16170                            "GSSAPI sign failed");
16171             return(ERR);
16172         }
16173         if (bufsize < out_buf.length) {
16174             if (outbuf ?
16175                 (outbuf = realloc(outbuf, (unsigned) out_buf.length)):
16176                 (outbuf = malloc((unsigned) out_buf.length))) {
16177                 bufsize = out_buf.length;
16178             } else {
16179                 bufsize = 0;
16180                 secure_error("%s (in malloc of PROT buffer)",
16181                              ck_errstr());
16182                 return(ERR);
16183             }
16184         }
16185         memcpy(outbuf, out_buf.value, length=out_buf.length);
16186         gss_release_buffer(&min_stat, &out_buf);
16187     }
16188 #endif /* FTP_GSSAPI */
16189     net_len = htonl((ULONG) length);
16190     if (looping_write(fd, (char *)&net_len, 4) == -1)
16191       return(-1);
16192     if (looping_write(fd, outbuf, length) != length)
16193       return(-1);
16194     return(0);
16195 }
16196
16197
16198 /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */
16199
16200 static int
16201 secure_getbyte(fd,fc) int fd,fc; {
16202     /* number of chars in ucbuf, pointer into ucbuf */
16203     static unsigned int nin = 0, bufp = 0;
16204     int kerror;
16205     ftp_uint32 length;
16206
16207     if (fc) {
16208         nin = bufp = 0;
16209         ucbuf[0] = NUL;
16210         return(0);
16211     }
16212     if (nin == 0) {
16213         if (iscanceled())
16214           return(-9);
16215
16216 #ifdef FTP_TIMEOUT
16217         if (check_data_connection(fd,0) < 0)
16218           return(-3);
16219 #endif  /* FTP_TIMEOUT */
16220
16221 #ifdef CK_SSL
16222         if (ssl_ftp_data_active_flag) {
16223             int count, error;
16224             count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz);
16225             error = SSL_get_error(ssl_ftp_data_con,count);
16226 #ifdef DEBUG
16227             if (error != SSL_ERROR_NONE)
16228               debug(F101,"ftp secure_getbyte error","",error);
16229             if (count == 0)
16230               debug(F101,"ftp secure_getbyte count","",count);
16231 #endif  /* DEBUG */
16232             switch (error) {
16233               case SSL_ERROR_NONE:
16234                 if (count > 0) {
16235                     nin = bufp = count;
16236                     rpackets++;
16237                     pktnum++;
16238                     if (fdispla != XYFD_B) {
16239                         rpktl = count;
16240                         ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16241                     }
16242                     break;
16243                 }
16244               case SSL_ERROR_WANT_WRITE:
16245               case SSL_ERROR_WANT_READ:
16246               case SSL_ERROR_SYSCALL:
16247 #ifdef NT
16248                 {
16249                     int gle = GetLastError();
16250                 }
16251 #endif /* NT */
16252               case SSL_ERROR_WANT_X509_LOOKUP:
16253               case SSL_ERROR_SSL:
16254               case SSL_ERROR_ZERO_RETURN:
16255               default:
16256                 nin = bufp = count = 0;
16257                 SSL_shutdown(ssl_ftp_data_con);
16258                 SSL_free(ssl_ftp_data_con);
16259                 ssl_ftp_data_active_flag = 0;
16260                 ssl_ftp_data_con = NULL;
16261 #ifdef TCPIPLIB
16262                 socket_close(data);
16263 #else /* TCPIPLIB */
16264 #ifdef USE_SHUTDOWN
16265                 shutdown(data, 1+1);
16266 #endif /* USE_SHUTDOWN */
16267                 close(data);
16268 #endif /* TCPIPLIB */
16269                 data = -1;
16270                 globaldin = data;
16271                 break;
16272             }
16273         } else
16274 #endif /* CK_SSL */
16275           {
16276               kerror = looping_read(fd, (char *)&length, sizeof(length));
16277               if (kerror != sizeof(length)) {
16278                   secure_error("Couldn't read PROT buffer length: %d/%s",
16279                                kerror,
16280                                kerror == -1 ? ck_errstr()
16281                                : "premature EOF"
16282                                );
16283                   return(ERR);
16284               }
16285               debug(F101,"secure_getbyte length","",length);
16286               debug(F101,"secure_getbyte ntohl(length)","",ntohl(length));
16287
16288               length = (ULONG) ntohl(length);
16289               if (length > maxbuf) {
16290                   secure_error("Length (%d) of PROT buffer > PBSZ=%u",
16291                                length,
16292                                maxbuf
16293                                );
16294                   return(ERR);
16295               }
16296               if ((kerror = looping_read(fd, ucbuf, length)) != length) {
16297                   secure_error("Couldn't read %u byte PROT buffer: %s",
16298                                length,
16299                                kerror == -1 ? ck_errstr() : "premature EOF"
16300                                );
16301                   return(ERR);
16302               }
16303
16304               /* Other auth types go here ... */
16305 #ifdef FTP_SRP
16306               if (strcmp(auth_type, "SRP") == 0) {
16307                   if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV,
16308                                                 (CHAR *) ucbuf,
16309                                                 ucbuf,
16310                                                 length
16311                                                 )
16312                        ) == -1) {
16313                       secure_error ("srp_encode failed" );
16314                       return ERR;
16315                   }
16316               }
16317 #endif /* FTP_SRP */
16318 #ifdef FTP_KRB4
16319               if (strcmp(auth_type, "KERBEROS_V4") == 0) {
16320                   struct sockaddr_in myaddr, hisaddr;
16321                   GSOCKNAME_T len;
16322                   len = sizeof(myaddr);
16323                   if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) {
16324                       secure_error("secure_putbuf: getsockname failed");
16325                       return(ERR);
16326                   }
16327                   len = sizeof(hisaddr);
16328                   if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) {
16329                       secure_error("secure_putbuf: getpeername failed");
16330                       return(ERR);
16331                   }
16332                   if (ftp_dpl) {
16333                       kerror = krb_rd_priv(ucbuf, length, ftp_sched,
16334 #ifdef KRB524
16335                                            ftp_cred.session,
16336 #else /* KRB524 */
16337                                            &ftp_cred.session,
16338 #endif /* KRB524 */
16339                                            &hisaddr, &myaddr, &ftp_msg_data);
16340                   } else {
16341                       kerror = krb_rd_safe(ucbuf, length,
16342 #ifdef KRB524
16343                                            ftp_cred.session,
16344 #else /* KRB524 */
16345                                            &ftp_cred.session,
16346 #endif /* KRB524 */
16347                                            &hisaddr, &myaddr, &ftp_msg_data);
16348                   }
16349                   if (kerror) {
16350                       secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
16351                                    ftp_dpl == FPL_PRV ? "priv" : "safe",
16352                                    krb_get_err_text(kerror));
16353                       return(ERR);
16354                   }
16355                   memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length);
16356                   nin = bufp = ftp_msg_data.app_length;
16357               }
16358 #endif /* FTP_KRB4 */
16359 #ifdef FTP_GSSAPI
16360               if (strcmp(auth_type, "GSSAPI") == 0) {
16361                   gss_buffer_desc xmit_buf, msg_buf;
16362                   OM_uint32 maj_stat, min_stat;
16363                   int conf_state;
16364
16365                   xmit_buf.value = ucbuf;
16366                   xmit_buf.length = length;
16367                   conf_state = (ftp_dpl == FPL_PRV);
16368                   /* decrypt/verify the message */
16369                   maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
16370                                         &msg_buf, &conf_state, NULL);
16371                   if (maj_stat != GSS_S_COMPLETE) {
16372                       user_gss_error(maj_stat, min_stat,
16373                                      (ftp_dpl == FPL_PRV)?
16374                                      "failed unsealing ENC message":
16375                                      "failed unsealing MIC message");
16376                       return ERR;
16377                   }
16378                   memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
16379                   gss_release_buffer(&min_stat, &msg_buf);
16380               }
16381 #endif /* FTP_GSSAPI */
16382               /* Other auth types go here ... */
16383
16384               /* Update file transfer display */
16385               rpackets++;
16386               pktnum++;
16387               if (fdispla != XYFD_B) {
16388                   rpktl = nin;
16389                   ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16390               }
16391           }
16392     }
16393     if (nin == 0)
16394       return(EOF);
16395     else
16396       return(ucbuf[bufp - nin--]);
16397 }
16398
16399 /* secure_getc(fd,fc)
16400  * Call with:
16401  *   fd = file descriptor for connection.
16402  *   fc = 0 to get a character, fc != 0 to initialize buffer pointers.
16403  * Returns:
16404  *   c>=0 on success (character value)
16405  *   -1   on EOF
16406  *   -2   on security error
16407  *   -3   on timeout (if built with FTP_TIMEOUT defined)
16408  */
16409 static int
16410 secure_getc(fd,fc) int fd,fc; {         /* file descriptor, function code */
16411
16412     if (!ftpissecure()) {
16413         static unsigned int nin = 0, bufp = 0;
16414         if (fc) {
16415             nin = bufp = 0;
16416             ucbuf[0] = NUL;
16417             return(0);
16418         }
16419         if (nin == 0) {
16420             if (iscanceled())
16421               return(-9);
16422
16423 #ifdef FTP_TIMEOUT
16424             if (check_data_connection(fd,0) < 0) {
16425                 debug(F100,"secure_getc TIMEOUT","",0);
16426                 nin = bufp = 0;
16427                 ftp_timed_out = 1;
16428                 return(-3);
16429             }           
16430 #endif  /* FTP_TIMEOUT */
16431
16432             nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0);
16433             if ((nin == 0) || (nin == (unsigned int)-1)) {
16434                 debug(F111,"secure_getc recv errno",ckitoa(nin),errno);
16435                 debug(F101,"secure_getc returns EOF","",EOF);
16436                 nin = bufp = 0;
16437                 return(EOF);
16438             }
16439             debug(F101,"ftp secure_getc recv","",nin);
16440             ckhexdump("ftp secure_getc recv",ucbuf,16);
16441             rpackets++;
16442             pktnum++;
16443             if (fdispla != XYFD_B) {
16444                 rpktl = nin;
16445                 ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL);
16446             }
16447         }
16448         return(ucbuf[bufp - nin--]);
16449     } else
16450       return(secure_getbyte(fd,fc));
16451 }
16452
16453 /* returns:
16454  *     n>0  on success (n == # of bytes read)
16455  *       0  on EOF
16456  *      -1  on error (errno set), only for FPL_CLR
16457  *      -2  on security error
16458  */
16459 static int
16460 secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; {
16461     static int c = 0;
16462     int i;
16463
16464     debug(F101,"secure_read bytes requested","",nbyte);
16465     if (c == EOF)
16466       return(c = 0);
16467     for (i = 0; nbyte > 0; nbyte--) {
16468         c = secure_getc(fd,0);
16469         switch (c) {
16470           case -9:                      /* Canceled from keyboard */
16471             debug(F101,"ftp secure_read interrupted","",c);
16472             return(0);
16473           case ERR:
16474             debug(F101,"ftp secure_read error","",c);
16475             return(c);
16476           case EOF:
16477             debug(F101,"ftp secure_read EOF","",c);
16478             if (!i)
16479               c = 0;
16480             return(i);
16481 #ifdef FTP_TIMEOUT
16482           case -3:
16483             debug(F101,"ftp secure_read timeout","",c);
16484             return(c);
16485 #endif  /* FTP_TIMEOUT */
16486           default:
16487             buf[i++] = c;
16488         }
16489     }
16490     return(i);
16491 }
16492
16493 #ifdef USE_RUSERPASS
16494 /* BEGIN_RUSERPASS
16495  *
16496  * Copyright (c) 1985 Regents of the University of California.
16497  * All rights reserved.
16498  *
16499  * Redistribution and use in source and binary forms, with or without
16500  * modification, are permitted provided that the following conditions
16501  * are met:
16502  * 1. Redistributions of source code must retain the above copyright
16503  *    notice, this list of conditions and the following disclaimer.
16504  * 2. Redistributions in binary form must reproduce the above copyright
16505  *    notice, this list of conditions and the following disclaimer in the
16506  *    documentation and/or other materials provided with the distribution.
16507  * 3. All advertising materials mentioning features or use of this software
16508  *    must display the following acknowledgement:
16509  *      This product includes software developed by the University of
16510  *      California, Berkeley and its contributors.
16511  * 4. Neither the name of the University nor the names of its contributors
16512  *    may be used to endorse or promote products derived from this software
16513  *    without specific prior written permission.
16514  *
16515  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16516  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16517  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16518  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
16519  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
16520  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
16521  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
16522  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
16523  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16524  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
16525  * SUCH DAMAGE.
16526  */
16527
16528 #ifndef lint
16529 static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
16530 #endif /* not lint */
16531
16532 #ifndef MAXHOSTNAMELEN
16533 #define MAXHOSTNAMELEN 64
16534 #endif
16535
16536 char * renvlook();
16537 static FILE * cfile;
16538
16539 #define DEFAULT 1
16540 #define LOGIN   2
16541 #define PASSWD  3
16542 #define ACCOUNT 4
16543 #define MACDEF  5
16544 #define ID      10
16545 #define MACH    11
16546
16547 static char tokval[100];
16548
16549 static struct toktab {
16550     char *tokstr;
16551     int tval;
16552 } toktab[]= {
16553     "default",  DEFAULT,
16554     "login",    LOGIN,
16555     "password", PASSWD,
16556     "passwd",   PASSWD,
16557     "account",  ACCOUNT,
16558     "machine",  MACH,
16559     "macdef",   MACDEF,
16560     0,          0
16561 };
16562
16563 static int
16564 token() {
16565     char *cp;
16566     int c;
16567     struct toktab *t;
16568
16569     if (feof(cfile))
16570       return(0);
16571     while ((c = getc(cfile)) != EOF &&
16572            (c == '\n' || c == '\t' || c == ' ' || c == ','))
16573       continue;
16574     if (c == EOF)
16575       return(0);
16576     cp = tokval;
16577     if (c == '"') {
16578         while ((c = getc(cfile)) != EOF && c != '"') {
16579             if (c == '\\')
16580               c = getc(cfile);
16581             *cp++ = c;
16582         }
16583     } else {
16584         *cp++ = c;
16585         while ((c = getc(cfile)) != EOF
16586                && c != '\n' && c != '\t' && c != ' ' && c != ',') {
16587             if (c == '\\')
16588               c = getc(cfile);
16589             *cp++ = c;
16590         }
16591     }
16592     *cp = 0;
16593     if (tokval[0] == 0)
16594       return(0);
16595     for (t = toktab; t->tokstr; t++)
16596       if (!strcmp(t->tokstr, tokval))
16597         return(t->tval);
16598     return(ID);
16599 }
16600
16601 ruserpass(host, aname, apass, aacct)
16602     char *host, **aname, **apass, **aacct;
16603 {
16604     char *hdir, buf[FTP_BUFSIZ], *tmp;
16605     char myname[MAXHOSTNAMELEN], *mydomain;
16606     int t, i, c, usedefault = 0;
16607 #ifdef NT
16608     struct _stat stb;
16609 #else /* NT */
16610     struct stat stb;
16611 #endif /* NT */
16612
16613     hdir = getenv("HOME");
16614     if (hdir == NULL)
16615         hdir = ".";
16616     ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL);
16617     cfile = fopen(buf, "r");
16618     if (cfile == NULL) {
16619         if (errno != ENOENT)
16620           perror(buf);
16621         return(0);
16622     }
16623     if (gethostname(myname, MAXHOSTNAMELEN) < 0)
16624       myname[0] = '\0';
16625     if ((mydomain = ckstrchr(myname, '.')) == NULL)
16626       mydomain = "";
16627
16628   next:
16629     while ((t = token())) switch(t) {
16630
16631       case DEFAULT:
16632         usedefault = 1;
16633         /* FALL THROUGH */
16634
16635       case MACH:
16636         if (!usedefault) {
16637             if (token() != ID)
16638               continue;
16639             /*
16640              * Allow match either for user's input host name
16641              * or official hostname.  Also allow match of
16642              * incompletely-specified host in local domain.
16643              */
16644             if (ckstrcmp(host, tokval,-1,1) == 0)
16645               goto match;
16646             if (ckstrcmp(ftp_host, tokval,-1,0) == 0)
16647               goto match;
16648             if ((tmp = ckstrchr(ftp_host, '.')) != NULL &&
16649                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16650                 ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 &&
16651                 tokval[tmp - ftp_host] == '\0')
16652               goto match;
16653             if ((tmp = ckstrchr(host, '.')) != NULL &&
16654                 ckstrcmp(tmp, mydomain,-1,1) == 0 &&
16655                 ckstrcmp(host, tokval, tmp - host, 0) == 0 &&
16656                 tokval[tmp - host] == '\0')
16657               goto match;
16658             continue;
16659         }
16660
16661       match:
16662         while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
16663
16664           case LOGIN:
16665             if (token())
16666               if (*aname == 0) {
16667                   *aname = malloc((unsigned) strlen(tokval) + 1);
16668                   strcpy(*aname, tokval);      /* safe */
16669               } else {
16670                   if (strcmp(*aname, tokval))
16671                     goto next;
16672               }
16673             break;
16674           case PASSWD:
16675             if (strcmp(*aname, "anonymous") &&
16676                 fstat(fileno(cfile), &stb) >= 0 &&
16677                 (stb.st_mode & 077) != 0) {
16678                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16679                 fprintf(stderr, "Remove password or correct mode.\n");
16680                 goto bad;
16681             }
16682             if (token() && *apass == 0) {
16683                 *apass = malloc((unsigned) strlen(tokval) + 1);
16684                 strcpy(*apass, tokval);          /* safe */
16685             }
16686             break;
16687           case ACCOUNT:
16688             if (fstat(fileno(cfile), &stb) >= 0
16689                 && (stb.st_mode & 077) != 0) {
16690                 fprintf(stderr, "Error - .netrc file not correct mode.\n");
16691                 fprintf(stderr, "Remove account or correct mode.\n");
16692                 goto bad;
16693             }
16694             if (token() && *aacct == 0) {
16695                 *aacct = malloc((unsigned) strlen(tokval) + 1);
16696                 strcpy(*aacct, tokval);          /* safe */
16697             }
16698             break;
16699
16700           default:
16701             fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
16702             break;
16703         }
16704         goto done;
16705     }
16706
16707   done:
16708     fclose(cfile);
16709     return(0);
16710
16711   bad:
16712     fclose(cfile);
16713     return(-1);
16714 }
16715 #endif /* USE_RUSERPASS */
16716
16717 static char *radixN =
16718   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16719
16720 static char pad = '=';
16721
16722 static int
16723 radix_encode(inbuf, outbuf, inlen, outlen, decode)
16724     CHAR inbuf[], outbuf[];
16725     int inlen, *outlen, decode;
16726 {
16727     int i, j, D = 0;
16728     char *p;
16729     CHAR c = NUL;
16730
16731     if (decode) {
16732         for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) {
16733             if ((p = ckstrchr(radixN, inbuf[i])) == NULL)
16734               return(1);
16735             D = p - radixN;
16736             switch (i&3) {
16737               case 0:
16738                 outbuf[j] = D<<2;
16739                 break;
16740               case 1:
16741                 outbuf[j++] |= D>>4;
16742                 outbuf[j] = (D&15)<<4;
16743                 break;
16744               case 2:
16745                 outbuf[j++] |= D>>2;
16746                 outbuf[j] = (D&3)<<6;
16747                 break;
16748               case 3:
16749                 outbuf[j++] |= D;
16750             }
16751             if (j == *outlen)
16752               return(4);
16753         }
16754         switch (i&3) {
16755           case 1: return(3);
16756           case 2: if (D&15) return(3);
16757             if (strcmp((char *)&inbuf[i], "==")) return(2);
16758             break;
16759           case 3: if (D&3) return(3);
16760             if (strcmp((char *)&inbuf[i], "="))  return(2);
16761         }
16762         *outlen = j;
16763     } else {
16764         for (i = 0, j = 0; i < inlen; i++) {
16765             switch (i%3) {
16766               case 0:
16767                 outbuf[j++] = radixN[inbuf[i]>>2];
16768                 c = (inbuf[i]&3)<<4;
16769                 break;
16770               case 1:
16771                 outbuf[j++] = radixN[c|inbuf[i]>>4];
16772                 c = (inbuf[i]&15)<<2;
16773                 break;
16774               case 2:
16775                 outbuf[j++] = radixN[c|inbuf[i]>>6];
16776                 outbuf[j++] = radixN[inbuf[i]&63];
16777                 c = 0;
16778             }
16779             if (j == *outlen)
16780               return(4);
16781         }
16782         if (i%3) outbuf[j++] = radixN[c];
16783         switch (i%3) {
16784           case 1: outbuf[j++] = pad;
16785           case 2: outbuf[j++] = pad;
16786         }
16787         outbuf[*outlen = j] = '\0';
16788     }
16789     return(0);
16790 }
16791
16792 static char *
16793 radix_error(e) int e;
16794 {
16795     switch (e) {
16796       case 0:  return("Success");
16797       case 1:  return("Bad character in encoding");
16798       case 2:  return("Encoding not properly padded");
16799       case 3:  return("Decoded # of bits not a multiple of 8");
16800       case 4:  return("Output buffer too small");
16801       default: return("Unknown error");
16802     }
16803 }
16804 /* END_RUSERPASS */
16805
16806 #ifdef FTP_SRP
16807 /*---------------------------------------------------------------------------+
16808  |                                                                           |
16809  |   Package: srpftp                                                         |
16810  |   Author: Eugene Jhong                                                    |
16811  |                                                                           |
16812  +---------------------------------------------------------------------------*/
16813
16814 /*
16815  * Copyright (c) 1997-1999  The Stanford SRP Authentication Project
16816  * All Rights Reserved.
16817  *
16818  * Permission is hereby granted, free of charge, to any person obtaining
16819  * a copy of this software and associated documentation files (the
16820  * "Software"), to deal in the Software without restriction, including
16821  * without limitation the rights to use, copy, modify, merge, publish,
16822  * distribute, sublicense, and/or sell copies of the Software, and to
16823  * permit persons to whom the Software is furnished to do so, subject to
16824  * the following conditions:
16825  *
16826  * The above copyright notice and this permission notice shall be
16827  * included in all copies or substantial portions of the Software.
16828  *
16829  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16830  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16831  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16832  *
16833  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
16834  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
16835  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
16836  * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
16837  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16838  *
16839  * In addition, the following conditions apply:
16840  *
16841  * 1. Any software that incorporates the SRP authentication technology
16842  *    must display the following acknowlegment:
16843  *    "This product uses the 'Secure Remote Password' cryptographic
16844  *     authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)."
16845  *
16846  * 2. Any software that incorporates all or part of the SRP distribution
16847  *    itself must also display the following acknowledgment:
16848  *    "This product includes software developed by Tom Wu and Eugene
16849  *     Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)."
16850  *
16851  * 3. Redistributions in source or binary form must retain an intact copy
16852  *    of this copyright notice and list of conditions.
16853  */
16854
16855 #define SRP_PROT_VERSION        1
16856
16857 #ifdef CK_ENCRYPTION
16858 #define SRP_DEFAULT_CIPHER      CIPHER_ID_CAST5_CBC
16859 #else
16860 #define SRP_DEFAULT_CIPHER      CIPHER_ID_NONE
16861 #endif /* CK_ENCRYPTION */
16862
16863 #define SRP_DEFAULT_HASH        HASH_ID_SHA
16864
16865 CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB;
16866 CHAR srp_pref_hash = HASH_ID_SHA;
16867
16868 static struct t_client *tc = NULL;
16869 static CHAR *skey = NULL;
16870 static krypto_context *incrypt = NULL;
16871 static krypto_context *outcrypt = NULL;
16872
16873 typedef unsigned int srp_uint32;
16874
16875 /*--------------------------------------------------------------+
16876  | srp_selcipher: select cipher                                 |
16877  +--------------------------------------------------------------*/
16878 static int
16879 srp_selcipher (cname) char *cname; {
16880     cipher_desc *cd;
16881
16882     if (!(cd = cipher_getdescbyname (cname))) {
16883         int i;
16884         CHAR *list = cipher_getlist ();
16885
16886         fprintf (stderr, "ftp: supported ciphers:\n\n");
16887         for (i = 0; i < strlen (list); i++)
16888           fprintf (stderr, "    %s\n", (cipher_getdescbyid(list[i]))->name);
16889         fprintf (stderr, "\n");
16890         return -1;
16891     }
16892     srp_pref_cipher = cd->id;
16893     return 0;
16894 }
16895
16896 /*--------------------------------------------------------------+
16897  | srp_selhash: select hash                                     |
16898  +--------------------------------------------------------------*/
16899 static int
16900 srp_selhash (hname) char *hname; {
16901     hash_desc *hd;
16902
16903     if (!(hd = hash_getdescbyname (hname))) {
16904         int i;
16905         CHAR *list = hash_getlist ();
16906
16907         fprintf (stderr, "ftp: supported hash functions:\n\n");
16908         for (i = 0; i < strlen (list); i++)
16909           fprintf (stderr, "    %s\n", (hash_getdescbyid(list[i]))->name);
16910         fprintf (stderr, "\n");
16911         return -1;
16912     }
16913     srp_pref_hash = hd->id;
16914     return 0;
16915 }
16916
16917 /*--------------------------------------------------------------+
16918  | srp_userpass: get username and password                      |
16919  +--------------------------------------------------------------*/
16920 static int
16921 srp_userpass (host) char *host; {
16922     char tmp[BUFSIZ], prompt[PROMPTSIZ];
16923     char *user;
16924
16925     user = NULL;
16926 #ifdef USE_RUSERPASS
16927     ruserpass (host, &user, &srp_pass, &srp_acct);
16928 #endif /* USE_RUSERPASS */
16929
16930     while (user == NULL)     {
16931         char *myname;
16932         int ok;
16933
16934         myname = whoami();
16935         if (!myname) myname = "";
16936         if (myname[0])
16937           ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ",
16938                     NULL,NULL,NULL,NULL,NULL,NULL,NULL);
16939         else
16940           ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL);
16941         tmp[0] = '\0';
16942         ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL,
16943                     DEFAULT_UQ_TIMEOUT);
16944         if (!ok || *tmp == '\0')
16945           user = myname;
16946         else
16947           user = brstrip(tmp);
16948     }
16949     ckstrncpy (srp_user, user,BUFSIZ);
16950     return(0);
16951 }
16952
16953 /*--------------------------------------------------------------+
16954  | srp_reset: reset srp information                             |
16955  +--------------------------------------------------------------*/
16956 static int
16957 srp_reset () {
16958     if (tc) { t_clientclose (tc); tc = NULL; }
16959     if (incrypt) { krypto_delete (incrypt); incrypt = NULL; }
16960     if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; }
16961     return(0);
16962 }
16963
16964 /*--------------------------------------------------------------+
16965  | srp_ftp_auth: perform srp authentication                         |
16966  +--------------------------------------------------------------*/
16967 static int
16968 srp_ftp_auth(host, user, pass)
16969     char *host;
16970     char *user;
16971     char *pass;
16972 {
16973     struct t_num *wp;
16974     struct t_num N;
16975     struct t_num g;
16976     struct t_num s;
16977     struct t_num yp;
16978     CHAR buf[FTP_BUFSIZ];
16979     CHAR tmp[FTP_BUFSIZ];
16980     CHAR *bp, *cp;
16981     int n, e, clen, blen, len, i;
16982     CHAR cid = 0;
16983     CHAR hid = 0;
16984
16985     srp_pass = srp_acct = 0;
16986
16987     n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm);
16988     if (n != REPLY_CONTINUE) {
16989         if (ftp_deb)
16990             fprintf(stderr, "SRP rejected as an authentication type\n");
16991         return(0);
16992     } else {                            /* Send protocol version */
16993         CHAR vers[4];
16994         memset (vers, 0, 4);
16995         vers[3] = SRP_PROT_VERSION;
16996         if (!quiet)
16997           printf ("SRP accepted as authentication type.\n");
16998         bp = tmp; blen = 0;
16999         srp_put (vers, &bp, 4, &blen);
17000         len = FTP_BUFSIZ;
17001         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17002           goto encode_error;
17003         reply_parse = "ADAT=";
17004         n = ftpcmd("ADAT",buf,-1,-1,0);
17005     }
17006     if (n == REPLY_CONTINUE) {          /* Get protocol version */
17007         bp = buf;
17008         if (!reply_parse)
17009           goto data_error;
17010         blen = FTP_BUFSIZ;
17011         if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE))
17012           goto decode_error;
17013         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17014           goto data_error;
17015
17016         if (host) {                     /* Get username/password if needed */
17017             srp_userpass (host);
17018         } else {
17019             ckstrncpy (srp_user, user, BUFSIZ);
17020             srp_pass = pass;
17021         }
17022         bp = tmp; blen = 0;             /* Send username */
17023         srp_put (srp_user, &bp, strlen (srp_user), &blen);
17024         len = FTP_BUFSIZ;
17025         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17026           goto encode_error;
17027         reply_parse = "ADAT=";
17028         n = ftpcmd("ADAT",buf,-1,-1,0);
17029     }
17030     if (n == REPLY_CONTINUE) {          /* Get N, g and s */
17031         bp = buf;
17032         if (!reply_parse)
17033           goto data_error;
17034         blen = FTP_BUFSIZ;
17035         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17036           goto decode_error;
17037         if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0)
17038           goto data_error;
17039         if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0)
17040           goto data_error;
17041         if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0)
17042           goto data_error;
17043         if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) {
17044             fprintf (stderr, "Unable to open SRP client structure.\n");
17045             goto bad;
17046         }
17047         wp = t_clientgenexp (tc);       /* Send wp */
17048         bp = tmp; blen = 0;
17049         srp_put (wp->data, &bp, wp->len, &blen);
17050         len = FTP_BUFSIZ;
17051         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17052           goto encode_error;
17053         reply_parse = "ADAT=";
17054         n = ftpcmd("ADAT",buf,-1,-1,0);
17055     }
17056     if (n == REPLY_CONTINUE) {          /* Get yp */
17057         bp = buf;
17058         if (!reply_parse)
17059           goto data_error;
17060         blen = FTP_BUFSIZ;
17061         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17062           goto decode_error;
17063         if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0)
17064           goto data_error;
17065         if (!srp_pass) {
17066             static char ftppass[PASSBUFSIZ];
17067             int ok;
17068             setint();
17069             ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL,
17070                         DEFAULT_UQ_TIMEOUT);
17071             if (ok)
17072               srp_pass = brstrip(ftppass);
17073         }
17074         t_clientpasswd (tc, srp_pass);
17075         memset (srp_pass, 0, strlen (srp_pass));
17076         skey = t_clientgetkey (tc, &yp); /* Send response */
17077         bp = tmp; blen = 0;
17078         srp_put (t_clientresponse (tc), &bp, 20, &blen);
17079         len = FTP_BUFSIZ;
17080         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17081           goto encode_error;
17082         reply_parse = "ADAT=";
17083         n = ftpcmd("ADAT",buf,-1,-1,0);
17084     }
17085     if (n == REPLY_CONTINUE) {          /* Get response */
17086         bp = buf;
17087         if (!reply_parse)
17088           goto data_error;
17089         blen = FTP_BUFSIZ;
17090         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17091           goto encode_error;
17092         if (srp_get (&bp, &cp, &blen, &clen) != 20)
17093           goto data_error;
17094         if (t_clientverify (tc, cp)) {
17095             fprintf (stderr, "WARNING: bad response to client challenge.\n");
17096             goto bad;
17097         }
17098         bp = tmp; blen = 0;             /* Send nothing */
17099         srp_put ("\0", &bp, 1, &blen);
17100         len = FTP_BUFSIZ;
17101         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17102           goto encode_error;
17103         reply_parse = "ADAT=";
17104         n = ftpcmd("ADAT",buf,-1,-1,0);
17105     }
17106     if (n == REPLY_CONTINUE) {          /* Get cipher & hash lists, seqnum */
17107         CHAR seqnum[4];
17108         CHAR *clist;
17109         CHAR *hlist;
17110         CHAR *p1;
17111         int clist_len, hlist_len;
17112         bp = buf;
17113         if (!reply_parse)
17114           goto data_error;
17115         blen = FTP_BUFSIZ;
17116         if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE))
17117           goto encode_error;
17118         if (srp_get (&bp, &clist, &blen, &clist_len) < 0)
17119           goto data_error;
17120         if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0)
17121           goto data_error;
17122         if (srp_get (&bp, &cp, &blen, &clen) != 4)
17123           goto data_error;
17124         memcpy (seqnum, cp, 4);
17125         if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */
17126           cid = srp_pref_cipher;
17127         if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER))
17128           cid = SRP_DEFAULT_CIPHER;
17129         if (!cid) {
17130             CHAR *loclist = cipher_getlist ();
17131             for (i = 0; i < strlen (loclist); i++)
17132               if (cipher_supported (clist, loclist[i])) {
17133                   cid = loclist[i];
17134                   break;
17135               }
17136         }
17137         if (!cid) {
17138             fprintf (stderr, "Unable to agree on cipher.\n");
17139             goto bad;
17140         }
17141         /* Choose hash */
17142
17143         if (srp_pref_hash && hash_supported (hlist, srp_pref_hash))
17144           hid = srp_pref_hash;
17145
17146         if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH))
17147           hid = SRP_DEFAULT_HASH;
17148
17149         if (!hid) {
17150             CHAR *loclist = hash_getlist ();
17151             for (i = 0; i < strlen (loclist); i++)
17152               if (hash_supported (hlist, loclist[i])) {
17153                   hid = loclist[i];
17154                   break;
17155               }
17156         }
17157         if (!hid) {
17158             fprintf (stderr, "Unable to agree on hash.\n");
17159             goto bad;
17160         }
17161         /* Set incrypt */
17162
17163         if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum,
17164                                     KRYPTO_DECODE)))
17165           goto bad;
17166
17167         /* Generate random number for outkey and outseqnum */
17168
17169         t_random (seqnum, 4);
17170
17171         /* Send cid, hid, outkey, outseqnum */
17172
17173         bp = tmp; blen = 0;
17174         srp_put (&cid, &bp, 1, &blen);
17175         srp_put (&hid, &bp, 1, &blen);
17176         srp_put (seqnum, &bp, 4, &blen);
17177         len = FTP_BUFSIZ;
17178         if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE))
17179           goto encode_error;
17180         reply_parse = "ADAT=";
17181         n = ftpcmd("ADAT",buf,-1,-1,0);
17182
17183         /* Set outcrypt */
17184
17185         if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum,
17186                                      KRYPTO_ENCODE)))
17187           goto bad;
17188
17189         t_clientclose (tc);
17190         tc = NULL;
17191     }
17192     if (n != REPLY_COMPLETE)
17193       goto bad;
17194
17195     if (ftp_vbm) {
17196         if (ftp_deb)
17197           printf("\n");
17198         printf ("SRP authentication succeeded.\n");
17199         printf ("Using cipher %s and hash function %s.\n",
17200                 (cipher_getdescbyid(cid))->name,
17201                 (hash_getdescbyid(hid))->name
17202                 );
17203     }
17204     reply_parse = NULL;
17205     auth_type = "SRP";
17206     return(1);
17207
17208   encode_error:
17209     fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e));
17210     goto bad;
17211
17212   decode_error:
17213     fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e));
17214     goto bad;
17215
17216   data_error:
17217     fprintf (stderr, "Unable to unmarshal authentication data.\n");
17218     goto bad;
17219
17220   bad:
17221     fprintf (stderr, "SRP authentication failed, trying regular login.\n");
17222     reply_parse = NULL;
17223     return(0);
17224 }
17225
17226 /*--------------------------------------------------------------+
17227  | srp_put: put item to send buffer                             |
17228  +--------------------------------------------------------------*/
17229 static int
17230 srp_put (in, out, inlen, outlen)
17231     CHAR *in;
17232     CHAR **out;
17233     int inlen;
17234     int *outlen;
17235 {
17236     srp_uint32 net_len;
17237
17238     net_len = htonl (inlen);
17239     memcpy (*out, &net_len, 4);
17240
17241     *out += 4; *outlen += 4;
17242
17243     memcpy (*out, in, inlen);
17244
17245     *out += inlen; *outlen += inlen;
17246     return(0);
17247 }
17248
17249 /*--------------------------------------------------------------+
17250  | srp_get: get item from receive buffer                        |
17251  +--------------------------------------------------------------*/
17252 static int
17253 srp_get (in, out, inlen, outlen)
17254     CHAR **in;
17255     CHAR **out;
17256     int *inlen;
17257     int *outlen;
17258 {
17259     srp_uint32 net_len;
17260
17261     if (*inlen < 4) return -1;
17262
17263     memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4;
17264     *outlen = ntohl (net_len);
17265
17266     if (*inlen < *outlen) return -1;
17267
17268     *out = *in; *inlen -= *outlen; *in += *outlen;
17269
17270     return *outlen;
17271 }
17272
17273 /*--------------------------------------------------------------+
17274  | srp_encode: encode control message                           |
17275  +--------------------------------------------------------------*/
17276 static int
17277 srp_encode (private, in, out, len)
17278     int private;
17279     CHAR *in;
17280     CHAR *out;
17281     unsigned len;
17282 {
17283     if (private)
17284       return krypto_msg_priv (outcrypt, in, out, len);
17285     else
17286       return krypto_msg_safe (outcrypt, in, out, len);
17287 }
17288
17289 /*--------------------------------------------------------------+
17290  | srp_decode: decode control message                           |
17291  +--------------------------------------------------------------*/
17292 static int
17293 srp_decode (private, in, out, len)
17294     int private;
17295     CHAR *in;
17296     CHAR *out;
17297     unsigned len;
17298 {
17299     if (private)
17300       return krypto_msg_priv (incrypt, in, out, len);
17301     else
17302       return krypto_msg_safe (incrypt, in, out, len);
17303 }
17304
17305 #endif /* FTP_SRP */
17306
17307
17308
17309 #ifdef NOT_USED
17310 /*
17311   The following code is from the Unix FTP client.  Be sure to
17312   make sure that the functionality is not lost.  Especially
17313   the Proxy stuff even though we have not yet implemented it.
17314 */
17315
17316 /* Send multiple files  */
17317
17318 static int
17319 ftp_mput(argc, argv) int argc; char **argv; {
17320     register int i;
17321     sig_t oldintr;
17322     int ointer;
17323     char *tp;
17324     sigtype mcancel();
17325
17326     if (argc < 2 && !another(&argc, &argv, "local-files")) {
17327         printf("usage: %s local-files\n", argv[0]);
17328         ftpcode = -1;
17329         return;
17330     }
17331     mname = argv[0];
17332     mflag = 1;
17333     oldintr = signal(SIGINT, mcancel);
17334
17335     /* Replace with calls to cc_execute() */
17336     setjmp(jcancel);
17337 #ifdef FTP_PROXY
17338     if (proxy) {
17339         char *cp, *tp2, tmpbuf[CKMAXPATH];
17340
17341         while ((cp = remglob(argv,0)) != NULL) {
17342             if (*cp == 0) {
17343                 mflag = 0;
17344                 continue;
17345             }
17346             if (mflag && confirm(argv[0], cp)) {
17347                 tp = cp;
17348                 if (mcase) {
17349                     while (*tp && !islower(*tp)) {
17350                         tp++;
17351                     }
17352                     if (!*tp) {
17353                         tp = cp;
17354                         tp2 = tmpbuf;
17355                         while ((*tp2 = *tp) != 0) {
17356                             if (isupper(*tp2)) {
17357                                 *tp2 = 'a' + *tp2 - 'A';
17358                             }
17359                             tp++;
17360                             tp2++;
17361                         }
17362                     }
17363                     tp = tmpbuf;
17364                 }
17365                 if (ntflag) {
17366                     tp = dotrans(tp);
17367                 }
17368                 if (mapflag) {
17369                     tp = domap(tp);
17370                 }
17371                 sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0);
17372                 if (!mflag && fromatty) {
17373                     ointer = interactive;
17374                     interactive = 1;
17375                     if (confirm("Continue with","mput")) {
17376                         mflag++;
17377                     }
17378                     interactive = ointer;
17379                 }
17380             }
17381         }
17382         signal(SIGINT, oldintr);
17383         mflag = 0;
17384         return;
17385     }
17386 #endif /* FTP_PROXY */
17387     for (i = 1; i < argc; i++) {
17388         register char **cpp, **gargs;
17389
17390         if (mflag && confirm(argv[0], argv[i])) {
17391             tp = argv[i];
17392             sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0);
17393             if (!mflag && fromatty) {
17394                 ointer = interactive;
17395                 interactive = 1;
17396                 if (confirm("Continue with","mput")) {
17397                     mflag++;
17398                 }
17399                 interactive = ointer;
17400             }
17401         }
17402         continue;
17403
17404         gargs = ftpglob(argv[i]);
17405         if (globerr != NULL) {
17406             printf("%s\n", globerr);
17407             if (gargs) {
17408                 blkfree(gargs);
17409                 free((char *)gargs);
17410             }
17411             continue;
17412         }
17413         for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
17414             if (mflag && confirm(argv[0], *cpp)) {
17415                 tp = *cpp;
17416                 sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0);
17417                 if (!mflag && fromatty) {
17418                     ointer = interactive;
17419                     interactive = 1;
17420                     if (confirm("Continue with","mput")) {
17421                         mflag++;
17422                     }
17423                     interactive = ointer;
17424                 }
17425             }
17426         }
17427         if (gargs != NULL) {
17428             blkfree(gargs);
17429             free((char *)gargs);
17430         }
17431     }
17432     signal(SIGINT, oldintr);
17433     mflag = 0;
17434 }
17435
17436 /* Get multiple files */
17437
17438 static int
17439 ftp_mget(argc, argv) int argc; char **argv; {
17440     int rc = -1;
17441     sig_t oldintr;
17442     int ointer;
17443     char *cp, *tp, *tp2, tmpbuf[CKMAXPATH];
17444     sigtype mcancel();
17445
17446     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17447         printf("usage: %s remote-files\n", argv[0]);
17448         ftpcode = -1;
17449         return(-1);
17450     }
17451     mname = argv[0];
17452     mflag = 1;
17453     oldintr = signal(SIGINT,mcancel);
17454     /* Replace with calls to cc_execute() */
17455     setjmp(jcancel);
17456     while ((cp = remglob(argv,proxy)) != NULL) {
17457         if (*cp == '\0') {
17458             mflag = 0;
17459             continue;
17460         }
17461         if (mflag && confirm(argv[0], cp)) {
17462             tp = cp;
17463             if (mcase) {
17464                 while (*tp && !islower(*tp)) {
17465                     tp++;
17466                 }
17467                 if (!*tp) {
17468                     tp = cp;
17469                     tp2 = tmpbuf;
17470                     while ((*tp2 = *tp) != 0) {
17471                         if (isupper(*tp2)) {
17472                             *tp2 = 'a' + *tp2 - 'A';
17473                         }
17474                         tp++;
17475                         tp2++;
17476                     }
17477                 }
17478                 tp = tmpbuf;
17479             }
17480             rc = (recvrequest("RETR", tp, cp, "wb",
17481                                tp != cp || !interactive) == 0,0,NULL,0,0,0);
17482             if (!mflag && fromatty) {
17483                 ointer = interactive;
17484                 interactive = 1;
17485                 if (confirm("Continue with","mget")) {
17486                     mflag++;
17487                 }
17488                 interactive = ointer;
17489             }
17490         }
17491     }
17492     signal(SIGINT,oldintr);
17493     mflag = 0;
17494     return(rc);
17495 }
17496
17497 /* Delete multiple files */
17498
17499 static int
17500 mdelete(argc, argv) int argc; char **argv; {
17501     sig_t oldintr;
17502     int ointer;
17503     char *cp;
17504     sigtype mcancel();
17505
17506     if (argc < 2 && !another(&argc, &argv, "remote-files")) {
17507         printf("usage: %s remote-files\n", argv[0]);
17508         ftpcode = -1;
17509         return(-1);
17510     }
17511     mname = argv[0];
17512     mflag = 1;
17513     oldintr = signal(SIGINT, mcancel);
17514     /* Replace with calls to cc_execute() */
17515     setjmp(jcancel);
17516     while ((cp = remglob(argv,0)) != NULL) {
17517         if (*cp == '\0') {
17518             mflag = 0;
17519             continue;
17520         }
17521         if (mflag && confirm(argv[0], cp)) {
17522             rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE);
17523             if (!mflag && fromatty) {
17524                 ointer = interactive;
17525                 interactive = 1;
17526                 if (confirm("Continue with", "mdelete")) {
17527                     mflag++;
17528                 }
17529                 interactive = ointer;
17530             }
17531         }
17532     }
17533     signal(SIGINT, oldintr);
17534     mflag = 0;
17535     return(rc);
17536 }
17537
17538 /* Get a directory listing of multiple remote files */
17539
17540 static int
17541 mls(argc, argv) int argc; char **argv; {
17542     sig_t oldintr;
17543     int ointer, i;
17544     char *cmd, mode[1], *dest;
17545     sigtype mcancel();
17546     int rc = -1;
17547
17548     if (argc < 2 && !another(&argc, &argv, "remote-files"))
17549       goto usage;
17550     if (argc < 3 && !another(&argc, &argv, "local-file")) {
17551       usage:
17552         printf("usage: %s remote-files local-file\n", argv[0]);
17553         ftpcode = -1;
17554         return(-1);
17555     }
17556     dest = argv[argc - 1];
17557     argv[argc - 1] = NULL;
17558     if (strcmp(dest, "-") && *dest != '|')
17559       if (!globulize(&dest) ||
17560           !confirm("output to local-file:", dest)) {
17561           ftpcode = -1;
17562           return(-1);
17563       }
17564     cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
17565     mname = argv[0];
17566     mflag = 1;
17567     oldintr = signal(SIGINT, mcancel);
17568     /* Replace with calls to cc_execute() */
17569     setjmp(jcancel);
17570     for (i = 1; mflag && i < argc-1; ++i) {
17571         *mode = (i == 1) ? 'w' : 'a';
17572         rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0);
17573         if (!mflag && fromatty) {
17574             ointer = interactive;
17575             interactive = 1;
17576             if (confirm("Continue with", argv[0])) {
17577                 mflag ++;
17578             }
17579             interactive = ointer;
17580         }
17581     }
17582     signal(SIGINT, oldintr);
17583     mflag = 0;
17584     return(rc);
17585 }
17586
17587 static char *
17588 remglob(argv,doswitch) char *argv[]; int doswitch; {
17589     char temp[16];
17590     static char buf[CKMAXPATH];
17591     static FILE *ftemp = NULL;
17592     static char **args;
17593     int oldhash;
17594     char *cp, *mode;
17595
17596     if (!mflag) {
17597         if (!doglob) {
17598             args = NULL;
17599         } else {
17600             if (ftemp) {
17601                 (void) fclose(ftemp);
17602                 ftemp = NULL;
17603             }
17604         }
17605         return(NULL);
17606     }
17607     if (!doglob) {
17608         if (args == NULL)
17609           args = argv;
17610         if ((cp = *++args) == NULL)
17611           args = NULL;
17612         return(cp);
17613     }
17614     if (ftemp == NULL) {
17615         (void) strcpy(temp, _PATH_TMP);
17616 #ifdef MKTEMP
17617 #ifndef MKSTEMP
17618         (void) mktemp(temp);
17619 #endif /* MKSTEMP */
17620 #endif /* MKTEMP */
17621         verbose = 0;
17622         oldhash = hash, hash = 0;
17623 #ifdef FTP_PROXY
17624         if (doswitch) {
17625             pswitch(!proxy);
17626         }
17627 #endif /* FTP_PROXY */
17628         for (mode = "wb"; *++argv != NULL; mode = "ab")
17629           recvrequest ("NLST", temp, *argv, mode, 0);
17630 #ifdef FTP_PROXY
17631         if (doswitch) {
17632             pswitch(!proxy);
17633         }
17634 #endif /* FTP_PROXY */
17635         hash = oldhash;
17636         ftemp = fopen(temp, "r");
17637         unlink(temp);
17638         if (ftemp == NULL && (!dpyactive || ftp_deb)) {
17639             printf("Can't find list of remote files, oops\n");
17640             return(NULL);
17641         }
17642     }
17643     if (fgets(buf, CKMAXPATH, ftemp) == NULL) {
17644         fclose(ftemp), ftemp = NULL;
17645         return(NULL);
17646     }
17647     if ((cp = ckstrchr(buf,'\n')) != NULL)
17648       *cp = '\0';
17649     return(buf);
17650 }
17651 #endif /* NOT_USED */
17652 #endif /* TCPSOCKET (top of file) */
17653 #endif /* SYSFTP (top of file) */
17654 #endif /* NOFTP (top of file) */