cmds.c revision 1.11 1 /* $NetBSD: cmds.c,v 1.11 1996/12/06 02:06:46 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.11 1996/12/06 02:06:46 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]);
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[16];
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) strcpy(temp, _PATH_TMP);
791 (void) mktemp(temp);
792 /* create a zero-length version of the file so that
793 * people can't play symlink games.
794 */
795 fd = open (temp, O_CREAT | O_EXCL | O_WRONLY, 600);
796 if (fd < 0) {
797 printf("Temporary file %s already exists\n", temp);
798 return (NULL);
799 }
800 close(fd);
801 oldverbose = verbose, verbose = 0;
802 oldhash = hash, hash = 0;
803 if (doswitch) {
804 pswitch(!proxy);
805 }
806 for (mode = "w"; *++argv != NULL; mode = "a")
807 recvrequest ("NLST", temp, *argv, mode, 0);
808 if (doswitch) {
809 pswitch(!proxy);
810 }
811 verbose = oldverbose; hash = oldhash;
812 ftemp = fopen(temp, "r");
813 (void) unlink(temp);
814 if (ftemp == NULL) {
815 printf("can't find list of remote files, oops\n");
816 return (NULL);
817 }
818 }
819 if (fgets(buf, sizeof (buf), ftemp) == NULL) {
820 (void) fclose(ftemp), ftemp = NULL;
821 return (NULL);
822 }
823 if ((cp = strchr(buf, '\n')) != NULL)
824 *cp = '\0';
825 return (buf);
826 }
827
828 char *
829 onoff(bool)
830 int bool;
831 {
832
833 return (bool ? "on" : "off");
834 }
835
836 /*
837 * Show status.
838 */
839 /*ARGSUSED*/
840 void
841 status(argc, argv)
842 int argc;
843 char *argv[];
844 {
845 int i;
846
847 if (connected)
848 printf("Connected to %s.\n", hostname);
849 else
850 printf("Not connected.\n");
851 if (!proxy) {
852 pswitch(1);
853 if (connected) {
854 printf("Connected for proxy commands to %s.\n",
855 hostname);
856 }
857 else {
858 printf("No proxy connection.\n");
859 }
860 pswitch(0);
861 }
862 printf("Passive mode: %s.\n", onoff(passivemode));
863 printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
864 modename, typename, formname, structname);
865 printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
866 onoff(verbose), onoff(bell), onoff(interactive),
867 onoff(doglob));
868 printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
869 onoff(runique));
870 printf("Preserve modification times: %s\n", onoff(preserve));
871 printf("Case: %s; CR stripping: %s\n", onoff(mcase), onoff(crflag));
872 if (ntflag) {
873 printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
874 }
875 else {
876 printf("Ntrans: off\n");
877 }
878 if (mapflag) {
879 printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
880 }
881 else {
882 printf("Nmap: off\n");
883 }
884 printf("Hash mark printing: %s; Mark count: %d; Progress bar: %s\n",
885 onoff(hash), mark, onoff(progress));
886 printf("Use of PORT cmds: %s\n", onoff(sendport));
887 if (macnum > 0) {
888 printf("Macros:\n");
889 for (i=0; i<macnum; i++) {
890 printf("\t%s\n", macros[i].mac_name);
891 }
892 }
893 code = 0;
894 }
895
896 /*
897 * Toggle a variable
898 */
899 int
900 togglevar(argc, argv, var, mesg)
901 int argc;
902 char *argv[];
903 int *var;
904 const char *mesg;
905 {
906 if (argc < 2) {
907 *var = !*var;
908 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
909 *var = 1;
910 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
911 *var = 0;
912 } else {
913 printf("usage: %s [ on | off ]\n", argv[0]);
914 return -1;
915 }
916 printf("%s %s.\n", mesg, onoff(*var));
917 return *var;
918 }
919
920 /*
921 * Set beep on cmd completed mode.
922 */
923 /*VARARGS*/
924 void
925 setbell(argc, argv)
926 int argc;
927 char *argv[];
928 {
929
930 code = togglevar(argc, argv, &bell, "Bell mode");
931 }
932
933 /*
934 * Turn on packet tracing.
935 */
936 /*VARARGS*/
937 void
938 settrace(argc, argv)
939 int argc;
940 char *argv[];
941 {
942
943 code = togglevar(argc, argv, &trace, "Packet tracing");
944 }
945
946 /*
947 * Toggle hash mark printing during transfers, or set hash mark bytecount.
948 */
949 /*VARARGS*/
950 void
951 sethash(argc, argv)
952 int argc;
953 char *argv[];
954 {
955 if (argc == 1)
956 hash = !hash;
957 else if (argc != 2) {
958 printf("usage: %s [ on | off | bytecount ]\n", argv[0]);
959 code = -1;
960 return;
961 } else if (strcasecmp(argv[1], "on") == 0)
962 hash = 1;
963 else if (strcasecmp(argv[1], "off") == 0)
964 hash = 0;
965 else {
966 int nmark = atol(argv[1]);
967 if (nmark < 1) {
968 printf("%s: bad bytecount value\n", argv[1]);
969 code = -1;
970 return;
971 }
972 mark = nmark;
973 hash = 1;
974 }
975 printf("Hash mark printing %s", onoff(hash));
976 if (hash)
977 printf(" (%d bytes/hash mark)", mark);
978 printf(".\n");
979 code = hash;
980 }
981
982 /*
983 * Turn on printing of server echo's.
984 */
985 /*VARARGS*/
986 void
987 setverbose(argc, argv)
988 int argc;
989 char *argv[];
990 {
991
992 code = togglevar(argc, argv, &verbose, "Verbose mode");
993 }
994
995 /*
996 * Toggle PORT cmd use before each data connection.
997 */
998 /*VARARGS*/
999 void
1000 setport(argc, argv)
1001 int argc;
1002 char *argv[];
1003 {
1004
1005 code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
1006 }
1007
1008 /*
1009 * Toggle transfer progress bar.
1010 */
1011 /*VARARGS*/
1012 void
1013 setprogress(argc, argv)
1014 int argc;
1015 char *argv[];
1016 {
1017
1018 code = togglevar(argc, argv, &progress, "Progress bar");
1019 }
1020
1021 /*
1022 * Turn on interactive prompting
1023 * during mget, mput, and mdelete.
1024 */
1025 /*VARARGS*/
1026 void
1027 setprompt(argc, argv)
1028 int argc;
1029 char *argv[];
1030 {
1031
1032 code = togglevar(argc, argv, &interactive, "Interactive mode");
1033 }
1034
1035 /*
1036 * Toggle metacharacter interpretation
1037 * on local file names.
1038 */
1039 /*VARARGS*/
1040 void
1041 setglob(argc, argv)
1042 int argc;
1043 char *argv[];
1044 {
1045
1046 code = togglevar(argc, argv, &doglob, "Globbing");
1047 }
1048
1049 /*
1050 * Toggle preserving modification times on retreived files.
1051 */
1052 /*VARARGS*/
1053 void
1054 setpreserve(argc, argv)
1055 int argc;
1056 char *argv[];
1057 {
1058
1059 code = togglevar(argc, argv, &preserve, "Preserve modification times");
1060 }
1061
1062 /*
1063 * Set debugging mode on/off and/or
1064 * set level of debugging.
1065 */
1066 /*VARARGS*/
1067 void
1068 setdebug(argc, argv)
1069 int argc;
1070 char *argv[];
1071 {
1072 int val;
1073
1074 if (argc > 2) {
1075 printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
1076 code = -1;
1077 return;
1078 } else if (argc == 2) {
1079 if (strcasecmp(argv[1], "on") == 0)
1080 debug = 1;
1081 else if (strcasecmp(argv[1], "off") == 0)
1082 debug = 0;
1083 else {
1084 val = atoi(argv[1]);
1085 if (val < 0) {
1086 printf("%s: bad debugging value.\n", argv[1]);
1087 code = -1;
1088 return;
1089 }
1090 debug = val;
1091 }
1092 } else
1093 debug = !debug;
1094 if (debug)
1095 options |= SO_DEBUG;
1096 else
1097 options &= ~SO_DEBUG;
1098 printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
1099 code = debug > 0;
1100 }
1101
1102 /*
1103 * Set current working directory
1104 * on remote machine.
1105 */
1106 void
1107 cd(argc, argv)
1108 int argc;
1109 char *argv[];
1110 {
1111
1112 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
1113 argc > 2) {
1114 printf("usage: %s remote-directory\n", argv[0]);
1115 code = -1;
1116 return;
1117 }
1118 if (command("CWD %s", argv[1]) == ERROR && code == 500) {
1119 if (verbose)
1120 printf("CWD command not recognized, trying XCWD\n");
1121 (void) command("XCWD %s", argv[1]);
1122 }
1123 }
1124
1125 /*
1126 * Set current working directory
1127 * on local machine.
1128 */
1129 void
1130 lcd(argc, argv)
1131 int argc;
1132 char *argv[];
1133 {
1134 char buf[MAXPATHLEN];
1135
1136 if (argc < 2)
1137 argc++, argv[1] = home;
1138 if (argc != 2) {
1139 printf("usage: %s local-directory\n", argv[0]);
1140 code = -1;
1141 return;
1142 }
1143 if (!globulize(&argv[1])) {
1144 code = -1;
1145 return;
1146 }
1147 if (chdir(argv[1]) < 0) {
1148 warn("local: %s", argv[1]);
1149 code = -1;
1150 return;
1151 }
1152 if (getwd(buf) != NULL)
1153 printf("Local directory now %s\n", buf);
1154 else
1155 warnx("getwd: %s", buf);
1156 code = 0;
1157 }
1158
1159 /*
1160 * Delete a single file.
1161 */
1162 void
1163 delete(argc, argv)
1164 int argc;
1165 char *argv[];
1166 {
1167
1168 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1169 printf("usage: %s remote-file\n", argv[0]);
1170 code = -1;
1171 return;
1172 }
1173 (void) command("DELE %s", argv[1]);
1174 }
1175
1176 /*
1177 * Delete multiple files.
1178 */
1179 void
1180 mdelete(argc, argv)
1181 int argc;
1182 char *argv[];
1183 {
1184 sig_t oldintr;
1185 int ointer;
1186 char *cp;
1187
1188 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1189 printf("usage: %s remote-files\n", argv[0]);
1190 code = -1;
1191 return;
1192 }
1193 mname = argv[0];
1194 mflag = 1;
1195 oldintr = signal(SIGINT, mabort);
1196 (void) setjmp(jabort);
1197 while ((cp = remglob(argv, 0)) != NULL) {
1198 if (*cp == '\0') {
1199 mflag = 0;
1200 continue;
1201 }
1202 if (mflag && confirm(argv[0], cp)) {
1203 (void) command("DELE %s", cp);
1204 if (!mflag && fromatty) {
1205 ointer = interactive;
1206 interactive = 1;
1207 if (confirm("Continue with", "mdelete")) {
1208 mflag++;
1209 }
1210 interactive = ointer;
1211 }
1212 }
1213 }
1214 (void) signal(SIGINT, oldintr);
1215 mflag = 0;
1216 }
1217
1218 /*
1219 * Rename a remote file.
1220 */
1221 void
1222 renamefile(argc, argv)
1223 int argc;
1224 char *argv[];
1225 {
1226
1227 if (argc < 2 && !another(&argc, &argv, "from-name"))
1228 goto usage;
1229 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1230 usage:
1231 printf("%s from-name to-name\n", argv[0]);
1232 code = -1;
1233 return;
1234 }
1235 if (command("RNFR %s", argv[1]) == CONTINUE)
1236 (void) command("RNTO %s", argv[2]);
1237 }
1238
1239 /*
1240 * Get a directory listing
1241 * of remote files.
1242 */
1243 void
1244 ls(argc, argv)
1245 int argc;
1246 char *argv[];
1247 {
1248 const char *cmd;
1249
1250 if (argc < 2)
1251 argc++, argv[1] = NULL;
1252 if (argc < 3)
1253 argc++, argv[2] = "-";
1254 if (argc > 3) {
1255 printf("usage: %s remote-directory local-file\n", argv[0]);
1256 code = -1;
1257 return;
1258 }
1259 cmd = strcmp(argv[0], "dir") == 0 ? "LIST" : "NLST";
1260 if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
1261 code = -1;
1262 return;
1263 }
1264 if (strcmp(argv[2], "-") && *argv[2] != '|')
1265 if (!globulize(&argv[2]) || !confirm("output to local-file:",
1266 argv[2])) {
1267 code = -1;
1268 return;
1269 }
1270 recvrequest(cmd, argv[2], argv[1], "w", 0);
1271
1272 /* flush results in case commands are coming from a pipe */
1273 fflush(stdout);
1274 }
1275
1276 /*
1277 * Get a directory listing
1278 * of multiple remote files.
1279 */
1280 void
1281 mls(argc, argv)
1282 int argc;
1283 char *argv[];
1284 {
1285 sig_t oldintr;
1286 int ointer, i;
1287 const char *cmd;
1288 char mode[1], *dest;
1289
1290 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1291 goto usage;
1292 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1293 usage:
1294 printf("usage: %s remote-files local-file\n", argv[0]);
1295 code = -1;
1296 return;
1297 }
1298 dest = argv[argc - 1];
1299 argv[argc - 1] = NULL;
1300 if (strcmp(dest, "-") && *dest != '|')
1301 if (!globulize(&dest) ||
1302 !confirm("output to local-file:", dest)) {
1303 code = -1;
1304 return;
1305 }
1306 cmd = strcmp(argv[0], "mls") == 0 ? "NLST" : "LIST";
1307 mname = argv[0];
1308 mflag = 1;
1309 oldintr = signal(SIGINT, mabort);
1310 (void) setjmp(jabort);
1311 for (i = 1; mflag && i < argc-1; ++i) {
1312 *mode = (i == 1) ? 'w' : 'a';
1313 recvrequest(cmd, dest, argv[i], mode, 0);
1314 if (!mflag && fromatty) {
1315 ointer = interactive;
1316 interactive = 1;
1317 if (confirm("Continue with", argv[0])) {
1318 mflag ++;
1319 }
1320 interactive = ointer;
1321 }
1322 }
1323 (void) signal(SIGINT, oldintr);
1324 mflag = 0;
1325 }
1326
1327 /*
1328 * Do a shell escape
1329 */
1330 /*ARGSUSED*/
1331 void
1332 shell(argc, argv)
1333 int argc;
1334 char *argv[];
1335 {
1336 pid_t pid;
1337 sig_t old1, old2;
1338 char shellnam[40], *shell, *namep;
1339 union wait status;
1340
1341 old1 = signal (SIGINT, SIG_IGN);
1342 old2 = signal (SIGQUIT, SIG_IGN);
1343 if ((pid = fork()) == 0) {
1344 for (pid = 3; pid < 20; pid++)
1345 (void) close(pid);
1346 (void) signal(SIGINT, SIG_DFL);
1347 (void) signal(SIGQUIT, SIG_DFL);
1348 shell = getenv("SHELL");
1349 if (shell == NULL)
1350 shell = _PATH_BSHELL;
1351 namep = strrchr(shell, '/');
1352 if (namep == NULL)
1353 namep = shell;
1354 (void) strcpy(shellnam, "-");
1355 (void) strcat(shellnam, ++namep);
1356 if (strcmp(namep, "sh") != 0)
1357 shellnam[0] = '+';
1358 if (debug) {
1359 printf ("%s\n", shell);
1360 (void) fflush (stdout);
1361 }
1362 if (argc > 1) {
1363 execl(shell, shellnam, "-c", altarg, (char *)0);
1364 }
1365 else {
1366 execl(shell, shellnam, (char *)0);
1367 }
1368 warn("%s", shell);
1369 code = -1;
1370 exit(1);
1371 }
1372 if (pid > 0)
1373 while (wait((int *)&status) != pid)
1374 ;
1375 (void) signal(SIGINT, old1);
1376 (void) signal(SIGQUIT, old2);
1377 if (pid == -1) {
1378 warn("%s", "Try again later");
1379 code = -1;
1380 }
1381 else {
1382 code = 0;
1383 }
1384 }
1385
1386 /*
1387 * Send new user information (re-login)
1388 */
1389 void
1390 user(argc, argv)
1391 int argc;
1392 char *argv[];
1393 {
1394 char acct[80];
1395 int n, aflag = 0;
1396
1397 if (argc < 2)
1398 (void) another(&argc, &argv, "username");
1399 if (argc < 2 || argc > 4) {
1400 printf("usage: %s username [password] [account]\n", argv[0]);
1401 code = -1;
1402 return;
1403 }
1404 n = command("USER %s", argv[1]);
1405 if (n == CONTINUE) {
1406 if (argc < 3 )
1407 argv[2] = getpass("Password: "), argc++;
1408 n = command("PASS %s", argv[2]);
1409 }
1410 if (n == CONTINUE) {
1411 if (argc < 4) {
1412 printf("Account: "); (void) fflush(stdout);
1413 (void) fgets(acct, sizeof(acct) - 1, stdin);
1414 acct[strlen(acct) - 1] = '\0';
1415 argv[3] = acct; argc++;
1416 }
1417 n = command("ACCT %s", argv[3]);
1418 aflag++;
1419 }
1420 if (n != COMPLETE) {
1421 fprintf(stdout, "Login failed.\n");
1422 return;
1423 }
1424 if (!aflag && argc == 4) {
1425 (void) command("ACCT %s", argv[3]);
1426 }
1427 }
1428
1429 /*
1430 * Print working directory on remote machine.
1431 */
1432 /*VARARGS*/
1433 void
1434 pwd(argc, argv)
1435 int argc;
1436 char *argv[];
1437 {
1438 int oldverbose = verbose;
1439
1440 /*
1441 * If we aren't verbose, this doesn't do anything!
1442 */
1443 verbose = 1;
1444 if (command("PWD") == ERROR && code == 500) {
1445 printf("PWD command not recognized, trying XPWD\n");
1446 (void) command("XPWD");
1447 }
1448 verbose = oldverbose;
1449 }
1450
1451 /*
1452 * Print working directory on local machine.
1453 */
1454 void
1455 lpwd(argc, argv)
1456 int argc;
1457 char *argv[];
1458 {
1459 char buf[MAXPATHLEN];
1460
1461 if (getwd(buf) != NULL)
1462 printf("Local directory %s\n", buf);
1463 else
1464 warnx("getwd: %s", buf);
1465 code = 0;
1466 }
1467
1468 /*
1469 * Make a directory.
1470 */
1471 void
1472 makedir(argc, argv)
1473 int argc;
1474 char *argv[];
1475 {
1476
1477 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1478 argc > 2) {
1479 printf("usage: %s directory-name\n", argv[0]);
1480 code = -1;
1481 return;
1482 }
1483 if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1484 if (verbose)
1485 printf("MKD command not recognized, trying XMKD\n");
1486 (void) command("XMKD %s", argv[1]);
1487 }
1488 }
1489
1490 /*
1491 * Remove a directory.
1492 */
1493 void
1494 removedir(argc, argv)
1495 int argc;
1496 char *argv[];
1497 {
1498
1499 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1500 argc > 2) {
1501 printf("usage: %s directory-name\n", argv[0]);
1502 code = -1;
1503 return;
1504 }
1505 if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1506 if (verbose)
1507 printf("RMD command not recognized, trying XRMD\n");
1508 (void) command("XRMD %s", argv[1]);
1509 }
1510 }
1511
1512 /*
1513 * Send a line, verbatim, to the remote machine.
1514 */
1515 void
1516 quote(argc, argv)
1517 int argc;
1518 char *argv[];
1519 {
1520
1521 if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1522 printf("usage: %s line-to-send\n", argv[0]);
1523 code = -1;
1524 return;
1525 }
1526 quote1("", argc, argv);
1527 }
1528
1529 /*
1530 * Send a SITE command to the remote machine. The line
1531 * is sent verbatim to the remote machine, except that the
1532 * word "SITE" is added at the front.
1533 */
1534 void
1535 site(argc, argv)
1536 int argc;
1537 char *argv[];
1538 {
1539
1540 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1541 printf("usage: %s line-to-send\n", argv[0]);
1542 code = -1;
1543 return;
1544 }
1545 quote1("SITE ", argc, argv);
1546 }
1547
1548 /*
1549 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1550 * Send the result as a one-line command and get response.
1551 */
1552 void
1553 quote1(initial, argc, argv)
1554 const char *initial;
1555 int argc;
1556 char *argv[];
1557 {
1558 int i, len;
1559 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1560
1561 (void) strcpy(buf, initial);
1562 if (argc > 1) {
1563 len = strlen(buf);
1564 len += strlen(strcpy(&buf[len], argv[1]));
1565 for (i = 2; i < argc; i++) {
1566 buf[len++] = ' ';
1567 len += strlen(strcpy(&buf[len], argv[i]));
1568 }
1569 }
1570 if (command(buf) == PRELIM) {
1571 while (getreply(0) == PRELIM)
1572 continue;
1573 }
1574 }
1575
1576 void
1577 do_chmod(argc, argv)
1578 int argc;
1579 char *argv[];
1580 {
1581
1582 if (argc < 2 && !another(&argc, &argv, "mode"))
1583 goto usage;
1584 if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1585 usage:
1586 printf("usage: %s mode file-name\n", argv[0]);
1587 code = -1;
1588 return;
1589 }
1590 (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
1591 }
1592
1593 void
1594 do_umask(argc, argv)
1595 int argc;
1596 char *argv[];
1597 {
1598 int oldverbose = verbose;
1599
1600 verbose = 1;
1601 (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1602 verbose = oldverbose;
1603 }
1604
1605 void
1606 idle(argc, argv)
1607 int argc;
1608 char *argv[];
1609 {
1610 int oldverbose = verbose;
1611
1612 verbose = 1;
1613 (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1614 verbose = oldverbose;
1615 }
1616
1617 /*
1618 * Ask the other side for help.
1619 */
1620 void
1621 rmthelp(argc, argv)
1622 int argc;
1623 char *argv[];
1624 {
1625 int oldverbose = verbose;
1626
1627 verbose = 1;
1628 (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1629 verbose = oldverbose;
1630 }
1631
1632 /*
1633 * Terminate session and exit.
1634 */
1635 /*VARARGS*/
1636 void
1637 quit(argc, argv)
1638 int argc;
1639 char *argv[];
1640 {
1641
1642 if (connected)
1643 disconnect(0, 0);
1644 pswitch(1);
1645 if (connected) {
1646 disconnect(0, 0);
1647 }
1648 exit(0);
1649 }
1650
1651 /*
1652 * Terminate session, but don't exit.
1653 */
1654 void
1655 disconnect(argc, argv)
1656 int argc;
1657 char *argv[];
1658 {
1659
1660 if (!connected)
1661 return;
1662 (void) command("QUIT");
1663 if (cout) {
1664 (void) fclose(cout);
1665 }
1666 cout = NULL;
1667 connected = 0;
1668 data = -1;
1669 if (!proxy) {
1670 macnum = 0;
1671 }
1672 }
1673
1674 int
1675 confirm(cmd, file)
1676 const char *cmd, *file;
1677 {
1678 char line[BUFSIZ];
1679
1680 if (!interactive || confirmrest)
1681 return (1);
1682 printf("%s %s? ", cmd, file);
1683 (void) fflush(stdout);
1684 if (fgets(line, sizeof(line), stdin) == NULL)
1685 return (0);
1686 switch (tolower(*line)) {
1687 case 'n':
1688 return (0);
1689 case 'p':
1690 interactive = 0;
1691 printf("Interactive mode: off\n");
1692 break;
1693 case 'a':
1694 confirmrest = 1;
1695 printf("Prompting off for duration of %s\n", cmd);
1696 break;
1697 }
1698 return (1);
1699 }
1700
1701 void
1702 fatal(msg)
1703 const char *msg;
1704 {
1705
1706 errx(1, "%s", msg);
1707 }
1708
1709 /*
1710 * Glob a local file name specification with
1711 * the expectation of a single return value.
1712 * Can't control multiple values being expanded
1713 * from the expression, we return only the first.
1714 */
1715 int
1716 globulize(cpp)
1717 char **cpp;
1718 {
1719 glob_t gl;
1720 int flags;
1721
1722 if (!doglob)
1723 return (1);
1724
1725 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
1726 memset(&gl, 0, sizeof(gl));
1727 if (glob(*cpp, flags, NULL, &gl) ||
1728 gl.gl_pathc == 0) {
1729 warnx("%s: not found", *cpp);
1730 globfree(&gl);
1731 return (0);
1732 }
1733 *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
1734 globfree(&gl);
1735 return (1);
1736 }
1737
1738 void
1739 account(argc, argv)
1740 int argc;
1741 char *argv[];
1742 {
1743 char acct[50], *ap;
1744
1745 if (argc > 1) {
1746 ++argv;
1747 --argc;
1748 (void) strncpy(acct, *argv, 49);
1749 acct[49] = '\0';
1750 while (argc > 1) {
1751 --argc;
1752 ++argv;
1753 (void) strncat(acct, *argv, 49-strlen(acct));
1754 }
1755 ap = acct;
1756 }
1757 else {
1758 ap = getpass("Account:");
1759 }
1760 (void) command("ACCT %s", ap);
1761 }
1762
1763 jmp_buf abortprox;
1764
1765 void
1766 proxabort()
1767 {
1768
1769 if (!proxy) {
1770 pswitch(1);
1771 }
1772 if (connected) {
1773 proxflag = 1;
1774 }
1775 else {
1776 proxflag = 0;
1777 }
1778 pswitch(0);
1779 longjmp(abortprox, 1);
1780 }
1781
1782 void
1783 doproxy(argc, argv)
1784 int argc;
1785 char *argv[];
1786 {
1787 struct cmd *c;
1788 sig_t oldintr;
1789
1790 if (argc < 2 && !another(&argc, &argv, "command")) {
1791 printf("usage: %s command\n", argv[0]);
1792 code = -1;
1793 return;
1794 }
1795 c = getcmd(argv[1]);
1796 if (c == (struct cmd *) -1) {
1797 printf("?Ambiguous command\n");
1798 (void) fflush(stdout);
1799 code = -1;
1800 return;
1801 }
1802 if (c == 0) {
1803 printf("?Invalid command\n");
1804 (void) fflush(stdout);
1805 code = -1;
1806 return;
1807 }
1808 if (!c->c_proxy) {
1809 printf("?Invalid proxy command\n");
1810 (void) fflush(stdout);
1811 code = -1;
1812 return;
1813 }
1814 if (setjmp(abortprox)) {
1815 code = -1;
1816 return;
1817 }
1818 oldintr = signal(SIGINT, proxabort);
1819 pswitch(1);
1820 if (c->c_conn && !connected) {
1821 printf("Not connected\n");
1822 (void) fflush(stdout);
1823 pswitch(0);
1824 (void) signal(SIGINT, oldintr);
1825 code = -1;
1826 return;
1827 }
1828 (*c->c_handler)(argc-1, argv+1);
1829 if (connected) {
1830 proxflag = 1;
1831 }
1832 else {
1833 proxflag = 0;
1834 }
1835 pswitch(0);
1836 (void) signal(SIGINT, oldintr);
1837 }
1838
1839 void
1840 setcase(argc, argv)
1841 int argc;
1842 char *argv[];
1843 {
1844
1845 code = togglevar(argc, argv, &mcase, "Case mapping");
1846 }
1847
1848 void
1849 setcr(argc, argv)
1850 int argc;
1851 char *argv[];
1852 {
1853
1854 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1855 }
1856
1857 void
1858 setntrans(argc, argv)
1859 int argc;
1860 char *argv[];
1861 {
1862 if (argc == 1) {
1863 ntflag = 0;
1864 printf("Ntrans off.\n");
1865 code = ntflag;
1866 return;
1867 }
1868 ntflag++;
1869 code = ntflag;
1870 (void) strncpy(ntin, argv[1], 16);
1871 ntin[16] = '\0';
1872 if (argc == 2) {
1873 ntout[0] = '\0';
1874 return;
1875 }
1876 (void) strncpy(ntout, argv[2], 16);
1877 ntout[16] = '\0';
1878 }
1879
1880 char *
1881 dotrans(name)
1882 char *name;
1883 {
1884 static char new[MAXPATHLEN];
1885 char *cp1, *cp2 = new;
1886 int i, ostop, found;
1887
1888 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1889 continue;
1890 for (cp1 = name; *cp1; cp1++) {
1891 found = 0;
1892 for (i = 0; *(ntin + i) && i < 16; i++) {
1893 if (*cp1 == *(ntin + i)) {
1894 found++;
1895 if (i < ostop) {
1896 *cp2++ = *(ntout + i);
1897 }
1898 break;
1899 }
1900 }
1901 if (!found) {
1902 *cp2++ = *cp1;
1903 }
1904 }
1905 *cp2 = '\0';
1906 return (new);
1907 }
1908
1909 void
1910 setnmap(argc, argv)
1911 int argc;
1912 char *argv[];
1913 {
1914 char *cp;
1915
1916 if (argc == 1) {
1917 mapflag = 0;
1918 printf("Nmap off.\n");
1919 code = mapflag;
1920 return;
1921 }
1922 if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1923 printf("Usage: %s [mapin mapout]\n", argv[0]);
1924 code = -1;
1925 return;
1926 }
1927 mapflag = 1;
1928 code = 1;
1929 cp = strchr(altarg, ' ');
1930 if (proxy) {
1931 while(*++cp == ' ')
1932 continue;
1933 altarg = cp;
1934 cp = strchr(altarg, ' ');
1935 }
1936 *cp = '\0';
1937 (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
1938 while (*++cp == ' ')
1939 continue;
1940 (void) strncpy(mapout, cp, MAXPATHLEN - 1);
1941 }
1942
1943 char *
1944 domap(name)
1945 char *name;
1946 {
1947 static char new[MAXPATHLEN];
1948 char *cp1 = name, *cp2 = mapin;
1949 char *tp[9], *te[9];
1950 int i, toks[9], toknum = 0, match = 1;
1951
1952 for (i=0; i < 9; ++i) {
1953 toks[i] = 0;
1954 }
1955 while (match && *cp1 && *cp2) {
1956 switch (*cp2) {
1957 case '\\':
1958 if (*++cp2 != *cp1) {
1959 match = 0;
1960 }
1961 break;
1962 case '$':
1963 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1964 if (*cp1 != *(++cp2+1)) {
1965 toks[toknum = *cp2 - '1']++;
1966 tp[toknum] = cp1;
1967 while (*++cp1 && *(cp2+1)
1968 != *cp1);
1969 te[toknum] = cp1;
1970 }
1971 cp2++;
1972 break;
1973 }
1974 /* FALLTHROUGH */
1975 default:
1976 if (*cp2 != *cp1) {
1977 match = 0;
1978 }
1979 break;
1980 }
1981 if (match && *cp1) {
1982 cp1++;
1983 }
1984 if (match && *cp2) {
1985 cp2++;
1986 }
1987 }
1988 if (!match && *cp1) /* last token mismatch */
1989 {
1990 toks[toknum] = 0;
1991 }
1992 cp1 = new;
1993 *cp1 = '\0';
1994 cp2 = mapout;
1995 while (*cp2) {
1996 match = 0;
1997 switch (*cp2) {
1998 case '\\':
1999 if (*(cp2 + 1)) {
2000 *cp1++ = *++cp2;
2001 }
2002 break;
2003 case '[':
2004 LOOP:
2005 if (*++cp2 == '$' && isdigit(*(cp2+1))) {
2006 if (*++cp2 == '0') {
2007 char *cp3 = name;
2008
2009 while (*cp3) {
2010 *cp1++ = *cp3++;
2011 }
2012 match = 1;
2013 }
2014 else if (toks[toknum = *cp2 - '1']) {
2015 char *cp3 = tp[toknum];
2016
2017 while (cp3 != te[toknum]) {
2018 *cp1++ = *cp3++;
2019 }
2020 match = 1;
2021 }
2022 }
2023 else {
2024 while (*cp2 && *cp2 != ',' &&
2025 *cp2 != ']') {
2026 if (*cp2 == '\\') {
2027 cp2++;
2028 }
2029 else if (*cp2 == '$' &&
2030 isdigit(*(cp2+1))) {
2031 if (*++cp2 == '0') {
2032 char *cp3 = name;
2033
2034 while (*cp3) {
2035 *cp1++ = *cp3++;
2036 }
2037 }
2038 else if (toks[toknum =
2039 *cp2 - '1']) {
2040 char *cp3=tp[toknum];
2041
2042 while (cp3 !=
2043 te[toknum]) {
2044 *cp1++ = *cp3++;
2045 }
2046 }
2047 }
2048 else if (*cp2) {
2049 *cp1++ = *cp2++;
2050 }
2051 }
2052 if (!*cp2) {
2053 printf("nmap: unbalanced "
2054 "brackets\n");
2055 return (name);
2056 }
2057 match = 1;
2058 cp2--;
2059 }
2060 if (match) {
2061 while (*++cp2 && *cp2 != ']') {
2062 if (*cp2 == '\\' && *(cp2 + 1)) {
2063 cp2++;
2064 }
2065 }
2066 if (!*cp2) {
2067 printf("nmap: unbalanced "
2068 "brackets\n");
2069 return (name);
2070 }
2071 break;
2072 }
2073 switch (*++cp2) {
2074 case ',':
2075 goto LOOP;
2076 case ']':
2077 break;
2078 default:
2079 cp2--;
2080 goto LOOP;
2081 }
2082 break;
2083 case '$':
2084 if (isdigit(*(cp2 + 1))) {
2085 if (*++cp2 == '0') {
2086 char *cp3 = name;
2087
2088 while (*cp3) {
2089 *cp1++ = *cp3++;
2090 }
2091 }
2092 else if (toks[toknum = *cp2 - '1']) {
2093 char *cp3 = tp[toknum];
2094
2095 while (cp3 != te[toknum]) {
2096 *cp1++ = *cp3++;
2097 }
2098 }
2099 break;
2100 }
2101 /* intentional drop through */
2102 default:
2103 *cp1++ = *cp2;
2104 break;
2105 }
2106 cp2++;
2107 }
2108 *cp1 = '\0';
2109 if (!*new) {
2110 return (name);
2111 }
2112 return (new);
2113 }
2114
2115 void
2116 setpassive(argc, argv)
2117 int argc;
2118 char *argv[];
2119 {
2120
2121 code = togglevar(argc, argv, &passivemode, "Passive mode");
2122 }
2123
2124 void
2125 setsunique(argc, argv)
2126 int argc;
2127 char *argv[];
2128 {
2129
2130 code = togglevar(argc, argv, &sunique, "Store unique");
2131 }
2132
2133 void
2134 setrunique(argc, argv)
2135 int argc;
2136 char *argv[];
2137 {
2138
2139 code = togglevar(argc, argv, &runique, "Receive unique");
2140 }
2141
2142 /* change directory to perent directory */
2143 void
2144 cdup(argc, argv)
2145 int argc;
2146 char *argv[];
2147 {
2148
2149 if (command("CDUP") == ERROR && code == 500) {
2150 if (verbose)
2151 printf("CDUP command not recognized, trying XCUP\n");
2152 (void) command("XCUP");
2153 }
2154 }
2155
2156 /* restart transfer at specific point */
2157 void
2158 restart(argc, argv)
2159 int argc;
2160 char *argv[];
2161 {
2162
2163 if (argc != 2)
2164 printf("restart: offset not specified\n");
2165 else {
2166 restart_point = atol(argv[1]);
2167 printf("Restarting at %qd. Execute get, put or append to"
2168 "initiate transfer\n", restart_point);
2169 }
2170 }
2171
2172 /* show remote system type */
2173 void
2174 syst(argc, argv)
2175 int argc;
2176 char *argv[];
2177 {
2178
2179 (void) command("SYST");
2180 }
2181
2182 void
2183 macdef(argc, argv)
2184 int argc;
2185 char *argv[];
2186 {
2187 char *tmp;
2188 int c;
2189
2190 if (macnum == 16) {
2191 printf("Limit of 16 macros have already been defined\n");
2192 code = -1;
2193 return;
2194 }
2195 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2196 printf("Usage: %s macro_name\n", argv[0]);
2197 code = -1;
2198 return;
2199 }
2200 if (interactive) {
2201 printf("Enter macro line by line, terminating it with a "
2202 "null line\n");
2203 }
2204 (void) strncpy(macros[macnum].mac_name, argv[1], 8);
2205 if (macnum == 0) {
2206 macros[macnum].mac_start = macbuf;
2207 }
2208 else {
2209 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2210 }
2211 tmp = macros[macnum].mac_start;
2212 while (tmp != macbuf+4096) {
2213 if ((c = getchar()) == EOF) {
2214 printf("macdef:end of file encountered\n");
2215 code = -1;
2216 return;
2217 }
2218 if ((*tmp = c) == '\n') {
2219 if (tmp == macros[macnum].mac_start) {
2220 macros[macnum++].mac_end = tmp;
2221 code = 0;
2222 return;
2223 }
2224 if (*(tmp-1) == '\0') {
2225 macros[macnum++].mac_end = tmp - 1;
2226 code = 0;
2227 return;
2228 }
2229 *tmp = '\0';
2230 }
2231 tmp++;
2232 }
2233 while (1) {
2234 while ((c = getchar()) != '\n' && c != EOF)
2235 /* LOOP */;
2236 if (c == EOF || getchar() == '\n') {
2237 printf("Macro not defined - 4k buffer exceeded\n");
2238 code = -1;
2239 return;
2240 }
2241 }
2242 }
2243
2244 /*
2245 * determine size of remote file
2246 */
2247 off_t
2248 remotesize(file)
2249 const char *file;
2250 {
2251 int overbose;
2252 off_t size;
2253
2254 overbose = verbose;
2255 size = -1;
2256 if (debug == 0)
2257 verbose = -1;
2258 if (command("SIZE %s", file) == COMPLETE)
2259 sscanf(reply_string, "%*s %qd", &size);
2260 else if (debug == 0)
2261 printf("%s\n", reply_string);
2262 verbose = overbose;
2263 return (size);
2264 }
2265
2266 /*
2267 * get size of file on remote machine
2268 */
2269 void
2270 sizecmd(argc, argv)
2271 int argc;
2272 char *argv[];
2273 {
2274 off_t size;
2275
2276 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2277 printf("usage: %s filename\n", argv[0]);
2278 code = -1;
2279 return;
2280 }
2281 size = remotesize(argv[1]);
2282 if (size != -1)
2283 printf("%s\t%qd\n", argv[1], size);
2284 code = size;
2285 }
2286
2287 /*
2288 * determine last modification time (in GMT) of remote file
2289 */
2290 time_t
2291 remotemodtime(file)
2292 const char *file;
2293 {
2294 int overbose;
2295 time_t rtime;
2296
2297 overbose = verbose;
2298 rtime = -1;
2299 if (debug == 0)
2300 verbose = -1;
2301 if (command("MDTM %s", file) == COMPLETE) {
2302 struct tm timebuf;
2303 int yy, mo, day, hour, min, sec;
2304 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
2305 &day, &hour, &min, &sec);
2306 memset(&timebuf, 0, sizeof(timebuf));
2307 timebuf.tm_sec = sec;
2308 timebuf.tm_min = min;
2309 timebuf.tm_hour = hour;
2310 timebuf.tm_mday = day;
2311 timebuf.tm_mon = mo - 1;
2312 timebuf.tm_year = yy - 1900;
2313 timebuf.tm_isdst = -1;
2314 rtime = mktime(&timebuf);
2315 if (rtime == -1)
2316 printf("Can't convert %s to a time\n", reply_string);
2317 else
2318 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
2319 } else if (debug == 0)
2320 printf("%s\n", reply_string);
2321 verbose = overbose;
2322 return(rtime);
2323 }
2324
2325 /*
2326 * get last modification time of file on remote machine
2327 */
2328 void
2329 modtime(argc, argv)
2330 int argc;
2331 char *argv[];
2332 {
2333 time_t mtime;
2334
2335 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2336 printf("usage: %s filename\n", argv[0]);
2337 code = -1;
2338 return;
2339 }
2340 mtime = remotemodtime(argv[1]);
2341 if (mtime != -1)
2342 printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
2343 code = mtime;
2344 }
2345
2346 /*
2347 * show status on reomte machine
2348 */
2349 void
2350 rmtstatus(argc, argv)
2351 int argc;
2352 char *argv[];
2353 {
2354
2355 (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2356 }
2357
2358 /*
2359 * get file if modtime is more recent than current file
2360 */
2361 void
2362 newer(argc, argv)
2363 int argc;
2364 char *argv[];
2365 {
2366
2367 if (getit(argc, argv, -1, "w"))
2368 printf("Local file \"%s\" is newer than remote file \"%s\"\n",
2369 argv[2], argv[1]);
2370 }
2371