cmds.c revision 1.13 1 /* $NetBSD: cmds.c,v 1.13 1996/12/29 04:05:29 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.13 1996/12/29 04:05:29 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 sig_t oldintr;
1780
1781 if (argc < 2 && !another(&argc, &argv, "command")) {
1782 printf("usage: %s command\n", argv[0]);
1783 code = -1;
1784 return;
1785 }
1786 c = getcmd(argv[1]);
1787 if (c == (struct cmd *) -1) {
1788 printf("?Ambiguous command\n");
1789 (void) fflush(stdout);
1790 code = -1;
1791 return;
1792 }
1793 if (c == 0) {
1794 printf("?Invalid command\n");
1795 (void) fflush(stdout);
1796 code = -1;
1797 return;
1798 }
1799 if (!c->c_proxy) {
1800 printf("?Invalid proxy command\n");
1801 (void) fflush(stdout);
1802 code = -1;
1803 return;
1804 }
1805 if (setjmp(abortprox)) {
1806 code = -1;
1807 return;
1808 }
1809 oldintr = signal(SIGINT, proxabort);
1810 pswitch(1);
1811 if (c->c_conn && !connected) {
1812 printf("Not connected\n");
1813 (void) fflush(stdout);
1814 pswitch(0);
1815 (void) signal(SIGINT, oldintr);
1816 code = -1;
1817 return;
1818 }
1819 (*c->c_handler)(argc-1, argv+1);
1820 if (connected) {
1821 proxflag = 1;
1822 }
1823 else {
1824 proxflag = 0;
1825 }
1826 pswitch(0);
1827 (void) signal(SIGINT, oldintr);
1828 }
1829
1830 void
1831 setcase(argc, argv)
1832 int argc;
1833 char *argv[];
1834 {
1835
1836 code = togglevar(argc, argv, &mcase, "Case mapping");
1837 }
1838
1839 void
1840 setcr(argc, argv)
1841 int argc;
1842 char *argv[];
1843 {
1844
1845 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1846 }
1847
1848 void
1849 setntrans(argc, argv)
1850 int argc;
1851 char *argv[];
1852 {
1853 if (argc == 1) {
1854 ntflag = 0;
1855 printf("Ntrans off.\n");
1856 code = ntflag;
1857 return;
1858 }
1859 ntflag++;
1860 code = ntflag;
1861 (void) strncpy(ntin, argv[1], 16);
1862 ntin[16] = '\0';
1863 if (argc == 2) {
1864 ntout[0] = '\0';
1865 return;
1866 }
1867 (void) strncpy(ntout, argv[2], 16);
1868 ntout[16] = '\0';
1869 }
1870
1871 char *
1872 dotrans(name)
1873 char *name;
1874 {
1875 static char new[MAXPATHLEN];
1876 char *cp1, *cp2 = new;
1877 int i, ostop, found;
1878
1879 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1880 continue;
1881 for (cp1 = name; *cp1; cp1++) {
1882 found = 0;
1883 for (i = 0; *(ntin + i) && i < 16; i++) {
1884 if (*cp1 == *(ntin + i)) {
1885 found++;
1886 if (i < ostop) {
1887 *cp2++ = *(ntout + i);
1888 }
1889 break;
1890 }
1891 }
1892 if (!found) {
1893 *cp2++ = *cp1;
1894 }
1895 }
1896 *cp2 = '\0';
1897 return (new);
1898 }
1899
1900 void
1901 setnmap(argc, argv)
1902 int argc;
1903 char *argv[];
1904 {
1905 char *cp;
1906
1907 if (argc == 1) {
1908 mapflag = 0;
1909 printf("Nmap off.\n");
1910 code = mapflag;
1911 return;
1912 }
1913 if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1914 printf("Usage: %s [mapin mapout]\n", argv[0]);
1915 code = -1;
1916 return;
1917 }
1918 mapflag = 1;
1919 code = 1;
1920 cp = strchr(altarg, ' ');
1921 if (proxy) {
1922 while(*++cp == ' ')
1923 continue;
1924 altarg = cp;
1925 cp = strchr(altarg, ' ');
1926 }
1927 *cp = '\0';
1928 (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1929 while (*++cp == ' ')
1930 continue;
1931 (void) strncpy(mapout, cp, MAXPATHLEN - 1);
1932 }
1933
1934 char *
1935 domap(name)
1936 char *name;
1937 {
1938 static char new[MAXPATHLEN];
1939 char *cp1 = name, *cp2 = mapin;
1940 char *tp[9], *te[9];
1941 int i, toks[9], toknum = 0, match = 1;
1942
1943 for (i=0; i < 9; ++i) {
1944 toks[i] = 0;
1945 }
1946 while (match && *cp1 && *cp2) {
1947 switch (*cp2) {
1948 case '\\':
1949 if (*++cp2 != *cp1) {
1950 match = 0;
1951 }
1952 break;
1953 case '$':
1954 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1955 if (*cp1 != *(++cp2+1)) {
1956 toks[toknum = *cp2 - '1']++;
1957 tp[toknum] = cp1;
1958 while (*++cp1 && *(cp2+1)
1959 != *cp1);
1960 te[toknum] = cp1;
1961 }
1962 cp2++;
1963 break;
1964 }
1965 /* FALLTHROUGH */
1966 default:
1967 if (*cp2 != *cp1) {
1968 match = 0;
1969 }
1970 break;
1971 }
1972 if (match && *cp1) {
1973 cp1++;
1974 }
1975 if (match && *cp2) {
1976 cp2++;
1977 }
1978 }
1979 if (!match && *cp1) /* last token mismatch */
1980 {
1981 toks[toknum] = 0;
1982 }
1983 cp1 = new;
1984 *cp1 = '\0';
1985 cp2 = mapout;
1986 while (*cp2) {
1987 match = 0;
1988 switch (*cp2) {
1989 case '\\':
1990 if (*(cp2 + 1)) {
1991 *cp1++ = *++cp2;
1992 }
1993 break;
1994 case '[':
1995 LOOP:
1996 if (*++cp2 == '$' && isdigit(*(cp2+1))) {
1997 if (*++cp2 == '0') {
1998 char *cp3 = name;
1999
2000 while (*cp3) {
2001 *cp1++ = *cp3++;
2002 }
2003 match = 1;
2004 }
2005 else if (toks[toknum = *cp2 - '1']) {
2006 char *cp3 = tp[toknum];
2007
2008 while (cp3 != te[toknum]) {
2009 *cp1++ = *cp3++;
2010 }
2011 match = 1;
2012 }
2013 }
2014 else {
2015 while (*cp2 && *cp2 != ',' &&
2016 *cp2 != ']') {
2017 if (*cp2 == '\\') {
2018 cp2++;
2019 }
2020 else if (*cp2 == '$' &&
2021 isdigit(*(cp2+1))) {
2022 if (*++cp2 == '0') {
2023 char *cp3 = name;
2024
2025 while (*cp3) {
2026 *cp1++ = *cp3++;
2027 }
2028 }
2029 else if (toks[toknum =
2030 *cp2 - '1']) {
2031 char *cp3=tp[toknum];
2032
2033 while (cp3 !=
2034 te[toknum]) {
2035 *cp1++ = *cp3++;
2036 }
2037 }
2038 }
2039 else if (*cp2) {
2040 *cp1++ = *cp2++;
2041 }
2042 }
2043 if (!*cp2) {
2044 printf("nmap: unbalanced "
2045 "brackets\n");
2046 return (name);
2047 }
2048 match = 1;
2049 cp2--;
2050 }
2051 if (match) {
2052 while (*++cp2 && *cp2 != ']') {
2053 if (*cp2 == '\\' && *(cp2 + 1)) {
2054 cp2++;
2055 }
2056 }
2057 if (!*cp2) {
2058 printf("nmap: unbalanced "
2059 "brackets\n");
2060 return (name);
2061 }
2062 break;
2063 }
2064 switch (*++cp2) {
2065 case ',':
2066 goto LOOP;
2067 case ']':
2068 break;
2069 default:
2070 cp2--;
2071 goto LOOP;
2072 }
2073 break;
2074 case '$':
2075 if (isdigit(*(cp2 + 1))) {
2076 if (*++cp2 == '0') {
2077 char *cp3 = name;
2078
2079 while (*cp3) {
2080 *cp1++ = *cp3++;
2081 }
2082 }
2083 else if (toks[toknum = *cp2 - '1']) {
2084 char *cp3 = tp[toknum];
2085
2086 while (cp3 != te[toknum]) {
2087 *cp1++ = *cp3++;
2088 }
2089 }
2090 break;
2091 }
2092 /* intentional drop through */
2093 default:
2094 *cp1++ = *cp2;
2095 break;
2096 }
2097 cp2++;
2098 }
2099 *cp1 = '\0';
2100 if (!*new) {
2101 return (name);
2102 }
2103 return (new);
2104 }
2105
2106 void
2107 setpassive(argc, argv)
2108 int argc;
2109 char *argv[];
2110 {
2111
2112 code = togglevar(argc, argv, &passivemode, "Passive mode");
2113 }
2114
2115 void
2116 setsunique(argc, argv)
2117 int argc;
2118 char *argv[];
2119 {
2120
2121 code = togglevar(argc, argv, &sunique, "Store unique");
2122 }
2123
2124 void
2125 setrunique(argc, argv)
2126 int argc;
2127 char *argv[];
2128 {
2129
2130 code = togglevar(argc, argv, &runique, "Receive unique");
2131 }
2132
2133 /* change directory to perent directory */
2134 void
2135 cdup(argc, argv)
2136 int argc;
2137 char *argv[];
2138 {
2139
2140 if (command("CDUP") == ERROR && code == 500) {
2141 if (verbose)
2142 printf("CDUP command not recognized, trying XCUP\n");
2143 (void) command("XCUP");
2144 }
2145 }
2146
2147 /* restart transfer at specific point */
2148 void
2149 restart(argc, argv)
2150 int argc;
2151 char *argv[];
2152 {
2153
2154 if (argc != 2)
2155 printf("restart: offset not specified\n");
2156 else {
2157 restart_point = atol(argv[1]);
2158 printf("Restarting at %qd. Execute get, put or append to"
2159 "initiate transfer\n", restart_point);
2160 }
2161 }
2162
2163 /* show remote system type */
2164 void
2165 syst(argc, argv)
2166 int argc;
2167 char *argv[];
2168 {
2169
2170 (void) command("SYST");
2171 }
2172
2173 void
2174 macdef(argc, argv)
2175 int argc;
2176 char *argv[];
2177 {
2178 char *tmp;
2179 int c;
2180
2181 if (macnum == 16) {
2182 printf("Limit of 16 macros have already been defined\n");
2183 code = -1;
2184 return;
2185 }
2186 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2187 printf("Usage: %s macro_name\n", argv[0]);
2188 code = -1;
2189 return;
2190 }
2191 if (interactive) {
2192 printf("Enter macro line by line, terminating it with a "
2193 "null line\n");
2194 }
2195 (void) strncpy(macros[macnum].mac_name, argv[1], 8);
2196 if (macnum == 0) {
2197 macros[macnum].mac_start = macbuf;
2198 }
2199 else {
2200 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2201 }
2202 tmp = macros[macnum].mac_start;
2203 while (tmp != macbuf+4096) {
2204 if ((c = getchar()) == EOF) {
2205 printf("macdef:end of file encountered\n");
2206 code = -1;
2207 return;
2208 }
2209 if ((*tmp = c) == '\n') {
2210 if (tmp == macros[macnum].mac_start) {
2211 macros[macnum++].mac_end = tmp;
2212 code = 0;
2213 return;
2214 }
2215 if (*(tmp-1) == '\0') {
2216 macros[macnum++].mac_end = tmp - 1;
2217 code = 0;
2218 return;
2219 }
2220 *tmp = '\0';
2221 }
2222 tmp++;
2223 }
2224 while (1) {
2225 while ((c = getchar()) != '\n' && c != EOF)
2226 /* LOOP */;
2227 if (c == EOF || getchar() == '\n') {
2228 printf("Macro not defined - 4k buffer exceeded\n");
2229 code = -1;
2230 return;
2231 }
2232 }
2233 }
2234
2235 /*
2236 * determine size of remote file
2237 */
2238 off_t
2239 remotesize(file, noisy)
2240 const char *file;
2241 int noisy;
2242 {
2243 int overbose;
2244 off_t size;
2245
2246 overbose = verbose;
2247 size = -1;
2248 if (debug == 0)
2249 verbose = -1;
2250 if (command("SIZE %s", file) == COMPLETE)
2251 sscanf(reply_string, "%*s %qd", &size);
2252 else if (noisy && debug == 0)
2253 printf("%s\n", reply_string);
2254 verbose = overbose;
2255 return (size);
2256 }
2257
2258 /*
2259 * get size of file on remote machine
2260 */
2261 void
2262 sizecmd(argc, argv)
2263 int argc;
2264 char *argv[];
2265 {
2266 off_t size;
2267
2268 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2269 printf("usage: %s filename\n", argv[0]);
2270 code = -1;
2271 return;
2272 }
2273 size = remotesize(argv[1], 1);
2274 if (size != -1)
2275 printf("%s\t%qd\n", argv[1], size);
2276 code = size;
2277 }
2278
2279 /*
2280 * determine last modification time (in GMT) of remote file
2281 */
2282 time_t
2283 remotemodtime(file, noisy)
2284 const char *file;
2285 int noisy;
2286 {
2287 int overbose;
2288 time_t rtime;
2289
2290 overbose = verbose;
2291 rtime = -1;
2292 if (debug == 0)
2293 verbose = -1;
2294 if (command("MDTM %s", file) == COMPLETE) {
2295 struct tm timebuf;
2296 int yy, mo, day, hour, min, sec;
2297 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
2298 &day, &hour, &min, &sec);
2299 memset(&timebuf, 0, sizeof(timebuf));
2300 timebuf.tm_sec = sec;
2301 timebuf.tm_min = min;
2302 timebuf.tm_hour = hour;
2303 timebuf.tm_mday = day;
2304 timebuf.tm_mon = mo - 1;
2305 timebuf.tm_year = yy - 1900;
2306 timebuf.tm_isdst = -1;
2307 rtime = mktime(&timebuf);
2308 if (rtime == -1 && (noisy || debug != 0))
2309 printf("Can't convert %s to a time\n", reply_string);
2310 else
2311 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
2312 } else if (noisy && debug == 0)
2313 printf("%s\n", reply_string);
2314 verbose = overbose;
2315 return(rtime);
2316 }
2317
2318 /*
2319 * get last modification time of file on remote machine
2320 */
2321 void
2322 modtime(argc, argv)
2323 int argc;
2324 char *argv[];
2325 {
2326 time_t mtime;
2327
2328 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2329 printf("usage: %s filename\n", argv[0]);
2330 code = -1;
2331 return;
2332 }
2333 mtime = remotemodtime(argv[1], 1);
2334 if (mtime != -1)
2335 printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2336 code = mtime;
2337 }
2338
2339 /*
2340 * show status on remote machine
2341 */
2342 void
2343 rmtstatus(argc, argv)
2344 int argc;
2345 char *argv[];
2346 {
2347
2348 (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2349 }
2350
2351 /*
2352 * get file if modtime is more recent than current file
2353 */
2354 void
2355 newer(argc, argv)
2356 int argc;
2357 char *argv[];
2358 {
2359
2360 if (getit(argc, argv, -1, "w"))
2361 printf("Local file \"%s\" is newer than remote file \"%s\"\n",
2362 argv[2], argv[1]);
2363 }
2364