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