cmds.c revision 1.103 1 /* $NetBSD: cmds.c,v 1.103 2004/06/06 01:37:41 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1996-2002 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.103 2004/06/06 01:37:41 christos 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 int i;
777
778 if (argc == 0) {
779 fprintf(ttyout, "usage: %s\n", argv[0]);
780 code = -1;
781 return;
782 }
783 #ifdef NO_STATUS
784 if (connected)
785 fprintf(ttyout, "Connected %sto %s.\n",
786 connected == -1 ? "and logged in" : "", hostname);
787 else
788 fputs("Not connected.\n", ttyout);
789 if (!proxy) {
790 pswitch(1);
791 if (connected) {
792 fprintf(ttyout, "Connected for proxy commands to %s.\n",
793 hostname);
794 }
795 else {
796 fputs("No proxy connection.\n", ttyout);
797 }
798 pswitch(0);
799 }
800 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
801 *gateserver ? gateserver : "(none)", gateport);
802 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
803 onoff(passivemode), onoff(activefallback));
804 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
805 modename, typename, formname, structname);
806 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
807 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
808 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
809 onoff(sunique), onoff(runique));
810 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
811 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
812 onoff(crflag));
813 if (ntflag) {
814 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
815 }
816 else {
817 fputs("Ntrans: off.\n", ttyout);
818 }
819 if (mapflag) {
820 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
821 }
822 else {
823 fputs("Nmap: off.\n", ttyout);
824 }
825 fprintf(ttyout,
826 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
827 onoff(hash), mark, onoff(progress));
828 fprintf(ttyout,
829 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
830 onoff(rate_get), rate_get, rate_get_incr);
831 fprintf(ttyout,
832 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
833 onoff(rate_put), rate_put, rate_put_incr);
834 fprintf(ttyout,
835 "Socket buffer sizes: send %d, receive %d.\n",
836 sndbuf_size, rcvbuf_size);
837 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
838 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4),
839 epsv4bad ? " (disabled for this connection)" : "");
840 fprintf(ttyout, "Command line editing: %s.\n",
841 #ifdef NO_EDITCOMPLETE
842 "support not compiled in"
843 #else /* !def NO_EDITCOMPLETE */
844 onoff(editing)
845 #endif /* !def NO_EDITCOMPLETE */
846 );
847 if (macnum > 0) {
848 fputs("Macros:\n", ttyout);
849 for (i=0; i<macnum; i++) {
850 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
851 }
852 }
853 #endif
854 fprintf(ttyout, "Version: %s %s\n", FTP_PRODUCT, FTP_VERSION);
855 code = 0;
856 }
857
858 /*
859 * Toggle a variable
860 */
861 int
862 togglevar(int argc, char *argv[], int *var, const char *mesg)
863 {
864 if (argc == 1) {
865 *var = !*var;
866 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
867 *var = 1;
868 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
869 *var = 0;
870 } else {
871 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
872 return (-1);
873 }
874 if (mesg)
875 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
876 return (*var);
877 }
878
879 /*
880 * Set beep on cmd completed mode.
881 */
882 /*VARARGS*/
883 void
884 setbell(int argc, char *argv[])
885 {
886
887 code = togglevar(argc, argv, &bell, "Bell mode");
888 }
889
890 /*
891 * Set command line editing
892 */
893 /*VARARGS*/
894 void
895 setedit(int argc, char *argv[])
896 {
897
898 #ifdef NO_EDITCOMPLETE
899 if (argc == 0) {
900 fprintf(ttyout, "usage: %s\n", argv[0]);
901 code = -1;
902 return;
903 }
904 if (verbose)
905 fputs("Editing support not compiled in; ignoring command.\n",
906 ttyout);
907 #else /* !def NO_EDITCOMPLETE */
908 code = togglevar(argc, argv, &editing, "Editing mode");
909 controlediting();
910 #endif /* !def NO_EDITCOMPLETE */
911 }
912
913 /*
914 * Turn on packet tracing.
915 */
916 /*VARARGS*/
917 void
918 settrace(int argc, char *argv[])
919 {
920
921 code = togglevar(argc, argv, &trace, "Packet tracing");
922 }
923
924 /*
925 * Toggle hash mark printing during transfers, or set hash mark bytecount.
926 */
927 /*VARARGS*/
928 void
929 sethash(int argc, char *argv[])
930 {
931 if (argc == 1)
932 hash = !hash;
933 else if (argc != 2) {
934 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n",
935 argv[0]);
936 code = -1;
937 return;
938 } else if (strcasecmp(argv[1], "on") == 0)
939 hash = 1;
940 else if (strcasecmp(argv[1], "off") == 0)
941 hash = 0;
942 else {
943 int nmark;
944
945 nmark = strsuftoi(argv[1]);
946 if (nmark < 1) {
947 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
948 argv[1]);
949 code = -1;
950 return;
951 }
952 mark = nmark;
953 hash = 1;
954 }
955 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
956 if (hash)
957 fprintf(ttyout, " (%d bytes/hash mark)", mark);
958 fputs(".\n", ttyout);
959 if (hash)
960 progress = 0;
961 code = hash;
962 }
963
964 /*
965 * Turn on printing of server echo's.
966 */
967 /*VARARGS*/
968 void
969 setverbose(int argc, char *argv[])
970 {
971
972 code = togglevar(argc, argv, &verbose, "Verbose mode");
973 }
974
975 /*
976 * Toggle PORT/LPRT cmd use before each data connection.
977 */
978 /*VARARGS*/
979 void
980 setport(int argc, char *argv[])
981 {
982
983 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
984 }
985
986 /*
987 * Toggle transfer progress bar.
988 */
989 /*VARARGS*/
990 void
991 setprogress(int argc, char *argv[])
992 {
993
994 code = togglevar(argc, argv, &progress, "Progress bar");
995 if (progress)
996 hash = 0;
997 }
998
999 /*
1000 * Turn on interactive prompting during mget, mput, and mdelete.
1001 */
1002 /*VARARGS*/
1003 void
1004 setprompt(int argc, char *argv[])
1005 {
1006
1007 code = togglevar(argc, argv, &interactive, "Interactive mode");
1008 }
1009
1010 /*
1011 * Toggle gate-ftp mode, or set gate-ftp server
1012 */
1013 /*VARARGS*/
1014 void
1015 setgate(int argc, char *argv[])
1016 {
1017 static char gsbuf[MAXHOSTNAMELEN];
1018
1019 if (argc == 0 || argc > 3) {
1020 fprintf(ttyout,
1021 "usage: %s [ on | off | gateserver [port] ]\n", argv[0]);
1022 code = -1;
1023 return;
1024 } else if (argc < 2) {
1025 gatemode = !gatemode;
1026 } else {
1027 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
1028 gatemode = 1;
1029 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
1030 gatemode = 0;
1031 else {
1032 if (argc == 3)
1033 gateport = xstrdup(argv[2]);
1034 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
1035 gateserver = gsbuf;
1036 gatemode = 1;
1037 }
1038 }
1039 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
1040 fprintf(ttyout,
1041 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
1042 gatemode = 0;
1043 } else {
1044 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
1045 onoff(gatemode), *gateserver ? gateserver : "(none)",
1046 gateport);
1047 }
1048 code = gatemode;
1049 }
1050
1051 /*
1052 * Toggle metacharacter interpretation on local file names.
1053 */
1054 /*VARARGS*/
1055 void
1056 setglob(int argc, char *argv[])
1057 {
1058
1059 code = togglevar(argc, argv, &doglob, "Globbing");
1060 }
1061
1062 /*
1063 * Toggle preserving modification times on retrieved files.
1064 */
1065 /*VARARGS*/
1066 void
1067 setpreserve(int argc, char *argv[])
1068 {
1069
1070 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1071 }
1072
1073 /*
1074 * Set debugging mode on/off and/or set level of debugging.
1075 */
1076 /*VARARGS*/
1077 void
1078 setdebug(int argc, char *argv[])
1079 {
1080 if (argc == 0 || argc > 2) {
1081 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n",
1082 argv[0]);
1083 code = -1;
1084 return;
1085 } else if (argc == 2) {
1086 if (strcasecmp(argv[1], "on") == 0)
1087 debug = 1;
1088 else if (strcasecmp(argv[1], "off") == 0)
1089 debug = 0;
1090 else {
1091 int val;
1092
1093 val = strsuftoi(argv[1]);
1094 if (val < 0) {
1095 fprintf(ttyout, "%s: bad debugging value.\n",
1096 argv[1]);
1097 code = -1;
1098 return;
1099 }
1100 debug = val;
1101 }
1102 } else
1103 debug = !debug;
1104 if (debug)
1105 options |= SO_DEBUG;
1106 else
1107 options &= ~SO_DEBUG;
1108 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1109 code = debug > 0;
1110 }
1111
1112 /*
1113 * Set current working directory on remote machine.
1114 */
1115 void
1116 cd(int argc, char *argv[])
1117 {
1118 int r;
1119
1120 if (argc == 0 || argc > 2 ||
1121 (argc == 1 && !another(&argc, &argv, "remote-directory"))) {
1122 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1123 code = -1;
1124 return;
1125 }
1126 r = command("CWD %s", argv[1]);
1127 if (r == ERROR && code == 500) {
1128 if (verbose)
1129 fputs("CWD command not recognized, trying XCWD.\n",
1130 ttyout);
1131 r = command("XCWD %s", argv[1]);
1132 }
1133 if (r == COMPLETE) {
1134 dirchange = 1;
1135 updateremotepwd();
1136 }
1137 }
1138
1139 /*
1140 * Set current working directory on local machine.
1141 */
1142 void
1143 lcd(int argc, char *argv[])
1144 {
1145 char buf[MAXPATHLEN];
1146 char *locdir;
1147
1148 code = -1;
1149 if (argc == 1) {
1150 argc++;
1151 argv[1] = localhome;
1152 }
1153 if (argc != 2) {
1154 fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]);
1155 return;
1156 }
1157 if ((locdir = globulize(argv[1])) == NULL)
1158 return;
1159 if (chdir(locdir) < 0)
1160 warn("local: %s", locdir);
1161 else {
1162 if (getcwd(buf, sizeof(buf)) != NULL) {
1163 fprintf(ttyout, "Local directory now %s\n", buf);
1164 code = 0;
1165 } else
1166 warn("getcwd: %s", locdir);
1167 }
1168 (void)free(locdir);
1169 }
1170
1171 /*
1172 * Delete a single file.
1173 */
1174 void
1175 delete(int argc, char *argv[])
1176 {
1177
1178
1179 if (argc == 0 || argc > 2 ||
1180 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
1181 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1182 code = -1;
1183 return;
1184 }
1185 if (command("DELE %s", argv[1]) == COMPLETE)
1186 dirchange = 1;
1187 }
1188
1189 /*
1190 * Delete multiple files.
1191 */
1192 void
1193 mdelete(int argc, char *argv[])
1194 {
1195 sigfunc oldintr;
1196 int ointer;
1197 char *cp;
1198
1199 if (argc == 0 ||
1200 (argc == 1 && !another(&argc, &argv, "remote-files"))) {
1201 fprintf(ttyout, "usage: %s [remote-files]\n", argv[0]);
1202 code = -1;
1203 return;
1204 }
1205 mname = argv[0];
1206 mflag = 1;
1207 oldintr = xsignal(SIGINT, mintr);
1208 if (sigsetjmp(jabort, 1))
1209 mabort();
1210 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1211 if (*cp == '\0') {
1212 mflag = 0;
1213 continue;
1214 }
1215 if (mflag && confirm(argv[0], cp)) {
1216 if (command("DELE %s", cp) == COMPLETE)
1217 dirchange = 1;
1218 if (!mflag && fromatty) {
1219 ointer = interactive;
1220 interactive = 1;
1221 if (confirm("Continue with", "mdelete")) {
1222 mflag++;
1223 }
1224 interactive = ointer;
1225 }
1226 }
1227 }
1228 (void)xsignal(SIGINT, oldintr);
1229 mflag = 0;
1230 }
1231
1232 /*
1233 * Rename a remote file.
1234 */
1235 void
1236 renamefile(int argc, char *argv[])
1237 {
1238
1239 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name")))
1240 goto usage;
1241 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1242 usage:
1243 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1244 code = -1;
1245 return;
1246 }
1247 if (command("RNFR %s", argv[1]) == CONTINUE &&
1248 command("RNTO %s", argv[2]) == COMPLETE)
1249 dirchange = 1;
1250 }
1251
1252 /*
1253 * Get a directory listing of remote files.
1254 * Supports being invoked as:
1255 * cmd runs
1256 * --- ----
1257 * dir, ls LIST
1258 * mlsd MLSD
1259 * nlist NLST
1260 * pdir, pls LIST |$PAGER
1261 * mmlsd MLSD |$PAGER
1262 */
1263 void
1264 ls(int argc, char *argv[])
1265 {
1266 const char *cmd;
1267 char *remdir, *locfile;
1268 int freelocfile, pagecmd, mlsdcmd;
1269
1270 remdir = NULL;
1271 locfile = "-";
1272 freelocfile = pagecmd = mlsdcmd = 0;
1273 /*
1274 * the only commands that start with `p' are
1275 * the `pager' versions.
1276 */
1277 if (argv[0][0] == 'p')
1278 pagecmd = 1;
1279 if (strcmp(argv[0] + pagecmd , "mlsd") == 0) {
1280 if (! features[FEAT_MLST]) {
1281 fprintf(ttyout,
1282 "MLSD is not supported by the remote server.\n");
1283 return;
1284 }
1285 mlsdcmd = 1;
1286 }
1287 if (argc == 0)
1288 goto usage;
1289
1290 if (mlsdcmd)
1291 cmd = "MLSD";
1292 else if (strcmp(argv[0] + pagecmd, "nlist") == 0)
1293 cmd = "NLST";
1294 else
1295 cmd = "LIST";
1296
1297 if (argc > 1)
1298 remdir = argv[1];
1299 if (argc > 2)
1300 locfile = argv[2];
1301 if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) {
1302 usage:
1303 if (pagecmd || mlsdcmd)
1304 fprintf(ttyout,
1305 "usage: %s [remote-path]\n", argv[0]);
1306 else
1307 fprintf(ttyout,
1308 "usage: %s [remote-path [local-file]]\n",
1309 argv[0]);
1310 code = -1;
1311 goto freels;
1312 }
1313
1314 if (pagecmd) {
1315 char *p;
1316 int len;
1317
1318 p = getoptionvalue("pager");
1319 if (EMPTYSTRING(p))
1320 p = DEFAULTPAGER;
1321 len = strlen(p) + 2;
1322 locfile = xmalloc(len);
1323 locfile[0] = '|';
1324 (void)strlcpy(locfile + 1, p, len - 1);
1325 freelocfile = 1;
1326 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1327 if ((locfile = globulize(locfile)) == NULL ||
1328 !confirm("output to local-file:", locfile)) {
1329 code = -1;
1330 goto freels;
1331 }
1332 freelocfile = 1;
1333 }
1334 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1335 freels:
1336 if (freelocfile && locfile)
1337 (void)free(locfile);
1338 }
1339
1340 /*
1341 * Get a directory listing of multiple remote files.
1342 */
1343 void
1344 mls(int argc, char *argv[])
1345 {
1346 sigfunc oldintr;
1347 int ointer, i;
1348 int dolist;
1349 char *mode, *dest, *odest;
1350
1351 if (argc == 0)
1352 goto usage;
1353 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1354 goto usage;
1355 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1356 usage:
1357 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1358 code = -1;
1359 return;
1360 }
1361 odest = dest = argv[argc - 1];
1362 argv[argc - 1] = NULL;
1363 if (strcmp(dest, "-") && *dest != '|')
1364 if (((dest = globulize(dest)) == NULL) ||
1365 !confirm("output to local-file:", dest)) {
1366 code = -1;
1367 return;
1368 }
1369 dolist = strcmp(argv[0], "mls");
1370 mname = argv[0];
1371 mflag = 1;
1372 oldintr = xsignal(SIGINT, mintr);
1373 if (sigsetjmp(jabort, 1))
1374 mabort();
1375 for (i = 1; mflag && i < argc-1 && connected; i++) {
1376 mode = (i == 1) ? "w" : "a";
1377 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1378 0, 0);
1379 if (!mflag && fromatty) {
1380 ointer = interactive;
1381 interactive = 1;
1382 if (confirm("Continue with", argv[0])) {
1383 mflag ++;
1384 }
1385 interactive = ointer;
1386 }
1387 }
1388 (void)xsignal(SIGINT, oldintr);
1389 mflag = 0;
1390 if (dest != odest) /* free up after globulize() */
1391 free(dest);
1392 }
1393
1394 /*
1395 * Do a shell escape
1396 */
1397 /*ARGSUSED*/
1398 void
1399 shell(int argc, char *argv[])
1400 {
1401 pid_t pid;
1402 sigfunc old1;
1403 char shellnam[MAXPATHLEN], *shell, *namep;
1404 int wait_status;
1405
1406 if (argc == 0) {
1407 fprintf(ttyout, "usage: %s [command [args]]\n", argv[0]);
1408 code = -1;
1409 return;
1410 }
1411 old1 = xsignal(SIGINT, SIG_IGN);
1412 if ((pid = fork()) == 0) {
1413 for (pid = 3; pid < 20; pid++)
1414 (void)close(pid);
1415 (void)xsignal(SIGINT, SIG_DFL);
1416 shell = getenv("SHELL");
1417 if (shell == NULL)
1418 shell = _PATH_BSHELL;
1419 namep = strrchr(shell, '/');
1420 if (namep == NULL)
1421 namep = shell;
1422 else
1423 namep++;
1424 (void)strlcpy(shellnam, namep, sizeof(shellnam));
1425 if (debug) {
1426 fputs(shell, ttyout);
1427 putc('\n', ttyout);
1428 }
1429 if (argc > 1) {
1430 execl(shell, shellnam, "-c", altarg, (char *)0);
1431 }
1432 else {
1433 execl(shell, shellnam, (char *)0);
1434 }
1435 warn("%s", shell);
1436 code = -1;
1437 exit(1);
1438 }
1439 if (pid > 0)
1440 while (wait(&wait_status) != pid)
1441 ;
1442 (void)xsignal(SIGINT, old1);
1443 if (pid == -1) {
1444 warn("Try again later");
1445 code = -1;
1446 } else
1447 code = 0;
1448 }
1449
1450 /*
1451 * Send new user information (re-login)
1452 */
1453 void
1454 user(int argc, char *argv[])
1455 {
1456 char acct[80];
1457 int n, aflag = 0;
1458
1459 if (argc == 0)
1460 goto usage;
1461 if (argc < 2)
1462 (void)another(&argc, &argv, "username");
1463 if (argc < 2 || argc > 4) {
1464 usage:
1465 fprintf(ttyout, "usage: %s username [password [account]]\n",
1466 argv[0]);
1467 code = -1;
1468 return;
1469 }
1470 n = command("USER %s", argv[1]);
1471 if (n == CONTINUE) {
1472 if (argc < 3) {
1473 argv[2] = getpass("Password: ");
1474 argc++;
1475 }
1476 n = command("PASS %s", argv[2]);
1477 }
1478 if (n == CONTINUE) {
1479 if (argc < 4) {
1480 (void)fputs("Account: ", ttyout);
1481 (void)fflush(ttyout);
1482 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1483 fprintf(ttyout,
1484 "\nEOF received; login aborted.\n");
1485 clearerr(stdin);
1486 code = -1;
1487 return;
1488 }
1489 acct[strlen(acct) - 1] = '\0';
1490 argv[3] = acct; argc++;
1491 }
1492 n = command("ACCT %s", argv[3]);
1493 aflag++;
1494 }
1495 if (n != COMPLETE) {
1496 fputs("Login failed.\n", ttyout);
1497 return;
1498 }
1499 if (!aflag && argc == 4) {
1500 (void)command("ACCT %s", argv[3]);
1501 }
1502 connected = -1;
1503 getremoteinfo();
1504 }
1505
1506 /*
1507 * Print working directory on remote machine.
1508 */
1509 /*VARARGS*/
1510 void
1511 pwd(int argc, char *argv[])
1512 {
1513 int oldverbose = verbose;
1514
1515 if (argc == 0) {
1516 fprintf(ttyout, "usage: %s\n", argv[0]);
1517 code = -1;
1518 return;
1519 }
1520 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
1521 if (command("PWD") == ERROR && code == 500) {
1522 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1523 (void)command("XPWD");
1524 }
1525 verbose = oldverbose;
1526 }
1527
1528 /*
1529 * Print working directory on local machine.
1530 */
1531 void
1532 lpwd(int argc, char *argv[])
1533 {
1534 char buf[MAXPATHLEN];
1535
1536 if (argc == 0) {
1537 fprintf(ttyout, "usage: %s\n", argv[0]);
1538 code = -1;
1539 return;
1540 }
1541 if (getcwd(buf, sizeof(buf)) != NULL) {
1542 fprintf(ttyout, "Local directory %s\n", buf);
1543 code = 0;
1544 } else {
1545 warn("getcwd");
1546 code = -1;
1547 }
1548 }
1549
1550 /*
1551 * Make a directory.
1552 */
1553 void
1554 makedir(int argc, char *argv[])
1555 {
1556 int r;
1557
1558 if (argc == 0 || argc > 2 ||
1559 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1560 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1561 code = -1;
1562 return;
1563 }
1564 r = command("MKD %s", argv[1]);
1565 if (r == ERROR && code == 500) {
1566 if (verbose)
1567 fputs("MKD command not recognized, trying XMKD.\n",
1568 ttyout);
1569 r = command("XMKD %s", argv[1]);
1570 }
1571 if (r == COMPLETE)
1572 dirchange = 1;
1573 }
1574
1575 /*
1576 * Remove a directory.
1577 */
1578 void
1579 removedir(int argc, char *argv[])
1580 {
1581 int r;
1582
1583 if (argc == 0 || argc > 2 ||
1584 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1585 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1586 code = -1;
1587 return;
1588 }
1589 r = command("RMD %s", argv[1]);
1590 if (r == ERROR && code == 500) {
1591 if (verbose)
1592 fputs("RMD command not recognized, trying XRMD.\n",
1593 ttyout);
1594 r = command("XRMD %s", argv[1]);
1595 }
1596 if (r == COMPLETE)
1597 dirchange = 1;
1598 }
1599
1600 /*
1601 * Send a line, verbatim, to the remote machine.
1602 */
1603 void
1604 quote(int argc, char *argv[])
1605 {
1606
1607 if (argc == 0 ||
1608 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1609 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1610 code = -1;
1611 return;
1612 }
1613 quote1("", argc, argv);
1614 }
1615
1616 /*
1617 * Send a SITE command to the remote machine. The line
1618 * is sent verbatim to the remote machine, except that the
1619 * word "SITE" is added at the front.
1620 */
1621 void
1622 site(int argc, char *argv[])
1623 {
1624
1625 if (argc == 0 ||
1626 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1627 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1628 code = -1;
1629 return;
1630 }
1631 quote1("SITE ", argc, argv);
1632 }
1633
1634 /*
1635 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1636 * Send the result as a one-line command and get response.
1637 */
1638 void
1639 quote1(const char *initial, int argc, char *argv[])
1640 {
1641 int i;
1642 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1643
1644 (void)strlcpy(buf, initial, sizeof(buf));
1645 for (i = 1; i < argc; i++) {
1646 (void)strlcat(buf, argv[i], sizeof(buf));
1647 if (i < (argc - 1))
1648 (void)strlcat(buf, " ", sizeof(buf));
1649 }
1650 if (command("%s", buf) == PRELIM) {
1651 while (getreply(0) == PRELIM)
1652 continue;
1653 }
1654 dirchange = 1;
1655 }
1656
1657 void
1658 do_chmod(int argc, char *argv[])
1659 {
1660
1661 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1662 goto usage;
1663 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1664 usage:
1665 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]);
1666 code = -1;
1667 return;
1668 }
1669 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1670 }
1671
1672 #define COMMAND_1ARG(argc, argv, cmd) \
1673 if (argc == 1) \
1674 command(cmd); \
1675 else \
1676 command(cmd " %s", argv[1])
1677
1678 void
1679 do_umask(int argc, char *argv[])
1680 {
1681 int oldverbose = verbose;
1682
1683 if (argc == 0) {
1684 fprintf(ttyout, "usage: %s [umask]\n", argv[0]);
1685 code = -1;
1686 return;
1687 }
1688 verbose = 1;
1689 COMMAND_1ARG(argc, argv, "SITE UMASK");
1690 verbose = oldverbose;
1691 }
1692
1693 void
1694 idlecmd(int argc, char *argv[])
1695 {
1696 int oldverbose = verbose;
1697
1698 if (argc < 1 || argc > 2) {
1699 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]);
1700 code = -1;
1701 return;
1702 }
1703 verbose = 1;
1704 COMMAND_1ARG(argc, argv, "SITE IDLE");
1705 verbose = oldverbose;
1706 }
1707
1708 /*
1709 * Ask the other side for help.
1710 */
1711 void
1712 rmthelp(int argc, char *argv[])
1713 {
1714 int oldverbose = verbose;
1715
1716 if (argc == 0) {
1717 fprintf(ttyout, "usage: %s\n", argv[0]);
1718 code = -1;
1719 return;
1720 }
1721 verbose = 1;
1722 COMMAND_1ARG(argc, argv, "HELP");
1723 verbose = oldverbose;
1724 }
1725
1726 /*
1727 * Terminate session and exit.
1728 * May be called with 0, NULL.
1729 */
1730 /*VARARGS*/
1731 void
1732 quit(int argc, char *argv[])
1733 {
1734
1735 /* this may be called with argc == 0, argv == NULL */
1736 if (argc == 0 && argv != NULL) {
1737 fprintf(ttyout, "usage: %s\n", argv[0]);
1738 code = -1;
1739 return;
1740 }
1741 if (connected)
1742 disconnect(0, NULL);
1743 pswitch(1);
1744 if (connected)
1745 disconnect(0, NULL);
1746 exit(0);
1747 }
1748
1749 /*
1750 * Terminate session, but don't exit.
1751 * May be called with 0, NULL.
1752 */
1753 void
1754 disconnect(int argc, char *argv[])
1755 {
1756
1757 /* this may be called with argc == 0, argv == NULL */
1758 if (argc == 0 && argv != NULL) {
1759 fprintf(ttyout, "usage: %s\n", argv[0]);
1760 code = -1;
1761 return;
1762 }
1763 if (!connected)
1764 return;
1765 (void)command("QUIT");
1766 cleanuppeer();
1767 }
1768
1769 void
1770 account(int argc, char *argv[])
1771 {
1772 char *ap;
1773
1774 if (argc == 0 || argc > 2) {
1775 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1776 code = -1;
1777 return;
1778 }
1779 else if (argc == 2)
1780 ap = argv[1];
1781 else
1782 ap = getpass("Account:");
1783 (void)command("ACCT %s", ap);
1784 }
1785
1786 sigjmp_buf abortprox;
1787
1788 void
1789 proxabort(int notused)
1790 {
1791
1792 alarmtimer(0);
1793 if (!proxy) {
1794 pswitch(1);
1795 }
1796 if (connected) {
1797 proxflag = 1;
1798 }
1799 else {
1800 proxflag = 0;
1801 }
1802 pswitch(0);
1803 siglongjmp(abortprox, 1);
1804 }
1805
1806 void
1807 doproxy(int argc, char *argv[])
1808 {
1809 struct cmd *c;
1810 int cmdpos;
1811 sigfunc oldintr;
1812
1813 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1814 fprintf(ttyout, "usage: %s command\n", argv[0]);
1815 code = -1;
1816 return;
1817 }
1818 c = getcmd(argv[1]);
1819 if (c == (struct cmd *) -1) {
1820 fputs("?Ambiguous command.\n", ttyout);
1821 code = -1;
1822 return;
1823 }
1824 if (c == 0) {
1825 fputs("?Invalid command.\n", ttyout);
1826 code = -1;
1827 return;
1828 }
1829 if (!c->c_proxy) {
1830 fputs("?Invalid proxy command.\n", ttyout);
1831 code = -1;
1832 return;
1833 }
1834 if (sigsetjmp(abortprox, 1)) {
1835 code = -1;
1836 return;
1837 }
1838 oldintr = xsignal(SIGINT, proxabort);
1839 pswitch(1);
1840 if (c->c_conn && !connected) {
1841 fputs("Not connected.\n", ttyout);
1842 pswitch(0);
1843 (void)xsignal(SIGINT, oldintr);
1844 code = -1;
1845 return;
1846 }
1847 cmdpos = strcspn(line, " \t");
1848 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1849 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1850 argv[1] = c->c_name;
1851 (*c->c_handler)(argc-1, argv+1);
1852 if (connected) {
1853 proxflag = 1;
1854 }
1855 else {
1856 proxflag = 0;
1857 }
1858 pswitch(0);
1859 (void)xsignal(SIGINT, oldintr);
1860 }
1861
1862 void
1863 setcase(int argc, char *argv[])
1864 {
1865
1866 code = togglevar(argc, argv, &mcase, "Case mapping");
1867 }
1868
1869 /*
1870 * convert the given name to lower case if it's all upper case, into
1871 * a static buffer which is returned to the caller
1872 */
1873 char *
1874 docase(char *name)
1875 {
1876 static char new[MAXPATHLEN];
1877 int i, dochange;
1878
1879 dochange = 1;
1880 for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) {
1881 new[i] = name[i];
1882 if (islower((unsigned char)new[i]))
1883 dochange = 0;
1884 }
1885 new[i] = '\0';
1886
1887 if (dochange) {
1888 for (i = 0; new[i] != '\0'; i++)
1889 if (isupper((unsigned char)new[i]))
1890 new[i] = tolower(new[i]);
1891 }
1892 return (new);
1893 }
1894
1895 void
1896 setcr(int argc, char *argv[])
1897 {
1898
1899 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1900 }
1901
1902 void
1903 setntrans(int argc, char *argv[])
1904 {
1905
1906 if (argc == 0 || argc > 3) {
1907 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]);
1908 code = -1;
1909 return;
1910 }
1911 if (argc == 1) {
1912 ntflag = 0;
1913 fputs("Ntrans off.\n", ttyout);
1914 code = ntflag;
1915 return;
1916 }
1917 ntflag++;
1918 code = ntflag;
1919 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1920 if (argc == 2) {
1921 ntout[0] = '\0';
1922 return;
1923 }
1924 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1925 }
1926
1927 char *
1928 dotrans(char *name)
1929 {
1930 static char new[MAXPATHLEN];
1931 char *cp1, *cp2 = new;
1932 int i, ostop, found;
1933
1934 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1935 continue;
1936 for (cp1 = name; *cp1; cp1++) {
1937 found = 0;
1938 for (i = 0; *(ntin + i) && i < 16; i++) {
1939 if (*cp1 == *(ntin + i)) {
1940 found++;
1941 if (i < ostop) {
1942 *cp2++ = *(ntout + i);
1943 }
1944 break;
1945 }
1946 }
1947 if (!found) {
1948 *cp2++ = *cp1;
1949 }
1950 }
1951 *cp2 = '\0';
1952 return (new);
1953 }
1954
1955 void
1956 setnmap(int argc, char *argv[])
1957 {
1958 char *cp;
1959
1960 if (argc == 1) {
1961 mapflag = 0;
1962 fputs("Nmap off.\n", ttyout);
1963 code = mapflag;
1964 return;
1965 }
1966 if (argc == 0 ||
1967 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1968 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1969 code = -1;
1970 return;
1971 }
1972 mapflag = 1;
1973 code = 1;
1974 cp = strchr(altarg, ' ');
1975 if (proxy) {
1976 while(*++cp == ' ')
1977 continue;
1978 altarg = cp;
1979 cp = strchr(altarg, ' ');
1980 }
1981 *cp = '\0';
1982 (void)strlcpy(mapin, altarg, MAXPATHLEN);
1983 while (*++cp == ' ')
1984 continue;
1985 (void)strlcpy(mapout, cp, MAXPATHLEN);
1986 }
1987
1988 char *
1989 domap(char *name)
1990 {
1991 static char new[MAXPATHLEN];
1992 char *cp1 = name, *cp2 = mapin;
1993 char *tp[9], *te[9];
1994 int i, toks[9], toknum = 0, match = 1;
1995
1996 for (i=0; i < 9; ++i) {
1997 toks[i] = 0;
1998 }
1999 while (match && *cp1 && *cp2) {
2000 switch (*cp2) {
2001 case '\\':
2002 if (*++cp2 != *cp1) {
2003 match = 0;
2004 }
2005 break;
2006 case '$':
2007 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2008 if (*cp1 != *(++cp2+1)) {
2009 toks[toknum = *cp2 - '1']++;
2010 tp[toknum] = cp1;
2011 while (*++cp1 && *(cp2+1)
2012 != *cp1);
2013 te[toknum] = cp1;
2014 }
2015 cp2++;
2016 break;
2017 }
2018 /* FALLTHROUGH */
2019 default:
2020 if (*cp2 != *cp1) {
2021 match = 0;
2022 }
2023 break;
2024 }
2025 if (match && *cp1) {
2026 cp1++;
2027 }
2028 if (match && *cp2) {
2029 cp2++;
2030 }
2031 }
2032 if (!match && *cp1) /* last token mismatch */
2033 {
2034 toks[toknum] = 0;
2035 }
2036 cp1 = new;
2037 *cp1 = '\0';
2038 cp2 = mapout;
2039 while (*cp2) {
2040 match = 0;
2041 switch (*cp2) {
2042 case '\\':
2043 if (*(cp2 + 1)) {
2044 *cp1++ = *++cp2;
2045 }
2046 break;
2047 case '[':
2048 LOOP:
2049 if (*++cp2 == '$' &&
2050 isdigit((unsigned char)*(cp2+1))) {
2051 if (*++cp2 == '0') {
2052 char *cp3 = name;
2053
2054 while (*cp3) {
2055 *cp1++ = *cp3++;
2056 }
2057 match = 1;
2058 }
2059 else if (toks[toknum = *cp2 - '1']) {
2060 char *cp3 = tp[toknum];
2061
2062 while (cp3 != te[toknum]) {
2063 *cp1++ = *cp3++;
2064 }
2065 match = 1;
2066 }
2067 }
2068 else {
2069 while (*cp2 && *cp2 != ',' &&
2070 *cp2 != ']') {
2071 if (*cp2 == '\\') {
2072 cp2++;
2073 }
2074 else if (*cp2 == '$' &&
2075 isdigit((unsigned char)*(cp2+1))) {
2076 if (*++cp2 == '0') {
2077 char *cp3 = name;
2078
2079 while (*cp3) {
2080 *cp1++ = *cp3++;
2081 }
2082 }
2083 else if (toks[toknum =
2084 *cp2 - '1']) {
2085 char *cp3=tp[toknum];
2086
2087 while (cp3 !=
2088 te[toknum]) {
2089 *cp1++ = *cp3++;
2090 }
2091 }
2092 }
2093 else if (*cp2) {
2094 *cp1++ = *cp2++;
2095 }
2096 }
2097 if (!*cp2) {
2098 fputs(
2099 "nmap: unbalanced brackets.\n",
2100 ttyout);
2101 return (name);
2102 }
2103 match = 1;
2104 cp2--;
2105 }
2106 if (match) {
2107 while (*++cp2 && *cp2 != ']') {
2108 if (*cp2 == '\\' && *(cp2 + 1)) {
2109 cp2++;
2110 }
2111 }
2112 if (!*cp2) {
2113 fputs(
2114 "nmap: unbalanced brackets.\n",
2115 ttyout);
2116 return (name);
2117 }
2118 break;
2119 }
2120 switch (*++cp2) {
2121 case ',':
2122 goto LOOP;
2123 case ']':
2124 break;
2125 default:
2126 cp2--;
2127 goto LOOP;
2128 }
2129 break;
2130 case '$':
2131 if (isdigit((unsigned char)*(cp2 + 1))) {
2132 if (*++cp2 == '0') {
2133 char *cp3 = name;
2134
2135 while (*cp3) {
2136 *cp1++ = *cp3++;
2137 }
2138 }
2139 else if (toks[toknum = *cp2 - '1']) {
2140 char *cp3 = tp[toknum];
2141
2142 while (cp3 != te[toknum]) {
2143 *cp1++ = *cp3++;
2144 }
2145 }
2146 break;
2147 }
2148 /* intentional drop through */
2149 default:
2150 *cp1++ = *cp2;
2151 break;
2152 }
2153 cp2++;
2154 }
2155 *cp1 = '\0';
2156 if (!*new) {
2157 return (name);
2158 }
2159 return (new);
2160 }
2161
2162 void
2163 setpassive(int argc, char *argv[])
2164 {
2165
2166 if (argc == 1) {
2167 passivemode = !passivemode;
2168 activefallback = passivemode;
2169 } else if (argc != 2) {
2170 passiveusage:
2171 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]);
2172 code = -1;
2173 return;
2174 } else if (strcasecmp(argv[1], "on") == 0) {
2175 passivemode = 1;
2176 activefallback = 0;
2177 } else if (strcasecmp(argv[1], "off") == 0) {
2178 passivemode = 0;
2179 activefallback = 0;
2180 } else if (strcasecmp(argv[1], "auto") == 0) {
2181 passivemode = 1;
2182 activefallback = 1;
2183 } else
2184 goto passiveusage;
2185 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2186 onoff(passivemode), onoff(activefallback));
2187 code = passivemode;
2188 }
2189
2190 void
2191 setepsv4(int argc, char *argv[])
2192 {
2193
2194 code = togglevar(argc, argv, &epsv4,
2195 verbose ? "EPSV/EPRT on IPv4" : NULL);
2196 epsv4bad = 0;
2197 }
2198
2199 void
2200 setsunique(int argc, char *argv[])
2201 {
2202
2203 code = togglevar(argc, argv, &sunique, "Store unique");
2204 }
2205
2206 void
2207 setrunique(int argc, char *argv[])
2208 {
2209
2210 code = togglevar(argc, argv, &runique, "Receive unique");
2211 }
2212
2213 int
2214 parserate(int argc, char *argv[], int cmdlineopt)
2215 {
2216 int dir, max, incr, showonly;
2217 sigfunc oldusr1, oldusr2;
2218
2219 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2220 usage:
2221 if (cmdlineopt)
2222 fprintf(ttyout,
2223 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2224 argv[0]);
2225 else
2226 fprintf(ttyout,
2227 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2228 argv[0]);
2229 return -1;
2230 }
2231 dir = max = incr = showonly = 0;
2232 #define RATE_GET 1
2233 #define RATE_PUT 2
2234 #define RATE_ALL (RATE_GET | RATE_PUT)
2235
2236 if (strcasecmp(argv[1], "all") == 0)
2237 dir = RATE_ALL;
2238 else if (strcasecmp(argv[1], "get") == 0)
2239 dir = RATE_GET;
2240 else if (strcasecmp(argv[1], "put") == 0)
2241 dir = RATE_PUT;
2242 else
2243 goto usage;
2244
2245 if (argc >= 3) {
2246 if ((max = strsuftoi(argv[2])) < 0)
2247 goto usage;
2248 } else
2249 showonly = 1;
2250
2251 if (argc == 4) {
2252 if ((incr = strsuftoi(argv[3])) <= 0)
2253 goto usage;
2254 } else
2255 incr = DEFAULTINCR;
2256
2257 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2258 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2259 if (dir & RATE_GET) {
2260 if (!showonly) {
2261 rate_get = max;
2262 rate_get_incr = incr;
2263 }
2264 if (!cmdlineopt || verbose)
2265 fprintf(ttyout,
2266 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2267 onoff(rate_get), rate_get, rate_get_incr);
2268 }
2269 if (dir & RATE_PUT) {
2270 if (!showonly) {
2271 rate_put = max;
2272 rate_put_incr = incr;
2273 }
2274 if (!cmdlineopt || verbose)
2275 fprintf(ttyout,
2276 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2277 onoff(rate_put), rate_put, rate_put_incr);
2278 }
2279 (void)xsignal(SIGUSR1, oldusr1);
2280 (void)xsignal(SIGUSR2, oldusr2);
2281 return 0;
2282 }
2283
2284 void
2285 setrate(int argc, char *argv[])
2286 {
2287
2288 code = parserate(argc, argv, 0);
2289 }
2290
2291 /* change directory to parent directory */
2292 void
2293 cdup(int argc, char *argv[])
2294 {
2295 int r;
2296
2297 if (argc == 0) {
2298 fprintf(ttyout, "usage: %s\n", argv[0]);
2299 code = -1;
2300 return;
2301 }
2302 r = command("CDUP");
2303 if (r == ERROR && code == 500) {
2304 if (verbose)
2305 fputs("CDUP command not recognized, trying XCUP.\n",
2306 ttyout);
2307 r = command("XCUP");
2308 }
2309 if (r == COMPLETE) {
2310 dirchange = 1;
2311 updateremotepwd();
2312 }
2313 }
2314
2315 /*
2316 * Restart transfer at specific point
2317 */
2318 void
2319 restart(int argc, char *argv[])
2320 {
2321
2322 if (argc == 0 || argc > 2) {
2323 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]);
2324 code = -1;
2325 return;
2326 }
2327 if (! features[FEAT_REST_STREAM]) {
2328 fprintf(ttyout,
2329 "Restart is not supported by the remote server.\n");
2330 return;
2331 }
2332 if (argc == 2) {
2333 off_t rp;
2334 char *ep;
2335
2336 rp = STRTOLL(argv[1], &ep, 10);
2337 if (rp < 0 || *ep != '\0')
2338 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2339 argv[1]);
2340 else
2341 restart_point = rp;
2342 }
2343 if (restart_point == 0)
2344 fputs("No restart point defined.\n", ttyout);
2345 else
2346 fprintf(ttyout,
2347 "Restarting at " LLF " for next get, put or append\n",
2348 (LLT)restart_point);
2349 }
2350
2351 /*
2352 * Show remote system type
2353 */
2354 void
2355 syst(int argc, char *argv[])
2356 {
2357 int oldverbose = verbose;
2358
2359 if (argc == 0) {
2360 fprintf(ttyout, "usage: %s\n", argv[0]);
2361 code = -1;
2362 return;
2363 }
2364 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2365 (void)command("SYST");
2366 verbose = oldverbose;
2367 }
2368
2369 void
2370 macdef(int argc, char *argv[])
2371 {
2372 char *tmp;
2373 int c;
2374
2375 if (argc == 0)
2376 goto usage;
2377 if (macnum == 16) {
2378 fputs("Limit of 16 macros have already been defined.\n",
2379 ttyout);
2380 code = -1;
2381 return;
2382 }
2383 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2384 usage:
2385 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2386 code = -1;
2387 return;
2388 }
2389 if (interactive)
2390 fputs(
2391 "Enter macro line by line, terminating it with a null line.\n",
2392 ttyout);
2393 (void)strlcpy(macros[macnum].mac_name, argv[1],
2394 sizeof(macros[macnum].mac_name));
2395 if (macnum == 0)
2396 macros[macnum].mac_start = macbuf;
2397 else
2398 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2399 tmp = macros[macnum].mac_start;
2400 while (tmp != macbuf+4096) {
2401 if ((c = getchar()) == EOF) {
2402 fputs("macdef: end of file encountered.\n", ttyout);
2403 code = -1;
2404 return;
2405 }
2406 if ((*tmp = c) == '\n') {
2407 if (tmp == macros[macnum].mac_start) {
2408 macros[macnum++].mac_end = tmp;
2409 code = 0;
2410 return;
2411 }
2412 if (*(tmp-1) == '\0') {
2413 macros[macnum++].mac_end = tmp - 1;
2414 code = 0;
2415 return;
2416 }
2417 *tmp = '\0';
2418 }
2419 tmp++;
2420 }
2421 while (1) {
2422 while ((c = getchar()) != '\n' && c != EOF)
2423 /* LOOP */;
2424 if (c == EOF || getchar() == '\n') {
2425 fputs("Macro not defined - 4K buffer exceeded.\n",
2426 ttyout);
2427 code = -1;
2428 return;
2429 }
2430 }
2431 }
2432
2433 /*
2434 * Get size of file on remote machine
2435 */
2436 void
2437 sizecmd(int argc, char *argv[])
2438 {
2439 off_t size;
2440
2441 if (argc == 0 || argc > 2 ||
2442 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2443 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2444 code = -1;
2445 return;
2446 }
2447 size = remotesize(argv[1], 1);
2448 if (size != -1)
2449 fprintf(ttyout,
2450 "%s\t" LLF "\n", argv[1], (LLT)size);
2451 code = (size > 0);
2452 }
2453
2454 /*
2455 * Get last modification time of file on remote machine
2456 */
2457 void
2458 modtime(int argc, char *argv[])
2459 {
2460 time_t mtime;
2461
2462 if (argc == 0 || argc > 2 ||
2463 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2464 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2465 code = -1;
2466 return;
2467 }
2468 mtime = remotemodtime(argv[1], 1);
2469 if (mtime != -1)
2470 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2471 code = (mtime > 0);
2472 }
2473
2474 /*
2475 * Show status on remote machine
2476 */
2477 void
2478 rmtstatus(int argc, char *argv[])
2479 {
2480
2481 if (argc == 0) {
2482 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]);
2483 code = -1;
2484 return;
2485 }
2486 COMMAND_1ARG(argc, argv, "STAT");
2487 }
2488
2489 /*
2490 * Get file if modtime is more recent than current file
2491 */
2492 void
2493 newer(int argc, char *argv[])
2494 {
2495
2496 if (getit(argc, argv, -1, "w"))
2497 fprintf(ttyout,
2498 "Local file \"%s\" is newer than remote file \"%s\".\n",
2499 argv[2], argv[1]);
2500 }
2501
2502 /*
2503 * Display one local file through $PAGER.
2504 */
2505 void
2506 lpage(int argc, char *argv[])
2507 {
2508 int len;
2509 char *p, *pager, *locfile;
2510
2511 if (argc == 0 || argc > 2 ||
2512 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2513 fprintf(ttyout, "usage: %s local-file\n", argv[0]);
2514 code = -1;
2515 return;
2516 }
2517 if ((locfile = globulize(argv[1])) == NULL) {
2518 code = -1;
2519 return;
2520 }
2521 p = getoptionvalue("pager");
2522 if (EMPTYSTRING(p))
2523 p = DEFAULTPAGER;
2524 len = strlen(p) + strlen(locfile) + 2;
2525 pager = xmalloc(len);
2526 (void)strlcpy(pager, p, len);
2527 (void)strlcat(pager, " ", len);
2528 (void)strlcat(pager, locfile, len);
2529 system(pager);
2530 code = 0;
2531 (void)free(pager);
2532 (void)free(locfile);
2533 }
2534
2535 /*
2536 * Display one remote file through $PAGER.
2537 */
2538 void
2539 page(int argc, char *argv[])
2540 {
2541 int ohash, orestart_point, overbose, len;
2542 char *p, *pager;
2543
2544 if (argc == 0 || argc > 2 ||
2545 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2546 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2547 code = -1;
2548 return;
2549 }
2550 p = getoptionvalue("pager");
2551 if (EMPTYSTRING(p))
2552 p = DEFAULTPAGER;
2553 len = strlen(p) + 2;
2554 pager = xmalloc(len);
2555 pager[0] = '|';
2556 (void)strlcpy(pager + 1, p, len - 1);
2557
2558 ohash = hash;
2559 orestart_point = restart_point;
2560 overbose = verbose;
2561 hash = restart_point = verbose = 0;
2562 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2563 hash = ohash;
2564 restart_point = orestart_point;
2565 verbose = overbose;
2566 (void)free(pager);
2567 }
2568
2569 /*
2570 * Set the socket send or receive buffer size.
2571 */
2572 void
2573 setxferbuf(int argc, char *argv[])
2574 {
2575 int size, dir;
2576
2577 if (argc != 2) {
2578 usage:
2579 fprintf(ttyout, "usage: %s size\n", argv[0]);
2580 code = -1;
2581 return;
2582 }
2583 if (strcasecmp(argv[0], "sndbuf") == 0)
2584 dir = RATE_PUT;
2585 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2586 dir = RATE_GET;
2587 else if (strcasecmp(argv[0], "xferbuf") == 0)
2588 dir = RATE_ALL;
2589 else
2590 goto usage;
2591
2592 if ((size = strsuftoi(argv[1])) == -1)
2593 goto usage;
2594
2595 if (size == 0) {
2596 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2597 goto usage;
2598 }
2599
2600 if (dir & RATE_PUT)
2601 sndbuf_size = size;
2602 if (dir & RATE_GET)
2603 rcvbuf_size = size;
2604 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2605 sndbuf_size, rcvbuf_size);
2606 code = 0;
2607 }
2608
2609 /*
2610 * Set or display options (defaults are provided by various env vars)
2611 */
2612 void
2613 setoption(int argc, char *argv[])
2614 {
2615 struct option *o;
2616
2617 code = -1;
2618 if (argc == 0 || (argc != 1 && argc != 3)) {
2619 fprintf(ttyout, "usage: %s [option value]\n", argv[0]);
2620 return;
2621 }
2622
2623 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2624 if (argc == 1) {
2625 for (o = optiontab; o->name != NULL; o++) {
2626 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2627 o->name, o->value ? o->value : "");
2628 }
2629 } else {
2630 o = getoption(argv[1]);
2631 if (o == NULL) {
2632 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2633 return;
2634 }
2635 FREEPTR(o->value);
2636 o->value = xstrdup(argv[2]);
2637 if (verbose)
2638 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2639 o->name, o->value);
2640 }
2641 code = 0;
2642 }
2643
2644 /*
2645 * Unset an option
2646 */
2647 void
2648 unsetoption(int argc, char *argv[])
2649 {
2650 struct option *o;
2651
2652 code = -1;
2653 if (argc == 0 || argc != 2) {
2654 fprintf(ttyout, "usage: %s option\n", argv[0]);
2655 return;
2656 }
2657
2658 o = getoption(argv[1]);
2659 if (o == NULL) {
2660 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2661 return;
2662 }
2663 FREEPTR(o->value);
2664 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2665 code = 0;
2666 }
2667
2668 /*
2669 * Display features supported by the remote host.
2670 */
2671 void
2672 feat(int argc, char *argv[])
2673 {
2674 int oldverbose = verbose;
2675
2676 if (argc == 0) {
2677 fprintf(ttyout, "usage: %s\n", argv[0]);
2678 code = -1;
2679 return;
2680 }
2681 if (! features[FEAT_FEAT]) {
2682 fprintf(ttyout,
2683 "FEAT is not supported by the remote server.\n");
2684 return;
2685 }
2686 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2687 (void)command("FEAT");
2688 verbose = oldverbose;
2689 }
2690
2691 void
2692 mlst(int argc, char *argv[])
2693 {
2694 int oldverbose = verbose;
2695
2696 if (argc < 1 || argc > 2) {
2697 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]);
2698 code = -1;
2699 return;
2700 }
2701 if (! features[FEAT_MLST]) {
2702 fprintf(ttyout,
2703 "MLST is not supported by the remote server.\n");
2704 return;
2705 }
2706 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2707 COMMAND_1ARG(argc, argv, "MLST");
2708 verbose = oldverbose;
2709 }
2710
2711 void
2712 opts(int argc, char *argv[])
2713 {
2714 int oldverbose = verbose;
2715
2716 if (argc < 2 || argc > 3) {
2717 fprintf(ttyout, "usage: %s command [options]\n", argv[0]);
2718 code = -1;
2719 return;
2720 }
2721 if (! features[FEAT_FEAT]) {
2722 fprintf(ttyout,
2723 "OPTS is not supported by the remote server.\n");
2724 return;
2725 }
2726 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2727 if (argc == 2)
2728 command("OPTS %s", argv[1]);
2729 else
2730 command("OPTS %s %s", argv[1], argv[2]);
2731 verbose = oldverbose;
2732 }
2733