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