cmds.c revision 1.14 1 /* $NetBSD: cmds.c,v 1.14 1997/01/03 02:55:59 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1985, 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
39 #else
40 static char rcsid[] = "$NetBSD: cmds.c,v 1.14 1997/01/03 02:55:59 lukem Exp $";
41 #endif
42 #endif /* not lint */
43
44 /*
45 * FTP User Program -- Command Routines.
46 */
47 #include <sys/param.h>
48 #include <sys/wait.h>
49 #include <sys/stat.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <arpa/ftp.h>
53
54 #include <ctype.h>
55 #include <err.h>
56 #include <fcntl.h>
57 #include <glob.h>
58 #include <netdb.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <time.h>
64 #include <unistd.h>
65
66 #include "ftp_var.h"
67 #include "pathnames.h"
68
69 jmp_buf jabort;
70 char *mname;
71 char *home = "/";
72
73 /*
74 * `Another' gets another argument, and stores the new argc and argv.
75 * It reverts to the top level (via main.c's intr()) on EOF/error.
76 *
77 * Returns false if no new arguments have been added.
78 */
79 int
80 another(pargc, pargv, prompt)
81 int *pargc;
82 char ***pargv;
83 const char *prompt;
84 {
85 int len = strlen(line), ret;
86
87 if (len >= sizeof(line) - 3) {
88 printf("sorry, arguments too long\n");
89 intr();
90 }
91 printf("(%s) ", prompt);
92 line[len++] = ' ';
93 if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
94 intr();
95 len += strlen(&line[len]);
96 if (len > 0 && line[len - 1] == '\n')
97 line[len - 1] = '\0';
98 makeargv();
99 ret = margc > *pargc;
100 *pargc = margc;
101 *pargv = margv;
102 return (ret);
103 }
104
105 /*
106 * Connect to peer server and
107 * auto-login, if possible.
108 */
109 void
110 setpeer(argc, argv)
111 int argc;
112 char *argv[];
113 {
114 char *host;
115 short port;
116
117 if (connected) {
118 printf("Already connected to %s, use close first.\n",
119 hostname);
120 code = -1;
121 return;
122 }
123 if (argc < 2)
124 (void) another(&argc, &argv, "to");
125 if (argc < 2 || argc > 3) {
126 printf("usage: %s host-name [port]\n", argv[0]);
127 code = -1;
128 return;
129 }
130 port = sp->s_port;
131 if (argc > 2) {
132 port = atoi(argv[2]);
133 if (port <= 0) {
134 printf("%s: bad port number-- %s\n", argv[1], argv[2]);
135 printf ("usage: %s host-name [port]\n", argv[0]);
136 code = -1;
137 return;
138 }
139 port = htons(port);
140 }
141 host = hookup(argv[1], port);
142 if (host) {
143 int overbose;
144
145 connected = 1;
146 /*
147 * Set up defaults for FTP.
148 */
149 (void) strcpy(typename, "ascii"), type = TYPE_A;
150 curtype = TYPE_A;
151 (void) strcpy(formname, "non-print"), form = FORM_N;
152 (void) strcpy(modename, "stream"), mode = MODE_S;
153 (void) strcpy(structname, "file"), stru = STRU_F;
154 (void) strcpy(bytename, "8"), bytesize = 8;
155 if (autologin)
156 (void) login(argv[1]);
157
158 #if defined(unix) && NBBY == 8
159 /*
160 * this ifdef is to keep someone form "porting" this to an incompatible
161 * system and not checking this out. This way they have to think about it.
162 */
163 overbose = verbose;
164 if (debug == 0)
165 verbose = -1;
166 if (command("SYST") == COMPLETE && overbose) {
167 char *cp, c;
168 c = 0;
169 cp = strchr(reply_string+4, ' ');
170 if (cp == NULL)
171 cp = strchr(reply_string+4, '\r');
172 if (cp) {
173 if (cp[-1] == '.')
174 cp--;
175 c = *cp;
176 *cp = '\0';
177 }
178
179 printf("Remote system type is %s.\n",
180 reply_string+4);
181 if (cp)
182 *cp = c;
183 }
184 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
185 if (proxy)
186 unix_proxy = 1;
187 else
188 unix_server = 1;
189 /*
190 * Set type to 0 (not specified by user),
191 * meaning binary by default, but don't bother
192 * telling server. We can use binary
193 * for text files unless changed by the user.
194 */
195 type = 0;
196 (void) strcpy(typename, "binary");
197 if (overbose)
198 printf("Using %s mode to transfer files.\n",
199 typename);
200 } else {
201 if (proxy)
202 unix_proxy = 0;
203 else
204 unix_server = 0;
205 if (overbose &&
206 !strncmp(reply_string, "215 TOPS20", 10))
207 printf("Remember to set tenex mode when "
208 "transferring binary files from this "
209 "machine.\n");
210 }
211 verbose = overbose;
212 #endif /* unix */
213 }
214 }
215
216 struct types {
217 char *t_name;
218 char *t_mode;
219 int t_type;
220 char *t_arg;
221 } types[] = {
222 { "ascii", "A", TYPE_A, 0 },
223 { "binary", "I", TYPE_I, 0 },
224 { "image", "I", TYPE_I, 0 },
225 { "ebcdic", "E", TYPE_E, 0 },
226 { "tenex", "L", TYPE_L, bytename },
227 { NULL }
228 };
229
230 /*
231 * Set transfer type.
232 */
233 void
234 settype(argc, argv)
235 int argc;
236 char *argv[];
237 {
238 struct types *p;
239 int comret;
240
241 if (argc > 2) {
242 char *sep;
243
244 printf("usage: %s [", argv[0]);
245 sep = " ";
246 for (p = types; p->t_name; p++) {
247 printf("%s%s", sep, p->t_name);
248 sep = " | ";
249 }
250 printf(" ]\n");
251 code = -1;
252 return;
253 }
254 if (argc < 2) {
255 printf("Using %s mode to transfer files.\n", typename);
256 code = 0;
257 return;
258 }
259 for (p = types; p->t_name; p++)
260 if (strcmp(argv[1], p->t_name) == 0)
261 break;
262 if (p->t_name == 0) {
263 printf("%s: unknown mode\n", argv[1]);
264 code = -1;
265 return;
266 }
267 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
268 comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
269 else
270 comret = command("TYPE %s", p->t_mode);
271 if (comret == COMPLETE) {
272 (void) strcpy(typename, p->t_name);
273 curtype = type = p->t_type;
274 }
275 }
276
277 /*
278 * Internal form of settype; changes current type in use with server
279 * without changing our notion of the type for data transfers.
280 * Used to change to and from ascii for listings.
281 */
282 void
283 changetype(newtype, show)
284 int newtype, show;
285 {
286 struct types *p;
287 int comret, oldverbose = verbose;
288
289 if (newtype == 0)
290 newtype = TYPE_I;
291 if (newtype == curtype)
292 return;
293 if (debug == 0 && show == 0)
294 verbose = 0;
295 for (p = types; p->t_name; p++)
296 if (newtype == p->t_type)
297 break;
298 if (p->t_name == 0) {
299 printf("ftp: internal error: unknown type %d\n", newtype);
300 return;
301 }
302 if (newtype == TYPE_L && bytename[0] != '\0')
303 comret = command("TYPE %s %s", p->t_mode, bytename);
304 else
305 comret = command("TYPE %s", p->t_mode);
306 if (comret == COMPLETE)
307 curtype = newtype;
308 verbose = oldverbose;
309 }
310
311 char *stype[] = {
312 "type",
313 "",
314 0
315 };
316
317 /*
318 * Set binary transfer type.
319 */
320 /*VARARGS*/
321 void
322 setbinary(argc, argv)
323 int argc;
324 char *argv[];
325 {
326
327 stype[1] = "binary";
328 settype(2, stype);
329 }
330
331 /*
332 * Set ascii transfer type.
333 */
334 /*VARARGS*/
335 void
336 setascii(argc, argv)
337 int argc;
338 char *argv[];
339 {
340
341 stype[1] = "ascii";
342 settype(2, stype);
343 }
344
345 /*
346 * Set tenex transfer type.
347 */
348 /*VARARGS*/
349 void
350 settenex(argc, argv)
351 int argc;
352 char *argv[];
353 {
354
355 stype[1] = "tenex";
356 settype(2, stype);
357 }
358
359 /*
360 * Set file transfer mode.
361 */
362 /*ARGSUSED*/
363 void
364 setftmode(argc, argv)
365 int argc;
366 char *argv[];
367 {
368
369 printf("We only support %s mode, sorry.\n", modename);
370 code = -1;
371 }
372
373 /*
374 * Set file transfer format.
375 */
376 /*ARGSUSED*/
377 void
378 setform(argc, argv)
379 int argc;
380 char *argv[];
381 {
382
383 printf("We only support %s format, sorry.\n", formname);
384 code = -1;
385 }
386
387 /*
388 * Set file transfer structure.
389 */
390 /*ARGSUSED*/
391 void
392 setstruct(argc, argv)
393 int argc;
394 char *argv[];
395 {
396
397 printf("We only support %s structure, sorry.\n", structname);
398 code = -1;
399 }
400
401 /*
402 * Send a single file.
403 */
404 void
405 put(argc, argv)
406 int argc;
407 char *argv[];
408 {
409 char *cmd;
410 int loc = 0;
411 char *oldargv1, *oldargv2;
412
413 if (argc == 2) {
414 argc++;
415 argv[2] = argv[1];
416 loc++;
417 }
418 if (argc < 2 && !another(&argc, &argv, "local-file"))
419 goto usage;
420 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
421 usage:
422 printf("usage: %s local-file [ remote-file ]\n", argv[0]);
423 code = -1;
424 return;
425 }
426 oldargv1 = argv[1];
427 oldargv2 = argv[2];
428 if (!globulize(&argv[1])) {
429 code = -1;
430 return;
431 }
432 /*
433 * If "globulize" modifies argv[1], and argv[2] is a copy of
434 * the old argv[1], make it a copy of the new argv[1].
435 */
436 if (argv[1] != oldargv1 && argv[2] == oldargv1) {
437 argv[2] = argv[1];
438 }
439 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
440 if (loc && ntflag) {
441 argv[2] = dotrans(argv[2]);
442 }
443 if (loc && mapflag) {
444 argv[2] = domap(argv[2]);
445 }
446 sendrequest(cmd, argv[1], argv[2],
447 argv[1] != oldargv1 || argv[2] != oldargv2);
448 }
449
450 /*
451 * Send multiple files.
452 */
453 void
454 mput(argc, argv)
455 int argc;
456 char *argv[];
457 {
458 int i;
459 sig_t oldintr;
460 int ointer;
461 char *tp;
462
463 if (argc < 2 && !another(&argc, &argv, "local-files")) {
464 printf("usage: %s local-files\n", argv[0]);
465 code = -1;
466 return;
467 }
468 mname = argv[0];
469 mflag = 1;
470 oldintr = signal(SIGINT, mabort);
471 (void) setjmp(jabort);
472 if (proxy) {
473 char *cp, *tp2, tmpbuf[MAXPATHLEN];
474
475 while ((cp = remglob(argv, 0)) != NULL) {
476 if (*cp == 0) {
477 mflag = 0;
478 continue;
479 }
480 if (mflag && confirm(argv[0], cp)) {
481 tp = cp;
482 if (mcase) {
483 while (*tp && !islower(*tp)) {
484 tp++;
485 }
486 if (!*tp) {
487 tp = cp;
488 tp2 = tmpbuf;
489 while ((*tp2 = *tp) != NULL) {
490 if (isupper(*tp2)) {
491 *tp2 = 'a' + *tp2 - 'A';
492 }
493 tp++;
494 tp2++;
495 }
496 }
497 tp = tmpbuf;
498 }
499 if (ntflag) {
500 tp = dotrans(tp);
501 }
502 if (mapflag) {
503 tp = domap(tp);
504 }
505 sendrequest((sunique) ? "STOU" : "STOR",
506 cp, tp, cp != tp || !interactive);
507 if (!mflag && fromatty) {
508 ointer = interactive;
509 interactive = 1;
510 if (confirm("Continue with", "mput")) {
511 mflag++;
512 }
513 interactive = ointer;
514 }
515 }
516 }
517 (void) signal(SIGINT, oldintr);
518 mflag = 0;
519 return;
520 }
521 for (i = 1; i < argc; i++) {
522 char **cpp;
523 glob_t gl;
524 int flags;
525
526 if (!doglob) {
527 if (mflag && confirm(argv[0], argv[i])) {
528 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
529 tp = (mapflag) ? domap(tp) : tp;
530 sendrequest((sunique) ? "STOU" : "STOR",
531 argv[i], tp, tp != argv[i] || !interactive);
532 if (!mflag && fromatty) {
533 ointer = interactive;
534 interactive = 1;
535 if (confirm("Continue with", "mput")) {
536 mflag++;
537 }
538 interactive = ointer;
539 }
540 }
541 continue;
542 }
543
544 memset(&gl, 0, sizeof(gl));
545 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
546 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
547 warnx("%s: not found", argv[i]);
548 globfree(&gl);
549 continue;
550 }
551 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
552 if (mflag && confirm(argv[0], *cpp)) {
553 tp = (ntflag) ? dotrans(*cpp) : *cpp;
554 tp = (mapflag) ? domap(tp) : tp;
555 sendrequest((sunique) ? "STOU" : "STOR",
556 *cpp, tp, *cpp != tp || !interactive);
557 if (!mflag && fromatty) {
558 ointer = interactive;
559 interactive = 1;
560 if (confirm("Continue with", "mput")) {
561 mflag++;
562 }
563 interactive = ointer;
564 }
565 }
566 }
567 globfree(&gl);
568 }
569 (void) signal(SIGINT, oldintr);
570 mflag = 0;
571 }
572
573 void
574 reget(argc, argv)
575 int argc;
576 char *argv[];
577 {
578
579 (void) getit(argc, argv, 1, "r+w");
580 }
581
582 void
583 get(argc, argv)
584 int argc;
585 char *argv[];
586 {
587
588 (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" );
589 }
590
591 /*
592 * Receive one file.
593 */
594 int
595 getit(argc, argv, restartit, mode)
596 int argc;
597 char *argv[];
598 int restartit;
599 const char *mode;
600 {
601 int loc = 0;
602 char *oldargv1, *oldargv2;
603
604 if (argc == 2) {
605 argc++;
606 argv[2] = argv[1];
607 loc++;
608 }
609 if (argc < 2 && !another(&argc, &argv, "remote-file"))
610 goto usage;
611 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
612 usage:
613 printf("usage: %s remote-file [ local-file ]\n", argv[0]);
614 code = -1;
615 return (0);
616 }
617 oldargv1 = argv[1];
618 oldargv2 = argv[2];
619 if (!globulize(&argv[2])) {
620 code = -1;
621 return (0);
622 }
623 if (loc && mcase) {
624 char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
625
626 while (*tp && !islower(*tp)) {
627 tp++;
628 }
629 if (!*tp) {
630 tp = argv[2];
631 tp2 = tmpbuf;
632 while ((*tp2 = *tp) != NULL) {
633 if (isupper(*tp2)) {
634 *tp2 = 'a' + *tp2 - 'A';
635 }
636 tp++;
637 tp2++;
638 }
639 argv[2] = tmpbuf;
640 }
641 }
642 if (loc && ntflag)
643 argv[2] = dotrans(argv[2]);
644 if (loc && mapflag)
645 argv[2] = domap(argv[2]);
646 if (restartit) {
647 struct stat stbuf;
648 int ret;
649
650 ret = stat(argv[2], &stbuf);
651 if (restartit == 1) {
652 if (ret < 0) {
653 warn("local: %s", argv[2]);
654 return (0);
655 }
656 restart_point = stbuf.st_size;
657 } else {
658 if (ret == 0) {
659 time_t mtime;
660
661 mtime = remotemodtime(argv[1], 0);
662 if (mtime == -1)
663 return(0);
664 if (stbuf.st_mtime >= mtime)
665 return (1);
666 }
667 }
668 }
669
670 recvrequest("RETR", argv[2], argv[1], mode,
671 argv[1] != oldargv1 || argv[2] != oldargv2);
672 restart_point = 0;
673 return (0);
674 }
675
676 /* ARGSUSED */
677 void
678 mabort(signo)
679 int signo;
680 {
681 int ointer, oconf;
682
683 printf("\n");
684 (void) fflush(stdout);
685 if (mflag && fromatty) {
686 ointer = interactive;
687 oconf = confirmrest;
688 interactive = 1;
689 confirmrest = 0;
690 if (confirm("Continue with", mname)) {
691 interactive = ointer;
692 confirmrest = oconf;
693 longjmp(jabort, 0);
694 }
695 interactive = ointer;
696 confirmrest = oconf;
697 }
698 mflag = 0;
699 longjmp(jabort, 0);
700 }
701
702 /*
703 * Get multiple files.
704 */
705 void
706 mget(argc, argv)
707 int argc;
708 char *argv[];
709 {
710 sig_t oldintr;
711 int ch, ointer;
712 char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
713
714 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
715 printf("usage: %s remote-files\n", argv[0]);
716 code = -1;
717 return;
718 }
719 mname = argv[0];
720 mflag = 1;
721 oldintr = signal(SIGINT, mabort);
722 (void) setjmp(jabort);
723 while ((cp = remglob(argv, proxy)) != NULL) {
724 if (*cp == '\0') {
725 mflag = 0;
726 continue;
727 }
728 if (mflag && confirm(argv[0], cp)) {
729 tp = cp;
730 if (mcase) {
731 for (tp2 = tmpbuf; (ch = *tp++) != NULL; )
732 *tp2++ = isupper(ch) ? tolower(ch) : ch;
733 *tp2 = '\0';
734 tp = tmpbuf;
735 }
736 if (ntflag) {
737 tp = dotrans(tp);
738 }
739 if (mapflag) {
740 tp = domap(tp);
741 }
742 recvrequest("RETR", tp, cp, "w",
743 tp != cp || !interactive);
744 if (!mflag && fromatty) {
745 ointer = interactive;
746 interactive = 1;
747 if (confirm("Continue with", "mget")) {
748 mflag++;
749 }
750 interactive = ointer;
751 }
752 }
753 }
754 (void) signal(SIGINT, oldintr);
755 mflag = 0;
756 }
757
758 char *
759 remglob(argv, doswitch)
760 char *argv[];
761 int doswitch;
762 {
763 char temp[MAXPATHLEN];
764 static char buf[MAXPATHLEN];
765 static FILE *ftemp = NULL;
766 static char **args;
767 int oldverbose, oldhash, fd;
768 char *cp, *mode;
769
770 if (!mflag) {
771 if (!doglob) {
772 args = NULL;
773 }
774 else {
775 if (ftemp) {
776 (void) fclose(ftemp);
777 ftemp = NULL;
778 }
779 }
780 return (NULL);
781 }
782 if (!doglob) {
783 if (args == NULL)
784 args = argv;
785 if ((cp = *++args) == NULL)
786 args = NULL;
787 return (cp);
788 }
789 if (ftemp == NULL) {
790 (void) snprintf(temp, sizeof(temp), "%s%s", _PATH_TMP, TMPFILE);
791 fd = mkstemp(temp);
792 if (fd < 0) {
793 warn("unable to create temporary file %s", temp);
794 return (NULL);
795 }
796 close(fd);
797 oldverbose = verbose, verbose = 0;
798 oldhash = hash, hash = 0;
799 if (doswitch) {
800 pswitch(!proxy);
801 }
802 for (mode = "w"; *++argv != NULL; mode = "a")
803 recvrequest ("NLST", temp, *argv, mode, 0);
804 if (doswitch) {
805 pswitch(!proxy);
806 }
807 verbose = oldverbose; hash = oldhash;
808 ftemp = fopen(temp, "r");
809 (void) unlink(temp);
810 if (ftemp == NULL) {
811 printf("can't find list of remote files, oops\n");
812 return (NULL);
813 }
814 }
815 if (fgets(buf, sizeof (buf), ftemp) == NULL) {
816 (void) fclose(ftemp), ftemp = NULL;
817 return (NULL);
818 }
819 if ((cp = strchr(buf, '\n')) != NULL)
820 *cp = '\0';
821 return (buf);
822 }
823
824 char *
825 onoff(bool)
826 int bool;
827 {
828
829 return (bool ? "on" : "off");
830 }
831
832 /*
833 * Show status.
834 */
835 /*ARGSUSED*/
836 void
837 status(argc, argv)
838 int argc;
839 char *argv[];
840 {
841 int i;
842
843 if (connected)
844 printf("Connected to %s.\n", hostname);
845 else
846 printf("Not connected.\n");
847 if (!proxy) {
848 pswitch(1);
849 if (connected) {
850 printf("Connected for proxy commands to %s.\n",
851 hostname);
852 }
853 else {
854 printf("No proxy connection.\n");
855 }
856 pswitch(0);
857 }
858 printf("Passive mode: %s.\n", onoff(passivemode));
859 printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
860 modename, typename, formname, structname);
861 printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
862 onoff(verbose), onoff(bell), onoff(interactive),
863 onoff(doglob));
864 printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
865 onoff(runique));
866 printf("Preserve modification times: %s\n", onoff(preserve));
867 printf("Case: %s; CR stripping: %s\n", onoff(mcase), onoff(crflag));
868 if (ntflag) {
869 printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
870 }
871 else {
872 printf("Ntrans: off\n");
873 }
874 if (mapflag) {
875 printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
876 }
877 else {
878 printf("Nmap: off\n");
879 }
880 printf("Hash mark printing: %s; Mark count: %d; Progress bar: %s\n",
881 onoff(hash), mark, onoff(progress));
882 printf("Use of PORT cmds: %s\n", onoff(sendport));
883 if (macnum > 0) {
884 printf("Macros:\n");
885 for (i=0; i<macnum; i++) {
886 printf("\t%s\n", macros[i].mac_name);
887 }
888 }
889 code = 0;
890 }
891
892 /*
893 * Toggle a variable
894 */
895 int
896 togglevar(argc, argv, var, mesg)
897 int argc;
898 char *argv[];
899 int *var;
900 const char *mesg;
901 {
902 if (argc < 2) {
903 *var = !*var;
904 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
905 *var = 1;
906 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
907 *var = 0;
908 } else {
909 printf("usage: %s [ on | off ]\n", argv[0]);
910 return -1;
911 }
912 printf("%s %s.\n", mesg, onoff(*var));
913 return *var;
914 }
915
916 /*
917 * Set beep on cmd completed mode.
918 */
919 /*VARARGS*/
920 void
921 setbell(argc, argv)
922 int argc;
923 char *argv[];
924 {
925
926 code = togglevar(argc, argv, &bell, "Bell mode");
927 }
928
929 /*
930 * Turn on packet tracing.
931 */
932 /*VARARGS*/
933 void
934 settrace(argc, argv)
935 int argc;
936 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(argc, argv)
948 int argc;
949 char *argv[];
950 {
951 if (argc == 1)
952 hash = !hash;
953 else if (argc != 2) {
954 printf("usage: %s [ on | off | bytecount ]\n", argv[0]);
955 code = -1;
956 return;
957 } else if (strcasecmp(argv[1], "on") == 0)
958 hash = 1;
959 else if (strcasecmp(argv[1], "off") == 0)
960 hash = 0;
961 else {
962 int nmark = atol(argv[1]);
963 if (nmark < 1) {
964 printf("%s: bad bytecount value\n", argv[1]);
965 code = -1;
966 return;
967 }
968 mark = nmark;
969 hash = 1;
970 }
971 printf("Hash mark printing %s", onoff(hash));
972 if (hash)
973 printf(" (%d bytes/hash mark)", mark);
974 printf(".\n");
975 code = hash;
976 }
977
978 /*
979 * Turn on printing of server echo's.
980 */
981 /*VARARGS*/
982 void
983 setverbose(argc, argv)
984 int argc;
985 char *argv[];
986 {
987
988 code = togglevar(argc, argv, &verbose, "Verbose mode");
989 }
990
991 /*
992 * Toggle PORT cmd use before each data connection.
993 */
994 /*VARARGS*/
995 void
996 setport(argc, argv)
997 int argc;
998 char *argv[];
999 {
1000
1001 code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
1002 }
1003
1004 /*
1005 * Toggle transfer progress bar.
1006 */
1007 /*VARARGS*/
1008 void
1009 setprogress(argc, argv)
1010 int argc;
1011 char *argv[];
1012 {
1013
1014 code = togglevar(argc, argv, &progress, "Progress bar");
1015 }
1016
1017 /*
1018 * Turn on interactive prompting
1019 * during mget, mput, and mdelete.
1020 */
1021 /*VARARGS*/
1022 void
1023 setprompt(argc, argv)
1024 int argc;
1025 char *argv[];
1026 {
1027
1028 code = togglevar(argc, argv, &interactive, "Interactive mode");
1029 }
1030
1031 /*
1032 * Toggle metacharacter interpretation
1033 * on local file names.
1034 */
1035 /*VARARGS*/
1036 void
1037 setglob(argc, argv)
1038 int argc;
1039 char *argv[];
1040 {
1041
1042 code = togglevar(argc, argv, &doglob, "Globbing");
1043 }
1044
1045 /*
1046 * Toggle preserving modification times on retreived files.
1047 */
1048 /*VARARGS*/
1049 void
1050 setpreserve(argc, argv)
1051 int argc;
1052 char *argv[];
1053 {
1054
1055 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1056 }
1057
1058 /*
1059 * Set debugging mode on/off and/or
1060 * set level of debugging.
1061 */
1062 /*VARARGS*/
1063 void
1064 setdebug(argc, argv)
1065 int argc;
1066 char *argv[];
1067 {
1068 int val;
1069
1070 if (argc > 2) {
1071 printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
1072 code = -1;
1073 return;
1074 } else if (argc == 2) {
1075 if (strcasecmp(argv[1], "on") == 0)
1076 debug = 1;
1077 else if (strcasecmp(argv[1], "off") == 0)
1078 debug = 0;
1079 else {
1080 val = atoi(argv[1]);
1081 if (val < 0) {
1082 printf("%s: bad debugging value.\n", argv[1]);
1083 code = -1;
1084 return;
1085 }
1086 debug = val;
1087 }
1088 } else
1089 debug = !debug;
1090 if (debug)
1091 options |= SO_DEBUG;
1092 else
1093 options &= ~SO_DEBUG;
1094 printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
1095 code = debug > 0;
1096 }
1097
1098 /*
1099 * Set current working directory
1100 * on remote machine.
1101 */
1102 void
1103 cd(argc, argv)
1104 int argc;
1105 char *argv[];
1106 {
1107
1108 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
1109 argc > 2) {
1110 printf("usage: %s remote-directory\n", argv[0]);
1111 code = -1;
1112 return;
1113 }
1114 if (command("CWD %s", argv[1]) == ERROR && code == 500) {
1115 if (verbose)
1116 printf("CWD command not recognized, trying XCWD\n");
1117 (void) command("XCWD %s", argv[1]);
1118 }
1119 }
1120
1121 /*
1122 * Set current working directory
1123 * on local machine.
1124 */
1125 void
1126 lcd(argc, argv)
1127 int argc;
1128 char *argv[];
1129 {
1130 char buf[MAXPATHLEN];
1131
1132 if (argc < 2)
1133 argc++, argv[1] = home;
1134 if (argc != 2) {
1135 printf("usage: %s local-directory\n", argv[0]);
1136 code = -1;
1137 return;
1138 }
1139 if (!globulize(&argv[1])) {
1140 code = -1;
1141 return;
1142 }
1143 if (chdir(argv[1]) < 0) {
1144 warn("local: %s", argv[1]);
1145 code = -1;
1146 return;
1147 }
1148 if (getwd(buf) != NULL)
1149 printf("Local directory now %s\n", buf);
1150 else
1151 warnx("getwd: %s", buf);
1152 code = 0;
1153 }
1154
1155 /*
1156 * Delete a single file.
1157 */
1158 void
1159 delete(argc, argv)
1160 int argc;
1161 char *argv[];
1162 {
1163
1164 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1165 printf("usage: %s remote-file\n", argv[0]);
1166 code = -1;
1167 return;
1168 }
1169 (void) command("DELE %s", argv[1]);
1170 }
1171
1172 /*
1173 * Delete multiple files.
1174 */
1175 void
1176 mdelete(argc, argv)
1177 int argc;
1178 char *argv[];
1179 {
1180 sig_t oldintr;
1181 int ointer;
1182 char *cp;
1183
1184 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1185 printf("usage: %s remote-files\n", argv[0]);
1186 code = -1;
1187 return;
1188 }
1189 mname = argv[0];
1190 mflag = 1;
1191 oldintr = signal(SIGINT, mabort);
1192 (void) setjmp(jabort);
1193 while ((cp = remglob(argv, 0)) != NULL) {
1194 if (*cp == '\0') {
1195 mflag = 0;
1196 continue;
1197 }
1198 if (mflag && confirm(argv[0], cp)) {
1199 (void) command("DELE %s", cp);
1200 if (!mflag && fromatty) {
1201 ointer = interactive;
1202 interactive = 1;
1203 if (confirm("Continue with", "mdelete")) {
1204 mflag++;
1205 }
1206 interactive = ointer;
1207 }
1208 }
1209 }
1210 (void) signal(SIGINT, oldintr);
1211 mflag = 0;
1212 }
1213
1214 /*
1215 * Rename a remote file.
1216 */
1217 void
1218 renamefile(argc, argv)
1219 int argc;
1220 char *argv[];
1221 {
1222
1223 if (argc < 2 && !another(&argc, &argv, "from-name"))
1224 goto usage;
1225 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1226 usage:
1227 printf("%s from-name to-name\n", argv[0]);
1228 code = -1;
1229 return;
1230 }
1231 if (command("RNFR %s", argv[1]) == CONTINUE)
1232 (void) command("RNTO %s", argv[2]);
1233 }
1234
1235 /*
1236 * Get a directory listing
1237 * of remote files.
1238 */
1239 void
1240 ls(argc, argv)
1241 int argc;
1242 char *argv[];
1243 {
1244 const char *cmd;
1245
1246 if (argc < 2)
1247 argc++, argv[1] = NULL;
1248 if (argc < 3)
1249 argc++, argv[2] = "-";
1250 if (argc > 3) {
1251 printf("usage: %s remote-directory local-file\n", argv[0]);
1252 code = -1;
1253 return;
1254 }
1255 cmd = strcmp(argv[0], "dir") == 0 ? "LIST" : "NLST";
1256 if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1257 code = -1;
1258 return;
1259 }
1260 if (strcmp(argv[2], "-") && *argv[2] != '|')
1261 if (!globulize(&argv[2]) || !confirm("output to local-file:",
1262 argv[2])) {
1263 code = -1;
1264 return;
1265 }
1266 recvrequest(cmd, argv[2], argv[1], "w", 0);
1267
1268 /* flush results in case commands are coming from a pipe */
1269 fflush(stdout);
1270 }
1271
1272 /*
1273 * Get a directory listing
1274 * of multiple remote files.
1275 */
1276 void
1277 mls(argc, argv)
1278 int argc;
1279 char *argv[];
1280 {
1281 sig_t oldintr;
1282 int ointer, i;
1283 const char *cmd;
1284 char mode[1], *dest;
1285
1286 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1287 goto usage;
1288 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1289 usage:
1290 printf("usage: %s remote-files local-file\n", argv[0]);
1291 code = -1;
1292 return;
1293 }
1294 dest = argv[argc - 1];
1295 argv[argc - 1] = NULL;
1296 if (strcmp(dest, "-") && *dest != '|')
1297 if (!globulize(&dest) ||
1298 !confirm("output to local-file:", dest)) {
1299 code = -1;
1300 return;
1301 }
1302 cmd = strcmp(argv[0], "mls") == 0 ? "NLST" : "LIST";
1303 mname = argv[0];
1304 mflag = 1;
1305 oldintr = signal(SIGINT, mabort);
1306 (void) setjmp(jabort);
1307 for (i = 1; mflag && i < argc-1; ++i) {
1308 *mode = (i == 1) ? 'w' : 'a';
1309 recvrequest(cmd, dest, argv[i], mode, 0);
1310 if (!mflag && fromatty) {
1311 ointer = interactive;
1312 interactive = 1;
1313 if (confirm("Continue with", argv[0])) {
1314 mflag ++;
1315 }
1316 interactive = ointer;
1317 }
1318 }
1319 (void) signal(SIGINT, oldintr);
1320 mflag = 0;
1321 }
1322
1323 /*
1324 * Do a shell escape
1325 */
1326 /*ARGSUSED*/
1327 void
1328 shell(argc, argv)
1329 int argc;
1330 char *argv[];
1331 {
1332 pid_t pid;
1333 sig_t old1, old2;
1334 char shellnam[MAXPATHLEN], *shell, *namep;
1335 union wait status;
1336
1337 old1 = signal (SIGINT, SIG_IGN);
1338 old2 = signal (SIGQUIT, SIG_IGN);
1339 if ((pid = fork()) == 0) {
1340 for (pid = 3; pid < 20; pid++)
1341 (void) close(pid);
1342 (void) signal(SIGINT, SIG_DFL);
1343 (void) signal(SIGQUIT, SIG_DFL);
1344 shell = getenv("SHELL");
1345 if (shell == NULL)
1346 shell = _PATH_BSHELL;
1347 namep = strrchr(shell, '/');
1348 if (namep == NULL)
1349 namep = shell;
1350 shellnam[0] = '-';
1351 (void) strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 1);
1352 if (strcmp(namep, "sh") != 0)
1353 shellnam[0] = '+';
1354 if (debug) {
1355 printf ("%s\n", shell);
1356 (void) fflush (stdout);
1357 }
1358 if (argc > 1) {
1359 execl(shell, shellnam, "-c", altarg, (char *)0);
1360 }
1361 else {
1362 execl(shell, shellnam, (char *)0);
1363 }
1364 warn("%s", shell);
1365 code = -1;
1366 exit(1);
1367 }
1368 if (pid > 0)
1369 while (wait((int *)&status) != pid)
1370 ;
1371 (void) signal(SIGINT, old1);
1372 (void) signal(SIGQUIT, old2);
1373 if (pid == -1) {
1374 warn("%s", "Try again later");
1375 code = -1;
1376 }
1377 else {
1378 code = 0;
1379 }
1380 }
1381
1382 /*
1383 * Send new user information (re-login)
1384 */
1385 void
1386 user(argc, argv)
1387 int argc;
1388 char *argv[];
1389 {
1390 char acct[80];
1391 int n, aflag = 0;
1392
1393 if (argc < 2)
1394 (void) another(&argc, &argv, "username");
1395 if (argc < 2 || argc > 4) {
1396 printf("usage: %s username [password] [account]\n", argv[0]);
1397 code = -1;
1398 return;
1399 }
1400 n = command("USER %s", argv[1]);
1401 if (n == CONTINUE) {
1402 if (argc < 3 )
1403 argv[2] = getpass("Password: "), argc++;
1404 n = command("PASS %s", argv[2]);
1405 }
1406 if (n == CONTINUE) {
1407 if (argc < 4) {
1408 printf("Account: "); (void) fflush(stdout);
1409 (void) fgets(acct, sizeof(acct) - 1, stdin);
1410 acct[strlen(acct) - 1] = '\0';
1411 argv[3] = acct; argc++;
1412 }
1413 n = command("ACCT %s", argv[3]);
1414 aflag++;
1415 }
1416 if (n != COMPLETE) {
1417 fprintf(stdout, "Login failed.\n");
1418 return;
1419 }
1420 if (!aflag && argc == 4) {
1421 (void) command("ACCT %s", argv[3]);
1422 }
1423 }
1424
1425 /*
1426 * Print working directory on remote machine.
1427 */
1428 /*VARARGS*/
1429 void
1430 pwd(argc, argv)
1431 int argc;
1432 char *argv[];
1433 {
1434 int oldverbose = verbose;
1435
1436 /*
1437 * If we aren't verbose, this doesn't do anything!
1438 */
1439 verbose = 1;
1440 if (command("PWD") == ERROR && code == 500) {
1441 printf("PWD command not recognized, trying XPWD\n");
1442 (void) command("XPWD");
1443 }
1444 verbose = oldverbose;
1445 }
1446
1447 /*
1448 * Print working directory on local machine.
1449 */
1450 void
1451 lpwd(argc, argv)
1452 int argc;
1453 char *argv[];
1454 {
1455 char buf[MAXPATHLEN];
1456
1457 if (getwd(buf) != NULL)
1458 printf("Local directory %s\n", buf);
1459 else
1460 warnx("getwd: %s", buf);
1461 code = 0;
1462 }
1463
1464 /*
1465 * Make a directory.
1466 */
1467 void
1468 makedir(argc, argv)
1469 int argc;
1470 char *argv[];
1471 {
1472
1473 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1474 argc > 2) {
1475 printf("usage: %s directory-name\n", argv[0]);
1476 code = -1;
1477 return;
1478 }
1479 if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1480 if (verbose)
1481 printf("MKD command not recognized, trying XMKD\n");
1482 (void) command("XMKD %s", argv[1]);
1483 }
1484 }
1485
1486 /*
1487 * Remove a directory.
1488 */
1489 void
1490 removedir(argc, argv)
1491 int argc;
1492 char *argv[];
1493 {
1494
1495 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1496 argc > 2) {
1497 printf("usage: %s directory-name\n", argv[0]);
1498 code = -1;
1499 return;
1500 }
1501 if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1502 if (verbose)
1503 printf("RMD command not recognized, trying XRMD\n");
1504 (void) command("XRMD %s", argv[1]);
1505 }
1506 }
1507
1508 /*
1509 * Send a line, verbatim, to the remote machine.
1510 */
1511 void
1512 quote(argc, argv)
1513 int argc;
1514 char *argv[];
1515 {
1516
1517 if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1518 printf("usage: %s line-to-send\n", argv[0]);
1519 code = -1;
1520 return;
1521 }
1522 quote1("", argc, argv);
1523 }
1524
1525 /*
1526 * Send a SITE command to the remote machine. The line
1527 * is sent verbatim to the remote machine, except that the
1528 * word "SITE" is added at the front.
1529 */
1530 void
1531 site(argc, argv)
1532 int argc;
1533 char *argv[];
1534 {
1535
1536 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1537 printf("usage: %s line-to-send\n", argv[0]);
1538 code = -1;
1539 return;
1540 }
1541 quote1("SITE ", argc, argv);
1542 }
1543
1544 /*
1545 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1546 * Send the result as a one-line command and get response.
1547 */
1548 void
1549 quote1(initial, argc, argv)
1550 const char *initial;
1551 int argc;
1552 char *argv[];
1553 {
1554 int i, len;
1555 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1556
1557 (void) strncpy(buf, initial, sizeof(buf));
1558 if (argc > 1) {
1559 len = strlen(buf);
1560 len += strlen(strncpy(&buf[len], argv[1], sizeof(buf) - len));
1561 for (i = 2; i < argc; i++) {
1562 buf[len++] = ' ';
1563 len += strlen(strncpy(&buf[len], argv[i],
1564 sizeof(buf) - len));
1565 }
1566 }
1567 if (command(buf) == PRELIM) {
1568 while (getreply(0) == PRELIM)
1569 continue;
1570 }
1571 }
1572
1573 void
1574 do_chmod(argc, argv)
1575 int argc;
1576 char *argv[];
1577 {
1578
1579 if (argc < 2 && !another(&argc, &argv, "mode"))
1580 goto usage;
1581 if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1582 usage:
1583 printf("usage: %s mode file-name\n", argv[0]);
1584 code = -1;
1585 return;
1586 }
1587 (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
1588 }
1589
1590 void
1591 do_umask(argc, argv)
1592 int argc;
1593 char *argv[];
1594 {
1595 int oldverbose = verbose;
1596
1597 verbose = 1;
1598 (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1599 verbose = oldverbose;
1600 }
1601
1602 void
1603 idle(argc, argv)
1604 int argc;
1605 char *argv[];
1606 {
1607 int oldverbose = verbose;
1608
1609 verbose = 1;
1610 (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1611 verbose = oldverbose;
1612 }
1613
1614 /*
1615 * Ask the other side for help.
1616 */
1617 void
1618 rmthelp(argc, argv)
1619 int argc;
1620 char *argv[];
1621 {
1622 int oldverbose = verbose;
1623
1624 verbose = 1;
1625 (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1626 verbose = oldverbose;
1627 }
1628
1629 /*
1630 * Terminate session and exit.
1631 */
1632 /*VARARGS*/
1633 void
1634 quit(argc, argv)
1635 int argc;
1636 char *argv[];
1637 {
1638
1639 if (connected)
1640 disconnect(0, 0);
1641 pswitch(1);
1642 if (connected) {
1643 disconnect(0, 0);
1644 }
1645 exit(0);
1646 }
1647
1648 /*
1649 * Terminate session, but don't exit.
1650 */
1651 void
1652 disconnect(argc, argv)
1653 int argc;
1654 char *argv[];
1655 {
1656
1657 if (!connected)
1658 return;
1659 (void) command("QUIT");
1660 if (cout) {
1661 (void) fclose(cout);
1662 }
1663 cout = NULL;
1664 connected = 0;
1665 data = -1;
1666 if (!proxy) {
1667 macnum = 0;
1668 }
1669 }
1670
1671 int
1672 confirm(cmd, file)
1673 const char *cmd, *file;
1674 {
1675 char line[BUFSIZ];
1676
1677 if (!interactive || confirmrest)
1678 return (1);
1679 printf("%s %s? ", cmd, file);
1680 (void) fflush(stdout);
1681 if (fgets(line, sizeof(line), stdin) == NULL)
1682 return (0);
1683 switch (tolower(*line)) {
1684 case 'n':
1685 return (0);
1686 case 'p':
1687 interactive = 0;
1688 printf("Interactive mode: off\n");
1689 break;
1690 case 'a':
1691 confirmrest = 1;
1692 printf("Prompting off for duration of %s\n", cmd);
1693 break;
1694 }
1695 return (1);
1696 }
1697
1698 void
1699 fatal(msg)
1700 const char *msg;
1701 {
1702
1703 errx(1, "%s", msg);
1704 }
1705
1706 /*
1707 * Glob a local file name specification with
1708 * the expectation of a single return value.
1709 * Can't control multiple values being expanded
1710 * from the expression, we return only the first.
1711 */
1712 int
1713 globulize(cpp)
1714 char **cpp;
1715 {
1716 glob_t gl;
1717 int flags;
1718
1719 if (!doglob)
1720 return (1);
1721
1722 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1723 memset(&gl, 0, sizeof(gl));
1724 if (glob(*cpp, flags, NULL, &gl) ||
1725 gl.gl_pathc == 0) {
1726 warnx("%s: not found", *cpp);
1727 globfree(&gl);
1728 return (0);
1729 }
1730 *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
1731 globfree(&gl);
1732 return (1);
1733 }
1734
1735 void
1736 account(argc, argv)
1737 int argc;
1738 char *argv[];
1739 {
1740 char *ap;
1741
1742 if (argc > 2) {
1743 printf("usage: %s [password]\n", argv[0]);
1744 code = -1;
1745 return;
1746 }
1747 else if (argc == 2)
1748 ap = argv[1];
1749 else
1750 ap = getpass("Account:");
1751 (void) command("ACCT %s", ap);
1752 }
1753
1754 jmp_buf abortprox;
1755
1756 void
1757 proxabort()
1758 {
1759
1760 if (!proxy) {
1761 pswitch(1);
1762 }
1763 if (connected) {
1764 proxflag = 1;
1765 }
1766 else {
1767 proxflag = 0;
1768 }
1769 pswitch(0);
1770 longjmp(abortprox, 1);
1771 }
1772
1773 void
1774 doproxy(argc, argv)
1775 int argc;
1776 char *argv[];
1777 {
1778 struct cmd *c;
1779 int cmdpos;
1780 sig_t oldintr;
1781
1782 if (argc < 2 && !another(&argc, &argv, "command")) {
1783 printf("usage: %s command\n", argv[0]);
1784 code = -1;
1785 return;
1786 }
1787 c = getcmd(argv[1]);
1788 if (c == (struct cmd *) -1) {
1789 printf("?Ambiguous command\n");
1790 (void) fflush(stdout);
1791 code = -1;
1792 return;
1793 }
1794 if (c == 0) {
1795 printf("?Invalid command\n");
1796 (void) fflush(stdout);
1797 code = -1;
1798 return;
1799 }
1800 if (!c->c_proxy) {
1801 printf("?Invalid proxy command\n");
1802 (void) fflush(stdout);
1803 code = -1;
1804 return;
1805 }
1806 if (setjmp(abortprox)) {
1807 code = -1;
1808 return;
1809 }
1810 oldintr = signal(SIGINT, proxabort);
1811 pswitch(1);
1812 if (c->c_conn && !connected) {
1813 printf("Not connected\n");
1814 (void) fflush(stdout);
1815 pswitch(0);
1816 (void) signal(SIGINT, oldintr);
1817 code = -1;
1818 return;
1819 }
1820 cmdpos = strcspn(line, " \t");
1821 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1822 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1823 (*c->c_handler)(argc-1, argv+1);
1824 if (connected) {
1825 proxflag = 1;
1826 }
1827 else {
1828 proxflag = 0;
1829 }
1830 pswitch(0);
1831 (void) signal(SIGINT, oldintr);
1832 }
1833
1834 void
1835 setcase(argc, argv)
1836 int argc;
1837 char *argv[];
1838 {
1839
1840 code = togglevar(argc, argv, &mcase, "Case mapping");
1841 }
1842
1843 void
1844 setcr(argc, argv)
1845 int argc;
1846 char *argv[];
1847 {
1848
1849 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1850 }
1851
1852 void
1853 setntrans(argc, argv)
1854 int argc;
1855 char *argv[];
1856 {
1857 if (argc == 1) {
1858 ntflag = 0;
1859 printf("Ntrans off.\n");
1860 code = ntflag;
1861 return;
1862 }
1863 ntflag++;
1864 code = ntflag;
1865 (void) strncpy(ntin, argv[1], 16);
1866 ntin[16] = '\0';
1867 if (argc == 2) {
1868 ntout[0] = '\0';
1869 return;
1870 }
1871 (void) strncpy(ntout, argv[2], 16);
1872 ntout[16] = '\0';
1873 }
1874
1875 char *
1876 dotrans(name)
1877 char *name;
1878 {
1879 static char new[MAXPATHLEN];
1880 char *cp1, *cp2 = new;
1881 int i, ostop, found;
1882
1883 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1884 continue;
1885 for (cp1 = name; *cp1; cp1++) {
1886 found = 0;
1887 for (i = 0; *(ntin + i) && i < 16; i++) {
1888 if (*cp1 == *(ntin + i)) {
1889 found++;
1890 if (i < ostop) {
1891 *cp2++ = *(ntout + i);
1892 }
1893 break;
1894 }
1895 }
1896 if (!found) {
1897 *cp2++ = *cp1;
1898 }
1899 }
1900 *cp2 = '\0';
1901 return (new);
1902 }
1903
1904 void
1905 setnmap(argc, argv)
1906 int argc;
1907 char *argv[];
1908 {
1909 char *cp;
1910
1911 if (argc == 1) {
1912 mapflag = 0;
1913 printf("Nmap off.\n");
1914 code = mapflag;
1915 return;
1916 }
1917 if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1918 printf("Usage: %s [mapin mapout]\n", argv[0]);
1919 code = -1;
1920 return;
1921 }
1922 mapflag = 1;
1923 code = 1;
1924 cp = strchr(altarg, ' ');
1925 if (proxy) {
1926 while(*++cp == ' ')
1927 continue;
1928 altarg = cp;
1929 cp = strchr(altarg, ' ');
1930 }
1931 *cp = '\0';
1932 (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1933 while (*++cp == ' ')
1934 continue;
1935 (void) strncpy(mapout, cp, MAXPATHLEN - 1);
1936 }
1937
1938 char *
1939 domap(name)
1940 char *name;
1941 {
1942 static char new[MAXPATHLEN];
1943 char *cp1 = name, *cp2 = mapin;
1944 char *tp[9], *te[9];
1945 int i, toks[9], toknum = 0, match = 1;
1946
1947 for (i=0; i < 9; ++i) {
1948 toks[i] = 0;
1949 }
1950 while (match && *cp1 && *cp2) {
1951 switch (*cp2) {
1952 case '\\':
1953 if (*++cp2 != *cp1) {
1954 match = 0;
1955 }
1956 break;
1957 case '$':
1958 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1959 if (*cp1 != *(++cp2+1)) {
1960 toks[toknum = *cp2 - '1']++;
1961 tp[toknum] = cp1;
1962 while (*++cp1 && *(cp2+1)
1963 != *cp1);
1964 te[toknum] = cp1;
1965 }
1966 cp2++;
1967 break;
1968 }
1969 /* FALLTHROUGH */
1970 default:
1971 if (*cp2 != *cp1) {
1972 match = 0;
1973 }
1974 break;
1975 }
1976 if (match && *cp1) {
1977 cp1++;
1978 }
1979 if (match && *cp2) {
1980 cp2++;
1981 }
1982 }
1983 if (!match && *cp1) /* last token mismatch */
1984 {
1985 toks[toknum] = 0;
1986 }
1987 cp1 = new;
1988 *cp1 = '\0';
1989 cp2 = mapout;
1990 while (*cp2) {
1991 match = 0;
1992 switch (*cp2) {
1993 case '\\':
1994 if (*(cp2 + 1)) {
1995 *cp1++ = *++cp2;
1996 }
1997 break;
1998 case '[':
1999 LOOP:
2000 if (*++cp2 == '$' && isdigit(*(cp2+1))) {
2001 if (*++cp2 == '0') {
2002 char *cp3 = name;
2003
2004 while (*cp3) {
2005 *cp1++ = *cp3++;
2006 }
2007 match = 1;
2008 }
2009 else if (toks[toknum = *cp2 - '1']) {
2010 char *cp3 = tp[toknum];
2011
2012 while (cp3 != te[toknum]) {
2013 *cp1++ = *cp3++;
2014 }
2015 match = 1;
2016 }
2017 }
2018 else {
2019 while (*cp2 && *cp2 != ',' &&
2020 *cp2 != ']') {
2021 if (*cp2 == '\\') {
2022 cp2++;
2023 }
2024 else if (*cp2 == '$' &&
2025 isdigit(*(cp2+1))) {
2026 if (*++cp2 == '0') {
2027 char *cp3 = name;
2028
2029 while (*cp3) {
2030 *cp1++ = *cp3++;
2031 }
2032 }
2033 else if (toks[toknum =
2034 *cp2 - '1']) {
2035 char *cp3=tp[toknum];
2036
2037 while (cp3 !=
2038 te[toknum]) {
2039 *cp1++ = *cp3++;
2040 }
2041 }
2042 }
2043 else if (*cp2) {
2044 *cp1++ = *cp2++;
2045 }
2046 }
2047 if (!*cp2) {
2048 printf("nmap: unbalanced "
2049 "brackets\n");
2050 return (name);
2051 }
2052 match = 1;
2053 cp2--;
2054 }
2055 if (match) {
2056 while (*++cp2 && *cp2 != ']') {
2057 if (*cp2 == '\\' && *(cp2 + 1)) {
2058 cp2++;
2059 }
2060 }
2061 if (!*cp2) {
2062 printf("nmap: unbalanced "
2063 "brackets\n");
2064 return (name);
2065 }
2066 break;
2067 }
2068 switch (*++cp2) {
2069 case ',':
2070 goto LOOP;
2071 case ']':
2072 break;
2073 default:
2074 cp2--;
2075 goto LOOP;
2076 }
2077 break;
2078 case '$':
2079 if (isdigit(*(cp2 + 1))) {
2080 if (*++cp2 == '0') {
2081 char *cp3 = name;
2082
2083 while (*cp3) {
2084 *cp1++ = *cp3++;
2085 }
2086 }
2087 else if (toks[toknum = *cp2 - '1']) {
2088 char *cp3 = tp[toknum];
2089
2090 while (cp3 != te[toknum]) {
2091 *cp1++ = *cp3++;
2092 }
2093 }
2094 break;
2095 }
2096 /* intentional drop through */
2097 default:
2098 *cp1++ = *cp2;
2099 break;
2100 }
2101 cp2++;
2102 }
2103 *cp1 = '\0';
2104 if (!*new) {
2105 return (name);
2106 }
2107 return (new);
2108 }
2109
2110 void
2111 setpassive(argc, argv)
2112 int argc;
2113 char *argv[];
2114 {
2115
2116 code = togglevar(argc, argv, &passivemode, "Passive mode");
2117 }
2118
2119 void
2120 setsunique(argc, argv)
2121 int argc;
2122 char *argv[];
2123 {
2124
2125 code = togglevar(argc, argv, &sunique, "Store unique");
2126 }
2127
2128 void
2129 setrunique(argc, argv)
2130 int argc;
2131 char *argv[];
2132 {
2133
2134 code = togglevar(argc, argv, &runique, "Receive unique");
2135 }
2136
2137 /* change directory to perent directory */
2138 void
2139 cdup(argc, argv)
2140 int argc;
2141 char *argv[];
2142 {
2143
2144 if (command("CDUP") == ERROR && code == 500) {
2145 if (verbose)
2146 printf("CDUP command not recognized, trying XCUP\n");
2147 (void) command("XCUP");
2148 }
2149 }
2150
2151 /* restart transfer at specific point */
2152 void
2153 restart(argc, argv)
2154 int argc;
2155 char *argv[];
2156 {
2157
2158 if (argc != 2)
2159 printf("restart: offset not specified\n");
2160 else {
2161 restart_point = atol(argv[1]);
2162 printf("Restarting at %qd. Execute get, put or append to"
2163 "initiate transfer\n", restart_point);
2164 }
2165 }
2166
2167 /* show remote system type */
2168 void
2169 syst(argc, argv)
2170 int argc;
2171 char *argv[];
2172 {
2173
2174 (void) command("SYST");
2175 }
2176
2177 void
2178 macdef(argc, argv)
2179 int argc;
2180 char *argv[];
2181 {
2182 char *tmp;
2183 int c;
2184
2185 if (macnum == 16) {
2186 printf("Limit of 16 macros have already been defined\n");
2187 code = -1;
2188 return;
2189 }
2190 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2191 printf("Usage: %s macro_name\n", argv[0]);
2192 code = -1;
2193 return;
2194 }
2195 if (interactive) {
2196 printf("Enter macro line by line, terminating it with a "
2197 "null line\n");
2198 }
2199 (void) strncpy(macros[macnum].mac_name, argv[1], 8);
2200 if (macnum == 0) {
2201 macros[macnum].mac_start = macbuf;
2202 }
2203 else {
2204 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2205 }
2206 tmp = macros[macnum].mac_start;
2207 while (tmp != macbuf+4096) {
2208 if ((c = getchar()) == EOF) {
2209 printf("macdef:end of file encountered\n");
2210 code = -1;
2211 return;
2212 }
2213 if ((*tmp = c) == '\n') {
2214 if (tmp == macros[macnum].mac_start) {
2215 macros[macnum++].mac_end = tmp;
2216 code = 0;
2217 return;
2218 }
2219 if (*(tmp-1) == '\0') {
2220 macros[macnum++].mac_end = tmp - 1;
2221 code = 0;
2222 return;
2223 }
2224 *tmp = '\0';
2225 }
2226 tmp++;
2227 }
2228 while (1) {
2229 while ((c = getchar()) != '\n' && c != EOF)
2230 /* LOOP */;
2231 if (c == EOF || getchar() == '\n') {
2232 printf("Macro not defined - 4k buffer exceeded\n");
2233 code = -1;
2234 return;
2235 }
2236 }
2237 }
2238
2239 /*
2240 * determine size of remote file
2241 */
2242 off_t
2243 remotesize(file, noisy)
2244 const char *file;
2245 int noisy;
2246 {
2247 int overbose;
2248 off_t size;
2249
2250 overbose = verbose;
2251 size = -1;
2252 if (debug == 0)
2253 verbose = -1;
2254 if (command("SIZE %s", file) == COMPLETE)
2255 sscanf(reply_string, "%*s %qd", &size);
2256 else if (noisy && debug == 0)
2257 printf("%s\n", reply_string);
2258 verbose = overbose;
2259 return (size);
2260 }
2261
2262 /*
2263 * get size of file on remote machine
2264 */
2265 void
2266 sizecmd(argc, argv)
2267 int argc;
2268 char *argv[];
2269 {
2270 off_t size;
2271
2272 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2273 printf("usage: %s filename\n", argv[0]);
2274 code = -1;
2275 return;
2276 }
2277 size = remotesize(argv[1], 1);
2278 if (size != -1)
2279 printf("%s\t%qd\n", argv[1], size);
2280 code = size;
2281 }
2282
2283 /*
2284 * determine last modification time (in GMT) of remote file
2285 */
2286 time_t
2287 remotemodtime(file, noisy)
2288 const char *file;
2289 int noisy;
2290 {
2291 int overbose;
2292 time_t rtime;
2293
2294 overbose = verbose;
2295 rtime = -1;
2296 if (debug == 0)
2297 verbose = -1;
2298 if (command("MDTM %s", file) == COMPLETE) {
2299 struct tm timebuf;
2300 int yy, mo, day, hour, min, sec;
2301 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
2302 &day, &hour, &min, &sec);
2303 memset(&timebuf, 0, sizeof(timebuf));
2304 timebuf.tm_sec = sec;
2305 timebuf.tm_min = min;
2306 timebuf.tm_hour = hour;
2307 timebuf.tm_mday = day;
2308 timebuf.tm_mon = mo - 1;
2309 timebuf.tm_year = yy - 1900;
2310 timebuf.tm_isdst = -1;
2311 rtime = mktime(&timebuf);
2312 if (rtime == -1 && (noisy || debug != 0))
2313 printf("Can't convert %s to a time\n", reply_string);
2314 else
2315 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
2316 } else if (noisy && debug == 0)
2317 printf("%s\n", reply_string);
2318 verbose = overbose;
2319 return(rtime);
2320 }
2321
2322 /*
2323 * get last modification time of file on remote machine
2324 */
2325 void
2326 modtime(argc, argv)
2327 int argc;
2328 char *argv[];
2329 {
2330 time_t mtime;
2331
2332 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2333 printf("usage: %s filename\n", argv[0]);
2334 code = -1;
2335 return;
2336 }
2337 mtime = remotemodtime(argv[1], 1);
2338 if (mtime != -1)
2339 printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2340 code = mtime;
2341 }
2342
2343 /*
2344 * show status on remote machine
2345 */
2346 void
2347 rmtstatus(argc, argv)
2348 int argc;
2349 char *argv[];
2350 {
2351
2352 (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2353 }
2354
2355 /*
2356 * get file if modtime is more recent than current file
2357 */
2358 void
2359 newer(argc, argv)
2360 int argc;
2361 char *argv[];
2362 {
2363
2364 if (getit(argc, argv, -1, "w"))
2365 printf("Local file \"%s\" is newer than remote file \"%s\"\n",
2366 argv[2], argv[1]);
2367 }
2368