This really needs to go in a branch. It needs heavy testing and can't coincide with...
[reactos.git] / base / applications / network / ftp / main.c
1 /*
2 * Copyright (c) 1985, 1989 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\
21 All rights reserved.\n";
22 #endif /* not lint */
23
24 #ifndef lint
25 static char sccsid[] = "@(#)main.c based on 5.13 (Berkeley) 3/14/89";
26 #endif /* not lint */
27
28 /*
29 * FTP User Program -- Command Interface.
30 */
31 #ifndef _WIN32
32 #include <netdb.h>
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <arpa/ftp.h>
36 #include <errno.h>
37 #include <pwd.h>
38 #endif
39 #include "ftp_var.h"
40 #include "prototypes.h"
41 #include <sys/types.h>
42
43 #include <io.h>
44 #include <fcntl.h>
45
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <ctype.h>
50
51
52 #if defined(sun) && !defined(FD_SET)
53 typedef int uid_t;
54 #endif
55
56 uid_t getuid(void);
57 void intr(void);
58 void lostpeer(void);
59 char *getlogin(void);
60
61 short portnum;
62
63 char home[128];
64 char *globerr;
65 int autologin;
66
67
68
69 /* Lot's of options... */
70 /*
71 * Options and other state info.
72 */
73 int trace; /* trace packets exchanged */
74 int hash; /* print # for each buffer transferred */
75 //int sendport; /* use PORT cmd for each data connection */
76 int verbose; /* print messages coming back from server */
77 int connected; /* connected to server */
78 int fromatty; /* input is from a terminal */
79 int interactive; /* interactively prompt on m* cmds */
80 int debug; /* debugging level */
81 int bell; /* ring bell on cmd completion */
82 int doglob; /* glob local file names */
83 int proxy; /* proxy server connection active */
84 int passivemode;
85 int proxflag; /* proxy connection exists */
86 int sunique; /* store files on server with unique name */
87 int runique; /* store local files with unique name */
88 int mcase; /* map upper to lower case for mget names */
89 int ntflag; /* use ntin ntout tables for name translation */
90 int mapflag; /* use mapin mapout templates on file names */
91 int code; /* return/reply code for ftp command */
92 int crflag; /* if 1, strip car. rets. on ascii gets */
93 char pasv[64]; /* passive port for proxy data connection */
94 char *altarg; /* argv[1] with no shell-like preprocessing */
95 char ntin[17]; /* input translation table */
96 char ntout[17]; /* output translation table */
97 // #include <sys/param.h>
98 char mapin[MAXPATHLEN]; /* input map template */
99 char mapout[MAXPATHLEN]; /* output map template */
100 char typename[32]; /* name of file transfer type */
101 int type; /* file transfer type */
102 char structname[32]; /* name of file transfer structure */
103 int stru; /* file transfer structure */
104 char formname[32]; /* name of file transfer format */
105 int form; /* file transfer format */
106 char modename[32]; /* name of file transfer mode */
107 int mode; /* file transfer mode */
108 char bytename[32]; /* local byte size in ascii */
109 int bytesize; /* local byte size in binary */
110
111 jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
112
113 char line[200]; /* input line buffer */
114 char *stringbase; /* current scan point in line buffer */
115 char argbuf[200]; /* argument storage buffer */
116 char *argbase; /* current storage point in arg buffer */
117 int margc; /* count of arguments on input line */
118 const char *margv[20]; /* args parsed from input line */
119 int cpend; /* flag: if != 0, then pending server reply */
120 int mflag; /* flag: if != 0, then active multi command */
121
122 int options; /* used during socket creation */
123
124 int macnum; /* number of defined macros */
125 struct macel macros[16];
126 char macbuf[4096];
127
128 /*
129 * Need to start a listen on the data channel
130 * before we send the command, otherwise the
131 * server's connect may fail.
132 */
133 int sendport = -1;
134
135 static const char *slurpstring();
136
137
138 int main(int argc, const char *argv[])
139 {
140 const char *cp;
141 int top;
142 #if 0
143 char homedir[MAXPATHLEN];
144 #endif
145
146 int err;
147 WORD wVerReq;
148
149 WSADATA WSAData;
150 struct servent *sp; /* service spec for tcp/ftp */
151
152 /* Disable output buffering, for the benefit of Emacs. */
153 //setbuf(stdout, NULL);
154
155 _fmode = O_BINARY; // This causes an error somewhere.
156
157 wVerReq = MAKEWORD(1,1);
158
159 err = WSAStartup(wVerReq, &WSAData);
160 if (err != 0)
161 {
162 fprintf(stderr, "Could not initialize Windows socket interface.");
163 exit(1);
164 }
165
166 sp = getservbyname("ftp", "tcp");
167 if (sp == 0) {
168 fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
169 exit(1);
170 }
171
172 portnum = sp->s_port;
173
174
175 doglob = 1;
176 interactive = 1;
177 autologin = 1;
178 argc--, argv++;
179 while (argc > 0 && **argv == '-') {
180 for (cp = *argv + 1; *cp; cp++)
181 switch (*cp) {
182
183 case 'd':
184 options |= SO_DEBUG;
185 debug++;
186 break;
187
188 case 'v':
189 verbose++;
190 break;
191
192 case 't':
193 trace++;
194 break;
195
196 case 'i':
197 interactive = 0;
198 break;
199
200 case 'n':
201 autologin = 0;
202 break;
203
204 case 'g':
205 doglob = 0;
206 break;
207
208 default:
209 fprintf(stdout,
210 "ftp: %c: unknown option\n", *cp);
211 exit(1);
212 }
213 argc--, argv++;
214 }
215 // fromatty = isatty(fileno(stdin));
216 fromatty = 1; // Strengthen this test
217 /*
218 * Set up defaults for FTP.
219 */
220 (void) strcpy(typename, "ascii"), type = TYPE_A;
221 (void) strcpy(formname, "non-print"), form = FORM_N;
222 (void) strcpy(modename, "stream"), mode = MODE_S;
223 (void) strcpy(structname, "file"), stru = STRU_F;
224 (void) strcpy(bytename, "8"), bytesize = 8;
225 if (fromatty)
226 verbose++;
227 cpend = 0; /* no pending replies */
228 proxy = 0; /* proxy not active */
229 passivemode = 1; /* passive mode *is* active */
230 crflag = 1; /* strip c.r. on ascii gets */
231 /*
232 * Set up the home directory in case we're globbing.
233 */
234 #if 0
235 cp = getlogin();
236 if (cp != NULL) {
237 pw = getpwnam(cp);
238 }
239 if (pw == NULL)
240 pw = getpwuid(getuid());
241 if (pw != NULL) {
242 home = homedir;
243 (void) strcpy(home, pw->pw_dir);
244 }
245 #endif
246 strcpy(home, "C:/");
247 if (argc > 0) {
248 if (setjmp(toplevel))
249 exit(0);
250 // (void) signal(SIGINT, intr);
251 // (void) signal(SIGPIPE, lostpeer);
252 setpeer(argc + 1, argv - 1);
253 }
254 top = setjmp(toplevel) == 0;
255 if (top) {
256 // (void) signal(SIGINT, intr);
257 // (void) signal(SIGPIPE, lostpeer);
258 }
259 for (;;) {
260 cmdscanner(top);
261 top = 1;
262 }
263 }
264
265 void intr(void)
266 {
267 longjmp(toplevel, 1);
268 }
269
270 void lostpeer(void)
271 {
272 extern int cout;
273 extern int data;
274
275 if (connected) {
276 if (cout) {
277 closesocket(cout);
278 cout = 0;
279 }
280 if (data >= 0) {
281 (void) shutdown(data, 1+1);
282 (void) close(data);
283 data = -1;
284 }
285 connected = 0;
286 }
287 pswitch(1);
288 if (connected) {
289 if (cout) {
290 closesocket(cout);
291 cout = 0;
292 }
293 connected = 0;
294 }
295 proxflag = 0;
296 pswitch(0);
297 }
298
299 /*char *
300 tail(char *filename)
301 {
302 register char *s;
303
304 while (*filename) {
305 s = rindex(filename, '/');
306 if (s == NULL)
307 break;
308 if (s[1])
309 return (s + 1);
310 *s = '\0';
311 }
312 return (filename);
313 }
314 */
315 /*
316 * Command parser.
317 */
318 void cmdscanner(int top)
319 {
320 register struct cmd *c;
321
322 if (!top)
323 (void) putchar('\n');
324 for (;;) {
325 (void) fflush(stdout);
326 if (fromatty) {
327 printf("ftp> ");
328 (void) fflush(stdout);
329 }
330 if (gets(line) == 0) {
331 if (feof(stdin) || ferror(stdin))
332 quit(0, NULL);
333 break;
334 }
335 if (line[0] == 0)
336 break;
337 makeargv();
338 if (margc == 0) {
339 continue;
340 }
341 c = getcmd(margv[0]);
342 if (c == (struct cmd *)-1) {
343 printf("?Ambiguous command\n");
344 continue;
345 }
346 if (c == 0) {
347 printf("?Invalid command\n");
348 continue;
349 }
350 if (c->c_conn && !connected) {
351 printf ("Not connected.\n");
352 continue;
353 }
354 (*c->c_handler)(margc, margv);
355 if (bell && c->c_bell)
356 (void) putchar('\007');
357 if (c->c_handler != help)
358 break;
359 }
360 (void) fflush(stdout);
361 // (void) signal(SIGINT, intr);
362 // (void) signal(SIGPIPE, lostpeer);
363 }
364
365 struct cmd *
366 getcmd(const char *name)
367 {
368 extern struct cmd cmdtab[];
369 const char *p, *q;
370 struct cmd *c, *found;
371 int nmatches, longest;
372
373 longest = 0;
374 nmatches = 0;
375 found = 0;
376 for (c = cmdtab; (p = c->c_name); c++) {
377 for (q = name; *q == *p++; q++)
378 if (*q == 0) /* exact match? */
379 return (c);
380 if (!*q) { /* the name was a prefix */
381 if (q - name > longest) {
382 longest = q - name;
383 nmatches = 1;
384 found = c;
385 } else if (q - name == longest)
386 nmatches++;
387 }
388 }
389 if (nmatches > 1)
390 return ((struct cmd *)-1);
391 return (found);
392 }
393
394 /*
395 * Slice a string up into argc/argv.
396 */
397
398 int slrflag;
399
400 void makeargv(void)
401 {
402 const char **argp;
403
404 margc = 0;
405 argp = margv;
406 stringbase = line; /* scan from first of buffer */
407 argbase = argbuf; /* store from first of buffer */
408 slrflag = 0;
409 while ((*argp++ = slurpstring()))
410 margc++;
411 }
412
413 /*
414 * Parse string into argbuf;
415 * implemented with FSM to
416 * handle quoting and strings
417 */
418 static const char *
419 slurpstring(void)
420 {
421 int got_one = 0;
422 register char *sb = stringbase;
423 register char *ap = argbase;
424 char *tmp = argbase; /* will return this if token found */
425
426 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
427 switch (slrflag) { /* and $ as token for macro invoke */
428 case 0:
429 slrflag++;
430 stringbase++;
431 return ((*sb == '!') ? "!" : "$");
432 /* NOTREACHED */
433 case 1:
434 slrflag++;
435 altarg = stringbase;
436 break;
437 default:
438 break;
439 }
440 }
441
442 S0:
443 switch (*sb) {
444
445 case '\0':
446 goto OUT1;
447
448 case ' ':
449 case '\t':
450 sb++; goto S0;
451
452 default:
453 switch (slrflag) {
454 case 0:
455 slrflag++;
456 break;
457 case 1:
458 slrflag++;
459 altarg = sb;
460 break;
461 default:
462 break;
463 }
464 goto S1;
465 }
466
467 S1:
468 switch (*sb) {
469
470 case ' ':
471 case '\t':
472 case '\0':
473 goto OUT1; /* end of token */
474
475 case '\\':
476 sb++; goto S2; /* slurp next character */
477
478 case '"':
479 sb++; goto S3; /* slurp quoted string */
480
481 default:
482 *ap++ = *sb++; /* add character to token */
483 got_one = 1;
484 goto S1;
485 }
486
487 S2:
488 switch (*sb) {
489
490 case '\0':
491 goto OUT1;
492
493 default:
494 *ap++ = *sb++;
495 got_one = 1;
496 goto S1;
497 }
498
499 S3:
500 switch (*sb) {
501
502 case '\0':
503 goto OUT1;
504
505 case '"':
506 sb++; goto S1;
507
508 default:
509 *ap++ = *sb++;
510 got_one = 1;
511 goto S3;
512 }
513
514 OUT1:
515 if (got_one)
516 *ap++ = '\0';
517 argbase = ap; /* update storage pointer */
518 stringbase = sb; /* update scan pointer */
519 if (got_one) {
520 return(tmp);
521 }
522 switch (slrflag) {
523 case 0:
524 slrflag++;
525 break;
526 case 1:
527 slrflag++;
528 altarg = (char *) 0;
529 break;
530 default:
531 break;
532 }
533 return((char *)0);
534 }
535
536 #define HELPINDENT (sizeof ("directory"))
537
538 /*
539 * Help command.
540 * Call each command handler with argc == 0 and argv[0] == name.
541 */
542 void help(int argc, const char *argv[])
543 {
544 extern struct cmd cmdtab[];
545 struct cmd *c;
546
547 if (argc == 1) {
548 register int i, j, w, k;
549 int columns, width = 0, lines;
550 extern int NCMDS;
551
552 printf("Commands may be abbreviated. Commands are:\n\n");
553 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
554 int len = strlen(c->c_name);
555
556 if (len > width)
557 width = len;
558 }
559 width = (width + 8) &~ 7;
560 columns = 80 / width;
561 if (columns == 0)
562 columns = 1;
563 lines = (NCMDS + columns - 1) / columns;
564 for (i = 0; i < lines; i++) {
565 for (j = 0; j < columns; j++) {
566 c = cmdtab + j * lines + i;
567 if (c->c_name && (!proxy || c->c_proxy)) {
568 printf("%s", c->c_name);
569 }
570 else if (c->c_name) {
571 for (k=0; k < (int) strlen(c->c_name); k++) {
572 (void) putchar(' ');
573 }
574 }
575 if (c + lines >= &cmdtab[NCMDS]) {
576 printf("\n");
577 break;
578 }
579 w = strlen(c->c_name);
580 while (w < width) {
581 w = (w + 8) &~ 7;
582 (void) putchar('\t');
583 }
584 }
585 }
586 (void) fflush(stdout);
587 return;
588 }
589 while (--argc > 0) {
590 const char *arg;
591 arg = *++argv;
592 c = getcmd(arg);
593 if (c == (struct cmd *)-1)
594 printf("?Ambiguous help command %s\n", arg);
595 else if (c == (struct cmd *)0)
596 printf("?Invalid help command %s\n", arg);
597 else
598 printf("%-*s\t%s\n", (int)HELPINDENT,
599 c->c_name, c->c_help);
600 }
601 (void) fflush(stdout);
602 }