cmds.c revision 1.112 1 /* $NetBSD: cmds.c,v 1.112 2005/04/11 01:49:31 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.112 2005/04/11 01:49:31 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 char line[BUFSIZ];
162
163 if (!interactive || confirmrest)
164 return (1);
165 while (1) {
166 fprintf(ttyout, "%s %s [anpqy?]? ", cmd, file);
167 (void)fflush(ttyout);
168 if (fgets(line, sizeof(line), stdin) == NULL) {
169 mflag = 0;
170 fprintf(ttyout, "\nEOF received; %s aborted\n", mname);
171 clearerr(stdin);
172 return (0);
173 }
174 switch (tolower((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 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 int 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 acct[80];
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 argv[2] = getpass("Password: ");
1493 argc++;
1494 }
1495 n = command("PASS %s", argv[2]);
1496 }
1497 if (n == CONTINUE) {
1498 if (argc < 4) {
1499 (void)fputs("Account: ", ttyout);
1500 (void)fflush(ttyout);
1501 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1502 fprintf(ttyout,
1503 "\nEOF received; login aborted.\n");
1504 clearerr(stdin);
1505 code = -1;
1506 return;
1507 }
1508 acct[strlen(acct) - 1] = '\0';
1509 argv[3] = acct; argc++;
1510 }
1511 n = command("ACCT %s", argv[3]);
1512 aflag++;
1513 }
1514 if (n != COMPLETE) {
1515 fputs("Login failed.\n", ttyout);
1516 return;
1517 }
1518 if (!aflag && argc == 4) {
1519 (void)command("ACCT %s", argv[3]);
1520 }
1521 connected = -1;
1522 getremoteinfo();
1523 }
1524
1525 /*
1526 * Print working directory on remote machine.
1527 */
1528 /*VARARGS*/
1529 void
1530 pwd(int argc, char *argv[])
1531 {
1532
1533 code = -1;
1534 if (argc != 1) {
1535 fprintf(ttyout, "usage: %s\n", argv[0]);
1536 return;
1537 }
1538 if (! remotecwd[0])
1539 updateremotecwd();
1540 if (! remotecwd[0])
1541 fprintf(ttyout, "Unable to determine remote directory\n");
1542 else {
1543 fprintf(ttyout, "Remote directory: %s\n", remotecwd);
1544 code = 0;
1545 }
1546 }
1547
1548 /*
1549 * Print working directory on local machine.
1550 */
1551 void
1552 lpwd(int argc, char *argv[])
1553 {
1554
1555 code = -1;
1556 if (argc != 1) {
1557 fprintf(ttyout, "usage: %s\n", argv[0]);
1558 return;
1559 }
1560 if (! localcwd[0])
1561 updatelocalcwd();
1562 if (! localcwd[0])
1563 fprintf(ttyout, "Unable to determine local directory\n");
1564 else {
1565 fprintf(ttyout, "Local directory: %s\n", localcwd);
1566 code = 0;
1567 }
1568 }
1569
1570 /*
1571 * Make a directory.
1572 */
1573 void
1574 makedir(int argc, char *argv[])
1575 {
1576 int r;
1577
1578 if (argc == 0 || argc > 2 ||
1579 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1580 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1581 code = -1;
1582 return;
1583 }
1584 r = command("MKD %s", argv[1]);
1585 if (r == ERROR && code == 500) {
1586 if (verbose)
1587 fputs("MKD command not recognized, trying XMKD.\n",
1588 ttyout);
1589 r = command("XMKD %s", argv[1]);
1590 }
1591 if (r == COMPLETE)
1592 dirchange = 1;
1593 }
1594
1595 /*
1596 * Remove a directory.
1597 */
1598 void
1599 removedir(int argc, char *argv[])
1600 {
1601 int r;
1602
1603 if (argc == 0 || argc > 2 ||
1604 (argc == 1 && !another(&argc, &argv, "directory-name"))) {
1605 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1606 code = -1;
1607 return;
1608 }
1609 r = command("RMD %s", argv[1]);
1610 if (r == ERROR && code == 500) {
1611 if (verbose)
1612 fputs("RMD command not recognized, trying XRMD.\n",
1613 ttyout);
1614 r = command("XRMD %s", argv[1]);
1615 }
1616 if (r == COMPLETE)
1617 dirchange = 1;
1618 }
1619
1620 /*
1621 * Send a line, verbatim, to the remote machine.
1622 */
1623 void
1624 quote(int argc, char *argv[])
1625 {
1626
1627 if (argc == 0 ||
1628 (argc == 1 && !another(&argc, &argv, "command line to send"))) {
1629 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1630 code = -1;
1631 return;
1632 }
1633 quote1("", argc, argv);
1634 }
1635
1636 /*
1637 * Send a SITE command to the remote machine. The line
1638 * is sent verbatim to the remote machine, except that the
1639 * word "SITE" is added at the front.
1640 */
1641 void
1642 site(int argc, char *argv[])
1643 {
1644
1645 if (argc == 0 ||
1646 (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){
1647 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1648 code = -1;
1649 return;
1650 }
1651 quote1("SITE ", argc, argv);
1652 }
1653
1654 /*
1655 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1656 * Send the result as a one-line command and get response.
1657 */
1658 void
1659 quote1(const char *initial, int argc, char *argv[])
1660 {
1661 int i;
1662 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1663
1664 (void)strlcpy(buf, initial, sizeof(buf));
1665 for (i = 1; i < argc; i++) {
1666 (void)strlcat(buf, argv[i], sizeof(buf));
1667 if (i < (argc - 1))
1668 (void)strlcat(buf, " ", sizeof(buf));
1669 }
1670 if (command("%s", buf) == PRELIM) {
1671 while (getreply(0) == PRELIM)
1672 continue;
1673 }
1674 dirchange = 1;
1675 }
1676
1677 void
1678 do_chmod(int argc, char *argv[])
1679 {
1680
1681 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode")))
1682 goto usage;
1683 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
1684 usage:
1685 fprintf(ttyout, "usage: %s mode remote-file\n", argv[0]);
1686 code = -1;
1687 return;
1688 }
1689 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1690 }
1691
1692 #define COMMAND_1ARG(argc, argv, cmd) \
1693 if (argc == 1) \
1694 command(cmd); \
1695 else \
1696 command(cmd " %s", argv[1])
1697
1698 void
1699 do_umask(int argc, char *argv[])
1700 {
1701 int oldverbose = verbose;
1702
1703 if (argc == 0) {
1704 fprintf(ttyout, "usage: %s [umask]\n", argv[0]);
1705 code = -1;
1706 return;
1707 }
1708 verbose = 1;
1709 COMMAND_1ARG(argc, argv, "SITE UMASK");
1710 verbose = oldverbose;
1711 }
1712
1713 void
1714 idlecmd(int argc, char *argv[])
1715 {
1716 int oldverbose = verbose;
1717
1718 if (argc < 1 || argc > 2) {
1719 fprintf(ttyout, "usage: %s [seconds]\n", argv[0]);
1720 code = -1;
1721 return;
1722 }
1723 verbose = 1;
1724 COMMAND_1ARG(argc, argv, "SITE IDLE");
1725 verbose = oldverbose;
1726 }
1727
1728 /*
1729 * Ask the other side for help.
1730 */
1731 void
1732 rmthelp(int argc, char *argv[])
1733 {
1734 int oldverbose = verbose;
1735
1736 if (argc == 0) {
1737 fprintf(ttyout, "usage: %s\n", argv[0]);
1738 code = -1;
1739 return;
1740 }
1741 verbose = 1;
1742 COMMAND_1ARG(argc, argv, "HELP");
1743 verbose = oldverbose;
1744 }
1745
1746 /*
1747 * Terminate session and exit.
1748 * May be called with 0, NULL.
1749 */
1750 /*VARARGS*/
1751 void
1752 quit(int argc, char *argv[])
1753 {
1754
1755 /* this may be called with argc == 0, argv == NULL */
1756 if (argc == 0 && argv != NULL) {
1757 fprintf(ttyout, "usage: %s\n", argv[0]);
1758 code = -1;
1759 return;
1760 }
1761 if (connected)
1762 disconnect(0, NULL);
1763 pswitch(1);
1764 if (connected)
1765 disconnect(0, NULL);
1766 exit(0);
1767 }
1768
1769 /*
1770 * Terminate session, but don't exit.
1771 * May be called with 0, NULL.
1772 */
1773 void
1774 disconnect(int argc, char *argv[])
1775 {
1776
1777 /* this may be called with argc == 0, argv == NULL */
1778 if (argc == 0 && argv != NULL) {
1779 fprintf(ttyout, "usage: %s\n", argv[0]);
1780 code = -1;
1781 return;
1782 }
1783 if (!connected)
1784 return;
1785 (void)command("QUIT");
1786 cleanuppeer();
1787 }
1788
1789 void
1790 account(int argc, char *argv[])
1791 {
1792 char *ap;
1793
1794 if (argc == 0 || argc > 2) {
1795 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1796 code = -1;
1797 return;
1798 }
1799 else if (argc == 2)
1800 ap = argv[1];
1801 else
1802 ap = getpass("Account:");
1803 (void)command("ACCT %s", ap);
1804 }
1805
1806 sigjmp_buf abortprox;
1807
1808 void
1809 proxabort(int notused)
1810 {
1811
1812 sigint_raised = 1;
1813 alarmtimer(0);
1814 if (!proxy) {
1815 pswitch(1);
1816 }
1817 if (connected) {
1818 proxflag = 1;
1819 }
1820 else {
1821 proxflag = 0;
1822 }
1823 pswitch(0);
1824 siglongjmp(abortprox, 1);
1825 }
1826
1827 void
1828 doproxy(int argc, char *argv[])
1829 {
1830 struct cmd *c;
1831 int cmdpos;
1832 sigfunc oldintr;
1833
1834 if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) {
1835 fprintf(ttyout, "usage: %s command\n", argv[0]);
1836 code = -1;
1837 return;
1838 }
1839 c = getcmd(argv[1]);
1840 if (c == (struct cmd *) -1) {
1841 fputs("?Ambiguous command.\n", ttyout);
1842 code = -1;
1843 return;
1844 }
1845 if (c == 0) {
1846 fputs("?Invalid command.\n", ttyout);
1847 code = -1;
1848 return;
1849 }
1850 if (!c->c_proxy) {
1851 fputs("?Invalid proxy command.\n", ttyout);
1852 code = -1;
1853 return;
1854 }
1855 if (sigsetjmp(abortprox, 1)) {
1856 code = -1;
1857 return;
1858 }
1859 oldintr = xsignal(SIGINT, proxabort);
1860 pswitch(1);
1861 if (c->c_conn && !connected) {
1862 fputs("Not connected.\n", ttyout);
1863 pswitch(0);
1864 (void)xsignal(SIGINT, oldintr);
1865 code = -1;
1866 return;
1867 }
1868 cmdpos = strcspn(line, " \t");
1869 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1870 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1871 argv[1] = c->c_name;
1872 (*c->c_handler)(argc-1, argv+1);
1873 if (connected) {
1874 proxflag = 1;
1875 }
1876 else {
1877 proxflag = 0;
1878 }
1879 pswitch(0);
1880 (void)xsignal(SIGINT, oldintr);
1881 }
1882
1883 void
1884 setcase(int argc, char *argv[])
1885 {
1886
1887 code = togglevar(argc, argv, &mcase, "Case mapping");
1888 }
1889
1890 /*
1891 * convert the given name to lower case if it's all upper case, into
1892 * a static buffer which is returned to the caller
1893 */
1894 static const char *
1895 docase(char *dst, size_t dlen, const char *src)
1896 {
1897 size_t i;
1898 int dochange = 1;
1899
1900 for (i = 0; src[i] != '\0' && i < dlen - 1; i++) {
1901 dst[i] = src[i];
1902 if (islower((unsigned char)dst[i]))
1903 dochange = 0;
1904 }
1905 dst[i] = '\0';
1906
1907 if (dochange) {
1908 for (i = 0; dst[i] != '\0'; i++)
1909 if (isupper((unsigned char)dst[i]))
1910 dst[i] = tolower((unsigned char)dst[i]);
1911 }
1912 return dst;
1913 }
1914
1915 void
1916 setcr(int argc, char *argv[])
1917 {
1918
1919 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1920 }
1921
1922 void
1923 setntrans(int argc, char *argv[])
1924 {
1925
1926 if (argc == 0 || argc > 3) {
1927 fprintf(ttyout, "usage: %s [inchars [outchars]]\n", argv[0]);
1928 code = -1;
1929 return;
1930 }
1931 if (argc == 1) {
1932 ntflag = 0;
1933 fputs("Ntrans off.\n", ttyout);
1934 code = ntflag;
1935 return;
1936 }
1937 ntflag++;
1938 code = ntflag;
1939 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1940 if (argc == 2) {
1941 ntout[0] = '\0';
1942 return;
1943 }
1944 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1945 }
1946
1947 static const char *
1948 dotrans(char *dst, size_t dlen, const char *src)
1949 {
1950 const char *cp1;
1951 char *cp2 = dst;
1952 size_t i, ostop;
1953
1954 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1955 continue;
1956 for (cp1 = src; *cp1; cp1++) {
1957 int found = 0;
1958 for (i = 0; *(ntin + i) && i < 16; i++) {
1959 if (*cp1 == *(ntin + i)) {
1960 found++;
1961 if (i < ostop) {
1962 *cp2++ = *(ntout + i);
1963 if (cp2 - dst >= dlen - 1)
1964 goto out;
1965 }
1966 break;
1967 }
1968 }
1969 if (!found) {
1970 *cp2++ = *cp1;
1971 }
1972 }
1973 out:
1974 *cp2 = '\0';
1975 return dst;
1976 }
1977
1978 void
1979 setnmap(int argc, char *argv[])
1980 {
1981 char *cp;
1982
1983 if (argc == 1) {
1984 mapflag = 0;
1985 fputs("Nmap off.\n", ttyout);
1986 code = mapflag;
1987 return;
1988 }
1989 if (argc == 0 ||
1990 (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1991 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1992 code = -1;
1993 return;
1994 }
1995 mapflag = 1;
1996 code = 1;
1997 cp = strchr(altarg, ' ');
1998 if (proxy) {
1999 while(*++cp == ' ')
2000 continue;
2001 altarg = cp;
2002 cp = strchr(altarg, ' ');
2003 }
2004 *cp = '\0';
2005 (void)strlcpy(mapin, altarg, MAXPATHLEN);
2006 while (*++cp == ' ')
2007 continue;
2008 (void)strlcpy(mapout, cp, MAXPATHLEN);
2009 }
2010
2011 static const char *
2012 domap(char *dst, size_t dlen, const char *src)
2013 {
2014 const char *cp1 = src;
2015 char *cp2 = mapin;
2016 const char *tp[9], *te[9];
2017 int i, toks[9], toknum = 0, match = 1;
2018
2019 for (i=0; i < 9; ++i) {
2020 toks[i] = 0;
2021 }
2022 while (match && *cp1 && *cp2) {
2023 switch (*cp2) {
2024 case '\\':
2025 if (*++cp2 != *cp1) {
2026 match = 0;
2027 }
2028 break;
2029 case '$':
2030 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
2031 if (*cp1 != *(++cp2+1)) {
2032 toks[toknum = *cp2 - '1']++;
2033 tp[toknum] = cp1;
2034 while (*++cp1 && *(cp2+1)
2035 != *cp1);
2036 te[toknum] = cp1;
2037 }
2038 cp2++;
2039 break;
2040 }
2041 /* FALLTHROUGH */
2042 default:
2043 if (*cp2 != *cp1) {
2044 match = 0;
2045 }
2046 break;
2047 }
2048 if (match && *cp1) {
2049 cp1++;
2050 }
2051 if (match && *cp2) {
2052 cp2++;
2053 }
2054 }
2055 if (!match && *cp1) /* last token mismatch */
2056 {
2057 toks[toknum] = 0;
2058 }
2059 cp2 = dst;
2060 *cp2 = '\0';
2061 cp1 = mapout;
2062 while (*cp1) {
2063 match = 0;
2064 switch (*cp1) {
2065 case '\\':
2066 if (*(cp1 + 1)) {
2067 *cp2++ = *++cp1;
2068 }
2069 break;
2070 case '[':
2071 LOOP:
2072 if (*++cp1 == '$' &&
2073 isdigit((unsigned char)*(cp1+1))) {
2074 if (*++cp1 == '0') {
2075 const char *cp3 = src;
2076
2077 while (*cp3) {
2078 *cp2++ = *cp3++;
2079 }
2080 match = 1;
2081 }
2082 else if (toks[toknum = *cp1 - '1']) {
2083 const char *cp3 = tp[toknum];
2084
2085 while (cp3 != te[toknum]) {
2086 *cp2++ = *cp3++;
2087 }
2088 match = 1;
2089 }
2090 }
2091 else {
2092 while (*cp1 && *cp1 != ',' &&
2093 *cp1 != ']') {
2094 if (*cp1 == '\\') {
2095 cp1++;
2096 }
2097 else if (*cp1 == '$' &&
2098 isdigit((unsigned char)*(cp1+1))) {
2099 if (*++cp1 == '0') {
2100 const char *cp3 = src;
2101
2102 while (*cp3) {
2103 *cp2++ = *cp3++;
2104 }
2105 }
2106 else if (toks[toknum =
2107 *cp1 - '1']) {
2108 const char *cp3=tp[toknum];
2109
2110 while (cp3 !=
2111 te[toknum]) {
2112 *cp2++ = *cp3++;
2113 }
2114 }
2115 }
2116 else if (*cp1) {
2117 *cp2++ = *cp1++;
2118 }
2119 }
2120 if (!*cp1) {
2121 fputs(
2122 "nmap: unbalanced brackets.\n",
2123 ttyout);
2124 return (src);
2125 }
2126 match = 1;
2127 cp1--;
2128 }
2129 if (match) {
2130 while (*++cp1 && *cp1 != ']') {
2131 if (*cp1 == '\\' && *(cp1 + 1)) {
2132 cp1++;
2133 }
2134 }
2135 if (!*cp1) {
2136 fputs(
2137 "nmap: unbalanced brackets.\n",
2138 ttyout);
2139 return (src);
2140 }
2141 break;
2142 }
2143 switch (*++cp1) {
2144 case ',':
2145 goto LOOP;
2146 case ']':
2147 break;
2148 default:
2149 cp1--;
2150 goto LOOP;
2151 }
2152 break;
2153 case '$':
2154 if (isdigit((unsigned char)*(cp1 + 1))) {
2155 if (*++cp1 == '0') {
2156 const char *cp3 = src;
2157
2158 while (*cp3) {
2159 *cp2++ = *cp3++;
2160 }
2161 }
2162 else if (toks[toknum = *cp1 - '1']) {
2163 const char *cp3 = tp[toknum];
2164
2165 while (cp3 != te[toknum]) {
2166 *cp2++ = *cp3++;
2167 }
2168 }
2169 break;
2170 }
2171 /* intentional drop through */
2172 default:
2173 *cp2++ = *cp1;
2174 break;
2175 }
2176 cp1++;
2177 }
2178 *cp2 = '\0';
2179 return *dst ? dst : src;
2180 }
2181
2182 void
2183 setpassive(int argc, char *argv[])
2184 {
2185
2186 if (argc == 1) {
2187 passivemode = !passivemode;
2188 activefallback = passivemode;
2189 } else if (argc != 2) {
2190 passiveusage:
2191 fprintf(ttyout, "usage: %s [ on | off | auto ]\n", argv[0]);
2192 code = -1;
2193 return;
2194 } else if (strcasecmp(argv[1], "on") == 0) {
2195 passivemode = 1;
2196 activefallback = 0;
2197 } else if (strcasecmp(argv[1], "off") == 0) {
2198 passivemode = 0;
2199 activefallback = 0;
2200 } else if (strcasecmp(argv[1], "auto") == 0) {
2201 passivemode = 1;
2202 activefallback = 1;
2203 } else
2204 goto passiveusage;
2205 fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n",
2206 onoff(passivemode), onoff(activefallback));
2207 code = passivemode;
2208 }
2209
2210 void
2211 setepsv4(int argc, char *argv[])
2212 {
2213
2214 code = togglevar(argc, argv, &epsv4,
2215 verbose ? "EPSV/EPRT on IPv4" : NULL);
2216 epsv4bad = 0;
2217 }
2218
2219 void
2220 setsunique(int argc, char *argv[])
2221 {
2222
2223 code = togglevar(argc, argv, &sunique, "Store unique");
2224 }
2225
2226 void
2227 setrunique(int argc, char *argv[])
2228 {
2229
2230 code = togglevar(argc, argv, &runique, "Receive unique");
2231 }
2232
2233 int
2234 parserate(int argc, char *argv[], int cmdlineopt)
2235 {
2236 int dir, max, incr, showonly;
2237 sigfunc oldusr1, oldusr2;
2238
2239 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2240 usage:
2241 if (cmdlineopt)
2242 fprintf(ttyout,
2243 "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n",
2244 argv[0]);
2245 else
2246 fprintf(ttyout,
2247 "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n",
2248 argv[0]);
2249 return -1;
2250 }
2251 dir = max = incr = showonly = 0;
2252 #define RATE_GET 1
2253 #define RATE_PUT 2
2254 #define RATE_ALL (RATE_GET | RATE_PUT)
2255
2256 if (strcasecmp(argv[1], "all") == 0)
2257 dir = RATE_ALL;
2258 else if (strcasecmp(argv[1], "get") == 0)
2259 dir = RATE_GET;
2260 else if (strcasecmp(argv[1], "put") == 0)
2261 dir = RATE_PUT;
2262 else
2263 goto usage;
2264
2265 if (argc >= 3) {
2266 if ((max = strsuftoi(argv[2])) < 0)
2267 goto usage;
2268 } else
2269 showonly = 1;
2270
2271 if (argc == 4) {
2272 if ((incr = strsuftoi(argv[3])) <= 0)
2273 goto usage;
2274 } else
2275 incr = DEFAULTINCR;
2276
2277 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2278 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2279 if (dir & RATE_GET) {
2280 if (!showonly) {
2281 rate_get = max;
2282 rate_get_incr = incr;
2283 }
2284 if (!cmdlineopt || verbose)
2285 fprintf(ttyout,
2286 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2287 onoff(rate_get), rate_get, rate_get_incr);
2288 }
2289 if (dir & RATE_PUT) {
2290 if (!showonly) {
2291 rate_put = max;
2292 rate_put_incr = incr;
2293 }
2294 if (!cmdlineopt || verbose)
2295 fprintf(ttyout,
2296 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2297 onoff(rate_put), rate_put, rate_put_incr);
2298 }
2299 (void)xsignal(SIGUSR1, oldusr1);
2300 (void)xsignal(SIGUSR2, oldusr2);
2301 return 0;
2302 }
2303
2304 void
2305 setrate(int argc, char *argv[])
2306 {
2307
2308 code = parserate(argc, argv, 0);
2309 }
2310
2311 /* change directory to parent directory */
2312 void
2313 cdup(int argc, char *argv[])
2314 {
2315 int r;
2316
2317 if (argc == 0) {
2318 fprintf(ttyout, "usage: %s\n", argv[0]);
2319 code = -1;
2320 return;
2321 }
2322 r = command("CDUP");
2323 if (r == ERROR && code == 500) {
2324 if (verbose)
2325 fputs("CDUP command not recognized, trying XCUP.\n",
2326 ttyout);
2327 r = command("XCUP");
2328 }
2329 if (r == COMPLETE) {
2330 dirchange = 1;
2331 updateremotecwd();
2332 }
2333 }
2334
2335 /*
2336 * Restart transfer at specific point
2337 */
2338 void
2339 restart(int argc, char *argv[])
2340 {
2341
2342 if (argc == 0 || argc > 2) {
2343 fprintf(ttyout, "usage: %s [restart-point]\n", argv[0]);
2344 code = -1;
2345 return;
2346 }
2347 if (! features[FEAT_REST_STREAM]) {
2348 fprintf(ttyout,
2349 "Restart is not supported by the remote server.\n");
2350 return;
2351 }
2352 if (argc == 2) {
2353 off_t rp;
2354 char *ep;
2355
2356 rp = STRTOLL(argv[1], &ep, 10);
2357 if (rp < 0 || *ep != '\0')
2358 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2359 argv[1]);
2360 else
2361 restart_point = rp;
2362 }
2363 if (restart_point == 0)
2364 fputs("No restart point defined.\n", ttyout);
2365 else
2366 fprintf(ttyout,
2367 "Restarting at " LLF " for next get, put or append\n",
2368 (LLT)restart_point);
2369 }
2370
2371 /*
2372 * Show remote system type
2373 */
2374 void
2375 syst(int argc, char *argv[])
2376 {
2377 int oldverbose = verbose;
2378
2379 if (argc == 0) {
2380 fprintf(ttyout, "usage: %s\n", argv[0]);
2381 code = -1;
2382 return;
2383 }
2384 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2385 (void)command("SYST");
2386 verbose = oldverbose;
2387 }
2388
2389 void
2390 macdef(int argc, char *argv[])
2391 {
2392 char *tmp;
2393 int c;
2394
2395 if (argc == 0)
2396 goto usage;
2397 if (macnum == 16) {
2398 fputs("Limit of 16 macros have already been defined.\n",
2399 ttyout);
2400 code = -1;
2401 return;
2402 }
2403 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2404 usage:
2405 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2406 code = -1;
2407 return;
2408 }
2409 if (interactive)
2410 fputs(
2411 "Enter macro line by line, terminating it with a null line.\n",
2412 ttyout);
2413 (void)strlcpy(macros[macnum].mac_name, argv[1],
2414 sizeof(macros[macnum].mac_name));
2415 if (macnum == 0)
2416 macros[macnum].mac_start = macbuf;
2417 else
2418 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2419 tmp = macros[macnum].mac_start;
2420 while (tmp != macbuf+4096) {
2421 if ((c = getchar()) == EOF) {
2422 fputs("macdef: end of file encountered.\n", ttyout);
2423 code = -1;
2424 return;
2425 }
2426 if ((*tmp = c) == '\n') {
2427 if (tmp == macros[macnum].mac_start) {
2428 macros[macnum++].mac_end = tmp;
2429 code = 0;
2430 return;
2431 }
2432 if (*(tmp-1) == '\0') {
2433 macros[macnum++].mac_end = tmp - 1;
2434 code = 0;
2435 return;
2436 }
2437 *tmp = '\0';
2438 }
2439 tmp++;
2440 }
2441 while (1) {
2442 while ((c = getchar()) != '\n' && c != EOF)
2443 /* LOOP */;
2444 if (c == EOF || getchar() == '\n') {
2445 fputs("Macro not defined - 4K buffer exceeded.\n",
2446 ttyout);
2447 code = -1;
2448 return;
2449 }
2450 }
2451 }
2452
2453 /*
2454 * Get size of file on remote machine
2455 */
2456 void
2457 sizecmd(int argc, char *argv[])
2458 {
2459 off_t size;
2460
2461 if (argc == 0 || argc > 2 ||
2462 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2463 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2464 code = -1;
2465 return;
2466 }
2467 size = remotesize(argv[1], 1);
2468 if (size != -1)
2469 fprintf(ttyout,
2470 "%s\t" LLF "\n", argv[1], (LLT)size);
2471 code = (size > 0);
2472 }
2473
2474 /*
2475 * Get last modification time of file on remote machine
2476 */
2477 void
2478 modtime(int argc, char *argv[])
2479 {
2480 time_t mtime;
2481
2482 if (argc == 0 || argc > 2 ||
2483 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2484 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2485 code = -1;
2486 return;
2487 }
2488 mtime = remotemodtime(argv[1], 1);
2489 if (mtime != -1)
2490 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2491 code = (mtime > 0);
2492 }
2493
2494 /*
2495 * Show status on remote machine
2496 */
2497 void
2498 rmtstatus(int argc, char *argv[])
2499 {
2500
2501 if (argc == 0) {
2502 fprintf(ttyout, "usage: %s [remote-file]\n", argv[0]);
2503 code = -1;
2504 return;
2505 }
2506 COMMAND_1ARG(argc, argv, "STAT");
2507 }
2508
2509 /*
2510 * Get file if modtime is more recent than current file
2511 */
2512 void
2513 newer(int argc, char *argv[])
2514 {
2515
2516 if (getit(argc, argv, -1, "w"))
2517 fprintf(ttyout,
2518 "Local file \"%s\" is newer than remote file \"%s\".\n",
2519 argv[2], argv[1]);
2520 }
2521
2522 /*
2523 * Display one local file through $PAGER.
2524 */
2525 void
2526 lpage(int argc, char *argv[])
2527 {
2528 int len;
2529 char *p, *pager, *locfile;
2530
2531 if (argc == 0 || argc > 2 ||
2532 (argc == 1 && !another(&argc, &argv, "local-file"))) {
2533 fprintf(ttyout, "usage: %s local-file\n", argv[0]);
2534 code = -1;
2535 return;
2536 }
2537 if ((locfile = globulize(argv[1])) == NULL) {
2538 code = -1;
2539 return;
2540 }
2541 p = getoptionvalue("pager");
2542 if (EMPTYSTRING(p))
2543 p = DEFAULTPAGER;
2544 len = strlen(p) + strlen(locfile) + 2;
2545 pager = xmalloc(len);
2546 (void)strlcpy(pager, p, len);
2547 (void)strlcat(pager, " ", len);
2548 (void)strlcat(pager, locfile, len);
2549 system(pager);
2550 code = 0;
2551 (void)free(pager);
2552 (void)free(locfile);
2553 }
2554
2555 /*
2556 * Display one remote file through $PAGER.
2557 */
2558 void
2559 page(int argc, char *argv[])
2560 {
2561 int ohash, orestart_point, overbose, len;
2562 char *p, *pager;
2563
2564 if (argc == 0 || argc > 2 ||
2565 (argc == 1 && !another(&argc, &argv, "remote-file"))) {
2566 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
2567 code = -1;
2568 return;
2569 }
2570 p = getoptionvalue("pager");
2571 if (EMPTYSTRING(p))
2572 p = DEFAULTPAGER;
2573 len = strlen(p) + 2;
2574 pager = xmalloc(len);
2575 pager[0] = '|';
2576 (void)strlcpy(pager + 1, p, len - 1);
2577
2578 ohash = hash;
2579 orestart_point = restart_point;
2580 overbose = verbose;
2581 hash = restart_point = verbose = 0;
2582 recvrequest("RETR", pager, argv[1], "r+", 1, 0);
2583 hash = ohash;
2584 restart_point = orestart_point;
2585 verbose = overbose;
2586 (void)free(pager);
2587 }
2588
2589 /*
2590 * Set the socket send or receive buffer size.
2591 */
2592 void
2593 setxferbuf(int argc, char *argv[])
2594 {
2595 int size, dir;
2596
2597 if (argc != 2) {
2598 usage:
2599 fprintf(ttyout, "usage: %s size\n", argv[0]);
2600 code = -1;
2601 return;
2602 }
2603 if (strcasecmp(argv[0], "sndbuf") == 0)
2604 dir = RATE_PUT;
2605 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2606 dir = RATE_GET;
2607 else if (strcasecmp(argv[0], "xferbuf") == 0)
2608 dir = RATE_ALL;
2609 else
2610 goto usage;
2611
2612 if ((size = strsuftoi(argv[1])) == -1)
2613 goto usage;
2614
2615 if (size == 0) {
2616 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2617 goto usage;
2618 }
2619
2620 if (dir & RATE_PUT)
2621 sndbuf_size = size;
2622 if (dir & RATE_GET)
2623 rcvbuf_size = size;
2624 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2625 sndbuf_size, rcvbuf_size);
2626 code = 0;
2627 }
2628
2629 /*
2630 * Set or display options (defaults are provided by various env vars)
2631 */
2632 void
2633 setoption(int argc, char *argv[])
2634 {
2635 struct option *o;
2636
2637 code = -1;
2638 if (argc == 0 || (argc != 1 && argc != 3)) {
2639 fprintf(ttyout, "usage: %s [option value]\n", argv[0]);
2640 return;
2641 }
2642
2643 #define OPTIONINDENT ((int) sizeof("http_proxy"))
2644 if (argc == 1) {
2645 for (o = optiontab; o->name != NULL; o++) {
2646 fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT,
2647 o->name, o->value ? o->value : "");
2648 }
2649 } else {
2650 o = getoption(argv[1]);
2651 if (o == NULL) {
2652 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2653 return;
2654 }
2655 FREEPTR(o->value);
2656 o->value = xstrdup(argv[2]);
2657 if (verbose)
2658 fprintf(ttyout, "Setting `%s' to `%s'.\n",
2659 o->name, o->value);
2660 }
2661 code = 0;
2662 }
2663
2664 /*
2665 * Unset an option
2666 */
2667 void
2668 unsetoption(int argc, char *argv[])
2669 {
2670 struct option *o;
2671
2672 code = -1;
2673 if (argc == 0 || argc != 2) {
2674 fprintf(ttyout, "usage: %s option\n", argv[0]);
2675 return;
2676 }
2677
2678 o = getoption(argv[1]);
2679 if (o == NULL) {
2680 fprintf(ttyout, "No such option `%s'.\n", argv[1]);
2681 return;
2682 }
2683 FREEPTR(o->value);
2684 fprintf(ttyout, "Unsetting `%s'.\n", o->name);
2685 code = 0;
2686 }
2687
2688 /*
2689 * Display features supported by the remote host.
2690 */
2691 void
2692 feat(int argc, char *argv[])
2693 {
2694 int oldverbose = verbose;
2695
2696 if (argc == 0) {
2697 fprintf(ttyout, "usage: %s\n", argv[0]);
2698 code = -1;
2699 return;
2700 }
2701 if (! features[FEAT_FEAT]) {
2702 fprintf(ttyout,
2703 "FEAT is not supported by the remote server.\n");
2704 return;
2705 }
2706 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2707 (void)command("FEAT");
2708 verbose = oldverbose;
2709 }
2710
2711 void
2712 mlst(int argc, char *argv[])
2713 {
2714 int oldverbose = verbose;
2715
2716 if (argc < 1 || argc > 2) {
2717 fprintf(ttyout, "usage: %s [remote-path]\n", argv[0]);
2718 code = -1;
2719 return;
2720 }
2721 if (! features[FEAT_MLST]) {
2722 fprintf(ttyout,
2723 "MLST is not supported by the remote server.\n");
2724 return;
2725 }
2726 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2727 COMMAND_1ARG(argc, argv, "MLST");
2728 verbose = oldverbose;
2729 }
2730
2731 void
2732 opts(int argc, char *argv[])
2733 {
2734 int oldverbose = verbose;
2735
2736 if (argc < 2 || argc > 3) {
2737 fprintf(ttyout, "usage: %s command [options]\n", argv[0]);
2738 code = -1;
2739 return;
2740 }
2741 if (! features[FEAT_FEAT]) {
2742 fprintf(ttyout,
2743 "OPTS is not supported by the remote server.\n");
2744 return;
2745 }
2746 verbose = 1; /* If we aren't verbose, this doesn't do anything! */
2747 if (argc == 2)
2748 command("OPTS %s", argv[1]);
2749 else
2750 command("OPTS %s %s", argv[1], argv[2]);
2751 verbose = oldverbose;
2752 }
2753