xref: /csrg-svn/usr.bin/ftp/cmds.c (revision 11353)
1 #ifndef lint
2 static char sccsid[] = "@(#)cmds.c	4.3 (Berkeley) 03/01/83";
3 #endif
4 
5 /*
6  * FTP User Program -- Command Routines.
7  */
8 #include <sys/param.h>
9 #include <sys/socket.h>
10 
11 #include <signal.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <netdb.h>
15 #include <stat.h>
16 
17 #include "ftp.h"
18 #include "ftp_var.h"
19 
20 extern	char *globerr;
21 extern	char **glob();
22 extern	short gflag;
23 
24 /*
25  * Connect to peer server and
26  * auto-login, if possible.
27  */
28 setpeer(argc, argv)
29 	int argc;
30 	char *argv[];
31 {
32 	struct hostent *host, *hookup();
33 	int port;
34 
35 	if (connected) {
36 		printf("Already connected to %s, use disconnect first.\n",
37 			hostname);
38 		return;
39 	}
40 	if (argc < 2) {
41 		strcat(line, " ");
42 		printf("(to) ");
43 		gets(&line[strlen(line)]);
44 		makeargv();
45 		argc = margc;
46 		argv = margv;
47 	}
48 	if (argc > 3) {
49 		printf("usage: %s host-name [port]\n", argv[0]);
50 		return;
51 	}
52 	port = sp->s_port;
53 	if (argc > 2) {
54 		port = atoi(argv[2]);
55 		if (port <= 0) {
56 			printf("%s: bad port number-- %s\n", argv[1], argv[2]);
57 			printf ("usage: %s host-name [port]\n", argv[0]);
58 			return;
59 		}
60 		port = htons(port);
61 	}
62 	host = hookup(argv[1], port);
63 	if (host) {
64 		connected = 1;
65 		if (autologin)
66 			login(host);
67 	}
68 }
69 
70 struct	types {
71 	char	*t_name;
72 	char	*t_mode;
73 	int	t_type;
74 	char	*t_arg;
75 } types[] = {
76 	{ "ascii",	"A",	TYPE_A,	0 },
77 	{ "binary",	"I",	TYPE_I,	0 },
78 	{ "image",	"I",	TYPE_I,	0 },
79 	{ "ebcdic",	"E",	TYPE_E,	0 },
80 	{ "tenex",	"L",	TYPE_L,	bytename },
81 	0
82 };
83 
84 /*
85  * Set transfer type.
86  */
87 settype(argc, argv)
88 	char *argv[];
89 {
90 	register struct types *p;
91 	int comret;
92 
93 	if (argc > 2) {
94 		char *sep;
95 
96 		printf("usage: %s [", argv[0]);
97 		sep = " ";
98 		for (p = types; p->t_name; p++) {
99 			printf("%s%s", sep, p->t_name);
100 			if (*sep == ' ')
101 				sep = " | ";
102 		}
103 		printf(" ]\n");
104 		return;
105 	}
106 	if (argc < 2) {
107 		printf("Using %s mode to transfer files.\n", typename);
108 		return;
109 	}
110 	for (p = types; p->t_name; p++)
111 		if (strcmp(argv[1], p->t_name) == 0)
112 			break;
113 	if (p->t_name == 0) {
114 		printf("%s: unknown mode\n", argv[1]);
115 		return;
116 	}
117 	if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
118 		comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
119 	else
120 		comret = command("TYPE %s", p->t_mode);
121 	if (comret == COMPLETE) {
122 		strcpy(typename, p->t_name);
123 		type = p->t_type;
124 	}
125 }
126 
127 /*
128  * Set binary transfer type.
129  */
130 /*VARARGS*/
131 setbinary()
132 {
133 
134 	call(settype, "type", "binary", 0);
135 }
136 
137 /*
138  * Set ascii transfer type.
139  */
140 /*VARARGS*/
141 setascii()
142 {
143 
144 	call(settype, "type", "ascii", 0);
145 }
146 
147 /*
148  * Set tenex transfer type.
149  */
150 /*VARARGS*/
151 settenex()
152 {
153 
154 	call(settype, "type", "tenex", 0);
155 }
156 
157 /*
158  * Set ebcdic transfer type.
159  */
160 /*VARARGS*/
161 setebcdic()
162 {
163 
164 	call(settype, "type", "ebcdic", 0);
165 }
166 
167 /*
168  * Set file transfer mode.
169  */
170 setmode(argc, argv)
171 	char *argv[];
172 {
173 
174 	printf("We only support %s mode, sorry.\n", modename);
175 }
176 
177 /*
178  * Set file transfer format.
179  */
180 setform(argc, argv)
181 	char *argv[];
182 {
183 
184 	printf("We only support %s format, sorry.\n", formname);
185 }
186 
187 /*
188  * Set file transfer structure.
189  */
190 setstruct(argc, argv)
191 	char *argv[];
192 {
193 
194 	printf("We only support %s structure, sorry.\n", structname);
195 }
196 
197 /*
198  * Send a single file.
199  */
200 put(argc, argv)
201 	char *argv[];
202 {
203 
204 	if (!connected) {
205 		printf("Not connected.\n");
206 		return;
207 	}
208 	if (argc == 2)
209 		argc++, argv[2] = argv[1];
210 	if (argc < 2) {
211 		strcat(line, " ");
212 		printf("(local-file) ");
213 		gets(&line[strlen(line)]);
214 		makeargv();
215 		argc = margc;
216 		argv = margv;
217 	}
218 	if (argc < 2) {
219 usage:
220 		printf("%s local-file remote-file\n", argv[0]);
221 		return;
222 	}
223 	if (argc < 3) {
224 		strcat(line, " ");
225 		printf("(remote-file) ");
226 		gets(&line[strlen(line)]);
227 		makeargv();
228 		argc = margc;
229 		argv = margv;
230 	}
231 	if (argc < 3)
232 		goto usage;
233 	if (!globulize(&argv[1]))
234 		return;
235 	sendrequest("STOR", argv[1], argv[2]);
236 }
237 
238 /*
239  * Send one or more files.
240  */
241 mput(argc, argv)
242 	char *argv[];
243 {
244 	char **cpp, *dst;
245 	int i;
246 
247 	if (!connected) {
248 		printf("Not connected.\n");
249 		return;
250 	}
251 	if (argc < 2) {
252 		printf("%s local-file remote-file, or\n", argv[0]);
253 		printf("%s local-files\n", argv[0]);
254 		return;
255 	}
256 	if (argc == 3)  {
257 		if (!globulize(&argv[1]))
258 			return;
259 		sendrequest("STOR", argv[1], argv[2]);
260 		return;
261 	}
262 	/*
263 	 * Check for shell metacharacters which we might
264 	 * want to expand into a list of file names.
265 	 */
266 	for (i = 1; i < argc; i++) {
267 		if (!doglob) {
268 			if (!skip(argv[0], argv[i]))
269 				sendrequest("STOR", argv[i], argv[i]);
270 			continue;
271 		}
272 		cpp = glob(argv[i]);
273 		if (globerr != NULL) {
274 			printf("%s: %s\n", argv[i], globerr);
275 			if (cpp)
276 				blkfree(cpp);
277 			continue;
278 		}
279 		if (cpp == NULL) {
280 			printf("%s: no match\n", argv[i]);
281 			continue;
282 		}
283 		while (*cpp != NULL) {
284 			if (!skip(argv[0], *cpp))
285 				sendrequest("STOR", *cpp, *cpp);
286 			free(*cpp), cpp++;
287 		}
288 	}
289 }
290 
291 /*
292  * Receive one file.
293  */
294 get(argc, argv)
295 	char *argv[];
296 {
297 
298 	if (!connected) {
299 		printf("Not connected.\n");
300 		return;
301 	}
302 	if (argc == 2)
303 		argc++, argv[2] = argv[1];
304 	if (argc < 2) {
305 		strcat(line, " ");
306 		printf("(remote-file) ");
307 		gets(&line[strlen(line)]);
308 		makeargv();
309 		argc = margc;
310 		argv = margv;
311 	}
312 	if (argc < 2) {
313 usage:
314 		printf("%s remote-file [ local-file ]\n", argv[0]);
315 		return;
316 	}
317 	if (argc < 3) {
318 		strcat(line, " ");
319 		printf("(local-file) ");
320 		gets(&line[strlen(line)]);
321 		makeargv();
322 		argc = margc;
323 		argv = margv;
324 	}
325 	if (argc < 3)
326 		goto usage;
327 	if (!globulize(&argv[2]))
328 		return;
329 	recvrequest("RETR", argv[2], argv[1]);
330 }
331 
332 /*
333  * Get multiple files.
334  */
335 mget(argc, argv)
336 	char *argv[];
337 {
338 	char temp[16], *dst, dstpath[MAXPATHLEN], buf[MAXPATHLEN];
339 	FILE *ftemp;
340 	int madedir = 0, oldverbose;
341 	struct stat stb;
342 
343 	if (!connected) {
344 		printf("Not connected.\n");
345 		return;
346 	}
347 	if (argc == 2)
348 		argc++, argv[2] = argv[1];
349 	if (argc < 2) {
350 		strcat(line, " ");
351 		printf("(remote-directory) ");
352 		gets(&line[strlen(line)]);
353 		makeargv();
354 		argc = margc;
355 		argv = margv;
356 	}
357 	if (argc < 2) {
358 usage:
359 		printf("%s remote-directory [ local-directory ], or\n",
360 			argv[0]);
361 		printf("%s remote-files local-directory\n", argv[0]);
362 		return;
363 	}
364 	if (argc < 3) {
365 		strcat(line, " ");
366 		printf("(local-directory) ");
367 		gets(&line[strlen(line)]);
368 		makeargv();
369 		argc = margc;
370 		argv = margv;
371 	}
372 	if (argc < 3)
373 		goto usage;
374 	dst = argv[argc - 1];
375 	if (!globulize(&dst))
376 		return;
377 	/*
378 	 * If destination doesn't exist,
379 	 * try and create it.
380 	 */
381 	if (stat(dst, &stb) < 0) {
382 		if (mkdir(dst, 0777) < 0) {
383 			perror(dst);
384 			return;
385 		}
386 		madedir++;
387 	} else {
388 		if ((stb.st_mode & S_IFMT) != S_IFDIR) {
389 			printf("%s: not a directory\n", dst);
390 			return;
391 		}
392 	}
393 	/*
394 	 * Multiple files, just get each one without an nlst.
395 	 */
396 	if (argc > 3) {
397 		int i;
398 
399 		for (i = 1; i < argc - 1; i++)
400 			recvrequest("RETR",
401 			  sprintf(dstpath, "%s/%s", dst, argv[i]), argv[i]);
402 		return;
403 	}
404 	/*
405 	 * Get a directory full of files.  Perform an
406 	 * nlst to find the file names, then retrieve
407 	 * each individually.  If prompting is on, ask
408 	 * before grabbing each file.
409 	 */
410 	strcpy(temp, "/tmp/ftpXXXXXX");
411 	mktemp(temp);
412 	oldverbose = verbose, verbose = 0;
413 	recvrequest("NLST", temp, argv[1]);
414 	verbose = oldverbose;
415 	ftemp = fopen(temp, "r");
416 	unlink(temp);
417 	if (ftemp == NULL) {
418 		printf("can't find list of remote files, oops\n");
419 		if (madedir)
420 			(void) rmdir(dst);
421 		return;
422 	}
423 	while (fgets(buf, sizeof (buf), ftemp) != NULL) {
424 		char *cp = index(buf, '\n');
425 
426 		if (cp)
427 			*cp = '\0';
428 		if (skip(argv[0], buf))
429 			continue;
430 		recvrequest("RETR", sprintf(dstpath, "%s/%s", dst, buf), buf);
431 	}
432 	fclose(ftemp);
433 }
434 
435 char *
436 onoff(bool)
437 	int bool;
438 {
439 
440 	return (bool ? "on" : "off");
441 }
442 
443 /*
444  * Show status.
445  */
446 status(argc, argv)
447 	char *argv[];
448 {
449 
450 	if (connected)
451 		printf("Connected to %s.\n", hostname);
452 	else
453 		printf("Not connected.\n");
454 	printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
455 		modename, typename, formname, structname);
456 	printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
457 		onoff(verbose), onoff(bell), onoff(interactive),
458 		onoff(doglob));
459 }
460 
461 /*
462  * Set beep on cmd completed mode.
463  */
464 /*VARARGS*/
465 setbell()
466 {
467 
468 	bell = !bell;
469 	printf("Bell mode %s.\n", onoff(bell));
470 }
471 
472 /*
473  * Turn on packet tracing.
474  */
475 /*VARARGS*/
476 settrace()
477 {
478 
479 	trace = !trace;
480 	printf("Packet tracing %s.\n", onoff(trace));
481 }
482 
483 /*
484  * Turn on printing of server echo's.
485  */
486 /*VARARGS*/
487 setverbose()
488 {
489 
490 	verbose = !verbose;
491 	printf("Verbose mode %s.\n", onoff(verbose));
492 }
493 
494 /*
495  * Turn on interactive prompting
496  * during mget, mput, and mdelete.
497  */
498 /*VARARGS*/
499 setprompt()
500 {
501 
502 	interactive = !interactive;
503 	printf("Interactive mode %s.\n", onoff(interactive));
504 }
505 
506 /*
507  * Toggle metacharacter interpretation
508  * on local file names.
509  */
510 /*VARARGS*/
511 setglob()
512 {
513 
514 	doglob = !doglob;
515 	printf("Globbing %s.\n", onoff(doglob));
516 }
517 
518 /*
519  * Set debugging mode on/off and/or
520  * set level of debugging.
521  */
522 setdebug(argc, argv)
523 	char *argv[];
524 {
525 	int val;
526 
527 	if (argc > 1) {
528 		val = atoi(argv[1]);
529 		if (val < 0) {
530 			printf("%s: bad debugging value.\n", argv[1]);
531 			return;
532 		}
533 	} else
534 		val = !debug;
535 	debug = val;
536 	if (debug)
537 		options |= SO_DEBUG;
538 	else
539 		options &= ~SO_DEBUG;
540 	printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
541 }
542 
543 /*
544  * Set current working directory
545  * on remote machine.
546  */
547 cd(argc, argv)
548 	char *argv[];
549 {
550 
551 	if (!connected) {
552 		printf("Not connected.\n");
553 		return;
554 	}
555 	if (argc < 2) {
556 		strcat(line, " ");
557 		printf("(remote-directory) ");
558 		gets(&line[strlen(line)]);
559 		makeargv();
560 		argc = margc;
561 		argv = margv;
562 	}
563 	if (argc < 2) {
564 		printf("%s remote-directory\n", argv[0]);
565 		return;
566 	}
567 	(void) command("CWD %s", argv[1]);
568 }
569 
570 /*
571  * Set current working directory
572  * on local machine.
573  */
574 lcd(argc, argv)
575 	char *argv[];
576 {
577 	char buf[MAXPATHLEN];
578 	extern char *home;
579 
580 	if (argc < 2)
581 		argc++, argv[1] = home;
582 	if (argc != 2) {
583 		printf("%s local-directory\n", argv[0]);
584 		return;
585 	}
586 	if (!globulize(&argv[1]))
587 		return;
588 	if (chdir(argv[1]) < 0) {
589 		perror(argv[1]);
590 		return;
591 	}
592 	printf("Local directory now %s\n", getwd(buf));
593 }
594 
595 /*
596  * Delete a single file.
597  */
598 delete(argc, argv)
599 	char *argv[];
600 {
601 
602 	if (argc < 2) {
603 		strcat(line, " ");
604 		printf("(remote-file) ");
605 		gets(&line[strlen(line)]);
606 		makeargv();
607 		argc = margc;
608 		argv = margv;
609 	}
610 	if (argc < 2) {
611 		printf("%s remote-file\n", argv[0]);
612 		return;
613 	}
614 	(void) command("DELE %s", argv[1]);
615 }
616 
617 /*
618  * Rename a remote file.
619  */
620 renamefile(argc, argv)
621 	char *argv[];
622 {
623 
624 	if (argc < 2) {
625 		strcat(line, " ");
626 		printf("(from-name) ");
627 		gets(&line[strlen(line)]);
628 		makeargv();
629 		argc = margc;
630 		argv = margv;
631 	}
632 	if (argc < 2) {
633 usage:
634 		printf("%s from-name to-name\n", argv[0]);
635 		return;
636 	}
637 	if (argc < 3) {
638 		strcat(line, " ");
639 		printf("(to-name) ");
640 		gets(&line[strlen(line)]);
641 		makeargv();
642 		argc = margc;
643 		argv = margv;
644 	}
645 	if (argc < 3)
646 		goto usage;
647 	if (command("RNFR %s", argv[1]) == CONTINUE)
648 		(void) command("RNTO %s", argv[2]);
649 }
650 
651 /*
652  * Get a directory listing
653  * of remote files.
654  */
655 ls(argc, argv)
656 	char *argv[];
657 {
658 	char *cmd;
659 
660 	if (argc < 2)
661 		argc++, argv[1] = NULL;
662 	if (argc < 3)
663 		argc++, argv[2] = "-";
664 	if (argc > 3) {
665 		printf("usage: %s remote-directory local-file\n", argv[0]);
666 		return;
667 	}
668 	cmd = argv[0][0] == 'l' ? "NLST" : "LIST";
669 	if (strcmp(argv[2], "-") && !globulize(&argv[2]))
670 		return;
671 	recvrequest(cmd, argv[2], argv[1]);
672 }
673 
674 /*
675  * Do a shell escape
676  */
677 shell(argc, argv)
678 	char *argv[];
679 {
680 
681 	printf("Sorry, this function is unimplemented.\n");
682 }
683 
684 /*
685  * Send new user information (re-login)
686  */
687 user(argc, argv)
688 	int argc;
689 	char **argv;
690 {
691 	char acct[80], *getpass();
692 	int n;
693 
694 	if (argc < 2) {
695 		strcat(line, " ");
696 		printf("(username) ");
697 		gets(&line[strlen(line)]);
698 		makeargv();
699 		argc = margc;
700 		argv = margv;
701 	}
702 	if (argc > 4) {
703 		printf("usage: %s username [password] [account]\n", argv[0]);
704 		return;
705 	}
706 	n = command("USER %s", argv[1]);
707 	if (n == CONTINUE) {
708 		if (argc < 3 )
709 			argv[2] = getpass("Password: "), argc++;
710 		n = command("PASS %s", argv[2]);
711 	}
712 	if (n == CONTINUE) {
713 		if (argc < 4) {
714 			printf("Account: "); (void) fflush(stdout);
715 			(void) fgets(acct, sizeof(acct) - 1, stdin);
716 			acct[strlen(acct) - 1] = '\0';
717 			argv[3] = acct; argc++;
718 		}
719 		n = command("ACCT %s", acct);
720 	}
721 	if (n != COMPLETE) {
722 		fprintf(stderr, "Login failed.\n");
723 		return (0);
724 	}
725 	return (1);
726 }
727 
728 /*
729  * Print working directory.
730  */
731 /*VARARGS*/
732 pwd()
733 {
734 	if (!connected) {
735 		printf("Not connected.\n");
736 		return;
737 	}
738 	(void) command("XPWD");
739 }
740 
741 /*
742  * Make a directory.
743  */
744 makedir(argc, argv)
745 	char *argv[];
746 {
747 
748 	if (argc < 2) {
749 		strcat(line, " ");
750 		printf("(directory-name) ");
751 		gets(&line[strlen(line)]);
752 		makeargv();
753 		argc = margc;
754 		argv = margv;
755 	}
756 	if (argc < 2) {
757 		printf("%s directory-name\n", argv[0]);
758 		return;
759 	}
760 	(void) command("XMKD %s", argv[1]);
761 }
762 
763 /*
764  * Remove a directory.
765  */
766 removedir(argc, argv)
767 	char *argv[];
768 {
769 
770 	if (argc < 2) {
771 		strcat(line, " ");
772 		printf("(directory-name) ");
773 		gets(&line[strlen(line)]);
774 		makeargv();
775 		argc = margc;
776 		argv = margv;
777 	}
778 	if (argc < 2) {
779 		printf("%s directory-name\n", argv[0]);
780 		return;
781 	}
782 	(void) command("XRMD %s", argv[1]);
783 }
784 
785 /*
786  * Send a line, verbatim, to the remote machine.
787  */
788 quote(argc, argv)
789 	char *argv[];
790 {
791 	int i;
792 	char buf[BUFSIZ];
793 
794 	if (argc < 2) {
795 		strcat(line, " ");
796 		printf("(command line to send) ");
797 		gets(&line[strlen(line)]);
798 		makeargv();
799 		argc = margc;
800 		argv = margv;
801 	}
802 	if (argc < 2) {
803 		printf("usage: %s line-to-send\n", argv[0]);
804 		return;
805 	}
806 	strcpy(buf, argv[1]);
807 	for (i = 2; i < argc; i++) {
808 		strcat(buf, " ");
809 		strcat(buf, argv[i]);
810 	}
811 	(void) command(buf);
812 }
813 
814 /*
815  * Ask the other side for help.
816  */
817 rmthelp(argc, argv)
818 	char *argv[];
819 {
820 	int oldverbose = verbose;
821 
822 	verbose = 1;
823 	(void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
824 	verbose = oldverbose;
825 }
826 
827 /*
828  * Terminate session and exit.
829  */
830 /*VARARGS*/
831 quit()
832 {
833 
834 	disconnect();
835 	exit(0);
836 }
837 
838 /*
839  * Terminate session, but don't exit.
840  */
841 disconnect()
842 {
843 	extern FILE *cout;
844 	extern int data;
845 
846 	if (!connected)
847 		return;
848 	(void) command("QUIT");
849 	(void) fclose(cout);
850 	cout = NULL;
851 	connected = 0;
852 	data = -1;
853 }
854 
855 skip(cmd, file)
856 	char *cmd, *file;
857 {
858 	char line[BUFSIZ];
859 
860 	if (!interactive)
861 		return (0);
862 	printf("%s %s? ", cmd, file);
863 	fflush(stdout);
864 	gets(line);
865 	return (*line == 'y');
866 }
867 
868 fatal(msg)
869 	char *msg;
870 {
871 
872 	fprintf(stderr, "ftp: %s\n");
873 	exit(1);
874 }
875 
876 /*
877  * Glob a local file name specification with
878  * the expectation of a single return value.
879  * Can't control multiple values being expanded
880  * from the expression, we return only the first.
881  */
882 globulize(cpp)
883 	char **cpp;
884 {
885 	char **globbed;
886 
887 	if (!doglob)
888 		return (1);
889 	globbed = glob(*cpp);
890 	if (globerr != NULL) {
891 		printf("%s: %s\n", *cpp, globerr);
892 		if (globbed)
893 			blkfree(globbed);
894 		return (0);
895 	}
896 	if (globbed) {
897 		*cpp = *globbed++;
898 		/* don't waste too much memory */
899 		if (*globbed)
900 			blkfree(globbed);
901 	}
902 	return (1);
903 }
904