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