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