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