1 /*        $NetBSD: cmds.c,v 1.144 2024/10/04 18:05:43 christos Exp $  */
2 
3 /*-
4  * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Luke Mewburn.
9  *
10  * This code is derived from software contributed to The NetBSD Foundation
11  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12  * NASA Ames Research Center.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /*
37  * Copyright (c) 1985, 1989, 1993, 1994
38  *        The Regents of the University of California.  All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  */
64 
65 /*
66  * Copyright (C) 1997 and 1998 WIDE Project.
67  * All rights reserved.
68  *
69  * Redistribution and use in source and binary forms, with or without
70  * modification, are permitted provided that the following conditions
71  * are met:
72  * 1. Redistributions of source code must retain the above copyright
73  *    notice, this list of conditions and the following disclaimer.
74  * 2. Redistributions in binary form must reproduce the above copyright
75  *    notice, this list of conditions and the following disclaimer in the
76  *    documentation and/or other materials provided with the distribution.
77  * 3. Neither the name of the project nor the names of its contributors
78  *    may be used to endorse or promote products derived from this software
79  *    without specific prior written permission.
80  *
81  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
82  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
83  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
84  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
85  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
86  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
87  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
88  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
89  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
90  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
91  * SUCH DAMAGE.
92  */
93 
94 #include <sys/cdefs.h>
95 #ifndef lint
96 #if 0
97 static char sccsid[] = "@(#)cmds.c      8.6 (Berkeley) 10/9/94";
98 #else
99 __RCSID("$NetBSD: cmds.c,v 1.144 2024/10/04 18:05:43 christos Exp $");
100 #endif
101 #endif /* not lint */
102 
103 /*
104  * FTP User Program -- Command Routines.
105  */
106 #include <sys/types.h>
107 #include <sys/socket.h>
108 #include <sys/stat.h>
109 #include <sys/wait.h>
110 #include <arpa/ftp.h>
111 
112 #include <ctype.h>
113 #include <err.h>
114 #include <errno.h>
115 #include <glob.h>
116 #include <limits.h>
117 #include <netdb.h>
118 #include <paths.h>
119 #include <stddef.h>
120 #include <stdio.h>
121 #include <stdlib.h>
122 #include <string.h>
123 #include <time.h>
124 #include <unistd.h>
125 
126 #include "ftp_var.h"
127 #include "version.h"
128 
129 static struct types {
130           const char          *t_name;
131           const char          *t_mode;
132           int                 t_type;
133           const char          *t_arg;
134 } types[] = {
135           { "ascii",          "A",      TYPE_A,   0 },
136           { "binary",         "I",      TYPE_I,   0 },
137           { "image",          "I",      TYPE_I,   0 },
138           { "ebcdic",         "E",      TYPE_E,   0 },
139           { "tenex",          "L",      TYPE_L,   bytename },
140           { NULL,             NULL,     0, NULL }
141 };
142 
143 static sigjmp_buf    jabort;
144 
145 static int          confirm(const char *, const char *);
146 __dead static void  mintr(int);
147 static void         mabort(const char *);
148 static void         set_type(const char *);
149 
150 static char *doprocess(char *, size_t, char *, int, int, int);
151 static char *domap(char *, size_t, const char *);
152 static char *docase(char *, size_t, const char *);
153 static char *dotrans(char *, size_t, const char *);
154 
155 /*
156  * Confirm if "cmd" is to be performed upon "file".
157  * If "file" is NULL, generate a "Continue with" prompt instead.
158  */
159 static int
confirm(const char * cmd,const char * file)160 confirm(const char *cmd, const char *file)
161 {
162           const char *errormsg;
163           char cline[BUFSIZ];
164           const char *promptleft, *promptright;
165 
166           if (!interactive || confirmrest)
167                     return (1);
168           if (file == NULL) {
169                     promptleft = "Continue with";
170                     promptright = cmd;
171           } else {
172                     promptleft = cmd;
173                     promptright = file;
174           }
175           for (;;) {
176                     fprintf(ttyout, "%s %s [anpqy?]? ", promptleft, promptright);
177                     (void)fflush(ttyout);
178                     if (get_line(stdin, cline, sizeof(cline), &errormsg) < 0) {
179                               mflag = 0;
180                               fprintf(ttyout, "%s; %s aborted\n", errormsg, cmd);
181                               return (0);
182                     }
183                     switch (tolower((unsigned char)*cline)) {
184                               case 'a':
185                                         confirmrest = 1;
186                                         fprintf(ttyout,
187                                             "Prompting off for duration of %s.\n", cmd);
188                                         break;
189                               case 'p':
190                                         interactive = 0;
191                                         fputs("Interactive mode: off.\n", ttyout);
192                                         break;
193                               case 'q':
194                                         mflag = 0;
195                                         fprintf(ttyout, "%s aborted.\n", cmd);
196                                         /* FALLTHROUGH */
197                               case 'n':
198                                         return (0);
199                               case '?':
200                                         fprintf(ttyout,
201                                             "  confirmation options:\n"
202                                             "\ta  answer `yes' for the duration of %s\n"
203                                             "\tn  answer `no' for this file\n"
204                                             "\tp  turn off `prompt' mode\n"
205                                             "\tq  stop the current %s\n"
206                                             "\ty  answer `yes' for this file\n"
207                                             "\t?  this help list\n",
208                                             cmd, cmd);
209                                         continue; /* back to while(1) */
210                     }
211                     return (1);
212           }
213           /* NOTREACHED */
214 }
215 
216 /*
217  * Set transfer type.
218  */
219 void
settype(int argc,char * argv[])220 settype(int argc, char *argv[])
221 {
222           struct types *p;
223 
224           if (argc == 0 || argc > 2) {
225                     const char *sep;
226 
227                     UPRINTF("usage: %s [", argv[0]);
228                     sep = " ";
229                     for (p = types; p->t_name; p++) {
230                               fprintf(ttyout, "%s%s", sep, p->t_name);
231                               sep = " | ";
232                     }
233                     fputs(" ]\n", ttyout);
234                     code = -1;
235                     return;
236           }
237           if (argc < 2) {
238                     fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
239                     code = 0;
240                     return;
241           }
242           set_type(argv[1]);
243 }
244 
245 void
set_type(const char * ttype)246 set_type(const char *ttype)
247 {
248           struct types *p;
249           int comret;
250 
251           for (p = types; p->t_name; p++)
252                     if (strcmp(ttype, p->t_name) == 0)
253                               break;
254           if (p->t_name == 0) {
255                     fprintf(ttyout, "%s: unknown mode.\n", ttype);
256                     code = -1;
257                     return;
258           }
259           if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
260                     comret = command("TYPE %s %s", p->t_mode, p->t_arg);
261           else
262                     comret = command("TYPE %s", p->t_mode);
263           if (comret == COMPLETE) {
264                     (void)strlcpy(typename, p->t_name, sizeof(typename));
265                     curtype = type = p->t_type;
266           }
267 }
268 
269 /*
270  * Internal form of settype; changes current type in use with server
271  * without changing our notion of the type for data transfers.
272  * Used to change to and from ascii for listings.
273  */
274 void
changetype(int newtype,int show)275 changetype(int newtype, int show)
276 {
277           struct types *p;
278           int comret, oldverbose = verbose;
279 
280           if (newtype == 0)
281                     newtype = TYPE_I;
282           if (newtype == curtype)
283                     return;
284           if (ftp_debug == 0 && show == 0)
285                     verbose = 0;
286           for (p = types; p->t_name; p++)
287                     if (newtype == p->t_type)
288                               break;
289           if (p->t_name == 0) {
290                     errx(1, "changetype: unknown type %d", newtype);
291           }
292           if (newtype == TYPE_L && bytename[0] != '\0')
293                     comret = command("TYPE %s %s", p->t_mode, bytename);
294           else
295                     comret = command("TYPE %s", p->t_mode);
296           if (comret == COMPLETE)
297                     curtype = newtype;
298           verbose = oldverbose;
299 }
300 
301 /*
302  * Set binary transfer type.
303  */
304 /*VARARGS*/
305 void
setbinary(int argc,char * argv[])306 setbinary(int argc, char *argv[])
307 {
308 
309           if (argc == 0) {
310                     UPRINTF("usage: %s\n", argv[0]);
311                     code = -1;
312                     return;
313           }
314           set_type("binary");
315 }
316 
317 /*
318  * Set ascii transfer type.
319  */
320 /*VARARGS*/
321 void
setascii(int argc,char * argv[])322 setascii(int argc, char *argv[])
323 {
324 
325           if (argc == 0) {
326                     UPRINTF("usage: %s\n", argv[0]);
327                     code = -1;
328                     return;
329           }
330           set_type("ascii");
331 }
332 
333 /*
334  * Set tenex transfer type.
335  */
336 /*VARARGS*/
337 void
settenex(int argc,char * argv[])338 settenex(int argc, char *argv[])
339 {
340 
341           if (argc == 0) {
342                     UPRINTF("usage: %s\n", argv[0]);
343                     code = -1;
344                     return;
345           }
346           set_type("tenex");
347 }
348 
349 /*
350  * Set file transfer mode.
351  */
352 /*ARGSUSED*/
353 void
setftmode(int argc,char * argv[])354 setftmode(int argc, char *argv[])
355 {
356 
357           if (argc != 2) {
358                     UPRINTF("usage: %s mode-name\n", argv[0]);
359                     code = -1;
360                     return;
361           }
362           fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
363           code = -1;
364 }
365 
366 /*
367  * Set file transfer format.
368  */
369 /*ARGSUSED*/
370 void
setform(int argc,char * argv[])371 setform(int argc, char *argv[])
372 {
373 
374           if (argc != 2) {
375                     UPRINTF("usage: %s format\n", argv[0]);
376                     code = -1;
377                     return;
378           }
379           fprintf(ttyout, "We only support %s format, sorry.\n", formname);
380           code = -1;
381 }
382 
383 /*
384  * Set file transfer structure.
385  */
386 /*ARGSUSED*/
387 void
setstruct(int argc,char * argv[])388 setstruct(int argc, char *argv[])
389 {
390 
391           if (argc != 2) {
392                     UPRINTF("usage: %s struct-mode\n", argv[0]);
393                     code = -1;
394                     return;
395           }
396           fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
397           code = -1;
398 }
399 
400 /*
401  * Send a single file.
402  */
403 void
put(int argc,char * argv[])404 put(int argc, char *argv[])
405 {
406           char buf[MAXPATHLEN];
407           const char *cmd;
408           int loc = 0;
409           char *locfile;
410           char *remfile;
411 
412           if (argc == 2) {
413                     argc++;
414                     argv[2] = argv[1];
415                     loc++;
416           }
417           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file")))
418                     goto usage;
419           if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
420  usage:
421                     UPRINTF("usage: %s local-file [remote-file]\n", argv[0]);
422                     code = -1;
423                     return;
424           }
425           if ((locfile = globulize(argv[1])) == NULL) {
426                     code = -1;
427                     return;
428           }
429           remfile = argv[2];
430           if (loc)  /* If argv[2] is a copy of the old argv[1], update it */
431                     remfile = locfile;
432           cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
433           remfile = doprocess(buf, sizeof(buf), remfile,
434                     0, loc && ntflag, loc && mapflag);
435           sendrequest(cmd, locfile, remfile,
436               locfile != argv[1] || remfile != argv[2]);
437           free(locfile);
438 }
439 
440 static char *
doprocess(char * dst,size_t dlen,char * src,int casef,int transf,int mapf)441 doprocess(char *dst, size_t dlen, char *src,
442     int casef, int transf, int mapf)
443 {
444           if (casef)
445                     src = docase(dst, dlen, src);
446           if (transf)
447                     src = dotrans(dst, dlen, src);
448           if (mapf)
449                     src = domap(dst, dlen, src);
450           return src;
451 }
452 
453 /*
454  * Send multiple files.
455  */
456 void
mput(int argc,char * argv[])457 mput(int argc, char *argv[])
458 {
459           int i;
460           sigfunc oldintr;
461           int ointer;
462           const char *tp;
463 
464           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) {
465                     UPRINTF("usage: %s local-files\n", argv[0]);
466                     code = -1;
467                     return;
468           }
469           mflag = 1;
470           oldintr = xsignal(SIGINT, mintr);
471           if (sigsetjmp(jabort, 1))
472                     mabort(argv[0]);
473           if (proxy) {
474                     char *cp;
475 
476                     while ((cp = remglob(argv, 0, NULL)) != NULL) {
477                               if (*cp == '\0' || !connected) {
478                                         mflag = 0;
479                                         continue;
480                               }
481                               if (mflag && confirm(argv[0], cp)) {
482                                         char buf[MAXPATHLEN];
483                                         tp = doprocess(buf, sizeof(buf), cp,
484                                             mcase, ntflag, mapflag);
485                                         sendrequest((sunique) ? "STOU" : "STOR",
486                                             cp, tp, cp != tp || !interactive);
487                                         if (!mflag && fromatty) {
488                                                   ointer = interactive;
489                                                   interactive = 1;
490                                                   if (confirm(argv[0], NULL)) {
491                                                             mflag++;
492                                                   }
493                                                   interactive = ointer;
494                                         }
495                               }
496                     }
497                     goto cleanupmput;
498           }
499           for (i = 1; i < argc && connected; i++) {
500                     char **cpp;
501                     glob_t gl;
502                     int flags;
503 
504                     if (!doglob) {
505                               if (mflag && confirm(argv[0], argv[i])) {
506                                         char buf[MAXPATHLEN];
507                                         tp = doprocess(buf, sizeof(buf), argv[i],
508                                                   0, ntflag, mapflag);
509                                         sendrequest((sunique) ? "STOU" : "STOR",
510                                             argv[i], tp, tp != argv[i] || !interactive);
511                                         if (!mflag && fromatty) {
512                                                   ointer = interactive;
513                                                   interactive = 1;
514                                                   if (confirm(argv[0], NULL)) {
515                                                             mflag++;
516                                                   }
517                                                   interactive = ointer;
518                                         }
519                               }
520                               continue;
521                     }
522 
523                     memset(&gl, 0, sizeof(gl));
524                     flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
525                     if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
526                               warnx("Glob pattern `%s' not found", argv[i]);
527                               globfree(&gl);
528                               continue;
529                     }
530                     for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected;
531                         cpp++) {
532                               if (mflag && confirm(argv[0], *cpp)) {
533                                         char buf[MAXPATHLEN];
534                                         tp = *cpp;
535                                         tp = doprocess(buf, sizeof(buf), *cpp,
536                                             0, ntflag, mapflag);
537                                         sendrequest((sunique) ? "STOU" : "STOR",
538                                             *cpp, tp, *cpp != tp || !interactive);
539                                         if (!mflag && fromatty) {
540                                                   ointer = interactive;
541                                                   interactive = 1;
542                                                   if (confirm(argv[0], NULL)) {
543                                                             mflag++;
544                                                   }
545                                                   interactive = ointer;
546                                         }
547                               }
548                     }
549                     globfree(&gl);
550           }
551  cleanupmput:
552           (void)xsignal(SIGINT, oldintr);
553           mflag = 0;
554 }
555 
556 void
reget(int argc,char * argv[])557 reget(int argc, char *argv[])
558 {
559 
560           (void)getit(argc, argv, 1, restart_point ? "r+" : "a");
561 }
562 
563 void
get(int argc,char * argv[])564 get(int argc, char *argv[])
565 {
566 
567           (void)getit(argc, argv, 0, restart_point ? "r+" : "w");
568 }
569 
570 /*
571  * Receive one file.
572  * If restartit is  1, restart the xfer always.
573  * If restartit is -1, restart the xfer only if the remote file is newer.
574  */
575 int
getit(int argc,char * argv[],int restartit,const char * gmode)576 getit(int argc, char *argv[], int restartit, const char *gmode)
577 {
578           int       loc, rval;
579           char      *remfile, *olocfile, *locfile;
580           char      buf[MAXPATHLEN];
581 
582           loc = rval = 0;
583           if (argc == 2) {
584                     argc++;
585                     argv[2] = argv[1];
586                     loc++;
587           }
588           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file")))
589                     goto usage;
590           if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
591  usage:
592                     UPRINTF("usage: %s remote-file [local-file]\n", argv[0]);
593                     code = -1;
594                     return (0);
595           }
596           remfile = argv[1];
597           if ((olocfile = globulize(argv[2])) == NULL) {
598                     code = -1;
599                     return (0);
600           }
601           locfile = doprocess(buf, sizeof(buf), olocfile,
602                     loc && mcase, loc && ntflag, loc && mapflag);
603           if (restartit) {
604                     struct stat stbuf;
605                     int ret;
606 
607                     if (! features[FEAT_REST_STREAM]) {
608                               fprintf(ttyout,
609                                   "Restart is not supported by the remote server.\n");
610                               return (0);
611                     }
612                     ret = stat(locfile, &stbuf);
613                     if (restartit == 1) {
614                               if (ret < 0) {
615                                         if (errno != ENOENT) {
616                                                   warn("Can't stat `%s'", locfile);
617                                                   goto freegetit;
618                                         }
619                                         restart_point = 0;
620                               }
621                               else
622                                         restart_point = stbuf.st_size;
623                     } else {
624                               if (ret == 0) {
625                                         time_t mtime;
626 
627                                         mtime = remotemodtime(argv[1], 0);
628                                         if (mtime == -1)
629                                                   goto freegetit;
630                                         if (stbuf.st_mtime >= mtime) {
631                                                   rval = 1;
632                                                   goto freegetit;
633                                         }
634                               }
635                     }
636           }
637 
638           recvrequest("RETR", locfile, remfile, gmode,
639               remfile != argv[1] || locfile != argv[2], loc);
640           restart_point = 0;
641  freegetit:
642           (void)free(olocfile);
643           return (rval);
644 }
645 
646 /* ARGSUSED */
647 static void
mintr(int signo)648 mintr(int signo)
649 {
650 
651           alarmtimer(0);
652           if (fromatty)
653                     write(fileno(ttyout), "\n", 1);
654           siglongjmp(jabort, 1);
655 }
656 
657 static void
mabort(const char * cmd)658 mabort(const char *cmd)
659 {
660           int ointer, oconf;
661 
662           if (mflag && fromatty) {
663                     ointer = interactive;
664                     oconf = confirmrest;
665                     interactive = 1;
666                     confirmrest = 0;
667                     if (confirm(cmd, NULL)) {
668                               interactive = ointer;
669                               confirmrest = oconf;
670                               return;
671                     }
672                     interactive = ointer;
673                     confirmrest = oconf;
674           }
675           mflag = 0;
676 }
677 
678 /*
679  * Get multiple files.
680  */
681 void
mget(int argc,char * argv[])682 mget(int argc, char *argv[])
683 {
684           sigfunc oldintr;
685           int ointer;
686           char *cp, *tp;
687           int volatile restartit;
688 
689           if (argc == 0 ||
690               (argc == 1 && !another(&argc, &argv, "remote-files"))) {
691                     UPRINTF("usage: %s remote-files\n", argv[0]);
692                     code = -1;
693                     return;
694           }
695           mflag = 1;
696           restart_point = 0;
697           restartit = 0;
698           if (strcmp(argv[0], "mreget") == 0) {
699                     if (! features[FEAT_REST_STREAM]) {
700                               fprintf(ttyout,
701                         "Restart is not supported by the remote server.\n");
702                               return;
703                     }
704                     restartit = 1;
705           }
706           oldintr = xsignal(SIGINT, mintr);
707           if (sigsetjmp(jabort, 1))
708                     mabort(argv[0]);
709           while ((cp = remglob(argv, proxy, NULL)) != NULL) {
710                     char buf[MAXPATHLEN];
711                     if (*cp == '\0' || !connected) {
712                               mflag = 0;
713                               continue;
714                     }
715                     if (! mflag)
716                               continue;
717                     if (! fileindir(cp, localcwd)) {
718                               fprintf(ttyout, "Skipping non-relative filename `%s'\n",
719                                   cp);
720                               continue;
721                     }
722                     if (!confirm(argv[0], cp))
723                               continue;
724                     tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag);
725                     if (restartit) {
726                               struct stat stbuf;
727 
728                               if (stat(tp, &stbuf) == 0)
729                                         restart_point = stbuf.st_size;
730                               else
731                                         warn("Can't stat `%s'", tp);
732                     }
733                     recvrequest("RETR", tp, cp, restart_point ? "r+" : "w",
734                         tp != cp || !interactive, 1);
735                     restart_point = 0;
736                     if (!mflag && fromatty) {
737                               ointer = interactive;
738                               interactive = 1;
739                               if (confirm(argv[0], NULL))
740                                         mflag++;
741                               interactive = ointer;
742                     }
743           }
744           (void)xsignal(SIGINT, oldintr);
745           mflag = 0;
746 }
747 
748 /*
749  * Read list of filenames from a local file and get those
750  */
751 void
fget(int argc,char * argv[])752 fget(int argc, char *argv[])
753 {
754           const char *gmode;
755           FILE      *fp;
756           char      buf[MAXPATHLEN], cmdbuf[MAX_C_NAME];
757 
758           if (argc != 2) {
759                     UPRINTF("usage: %s localfile\n", argv[0]);
760                     code = -1;
761                     return;
762           }
763 
764           fp = fopen(argv[1], "r");
765           if (fp == NULL) {
766                     fprintf(ttyout, "Can't open source file %s\n", argv[1]);
767                     code = -1;
768                     return;
769           }
770 
771           (void)strlcpy(cmdbuf, "get", sizeof(cmdbuf));
772           argv[0] = cmdbuf;
773           gmode = restart_point ? "r+" : "w";
774 
775           while (get_line(fp, buf, sizeof(buf), NULL) >= 0) {
776                     if (buf[0] == '\0')
777                               continue;
778                     argv[1] = buf;
779                     (void)getit(argc, argv, 0, gmode);
780           }
781           fclose(fp);
782 }
783 
784 const char *
onoff(int val)785 onoff(int val)
786 {
787 
788           return (val ? "on" : "off");
789 }
790 
791 /*
792  * Show status.
793  */
794 /*ARGSUSED*/
795 void
status(int argc,char * argv[])796 status(int argc, char *argv[])
797 {
798 
799           if (argc == 0) {
800                     UPRINTF("usage: %s\n", argv[0]);
801                     code = -1;
802                     return;
803           }
804 #ifndef NO_STATUS
805           if (connected)
806                     fprintf(ttyout, "Connected %sto %s.\n",
807                         connected == -1 ? "and logged in" : "", hostname);
808           else
809                     fputs("Not connected.\n", ttyout);
810           if (!proxy) {
811                     pswitch(1);
812                     if (connected) {
813                               fprintf(ttyout, "Connected for proxy commands to %s.\n",
814                                   hostname);
815                     }
816                     else {
817                               fputs("No proxy connection.\n", ttyout);
818                     }
819                     pswitch(0);
820           }
821           fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
822               *gateserver ? gateserver : "(none)", gateport);
823           fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
824               onoff(passivemode), onoff(activefallback));
825           fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
826               modename, typename, formname, structname);
827           fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
828               onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
829           fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
830               onoff(sunique), onoff(runique));
831           fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
832           fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
833               onoff(crflag));
834           if (ntflag) {
835                     fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
836           }
837           else {
838                     fputs("Ntrans: off.\n", ttyout);
839           }
840           if (mapflag) {
841                     fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
842           }
843           else {
844                     fputs("Nmap: off.\n", ttyout);
845           }
846           fprintf(ttyout,
847               "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
848               onoff(hash), mark, onoff(progress));
849           fprintf(ttyout,
850               "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
851               onoff(rate_get), rate_get, rate_get_incr);
852           fprintf(ttyout,
853               "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
854               onoff(rate_put), rate_put, rate_put_incr);
855           fprintf(ttyout,
856               "Socket buffer sizes: send %d, receive %d.\n",
857               sndbuf_size, rcvbuf_size);
858           fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
859           fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
860               epsv4bad ? " (disabled for this connection)" : "");
861           fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv6: %s%s.\n", onoff(epsv6),
862               epsv6bad ? " (disabled for this connection)" : "");
863           fprintf(ttyout, "Command line editing: %s.\n",
864 #ifdef NO_EDITCOMPLETE
865               "support not compiled in"
866 #else     /* !def NO_EDITCOMPLETE */
867               onoff(editing)
868 #endif    /* !def NO_EDITCOMPLETE */
869               );
870           if (macnum > 0) {
871                     int i;
872 
873                     fputs("Macros:\n", ttyout);
874                     for (i=0; i<macnum; i++) {
875                               fprintf(ttyout, "\t%s\n", macros[i].mac_name);
876                     }
877           }
878 #endif /* !def NO_STATUS */
879           fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
880           code = 0;
881 }
882 
883 /*
884  * Toggle a variable
885  */
886 int
togglevar(int argc,char * argv[],int * var,const char * mesg)887 togglevar(int argc, char *argv[], int *var, const char *mesg)
888 {
889           if (argc == 1) {
890                     *var = !*var;
891           } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
892                     *var = 1;
893           } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
894                     *var = 0;
895           } else {
896                     UPRINTF("usage: %s [ on | off ]\n", argv[0]);
897                     return (-1);
898           }
899           if (mesg)
900                     fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
901           return (*var);
902 }
903 
904 /*
905  * Set beep on cmd completed mode.
906  */
907 /*VARARGS*/
908 void
setbell(int argc,char * argv[])909 setbell(int argc, char *argv[])
910 {
911 
912           code = togglevar(argc, argv, &bell, "Bell mode");
913 }
914 
915 /*
916  * Set command line editing
917  */
918 /*VARARGS*/
919 void
setedit(int argc,char * argv[])920 setedit(int argc, char *argv[])
921 {
922 
923 #ifdef NO_EDITCOMPLETE
924           if (argc == 0) {
925                     UPRINTF("usage: %s\n", argv[0]);
926                     code = -1;
927                     return;
928           }
929           if (verbose)
930                     fputs("Editing support not compiled in; ignoring command.\n",
931                         ttyout);
932 #else     /* !def NO_EDITCOMPLETE */
933           code = togglevar(argc, argv, &editing, "Editing mode");
934           controlediting();
935 #endif    /* !def NO_EDITCOMPLETE */
936 }
937 
938 /*
939  * Turn on packet tracing.
940  */
941 /*VARARGS*/
942 void
settrace(int argc,char * argv[])943 settrace(int argc, char *argv[])
944 {
945 
946           code = togglevar(argc, argv, &trace, "Packet tracing");
947 }
948 
949 /*
950  * Toggle hash mark printing during transfers, or set hash mark bytecount.
951  */
952 /*VARARGS*/
953 void
sethash(int argc,char * argv[])954 sethash(int argc, char *argv[])
955 {
956           if (argc == 1)
957                     hash = !hash;
958           else if (argc != 2) {
959                     UPRINTF("usage: %s [ on | off | bytecount ]\n",
960                         argv[0]);
961                     code = -1;
962                     return;
963           } else if (strcasecmp(argv[1], "on") == 0)
964                     hash = 1;
965           else if (strcasecmp(argv[1], "off") == 0)
966                     hash = 0;
967           else {
968                     int nmark;
969 
970                     nmark = strsuftoi(argv[1]);
971                     if (nmark < 1) {
972                               fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
973                                   argv[1]);
974                               code = -1;
975                               return;
976                     }
977                     mark = nmark;
978                     hash = 1;
979           }
980           fprintf(ttyout, "Hash mark printing %s", onoff(hash));
981           if (hash)
982                     fprintf(ttyout, " (%d bytes/hash mark)", mark);
983           fputs(".\n", ttyout);
984           if (hash)
985                     progress = 0;
986           code = hash;
987 }
988 
989 /*
990  * Turn on printing of server echo's.
991  */
992 /*VARARGS*/
993 void
setverbose(int argc,char * argv[])994 setverbose(int argc, char *argv[])
995 {
996 
997           code = togglevar(argc, argv, &verbose, "Verbose mode");
998 }
999 
1000 /*
1001  * Toggle PORT/LPRT cmd use before each data connection.
1002  */
1003 /*VARARGS*/
1004 void
setport(int argc,char * argv[])1005 setport(int argc, char *argv[])
1006 {
1007 
1008           code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
1009 }
1010 
1011 /*
1012  * Toggle transfer progress bar.
1013  */
1014 /*VARARGS*/
1015 void
setprogress(int argc,char * argv[])1016 setprogress(int argc, char *argv[])
1017 {
1018 
1019           code = togglevar(argc, argv, &progress, "Progress bar");
1020           if (progress)
1021                     hash = 0;
1022 }
1023 
1024 /*
1025  * Turn on interactive prompting during mget, mput, and mdelete.
1026  */
1027 /*VARARGS*/
1028 void
setprompt(int argc,char * argv[])1029 setprompt(int argc, char *argv[])
1030 {
1031 
1032           code = togglevar(argc, argv, &interactive, "Interactive mode");
1033 }
1034 
1035 /*
1036  * Toggle gate-ftp mode, or set gate-ftp server
1037  */
1038 /*VARARGS*/
1039 void
setgate(int argc,char * argv[])1040 setgate(int argc, char *argv[])
1041 {
1042           static char gsbuf[MAXHOSTNAMELEN];
1043 
1044           if (argc == 0 || argc > 3) {
1045                     UPRINTF(
1046                         "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
1047                     code = -1;
1048                     return;
1049           } else if (argc < 2) {
1050                     gatemode = !gatemode;
1051           } else {
1052                     if (argc == 2 && strcasecmp(argv[1], "on") == 0)
1053                               gatemode = 1;
1054                     else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
1055                               gatemode = 0;
1056                     else {
1057                               if (argc == 3)
1058                                         gateport = ftp_strdup(argv[2]);
1059                               (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
1060                               gateserver = gsbuf;
1061                               gatemode = 1;
1062                     }
1063           }
1064           if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
1065                     fprintf(ttyout,
1066                         "Disabling gate-ftp mode - no gate-ftp server defined.\n");
1067                     gatemode = 0;
1068           } else {
1069                     fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
1070                         onoff(gatemode), *gateserver ? gateserver : "(none)",
1071                         gateport);
1072           }
1073           code = gatemode;
1074 }
1075 
1076 /*
1077  * Toggle metacharacter interpretation on local file names.
1078  */
1079 /*VARARGS*/
1080 void
setglob(int argc,char * argv[])1081 setglob(int argc, char *argv[])
1082 {
1083 
1084           code = togglevar(argc, argv, &doglob, "Globbing");
1085 }
1086 
1087 /*
1088  * Toggle preserving modification times on retrieved files.
1089  */
1090 /*VARARGS*/
1091 void
setpreserve(int argc,char * argv[])1092 setpreserve(int argc, char *argv[])
1093 {
1094 
1095           code = togglevar(argc, argv, &preserve, "Preserve modification times");
1096 }
1097 
1098 /*
1099  * Set debugging mode on/off and/or set level of debugging.
1100  */
1101 /*VARARGS*/
1102 void
setdebug(int argc,char * argv[])1103 setdebug(int argc, char *argv[])
1104 {
1105           if (argc == 0 || argc > 2) {
1106                     UPRINTF("usage: %s [ on | off | debuglevel ]\n", argv[0]);
1107                     code = -1;
1108                     return;
1109           } else if (argc == 2) {
1110                     if (strcasecmp(argv[1], "on") == 0)
1111                               ftp_debug = 1;
1112                     else if (strcasecmp(argv[1], "off") == 0)
1113                               ftp_debug = 0;
1114                     else {
1115                               int val;
1116 
1117                               val = strsuftoi(argv[1]);
1118                               if (val < 0) {
1119                                         fprintf(ttyout, "%s: bad debugging value.\n",
1120                                             argv[1]);
1121                                         code = -1;
1122                                         return;
1123                               }
1124                               ftp_debug = val;
1125                     }
1126           } else
1127                     ftp_debug = !ftp_debug;
1128           if (ftp_debug)
1129                     options |= SO_DEBUG;
1130           else
1131                     options &= ~SO_DEBUG;
1132           fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(ftp_debug), ftp_debug);
1133           code = ftp_debug > 0;
1134 }
1135 
1136 /*
1137  * Set current working directory on remote machine.
1138  */
1139 void
cd(int argc,char * argv[])1140 cd(int argc, char *argv[])
1141 {
1142           int r;
1143 
1144           if (argc == 0 || argc > 2 ||
1145               (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1146                     UPRINTF("usage: %s remote-directory\n", argv[0]);
1147                     code = -1;
1148                     return;
1149           }
1150           r = command("CWD %s", argv[1]);
1151           if (r == ERROR && code == 500) {
1152                     if (verbose)
1153                               fputs("CWD command not recognized, trying XCWD.\n",
1154                                   ttyout);
1155                     r = command("XCWD %s", argv[1]);
1156           }
1157           if (r == COMPLETE) {
1158                     dirchange = 1;
1159                     remotecwd[0] = '\0';
1160                     remcwdvalid = 0;
1161           }
1162 }
1163 
1164 /*
1165  * Set current working directory on local machine.
1166  */
1167 void
lcd(int argc,char * argv[])1168 lcd(int argc, char *argv[])
1169 {
1170           char *locdir;
1171 
1172           code = -1;
1173           if (argc == 1) {
1174                     argc++;
1175                     argv[1] = localhome;
1176           }
1177           if (argc != 2) {
1178                     UPRINTF("usage: %s [local-directory]\n", argv[0]);
1179                     return;
1180           }
1181           if ((locdir = globulize(argv[1])) == NULL)
1182                     return;
1183           if (chdir(locdir) == -1)
1184                     warn("Can't chdir `%s'", locdir);
1185           else {
1186                     updatelocalcwd();
1187                     if (localcwd[0]) {
1188                               fprintf(ttyout, "Local directory now: %s\n", localcwd);
1189                               code = 0;
1190                     } else {
1191                               fprintf(ttyout, "Unable to determine local directory\n");
1192                     }
1193           }
1194           (void)free(locdir);
1195 }
1196 
1197 /*
1198  * Delete a single file.
1199  */
1200 void
delete(int argc,char * argv[])1201 delete(int argc, char *argv[])
1202 {
1203 
1204           if (argc == 0 || argc > 2 ||
1205               (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1206                     UPRINTF("usage: %s remote-file\n", argv[0]);
1207                     code = -1;
1208                     return;
1209           }
1210           if (command("DELE %s", argv[1]) == COMPLETE)
1211                     dirchange = 1;
1212 }
1213 
1214 /*
1215  * Delete multiple files.
1216  */
1217 void
mdelete(int argc,char * argv[])1218 mdelete(int argc, char *argv[])
1219 {
1220           sigfunc oldintr;
1221           int ointer;
1222           char *cp;
1223 
1224           if (argc == 0 ||
1225               (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1226                     UPRINTF("usage: %s [remote-files]\n", argv[0]);
1227                     code = -1;
1228                     return;
1229           }
1230           mflag = 1;
1231           oldintr = xsignal(SIGINT, mintr);
1232           if (sigsetjmp(jabort, 1))
1233                     mabort(argv[0]);
1234           while ((cp = remglob(argv, 0, NULL)) != NULL) {
1235                     if (*cp == '\0') {
1236                               mflag = 0;
1237                               continue;
1238                     }
1239                     if (mflag && confirm(argv[0], cp)) {
1240                               if (command("DELE %s", cp) == COMPLETE)
1241                                         dirchange = 1;
1242                               if (!mflag && fromatty) {
1243                                         ointer = interactive;
1244                                         interactive = 1;
1245                                         if (confirm(argv[0], NULL)) {
1246                                                   mflag++;
1247                                         }
1248                                         interactive = ointer;
1249                               }
1250                     }
1251           }
1252           (void)xsignal(SIGINT, oldintr);
1253           mflag = 0;
1254 }
1255 
1256 /*
1257  * Rename a remote file.
1258  */
1259 void
renamefile(int argc,char * argv[])1260 renamefile(int argc, char *argv[])
1261 {
1262 
1263           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
1264                     goto usage;
1265           if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1266  usage:
1267                     UPRINTF("usage: %s from-name to-name\n", argv[0]);
1268                     code = -1;
1269                     return;
1270           }
1271           if (command("RNFR %s", argv[1]) == CONTINUE &&
1272               command("RNTO %s", argv[2]) == COMPLETE)
1273                     dirchange = 1;
1274 }
1275 
1276 /*
1277  * Get a directory listing of remote files.
1278  * Supports being invoked as:
1279  *        cmd                 runs
1280  *        ---                 ----
1281  *        dir, ls             LIST
1282  *        mlsd                MLSD
1283  *        nlist               NLST
1284  *        pdir, pls LIST |$PAGER
1285  *        pmlsd               MLSD |$PAGER
1286  */
1287 void
ls(int argc,char * argv[])1288 ls(int argc, char *argv[])
1289 {
1290           const char *cmd;
1291           char *remdir, *locbuf, *locfile;
1292           int pagecmd, mlsdcmd;
1293 
1294           remdir = NULL;
1295           locbuf = NULL;
1296           pagecmd = mlsdcmd = 0;
1297                               /*
1298                                * the only commands that start with `p' are
1299                                * the `pager' versions.
1300                                */
1301           if (argv[0][0] == 'p')
1302                     pagecmd = 1;
1303           if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
1304                     if (! features[FEAT_MLST]) {
1305                               fprintf(ttyout,
1306                                  "MLSD is not supported by the remote server.\n");
1307                               return;
1308                     }
1309                     mlsdcmd = 1;
1310           }
1311           if (argc == 0)
1312                     goto usage;
1313 
1314           if (mlsdcmd)
1315                     cmd = "MLSD";
1316           else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
1317                     cmd = "NLST";
1318           else
1319                     cmd = "LIST";
1320 
1321           if (argc > 1)
1322                     remdir = argv[1];
1323           if (argc > 2)
1324                     locfile = argv[2];
1325           else
1326                     locfile = locbuf = ftp_strdup("-");
1327           if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
1328  usage:
1329                     if (pagecmd || mlsdcmd)
1330                               UPRINTF("usage: %s [remote-path]\n", argv[0]);
1331                     else
1332                               UPRINTF("usage: %s [remote-path [local-file]]\n",
1333                                   argv[0]);
1334                     code = -1;
1335                     goto freels;
1336           }
1337 
1338           if (pagecmd) {
1339                     const char *p;
1340                     size_t len;
1341 
1342                     p = getoptionvalue("pager");
1343                     if (EMPTYSTRING(p))
1344                               p = DEFAULTPAGER;
1345                     len = strlen(p) + 2;
1346                     free(locbuf);
1347                     locbuf = ftp_malloc(len);
1348                     locbuf[0] = '|';
1349                     (void)strlcpy(locbuf + 1, p, len - 1);
1350                     locfile = locbuf;
1351           } else if (locfile != locbuf) {
1352                     if ((locbuf = globulize(locfile)) == NULL ||
1353                         !confirm("output to local-file:", locbuf)) {
1354                               code = -1;
1355                               goto freels;
1356                     }
1357                     locfile = locbuf;
1358           }
1359           recvrequest(cmd, locfile, remdir, "w", 0, 0);
1360  freels:
1361           if (locbuf)
1362                     (void)free(locbuf);
1363 }
1364 
1365 /*
1366  * Get a directory listing of multiple remote files.
1367  */
1368 void
mls(int argc,char * argv[])1369 mls(int argc, char *argv[])
1370 {
1371           sigfunc oldintr;
1372           int ointer, i;
1373           int volatile dolist;
1374           char * volatile dest, *odest;
1375           const char *lmode;
1376 
1377           if (argc == 0)
1378                     goto usage;
1379           if (argc < 2 && !another(&argc, &argv, "remote-files"))
1380                     goto usage;
1381           if (argc < 3 && !another(&argc, &argv, "local-file")) {
1382  usage:
1383                     UPRINTF("usage: %s remote-files local-file\n", argv[0]);
1384                     code = -1;
1385                     return;
1386           }
1387           odest = dest = argv[argc - 1];
1388           argv[argc - 1] = NULL;
1389           if (strcmp(dest, "-") && *dest != '|')
1390                     if (((dest = globulize(dest)) == NULL) ||
1391                         !confirm("output to local-file:", dest)) {
1392                               code = -1;
1393                               return;
1394           }
1395           dolist = strcmp(argv[0], "mls");
1396           mflag = 1;
1397           oldintr = xsignal(SIGINT, mintr);
1398           if (sigsetjmp(jabort, 1))
1399                     mabort(argv[0]);
1400           for (i = 1; mflag && i < argc-1 && connected; i++) {
1401                     lmode = (i == 1) ? "w" : "a";
1402                     recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], lmode,
1403                         0, 0);
1404                     if (!mflag && fromatty) {
1405                               ointer = interactive;
1406                               interactive = 1;
1407                               if (confirm(argv[0], NULL)) {
1408                                         mflag++;
1409                               }
1410                               interactive = ointer;
1411                     }
1412           }
1413           (void)xsignal(SIGINT, oldintr);
1414           mflag = 0;
1415           if (dest != odest)                      /* free up after globulize() */
1416                     free(dest);
1417 }
1418 
1419 /*
1420  * Do a shell escape
1421  */
1422 /*ARGSUSED*/
1423 void
shell(int argc,char * argv[])1424 shell(int argc, char *argv[])
1425 {
1426           pid_t pid;
1427           sigfunc oldintr;
1428           char shellnam[MAXPATHLEN];
1429           const char *shellp, *namep;
1430           int wait_status;
1431 
1432           if (argc == 0) {
1433                     UPRINTF("usage: %s [command [args]]\n", argv[0]);
1434                     code = -1;
1435                     return;
1436           }
1437           oldintr = xsignal(SIGINT, SIG_IGN);
1438           if ((pid = fork()) == 0) {
1439                     for (pid = 3; pid < 20; pid++)
1440                               (void)close(pid);
1441                     (void)xsignal(SIGINT, SIG_DFL);
1442                     shellp = getenv("SHELL");
1443                     if (shellp == NULL)
1444                               shellp = _PATH_BSHELL;
1445                     namep = strrchr(shellp, '/');
1446                     if (namep == NULL)
1447                               namep = shellp;
1448                     else
1449                               namep++;
1450                     (void)strlcpy(shellnam, namep, sizeof(shellnam));
1451                     if (ftp_debug) {
1452                               fputs(shellp, ttyout);
1453                               putc('\n', ttyout);
1454                     }
1455                     if (argc > 1) {
1456                               execl(shellp, shellnam, "-c", altarg, (char *)0);
1457                     }
1458                     else {
1459                               execl(shellp, shellnam, (char *)0);
1460                     }
1461                     warn("Can't execute `%s'", shellp);
1462                     code = -1;
1463                     exit(1);
1464           }
1465           if (pid > 0)
1466                     while (wait(&wait_status) != pid)
1467                               ;
1468           (void)xsignal(SIGINT, oldintr);
1469           if (pid == -1) {
1470                     warn("Can't fork a subshell; try again later");
1471                     code = -1;
1472           } else
1473                     code = 0;
1474 }
1475 
1476 /*
1477  * Send new user information (re-login)
1478  */
1479 void
user(int argc,char * argv[])1480 user(int argc, char *argv[])
1481 {
1482           char *password;
1483           char emptypass[] = "";
1484           int n, aflag = 0;
1485 
1486           if (argc == 0)
1487                     goto usage;
1488           if (argc < 2)
1489                     (void)another(&argc, &argv, "username");
1490           if (argc < 2 || argc > 4) {
1491  usage:
1492                     UPRINTF("usage: %s username [password [account]]\n",
1493                         argv[0]);
1494                     code = -1;
1495                     return;
1496           }
1497           n = command("USER %s", argv[1]);
1498           if (n == CONTINUE) {
1499                     if (argc < 3) {
1500                               password = getpass("Password: ");
1501                               if (password == NULL)
1502                                         password = emptypass;
1503                     } else {
1504                               password = argv[2];
1505                     }
1506                     n = command("PASS %s", password);
1507                     memset(password, 0, strlen(password));
1508           }
1509           if (n == CONTINUE) {
1510                     aflag++;
1511                     if (argc < 4) {
1512                               password = getpass("Account: ");
1513                               if (password == NULL)
1514                                         password = emptypass;
1515                     } else {
1516                               password = argv[3];
1517                     }
1518                     n = command("ACCT %s", password);
1519                     memset(password, 0, strlen(password));
1520           }
1521           if (n != COMPLETE) {
1522                     fputs("Login failed.\n", ttyout);
1523                     return;
1524           }
1525           if (!aflag && argc == 4) {
1526                     password = argv[3];
1527                     (void)command("ACCT %s", password);
1528                     memset(password, 0, strlen(password));
1529           }
1530           connected = -1;
1531           getremoteinfo();
1532 }
1533 
1534 /*
1535  * Print working directory on remote machine.
1536  */
1537 /*VARARGS*/
1538 void
pwd(int argc,char * argv[])1539 pwd(int argc, char *argv[])
1540 {
1541 
1542           code = -1;
1543           if (argc != 1) {
1544                     UPRINTF("usage: %s\n", argv[0]);
1545                     return;
1546           }
1547           if (!remcwdvalid || remotecwd[0] == '\0')
1548                     updateremotecwd();
1549           if (remotecwd[0] == '\0')
1550                     fprintf(ttyout, "Unable to determine remote directory\n");
1551           else {
1552                     fprintf(ttyout, "Remote directory: %s\n", remotecwd);
1553                     code = 0;
1554           }
1555 }
1556 
1557 /*
1558  * Print working directory on local machine.
1559  */
1560 void
lpwd(int argc,char * argv[])1561 lpwd(int argc, char *argv[])
1562 {
1563 
1564           code = -1;
1565           if (argc != 1) {
1566                     UPRINTF("usage: %s\n", argv[0]);
1567                     return;
1568           }
1569           if (! localcwd[0])
1570                     updatelocalcwd();
1571           if (! localcwd[0])
1572                     fprintf(ttyout, "Unable to determine local directory\n");
1573           else {
1574                     fprintf(ttyout, "Local directory: %s\n", localcwd);
1575                     code = 0;
1576           }
1577 }
1578 
1579 /*
1580  * Make a directory.
1581  */
1582 void
makedir(int argc,char * argv[])1583 makedir(int argc, char *argv[])
1584 {
1585           int r;
1586 
1587           if (argc == 0 || argc > 2 ||
1588               (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1589                     UPRINTF("usage: %s directory-name\n", argv[0]);
1590                     code = -1;
1591                     return;
1592           }
1593           r = command("MKD %s", argv[1]);
1594           if (r == ERROR && code == 500) {
1595                     if (verbose)
1596                               fputs("MKD command not recognized, trying XMKD.\n",
1597                                   ttyout);
1598                     r = command("XMKD %s", argv[1]);
1599           }
1600           if (r == COMPLETE)
1601                     dirchange = 1;
1602 }
1603 
1604 /*
1605  * Remove a directory.
1606  */
1607 void
removedir(int argc,char * argv[])1608 removedir(int argc, char *argv[])
1609 {
1610           int r;
1611 
1612           if (argc == 0 || argc > 2 ||
1613               (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1614                     UPRINTF("usage: %s directory-name\n", argv[0]);
1615                     code = -1;
1616                     return;
1617           }
1618           r = command("RMD %s", argv[1]);
1619           if (r == ERROR && code == 500) {
1620                     if (verbose)
1621                               fputs("RMD command not recognized, trying XRMD.\n",
1622                                   ttyout);
1623                     r = command("XRMD %s", argv[1]);
1624           }
1625           if (r == COMPLETE)
1626                     dirchange = 1;
1627 }
1628 
1629 /*
1630  * Send a line, verbatim, to the remote machine.
1631  */
1632 void
quote(int argc,char * argv[])1633 quote(int argc, char *argv[])
1634 {
1635 
1636           if (argc == 0 ||
1637               (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1638                     UPRINTF("usage: %s line-to-send\n", argv[0]);
1639                     code = -1;
1640                     return;
1641           }
1642           quote1("", argc, argv);
1643 }
1644 
1645 /*
1646  * Send a SITE command to the remote machine.  The line
1647  * is sent verbatim to the remote machine, except that the
1648  * word "SITE" is added at the front.
1649  */
1650 void
site(int argc,char * argv[])1651 site(int argc, char *argv[])
1652 {
1653 
1654           if (argc == 0 ||
1655               (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1656                     UPRINTF("usage: %s line-to-send\n", argv[0]);
1657                     code = -1;
1658                     return;
1659           }
1660           quote1("SITE ", argc, argv);
1661 }
1662 
1663 /*
1664  * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1665  * Send the result as a one-line command and get response.
1666  */
1667 void
quote1(const char * initial,int argc,char * argv[])1668 quote1(const char *initial, int argc, char *argv[])
1669 {
1670           int i;
1671           char buf[BUFSIZ];             /* must be >= sizeof(line) */
1672 
1673           (void)strlcpy(buf, initial, sizeof(buf));
1674           for (i = 1; i < argc; i++) {
1675                     (void)strlcat(buf, argv[i], sizeof(buf));
1676                     if (i < (argc - 1))
1677                               (void)strlcat(buf, " ", sizeof(buf));
1678           }
1679           if (command("%s", buf) == PRELIM) {
1680                     while (getreply(0) == PRELIM)
1681                               continue;
1682           }
1683           dirchange = 1;
1684 }
1685 
1686 void
do_chmod(int argc,char * argv[])1687 do_chmod(int argc, char *argv[])
1688 {
1689 
1690           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1691                     goto usage;
1692           if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1693  usage:
1694                     UPRINTF("usage: %s mode remote-file\n", argv[0]);
1695                     code = -1;
1696                     return;
1697           }
1698           (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1699 }
1700 
1701 #define COMMAND_1ARG(argc, argv, cmd)                       \
1702           if (argc == 1)                                              \
1703                     command(cmd);                                     \
1704           else                                                        \
1705                     command(cmd " %s", argv[1])
1706 
1707 void
do_umask(int argc,char * argv[])1708 do_umask(int argc, char *argv[])
1709 {
1710           int oldverbose = verbose;
1711 
1712           if (argc == 0) {
1713                     UPRINTF("usage: %s [umask]\n", argv[0]);
1714                     code = -1;
1715                     return;
1716           }
1717           verbose = 1;
1718           COMMAND_1ARG(argc, argv, "SITE UMASK");
1719           verbose = oldverbose;
1720 }
1721 
1722 void
idlecmd(int argc,char * argv[])1723 idlecmd(int argc, char *argv[])
1724 {
1725           int oldverbose = verbose;
1726 
1727           if (argc < 1 || argc > 2) {
1728                     UPRINTF("usage: %s [seconds]\n", argv[0]);
1729                     code = -1;
1730                     return;
1731           }
1732           verbose = 1;
1733           COMMAND_1ARG(argc, argv, "SITE IDLE");
1734           verbose = oldverbose;
1735 }
1736 
1737 /*
1738  * Ask the other side for help.
1739  */
1740 void
rmthelp(int argc,char * argv[])1741 rmthelp(int argc, char *argv[])
1742 {
1743           int oldverbose = verbose;
1744 
1745           if (argc == 0) {
1746                     UPRINTF("usage: %s\n", argv[0]);
1747                     code = -1;
1748                     return;
1749           }
1750           verbose = 1;
1751           COMMAND_1ARG(argc, argv, "HELP");
1752           verbose = oldverbose;
1753 }
1754 
1755 /*
1756  * Terminate session and exit.
1757  * May be called with 0, NULL.
1758  */
1759 /*VARARGS*/
1760 void
quit(int argc,char * argv[])1761 quit(int argc, char *argv[])
1762 {
1763 
1764                               /* this may be called with argc == 0, argv == NULL */
1765           if (argc == 0 && argv != NULL) {
1766                     UPRINTF("usage: %s\n", argv[0]);
1767                     code = -1;
1768                     return;
1769           }
1770           if (connected)
1771                     disconnect(0, NULL);
1772           pswitch(1);
1773           if (connected)
1774                     disconnect(0, NULL);
1775           exit(0);
1776 }
1777 
1778 void __dead
justquit(void)1779 justquit(void)
1780 {
1781 
1782           quit(0, NULL);
1783           /*
1784            * quit is not __dead, but for our invocation it never will return,
1785            * but some compilers are not smart enough to find this out.
1786            */
1787           exit(0);
1788 }
1789 
1790 /*
1791  * Terminate session, but don't exit.
1792  * May be called with 0, NULL.
1793  */
1794 void
disconnect(int argc,char * argv[])1795 disconnect(int argc, char *argv[])
1796 {
1797 
1798                               /* this may be called with argc == 0, argv == NULL */
1799           if (argc == 0 && argv != NULL) {
1800                     UPRINTF("usage: %s\n", argv[0]);
1801                     code = -1;
1802                     return;
1803           }
1804           if (!connected)
1805                     return;
1806           (void)command("QUIT");
1807           cleanuppeer();
1808 }
1809 
1810 void
account(int argc,char * argv[])1811 account(int argc, char *argv[])
1812 {
1813           char *ap;
1814           char emptypass[] = "";
1815 
1816           if (argc == 0 || argc > 2) {
1817                     UPRINTF("usage: %s [password]\n", argv[0]);
1818                     code = -1;
1819                     return;
1820           }
1821           else if (argc == 2)
1822                     ap = argv[1];
1823           else {
1824                     ap = getpass("Account:");
1825                     if (ap == NULL)
1826                               ap = emptypass;
1827           }
1828           (void)command("ACCT %s", ap);
1829           memset(ap, 0, strlen(ap));
1830 }
1831 
1832 static sigjmp_buf abortprox;
1833 
1834 void
proxabort(int notused __unused)1835 proxabort(int notused __unused)
1836 {
1837 
1838           sigint_raised = 1;
1839           alarmtimer(0);
1840           if (!proxy) {
1841                     pswitch(1);
1842           }
1843           if (connected) {
1844                     proxflag = 1;
1845           }
1846           else {
1847                     proxflag = 0;
1848           }
1849           pswitch(0);
1850           siglongjmp(abortprox, 1);
1851 }
1852 
1853 void
doproxy(int argc,char * argv[])1854 doproxy(int argc, char *argv[])
1855 {
1856           struct cmd *c;
1857           size_t cmdpos;
1858           sigfunc oldintr;
1859           char cmdbuf[MAX_C_NAME];
1860 
1861           if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1862                     UPRINTF("usage: %s command\n", argv[0]);
1863                     code = -1;
1864                     return;
1865           }
1866           c = getcmd(argv[1]);
1867           if (c == (struct cmd *) -1) {
1868                     fputs("?Ambiguous command.\n", ttyout);
1869                     code = -1;
1870                     return;
1871           }
1872           if (c == 0) {
1873                     fputs("?Invalid command.\n", ttyout);
1874                     code = -1;
1875                     return;
1876           }
1877           if (!c->c_proxy) {
1878                     fputs("?Invalid proxy command.\n", ttyout);
1879                     code = -1;
1880                     return;
1881           }
1882           if (sigsetjmp(abortprox, 1)) {
1883                     code = -1;
1884                     return;
1885           }
1886           oldintr = xsignal(SIGINT, proxabort);
1887           pswitch(1);
1888           if (c->c_conn && !connected) {
1889                     fputs("Not connected.\n", ttyout);
1890                     pswitch(0);
1891                     (void)xsignal(SIGINT, oldintr);
1892                     code = -1;
1893                     return;
1894           }
1895           cmdpos = strcspn(line, " \t");
1896           if (cmdpos > 0)               /* remove leading "proxy " from input buffer */
1897                     memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1898           (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf));
1899           argv[1] = cmdbuf;
1900           (*c->c_handler)(argc-1, argv+1);
1901           if (connected) {
1902                     proxflag = 1;
1903           }
1904           else {
1905                     proxflag = 0;
1906           }
1907           pswitch(0);
1908           (void)xsignal(SIGINT, oldintr);
1909 }
1910 
1911 void
setcase(int argc,char * argv[])1912 setcase(int argc, char *argv[])
1913 {
1914 
1915           code = togglevar(argc, argv, &mcase, "Case mapping");
1916 }
1917 
1918 /*
1919  * convert the given name to lower case if it's all upper case, into
1920  * a static buffer which is returned to the caller
1921  */
1922 static char *
docase(char * dst,size_t dlen,const char * src)1923 docase(char *dst, size_t dlen, const char *src)
1924 {
1925           size_t i;
1926           int dochange = 1;
1927 
1928           for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
1929                     dst[i] = src[i];
1930                     if (islower((unsigned char)dst[i]))
1931                               dochange = 0;
1932           }
1933           dst[i] = '\0';
1934 
1935           if (dochange) {
1936                     for (i = 0; dst[i] != '\0'; i++)
1937                               if (isupper((unsigned char)dst[i]))
1938                                         dst[i] = tolower((unsigned char)dst[i]);
1939           }
1940           return dst;
1941 }
1942 
1943 void
setcr(int argc,char * argv[])1944 setcr(int argc, char *argv[])
1945 {
1946 
1947           code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1948 }
1949 
1950 void
setntrans(int argc,char * argv[])1951 setntrans(int argc, char *argv[])
1952 {
1953 
1954           if (argc == 0 || argc > 3) {
1955                     UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]);
1956                     code = -1;
1957                     return;
1958           }
1959           if (argc == 1) {
1960                     ntflag = 0;
1961                     fputs("Ntrans off.\n", ttyout);
1962                     code = ntflag;
1963                     return;
1964           }
1965           ntflag++;
1966           code = ntflag;
1967           (void)strlcpy(ntin, argv[1], sizeof(ntin));
1968           if (argc == 2) {
1969                     ntout[0] = '\0';
1970                     return;
1971           }
1972           (void)strlcpy(ntout, argv[2], sizeof(ntout));
1973 }
1974 
1975 #define ADDC(x)                                             \
1976           do {                                                        \
1977                     *cp2++ = x;                                       \
1978                     if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) \
1979                               goto out;                     \
1980           } while (0)
1981 
1982 static char *
dotrans(char * dst,size_t dlen,const char * src)1983 dotrans(char *dst, size_t dlen, const char *src)
1984 {
1985           const char *cp1;
1986           char *cp2 = dst;
1987           size_t i, ostop;
1988 
1989           for (ostop = 0; ntout[ostop] && ostop < sizeof(ntout); ostop++)
1990                     continue;
1991           for (cp1 = src; *cp1; cp1++) {
1992                     int found = 0;
1993                     for (i = 0; i < sizeof(ntin) && ntin[i]; i++) {
1994                               if (*cp1 == ntin[i]) {
1995                                         found++;
1996                                         if (i < ostop) {
1997                                                   ADDC(ntout[i]);
1998                                         }
1999                                         break;
2000                               }
2001                     }
2002                     if (!found) {
2003                               ADDC(*cp1);
2004                     }
2005           }
2006 out:
2007           *cp2 = '\0';
2008           return dst;
2009 }
2010 
2011 void
setnmap(int argc,char * argv[])2012 setnmap(int argc, char *argv[])
2013 {
2014           char *cp;
2015 
2016           if (argc == 1) {
2017                     mapflag = 0;
2018                     fputs("Nmap off.\n", ttyout);
2019                     code = mapflag;
2020                     return;
2021           }
2022           if (argc == 0 ||
2023               (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
2024                     UPRINTF("usage: %s [mapin mapout]\n", argv[0]);
2025                     code = -1;
2026                     return;
2027           }
2028           mapflag = 1;
2029           code = 1;
2030           cp = strchr(altarg, ' ');
2031           if (proxy) {
2032                     while(*++cp == ' ')
2033                               continue;
2034                     altarg = cp;
2035                     cp = strchr(altarg, ' ');
2036           }
2037           *cp = '\0';
2038           (void)strlcpy(mapin, altarg, MAXPATHLEN);
2039           while (*++cp == ' ')
2040                     continue;
2041           (void)strlcpy(mapout, cp, MAXPATHLEN);
2042 }
2043 
2044 static char *
domap(char * dst,size_t dlen,const char * src)2045 domap(char *dst, size_t dlen, const char *src)
2046 {
2047           const char *cp1 = src;
2048           char *cp2 = mapin;
2049           const char *tp[9], *te[9];
2050           int i, toks[9], toknum = 0, match = 1;
2051 
2052           for (i=0; i < 9; ++i) {
2053                     toks[i] = 0;
2054           }
2055           while (match && *cp1 && *cp2) {
2056                     switch (*cp2) {
2057                               case '\\':
2058                                         if (*++cp2 != *cp1) {
2059                                                   match = 0;
2060                                         }
2061                                         break;
2062                               case '$':
2063                                         if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2064                                                   if (*cp1 != *(++cp2+1)) {
2065                                                             toks[toknum = *cp2 - '1']++;
2066                                                             tp[toknum] = cp1;
2067                                                             while (*++cp1 && *(cp2+1)
2068                                                                       != *cp1);
2069                                                             te[toknum] = cp1;
2070                                                   }
2071                                                   cp2++;
2072                                                   break;
2073                                         }
2074                                         /* FALLTHROUGH */
2075                               default:
2076                                         if (*cp2 != *cp1) {
2077                                                   match = 0;
2078                                         }
2079                                         break;
2080                     }
2081                     if (match && *cp1) {
2082                               cp1++;
2083                     }
2084                     if (match && *cp2) {
2085                               cp2++;
2086                     }
2087           }
2088           if (!match && *cp1) /* last token mismatch */
2089           {
2090                     toks[toknum] = 0;
2091           }
2092           cp2 = dst;
2093           *cp2 = '\0';
2094           cp1 = mapout;
2095           while (*cp1) {
2096                     match = 0;
2097                     switch (*cp1) {
2098                               case '\\':
2099                                         if (*(cp1 + 1)) {
2100                                                   ADDC(*++cp1);
2101                                         }
2102                                         break;
2103                               case '[':
2104 LOOP:
2105                                         if (*++cp1 == '$' &&
2106                                             isdigit((unsigned char)*(cp1+1))) {
2107                                                   if (*++cp1 == '0') {
2108                                                             const char *cp3 = src;
2109 
2110                                                             while (*cp3) {
2111                                                                       ADDC(*cp3++);
2112                                                             }
2113                                                             match = 1;
2114                                                   }
2115                                                   else if (toks[toknum = *cp1 - '1']) {
2116                                                             const char *cp3 = tp[toknum];
2117 
2118                                                             while (cp3 != te[toknum]) {
2119                                                                       ADDC(*cp3++);
2120                                                             }
2121                                                             match = 1;
2122                                                   }
2123                                         }
2124                                         else {
2125                                                   while (*cp1 && *cp1 != ',' &&
2126                                                       *cp1 != ']') {
2127                                                             if (*cp1 == '\\') {
2128                                                                       cp1++;
2129                                                             }
2130                                                             else if (*cp1 == '$' &&
2131                                                                 isdigit((unsigned char)*(cp1+1))) {
2132                                                                       if (*++cp1 == '0') {
2133                                                                          const char *cp3 = src;
2134 
2135                                                                          while (*cp3) {
2136                                                                                 ADDC(*cp3++);
2137                                                                          }
2138                                                                       }
2139                                                                       else if (toks[toknum =
2140                                                                           *cp1 - '1']) {
2141                                                                          const char *cp3=tp[toknum];
2142 
2143                                                                          while (cp3 !=
2144                                                                                   te[toknum]) {
2145                                                                                 ADDC(*cp3++);
2146                                                                          }
2147                                                                       }
2148                                                             }
2149                                                             else if (*cp1) {
2150                                                                       ADDC(*cp1++);
2151                                                             }
2152                                                   }
2153                                                   if (!*cp1) {
2154                                                             goto unbalanced;
2155                                                   }
2156                                                   match = 1;
2157                                                   cp1--;
2158                                         }
2159                                         if (match) {
2160                                                   while (*++cp1 && *cp1 != ']') {
2161                                                         if (*cp1 == '\\' && *(cp1 + 1)) {
2162                                                                       cp1++;
2163                                                         }
2164                                                   }
2165                                                   if (!*cp1) {
2166                                                             goto unbalanced;
2167                                                   }
2168                                                   break;
2169                                         }
2170                                         switch (*++cp1) {
2171                                                   case ',':
2172                                                             goto LOOP;
2173                                                   case ']':
2174                                                             break;
2175                                                   default:
2176                                                             cp1--;
2177                                                             goto LOOP;
2178                                         }
2179                                         break;
2180                               case '$':
2181                                         if (isdigit((unsigned char)*(cp1 + 1))) {
2182                                                   if (*++cp1 == '0') {
2183                                                             const char *cp3 = src;
2184 
2185                                                             while (*cp3) {
2186                                                                       ADDC(*cp3++);
2187                                                             }
2188                                                   }
2189                                                   else if (toks[toknum = *cp1 - '1']) {
2190                                                             const char *cp3 = tp[toknum];
2191 
2192                                                             while (cp3 != te[toknum]) {
2193                                                                       ADDC(*cp3++);
2194                                                             }
2195                                                   }
2196                                                   break;
2197                                         }
2198                                         /* FALLTHROUGH */
2199                               default:
2200                                         ADDC(*cp1);
2201                                         break;
2202                     }
2203                     cp1++;
2204           }
2205 out:
2206           *cp2 = '\0';
2207           if (!*dst)
2208                     strlcpy(dst, src, dlen);
2209           return dst;
2210 unbalanced:
2211           fputs("nmap: unbalanced brackets.\n", ttyout);
2212           *dst = '\0';
2213           goto out;
2214 }
2215 
2216 void
setpassive(int argc,char * argv[])2217 setpassive(int argc, char *argv[])
2218 {
2219 
2220           if (argc == 1) {
2221                     passivemode = !passivemode;
2222                     activefallback = passivemode;
2223           } else if (argc != 2) {
2224  passiveusage:
2225                     UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]);
2226                     code = -1;
2227                     return;
2228           } else if (strcasecmp(argv[1], "on") == 0) {
2229                     passivemode = 1;
2230                     activefallback = 0;
2231           } else if (strcasecmp(argv[1], "off") == 0) {
2232                     passivemode = 0;
2233                     activefallback = 0;
2234           } else if (strcasecmp(argv[1], "auto") == 0) {
2235                     passivemode = 1;
2236                     activefallback = 1;
2237           } else
2238                     goto passiveusage;
2239           fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2240               onoff(passivemode), onoff(activefallback));
2241           code = passivemode;
2242 }
2243 
2244 
2245 void
setepsv4(int argc,char * argv[])2246 setepsv4(int argc, char *argv[])
2247 {
2248           code = togglevar(argc, argv, &epsv4,
2249               verbose ? "EPSV/EPRT on IPv4" : NULL);
2250           epsv4bad = 0;
2251 }
2252 
2253 void
setepsv6(int argc,char * argv[])2254 setepsv6(int argc, char *argv[])
2255 {
2256           code = togglevar(argc, argv, &epsv6,
2257               verbose ? "EPSV/EPRT on IPv6" : NULL);
2258           epsv6bad = 0;
2259 }
2260 
2261 void
setepsv(int argc,char * argv[])2262 setepsv(int argc, char*argv[])
2263 {
2264           setepsv4(argc,argv);
2265           setepsv6(argc,argv);
2266 }
2267 
2268 void
setsunique(int argc,char * argv[])2269 setsunique(int argc, char *argv[])
2270 {
2271 
2272           code = togglevar(argc, argv, &sunique, "Store unique");
2273 }
2274 
2275 void
setrunique(int argc,char * argv[])2276 setrunique(int argc, char *argv[])
2277 {
2278 
2279           code = togglevar(argc, argv, &runique, "Receive unique");
2280 }
2281 
2282 int
parserate(int argc,char * argv[],int cmdlineopt)2283 parserate(int argc, char *argv[], int cmdlineopt)
2284 {
2285           int dir, max, incr, showonly;
2286           sigfunc oldusr1, oldusr2;
2287 
2288           if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2289  usage:
2290                     if (cmdlineopt)
2291                               UPRINTF(
2292           "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2293                                   argv[0]);
2294                     else
2295                               UPRINTF(
2296           "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2297                                   argv[0]);
2298                     return -1;
2299           }
2300           dir = max = incr = showonly = 0;
2301 #define   RATE_GET  1
2302 #define   RATE_PUT  2
2303 #define   RATE_ALL  (RATE_GET | RATE_PUT)
2304 
2305           if (strcasecmp(argv[1], "all") == 0)
2306                     dir = RATE_ALL;
2307           else if (strcasecmp(argv[1], "get") == 0)
2308                     dir = RATE_GET;
2309           else if (strcasecmp(argv[1], "put") == 0)
2310                     dir = RATE_PUT;
2311           else
2312                     goto usage;
2313 
2314           if (argc >= 3) {
2315                     if ((max = strsuftoi(argv[2])) < 0)
2316                               goto usage;
2317           } else
2318                     showonly = 1;
2319 
2320           if (argc == 4) {
2321                     if ((incr = strsuftoi(argv[3])) <= 0)
2322                               goto usage;
2323           } else
2324                     incr = DEFAULTINCR;
2325 
2326           oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2327           oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2328           if (dir & RATE_GET) {
2329                     if (!showonly) {
2330                               rate_get = max;
2331                               rate_get_incr = incr;
2332                     }
2333                     if (!cmdlineopt || verbose)
2334                               fprintf(ttyout,
2335                     "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2336                                   onoff(rate_get), rate_get, rate_get_incr);
2337           }
2338           if (dir & RATE_PUT) {
2339                     if (!showonly) {
2340                               rate_put = max;
2341                               rate_put_incr = incr;
2342                     }
2343                     if (!cmdlineopt || verbose)
2344                               fprintf(ttyout,
2345                     "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2346                                   onoff(rate_put), rate_put, rate_put_incr);
2347           }
2348           (void)xsignal(SIGUSR1, oldusr1);
2349           (void)xsignal(SIGUSR2, oldusr2);
2350           return 0;
2351 }
2352 
2353 void
setrate(int argc,char * argv[])2354 setrate(int argc, char *argv[])
2355 {
2356 
2357           code = parserate(argc, argv, 0);
2358 }
2359 
2360 /* change directory to parent directory */
2361 void
cdup(int argc,char * argv[])2362 cdup(int argc, char *argv[])
2363 {
2364           int r;
2365 
2366           if (argc == 0) {
2367                     UPRINTF("usage: %s\n", argv[0]);
2368                     code = -1;
2369                     return;
2370           }
2371           r = command("CDUP");
2372           if (r == ERROR && code == 500) {
2373                     if (verbose)
2374                               fputs("CDUP command not recognized, trying XCUP.\n",
2375                                   ttyout);
2376                     r = command("XCUP");
2377           }
2378           if (r == COMPLETE) {
2379                     dirchange = 1;
2380                     remotecwd[0] = '\0';
2381                     remcwdvalid = 0;
2382           }
2383 }
2384 
2385 /*
2386  * Restart transfer at specific point
2387  */
2388 void
restart(int argc,char * argv[])2389 restart(int argc, char *argv[])
2390 {
2391 
2392           if (argc == 0 || argc > 2) {
2393                     UPRINTF("usage: %s [restart-point]\n", argv[0]);
2394                     code = -1;
2395                     return;
2396           }
2397           if (! features[FEAT_REST_STREAM]) {
2398                     fprintf(ttyout,
2399                         "Restart is not supported by the remote server.\n");
2400                     return;
2401           }
2402           if (argc == 2) {
2403                     off_t rp;
2404                     char *ep;
2405 
2406                     rp = STRTOLL(argv[1], &ep, 10);
2407                     if (rp < 0 || *ep != '\0')
2408                               fprintf(ttyout, "restart: Invalid offset `%s'\n",
2409                                   argv[1]);
2410                     else
2411                               restart_point = rp;
2412           }
2413           if (restart_point == 0)
2414                     fputs("No restart point defined.\n", ttyout);
2415           else
2416                     fprintf(ttyout,
2417                         "Restarting at " LLF " for next get, put or append\n",
2418                         (LLT)restart_point);
2419 }
2420 
2421 /*
2422  * Show remote system type
2423  */
2424 void
syst(int argc,char * argv[])2425 syst(int argc, char *argv[])
2426 {
2427           int oldverbose = verbose;
2428 
2429           if (argc == 0) {
2430                     UPRINTF("usage: %s\n", argv[0]);
2431                     code = -1;
2432                     return;
2433           }
2434           verbose = 1;        /* If we aren't verbose, this doesn't do anything! */
2435           (void)command("SYST");
2436           verbose = oldverbose;
2437 }
2438 
2439 void
macdef(int argc,char * argv[])2440 macdef(int argc, char *argv[])
2441 {
2442           char *tmp;
2443           int c;
2444 
2445           if (argc == 0)
2446                     goto usage;
2447           if (macnum == 16) {
2448                     fputs("Limit of 16 macros have already been defined.\n",
2449                         ttyout);
2450                     code = -1;
2451                     return;
2452           }
2453           if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2454  usage:
2455                     UPRINTF("usage: %s macro_name\n", argv[0]);
2456                     code = -1;
2457                     return;
2458           }
2459           if (interactive)
2460                     fputs(
2461                     "Enter macro line by line, terminating it with a null line.\n",
2462                         ttyout);
2463           (void)strlcpy(macros[macnum].mac_name, argv[1],
2464               sizeof(macros[macnum].mac_name));
2465           if (macnum == 0)
2466                     macros[macnum].mac_start = macbuf;
2467           else
2468                     macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2469           tmp = macros[macnum].mac_start;
2470           while (tmp != macbuf+4096) {
2471                     if ((c = getchar()) == EOF) {
2472                               fputs("macdef: end of file encountered.\n", ttyout);
2473                               code = -1;
2474                               return;
2475                     }
2476                     if ((*tmp = c) == '\n') {
2477                               if (tmp == macros[macnum].mac_start) {
2478                                         macros[macnum++].mac_end = tmp;
2479                                         code = 0;
2480                                         return;
2481                               }
2482                               if (*(tmp-1) == '\0') {
2483                                         macros[macnum++].mac_end = tmp - 1;
2484                                         code = 0;
2485                                         return;
2486                               }
2487                               *tmp = '\0';
2488                     }
2489                     tmp++;
2490           }
2491           for (;;) {
2492                     while ((c = getchar()) != '\n' && c != EOF)
2493                               /* LOOP */;
2494                     if (c == EOF || getchar() == '\n') {
2495                               fputs("Macro not defined - 4 KiB buffer exceeded.\n",
2496                                   ttyout);
2497                               code = -1;
2498                               return;
2499                     }
2500           }
2501 }
2502 
2503 /*
2504  * Get size of file on remote machine
2505  */
2506 void
sizecmd(int argc,char * argv[])2507 sizecmd(int argc, char *argv[])
2508 {
2509           off_t size;
2510 
2511           if (argc == 0 || argc > 2 ||
2512               (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2513                     UPRINTF("usage: %s remote-file\n", argv[0]);
2514                     code = -1;
2515                     return;
2516           }
2517           size = remotesize(argv[1], 1);
2518           if (size != -1)
2519                     fprintf(ttyout,
2520                         "%s\t" LLF "\n", argv[1], (LLT)size);
2521           code = (size > 0);
2522 }
2523 
2524 /*
2525  * Get last modification time of file on remote machine
2526  */
2527 void
modtime(int argc,char * argv[])2528 modtime(int argc, char *argv[])
2529 {
2530           time_t mtime;
2531 
2532           if (argc == 0 || argc > 2 ||
2533               (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2534                     UPRINTF("usage: %s remote-file\n", argv[0]);
2535                     code = -1;
2536                     return;
2537           }
2538           mtime = remotemodtime(argv[1], 1);
2539           if (mtime != -1)
2540                     fprintf(ttyout, "%s\t%s", argv[1],
2541                         rfc2822time(localtime(&mtime)));
2542           code = (mtime > 0);
2543 }
2544 
2545 /*
2546  * Show status on remote machine
2547  */
2548 void
rmtstatus(int argc,char * argv[])2549 rmtstatus(int argc, char *argv[])
2550 {
2551 
2552           if (argc == 0) {
2553                     UPRINTF("usage: %s [remote-file]\n", argv[0]);
2554                     code = -1;
2555                     return;
2556           }
2557           COMMAND_1ARG(argc, argv, "STAT");
2558 }
2559 
2560 /*
2561  * Get file if modtime is more recent than current file
2562  */
2563 void
newer(int argc,char * argv[])2564 newer(int argc, char *argv[])
2565 {
2566 
2567           if (getit(argc, argv, -1, "w"))
2568                     fprintf(ttyout,
2569                         "Local file \"%s\" is newer than remote file \"%s\".\n",
2570                         argv[2], argv[1]);
2571 }
2572 
2573 /*
2574  * Display one local file through $PAGER.
2575  */
2576 void
lpage(int argc,char * argv[])2577 lpage(int argc, char *argv[])
2578 {
2579           size_t len;
2580           const char *p;
2581           char *pager, *locfile;
2582 
2583           if (argc == 0 || argc > 2 ||
2584               (argc == 1 && !another(&argc, &argv, "local-file"))) {
2585                     UPRINTF("usage: %s local-file\n", argv[0]);
2586                     code = -1;
2587                     return;
2588           }
2589           if ((locfile = globulize(argv[1])) == NULL) {
2590                     code = -1;
2591                     return;
2592           }
2593           p = getoptionvalue("pager");
2594           if (EMPTYSTRING(p))
2595                     p = DEFAULTPAGER;
2596           len = strlen(p) + strlen(locfile) + 2;
2597           pager = ftp_malloc(len);
2598           (void)strlcpy(pager, p,                 len);
2599           (void)strlcat(pager, " ",     len);
2600           (void)strlcat(pager, locfile, len);
2601           system(pager);
2602           code = 0;
2603           (void)free(pager);
2604           (void)free(locfile);
2605 }
2606 
2607 /*
2608  * Display one remote file through $PAGER.
2609  */
2610 void
page(int argc,char * argv[])2611 page(int argc, char *argv[])
2612 {
2613           int ohash, overbose;
2614           off_t orestart_point;
2615           size_t len;
2616           const char *p;
2617           char *pager;
2618 
2619           if (argc == 0 || argc > 2 ||
2620               (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2621                     UPRINTF("usage: %s remote-file\n", argv[0]);
2622                     code = -1;
2623                     return;
2624           }
2625           p = getoptionvalue("pager");
2626           if (EMPTYSTRING(p))
2627                     p = DEFAULTPAGER;
2628           len = strlen(p) + 2;
2629           pager = ftp_malloc(len);
2630           pager[0] = '|';
2631           (void)strlcpy(pager + 1, p, len - 1);
2632 
2633           ohash = hash;
2634           orestart_point = restart_point;
2635           overbose = verbose;
2636           hash = verbose = 0;
2637           restart_point = 0;
2638           recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2639           hash = ohash;
2640           restart_point = orestart_point;
2641           verbose = overbose;
2642           (void)free(pager);
2643 }
2644 
2645 /*
2646  * Set the socket send or receive buffer size.
2647  */
2648 void
setxferbuf(int argc,char * argv[])2649 setxferbuf(int argc, char *argv[])
2650 {
2651           int size, dir;
2652 
2653           if (argc != 2) {
2654  usage:
2655                     UPRINTF("usage: %s size\n", argv[0]);
2656                     code = -1;
2657                     return;
2658           }
2659           if (strcasecmp(argv[0], "sndbuf") == 0)
2660                     dir = RATE_PUT;
2661           else if (strcasecmp(argv[0], "rcvbuf") == 0)
2662                     dir = RATE_GET;
2663           else if (strcasecmp(argv[0], "xferbuf") == 0)
2664                     dir = RATE_ALL;
2665           else
2666                     goto usage;
2667 
2668           if ((size = strsuftoi(argv[1])) == -1)
2669                     goto usage;
2670 
2671           if (size == 0) {
2672                     fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2673                     goto usage;
2674           }
2675 
2676           if (dir & RATE_PUT)
2677                     sndbuf_size = size;
2678           if (dir & RATE_GET)
2679                     rcvbuf_size = size;
2680           fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2681               sndbuf_size, rcvbuf_size);
2682           code = 0;
2683 }
2684 
2685 /*
2686  * Set or display options (defaults are provided by various env vars)
2687  */
2688 void
setoption(int argc,char * argv[])2689 setoption(int argc, char *argv[])
2690 {
2691           struct option *o;
2692 
2693           code = -1;
2694           if (argc == 0 || (argc != 1 && argc != 3)) {
2695                     UPRINTF("usage: %s [option value]\n", argv[0]);
2696                     return;
2697           }
2698 
2699 #define   OPTIONINDENT ((int) sizeof("https_proxy"))
2700           if (argc == 1) {
2701                     for (o = optiontab; o->name != NULL; o++) {
2702                               fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2703                                   o->name, o->value ? o->value : "");
2704                     }
2705           } else {
2706                     set_option(argv[1], argv[2], 1);
2707           }
2708           code = 0;
2709 }
2710 
2711 void
set_option(const char * option,const char * value,int doverbose)2712 set_option(const char * option, const char * value, int doverbose)
2713 {
2714           struct option *o;
2715 
2716           o = getoption(option);
2717           if (o == NULL) {
2718                     fprintf(ttyout, "No such option `%s'.\n", option);
2719                     return;
2720           }
2721           FREEPTR(o->value);
2722           o->value = ftp_strdup(value);
2723           if (verbose && doverbose)
2724                     fprintf(ttyout, "Setting `%s' to `%s'.\n",
2725                         o->name, o->value);
2726 }
2727 
2728 /*
2729  * Unset an option
2730  */
2731 void
unsetoption(int argc,char * argv[])2732 unsetoption(int argc, char *argv[])
2733 {
2734           struct option *o;
2735 
2736           code = -1;
2737           if (argc == 0 || argc != 2) {
2738                     UPRINTF("usage: %s option\n", argv[0]);
2739                     return;
2740           }
2741 
2742           o = getoption(argv[1]);
2743           if (o == NULL) {
2744                     fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2745                     return;
2746           }
2747           FREEPTR(o->value);
2748           fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2749           code = 0;
2750 }
2751 
2752 /*
2753  * Display features supported by the remote host.
2754  */
2755 void
feat(int argc,char * argv[])2756 feat(int argc, char *argv[])
2757 {
2758           int oldverbose = verbose;
2759 
2760           if (argc == 0) {
2761                     UPRINTF("usage: %s\n", argv[0]);
2762                     code = -1;
2763                     return;
2764           }
2765           if (! features[FEAT_FEAT]) {
2766                     fprintf(ttyout,
2767                         "FEAT is not supported by the remote server.\n");
2768                     return;
2769           }
2770           verbose = 1;        /* If we aren't verbose, this doesn't do anything! */
2771           (void)command("FEAT");
2772           verbose = oldverbose;
2773 }
2774 
2775 void
mlst(int argc,char * argv[])2776 mlst(int argc, char *argv[])
2777 {
2778           int oldverbose = verbose;
2779 
2780           if (argc < 1 || argc > 2) {
2781                     UPRINTF("usage: %s [remote-path]\n", argv[0]);
2782                     code = -1;
2783                     return;
2784           }
2785           if (! features[FEAT_MLST]) {
2786                     fprintf(ttyout,
2787                         "MLST is not supported by the remote server.\n");
2788                     return;
2789           }
2790           verbose = 1;        /* If we aren't verbose, this doesn't do anything! */
2791           COMMAND_1ARG(argc, argv, "MLST");
2792           verbose = oldverbose;
2793 }
2794 
2795 void
opts(int argc,char * argv[])2796 opts(int argc, char *argv[])
2797 {
2798           int oldverbose = verbose;
2799 
2800           if (argc < 2 || argc > 3) {
2801                     UPRINTF("usage: %s command [options]\n", argv[0]);
2802                     code = -1;
2803                     return;
2804           }
2805           if (! features[FEAT_FEAT]) {
2806                     fprintf(ttyout,
2807                         "OPTS is not supported by the remote server.\n");
2808                     return;
2809           }
2810           verbose = 1;        /* If we aren't verbose, this doesn't do anything! */
2811           if (argc == 2)
2812                     command("OPTS %s", argv[1]);
2813           else
2814                     command("OPTS %s %s", argv[1], argv[2]);
2815           verbose = oldverbose;
2816 }
2817