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