Annotation of libwww/Library/src/HTFTP.c, revision 1.1.1.1
1.1 timbl 1: /* File Transfer Protocol (FTP) Client
2: ** for a WorldWideWeb browser
3: ** ===================================
4: **
5: ** A cache of control connections is kept.
6: **
7: ** Note: Port allocation
8: **
9: ** It is essential that the port is allocated by the system, rather
10: ** than chosen in rotation by us (POLL_PORTS), or the following
11: ** problem occurs.
12: **
13: ** It seems that an attempt by the server to connect to a port which has
14: ** been used recently by a listen on the same socket, or by another
15: ** socket this or another process causes a hangup of (almost exactly)
16: ** one minute. Therefore, we have to use a rotating port number.
17: ** The problem remains that if the application is run twice in quick
18: ** succession, it will hang for what remains of a minute.
19: **
20: ** Authors
21: ** TBL Tim Berners-lee <timbl@info.cern.ch>
22: ** DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
23: ** History:
24: ** 2 May 91 Written TBL, as a part of the WorldWideWeb project.
25: ** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc
26: ** 10 Feb 92 Retry if cached connection times out or breaks
27: ** 8 Dec 92 Bug fix 921208 TBL after DD
28: ** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD
29: **
30: ** Options:
31: ** LISTEN We listen, the other guy connects for data.
32: ** Otherwise, other way round, but problem finding our
33: ** internet address!
34: **
35: ** Bugs:
36: ** No binary mode! Always uses ASCII!
37: */
38:
39: #define LISTEN /* @@@@ Test */
40:
41: /*
42: BUGS: @@@ Limit connection cache size!
43: Error reporting to user.
44: 400 & 500 errors are acked by user with windows.
45: Use configuration file for user names
46: Prompt user for password
47:
48: ** Note for portablility this version does not use select() and
49: ** so does not watch the control and data channels at the
50: ** same time.
51: */
52:
53: #define REPEAT_PORT /* Give the port number for each file */
54: #define REPEAT_LISTEN /* Close each listen socket and open a new one */
55:
56: /* define POLL_PORTS If allocation does not work, poll ourselves.*/
57: #define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/
58:
59: #define FIRST_TCP_PORT 1024 /* Region to try for a listening port */
60: #define LAST_TCP_PORT 5999
61:
62: #define LINE_LENGTH 256
63: #define COMMAND_LENGTH 256
64:
65: #include "HTParse.h"
66: #include "HTUtils.h"
67: #include "tcp.h"
68: #include "HTTCP.h"
69: #include "HTAnchor.h"
70: #include "HText.h"
71: #include "HTStyle.h"
72:
73: #include "HTFTP.h"
74:
75: #ifndef IPPORT_FTP
76: #define IPPORT_FTP 21
77: #endif
78:
79: extern HTStyleSheet * styleSheet;
80: PRIVATE HTStyle *h1Style; /* For heading level 1 */
81: PRIVATE HTStyle *h2Style; /* For heading level 2 */
82: PRIVATE HTStyle *textStyle; /* Text style */
83: PRIVATE HTStyle *dirStyle; /* Compact List style */
84:
85: #ifdef REMOVED_CODE
86: extern char *malloc();
87: extern void free();
88: extern char *strncpy();
89: #endif
90:
91: typedef struct _connection {
92: struct _connection * next; /* Link on list */
93: u_long addr; /* IP address */
94: int socket; /* Socket number for communication */
95: BOOL binary; /* Binary mode? */
96: } connection;
97:
98: #ifndef NIL
99: #define NIL 0
100: #endif
101:
102:
103: /* Module-Wide Variables
104: ** ---------------------
105: */
106: PRIVATE connection * connections =0; /* Linked list of connections */
107: PRIVATE char response_text[LINE_LENGTH+1];/* Last response from NewsHost */
108: PRIVATE connection * control; /* Current connection */
109: PRIVATE int data_soc = -1; /* Socket for data transfer =invalid */
110:
111: #ifdef POLL_PORTS
112: PRIVATE unsigned short port_number = FIRST_TCP_PORT;
113: #endif
114:
115: #ifdef LISTEN
116: PRIVATE int master_socket = -1; /* Listening socket = invalid */
117: PRIVATE char port_command[255]; /* Command for setting the port */
118: PRIVATE fd_set open_sockets; /* Mask of active channels */
119: PRIVATE int num_sockets; /* Number of sockets to scan */
120: #else
121: PRIVATE unsigned short passive_port; /* Port server specified for data */
122: #endif
123:
124:
125: #define NEXT_CHAR HTGetChararcter() /* Use function in HTFormat.c */
126:
127: #define DATA_BUFFER_SIZE 2048
128: PRIVATE char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */
129: PRIVATE char * data_read_pointer;
130: PRIVATE char * data_write_pointer;
131: #define NEXT_DATA_CHAR next_data_char()
132:
133:
134: /* Procedure: Read a character from the data connection
135: ** ----------------------------------------------------
136: */
137: PRIVATE char next_data_char
138: NOARGS
139: {
140: int status;
141: if (data_read_pointer >= data_write_pointer) {
142: status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
143: /* Get some more data */
144: if (status <= 0) return (char)-1;
145: data_write_pointer = data_buffer + status;
146: data_read_pointer = data_buffer;
147: }
148: #ifdef NOT_ASCII
149: {
150: char c = *data_read_pointer++;
151: return FROMASCII(c);
152: }
153: #else
154: return *data_read_pointer++;
155: #endif
156: }
157:
158:
159: /* Close an individual connection
160: **
161: */
162: #ifdef __STDC__
163: PRIVATE int close_connection(connection * con)
164: #else
165: PRIVATE int close_connection(con)
166: connection *con;
167: #endif
168: {
169: connection * scan;
170: int status = NETCLOSE(con->socket);
171: if (TRACE) fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
172: if (connections==con) {
173: connections = con->next;
174: return status;
175: }
176: for(scan=connections; scan; scan=scan->next) {
177: if (scan->next == con) {
178: scan->next = con->next; /* Unlink */
179: if (control==con) control = (connection*)0;
180: return status;
181: } /*if */
182: } /* for */
183: return -1; /* very strange -- was not on list. */
184: }
185:
186:
187: /* Execute Command and get Response
188: ** --------------------------------
189: **
190: ** See the state machine illustrated in RFC959, p57. This implements
191: ** one command/reply sequence. It also interprets lines which are to
192: ** be continued, which are marked with a "-" immediately after the
193: ** status code.
194: **
195: ** On entry,
196: ** con points to the connection which is established.
197: ** cmd points to a command, or is NIL to just get the response.
198: **
199: ** The command is terminated with the CRLF pair.
200: **
201: ** On exit,
202: ** returns: The first digit of the reply type,
203: ** or negative for communication failure.
204: */
205: #ifdef __STDC__
206: PRIVATE int response(char * cmd)
207: #else
208: PRIVATE int response(cmd)
209: char * cmd;
210: #endif
211: {
212: int result; /* Three-digit decimal code */
213: char continuation;
214: int status;
215:
216: if (!control) {
217: if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
218: return -99;
219: }
220:
221: if (cmd) {
222:
223: if (TRACE) fprintf(stderr, " Tx: %s", cmd);
224:
225: #ifdef NOT_ASCII
226: {
227: char * p;
228: for(p=cmd; *p; p++) {
229: *p = TOASCII(*p);
230: }
231: }
232: #endif
233: status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
234: if (status<0) {
235: if (TRACE) fprintf(stderr,
236: "FTP: Error %d sending command: closing socket %d\n",
237: status, control->socket);
238: close_connection(control);
239: return status;
240: }
241: }
242:
243: do {
244: char *p = response_text;
245: for(;;) {
246: if (((*p++=NEXT_CHAR) == '\n')
247: || (p == &response_text[LINE_LENGTH])) {
248: *p++=0; /* Terminate the string */
249: if (TRACE) fprintf(stderr, " Rx: %s", response_text);
250: sscanf(response_text, "%d%c", &result, &continuation);
251: break;
252: } /* if end of line */
253:
254: if (*(p-1) < 0) {
255: if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
256: control->socket);
257: strcpy(response_text, "000 *** TCP read error on response\n");
258: close_connection(control);
259: return -1; /* End of file on response */
260: }
261: } /* Loop over characters */
262:
263: } while (continuation == '-');
264:
265: if (result==421) {
266: if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
267: control->socket);
268: close_connection(control);
269: return -1;
270: }
271: return result/100;
272: }
273:
274:
275: /* Get a valid connection to the host
276: ** ----------------------------------
277: **
278: ** On entry,
279: ** arg points to the name of the host in a hypertext address
280: ** On exit,
281: ** returns <0 if error
282: ** socket number if success
283: **
284: ** This routine takes care of managing timed-out connections, and
285: ** limiting the number of connections in use at any one time.
286: **
287: ** It ensures that all connections are logged in if they exist.
288: ** It ensures they have the port number transferred.
289: */
290: PRIVATE int get_connection ARGS1 (CONST char *,arg)
291: {
292: struct sockaddr_in soc_address; /* Binary network address */
293: struct sockaddr_in* sin = &soc_address;
294:
295: char * username=0;
296: char * password=0;
297:
298: if (!arg) return -1; /* Bad if no name sepcified */
299: if (!*arg) return -1; /* Bad if name had zero length */
300:
301: /* Set up defaults:
302: */
303: sin->sin_family = AF_INET; /* Family, host order */
304: sin->sin_port = htons(IPPORT_FTP); /* Well Known Number */
305:
306: if (TRACE) fprintf(stderr, "FTP: Looking for %s\n", arg);
307:
308: /* Get node name:
309: */
310: {
311: char *p1 = HTParse(arg, "", PARSE_HOST);
312: char *p2 = strchr(p1, '@'); /* user? */
313: char * pw;
314: if (p2) {
315: username = p1;
316: *p2=0; /* terminate */
317: p1 = p2+1; /* point to host */
318: pw = strchr(username, ':');
319: if (pw) {
320: *pw++ = 0;
321: password = pw;
322: }
323: }
324: if (HTParseInet(sin, p1)) { free(p1); return -1;} /* TBL 920622 */
325:
326: if (!username) free(p1);
327: } /* scope of p1 */
328:
329:
330: /* Now we check whether we already have a connection to that port.
331: */
332:
333: {
334: connection * scan;
335: for (scan=connections; scan; scan=scan->next) {
336: if (sin->sin_addr.s_addr == scan->addr) {
337: if (TRACE) fprintf(stderr,
338: "FTP: Already have connection for %d.%d.%d.%d.\n",
339: (int)*((unsigned char *)(&scan->addr)+0),
340: (int)*((unsigned char *)(&scan->addr)+1),
341: (int)*((unsigned char *)(&scan->addr)+2),
342: (int)*((unsigned char *)(&scan->addr)+3));
343: if (username) free(username);
344: return scan->socket; /* Good return */
345: } else {
346: if (TRACE) fprintf(stderr,
347: "FTP: Existing connection is %d.%d.%d.%d\n",
348: (int)*((unsigned char *)(&scan->addr)+0),
349: (int)*((unsigned char *)(&scan->addr)+1),
350: (int)*((unsigned char *)(&scan->addr)+2),
351: (int)*((unsigned char *)(&scan->addr)+3));
352: }
353: }
354: }
355:
356:
357: /* Now, let's get a socket set up from the server:
358: */
359: {
360: int status;
361: connection * con = (connection *)malloc(sizeof(*con));
362: if (con == NULL) outofmem(__FILE__, "get_connection");
363: con->addr = sin->sin_addr.s_addr; /* save it */
364: con->binary = NO;
365: status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
366: if (status<0) {
367: (void) HTInetStatus("socket");
368: free(con);
369: if (username) free(username);
370: return status;
371: }
372: con->socket = status;
373:
374: status = connect(con->socket, (struct sockaddr*)&soc_address,
375: sizeof(soc_address));
376: if (status<0){
377: (void) HTInetStatus("connect");
378: if (TRACE) fprintf(stderr,
379: "FTP: Unable to connect to remote host for `%s'.\n",
380: arg);
381: NETCLOSE(con->socket);
382: free(con);
383: if (username) free(username);
384: return status; /* Bad return */
385: }
386:
387: if (TRACE) fprintf(stderr, "FTP connected, socket %d\n", con->socket);
388: control = con; /* Current control connection */
389: con->next = connections; /* Link onto list of good ones */
390: connections = con;
391: HTInitInput(con->socket);/* Initialise buffering for contron connection */
392:
393:
394: /* Now we log in Look up username, prompt for pw.
395: */
396: {
397: int status = response(NIL); /* Get greeting */
398:
399: if (status == 2) { /* Send username */
400: char * command;
401: if (username) {
402: command = (char*)malloc(10+strlen(username)+2+1);
403: if (command == NULL) outofmem(__FILE__, "get_connection");
404: sprintf(command, "USER %s\r\n", username);
405: } else {
406: command = (char*)malloc(25);
407: if (command == NULL) outofmem(__FILE__, "get_connection");
408: sprintf(command, "USER anonymous\r\n");
409: }
410: status = response(command);
411: free(command);
412: }
413: if (status == 3) { /* Send password */
414: char * command;
415: if (password) {
416: command = (char*)malloc(10+strlen(password)+2+1);
417: if (command == NULL) outofmem(__FILE__, "get_connection");
418: sprintf(command, "PASS %s\r\n", password);
419: } else {
420: #ifdef ANON_FTP_HOSTNAME
421: command = (char*)malloc(20+strlen(HTHostName())+2+1);
422: if (command == NULL) outofmem(__FILE__, "get_connection");
423: sprintf(command,
424: "PASS WWWuser@%s\r\n", HTHostName()); /*@@*/
425: #else
426: /* In fact ftp.uu.net for example prefers just "@"
427: the fulle domain name, which we can't get - DD */
428: command = (char*)malloc(20);
429: if (command == NULL) outofmem(__FILE__, "get_connection");
430: sprintf(command, "PASS WWWuser@\r\n"); /*@@*/
431: #endif
432: }
433: status = response(command);
434: free(command);
435: }
436: if (username) free(username);
437:
438: if (status == 3) status = response("ACCT noaccount\r\n");
439:
440: if (status !=2) {
441: if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
442: if (control) close_connection(control);
443: return -1; /* Bad return */
444: }
445: if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
446: }
447:
448: /* Now we inform the server of the port number we will listen on
449: */
450: #ifndef REPEAT_PORT
451: {
452: int status = response(port_command);
453: if (status !=2) {
454: if (control) close_connection(control);
455: return -status; /* Bad return */
456: }
457: if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
458: }
459: #endif
460: return con->socket; /* Good return */
461: } /* Scope of con */
462: }
463:
464:
465: #ifdef LISTEN
466:
467: /* Close Master (listening) socket
468: ** -------------------------------
469: **
470: **
471: */
472: #ifdef __STDC__
473: PRIVATE int close_master_socket(void)
474: #else
475: PRIVATE int close_master_socket()
476: #endif
477: {
478: int status;
479: FD_CLR(master_socket, &open_sockets);
480: status = NETCLOSE(master_socket);
481: if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
482: master_socket = -1;
483: if (status<0) return HTInetStatus("close master socket");
484: else return status;
485: }
486:
487:
488: /* Open a master socket for listening on
489: ** -------------------------------------
490: **
491: ** When data is transferred, we open a port, and wait for the server to
492: ** connect with the data.
493: **
494: ** On entry,
495: ** master_socket Must be negative if not set up already.
496: ** On exit,
497: ** Returns socket number if good
498: ** less than zero if error.
499: ** master_socket is socket number if good, else negative.
500: ** port_number is valid if good.
501: */
502: #ifdef __STDC__
503: PRIVATE int get_listen_socket(void)
504: #else
505: PRIVATE int get_listen_socket()
506: #endif
507: {
508: struct sockaddr_in soc_address; /* Binary network address */
509: struct sockaddr_in* sin = &soc_address;
510: int new_socket; /* Will be master_socket */
511:
512:
513: FD_ZERO(&open_sockets); /* Clear our record of open sockets */
514: num_sockets = 0;
515:
516: #ifndef REPEAT_LISTEN
517: if (master_socket>=0) return master_socket; /* Done already */
518: #endif
519:
520: /* Create internet socket
521: */
522: new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
523:
524: if (new_socket<0)
525: return HTInetStatus("socket for master socket");
526:
527: if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
528:
529: /* Search for a free port.
530: */
531: sin->sin_family = AF_INET; /* Family = internet, host order */
532: sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
533: #ifdef POLL_PORTS
534: {
535: unsigned short old_port_number = port_number;
536: for(port_number=old_port_number+1;;port_number++){
537: int status;
538: if (port_number > LAST_TCP_PORT)
539: port_number = FIRST_TCP_PORT;
540: if (port_number == old_port_number) {
541: return HTInetStatus("bind");
542: }
543: soc_address.sin_port = htons(port_number);
544: if ((status=bind(new_socket,
545: (struct sockaddr*)&soc_address,
546: /* Cast to generic sockaddr */
547: sizeof(soc_address))) == 0)
548: break;
549: if (TRACE) fprintf(stderr,
550: "TCP bind attempt to port %d yields %d, errno=%d\n",
551: port_number, status, errno);
552: } /* for */
553: }
554: #else
555: {
556: int status;
557: int address_length = sizeof(soc_address);
558: status = getsockname(control->socket,
559: (struct sockaddr *)&soc_address,
560: &address_length);
561: if (status<0) return HTInetStatus("getsockname");
562: CTRACE(tfp, "FTP: This host is %s\n",
563: HTInetString(sin));
564:
565: soc_address.sin_port = 0; /* Unspecified: please allocate */
566: status=bind(new_socket,
567: (struct sockaddr*)&soc_address,
568: /* Cast to generic sockaddr */
569: sizeof(soc_address));
570: if (status<0) return HTInetStatus("bind");
571:
572: address_length = sizeof(soc_address);
573: status = getsockname(new_socket,
574: (struct sockaddr*)&soc_address,
575: &address_length);
576: if (status<0) return HTInetStatus("getsockname");
577: }
578: #endif
579:
580: CTRACE(tfp, "FTP: bound to port %d on %s\n",
581: (int)ntohs(sin->sin_port),
582: HTInetString(sin));
583:
584: #ifdef REPEAT_LISTEN
585: if (master_socket>=0)
586: (void) close_master_socket();
587: #endif
588:
589: master_socket = new_socket;
590:
591: /* Now we must find out who we are to tell the other guy
592: */
593: (void)HTHostName(); /* Make address valid - doesn't work*/
594: sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d\r\n",
595: (int)*((unsigned char *)(&sin->sin_addr)+0),
596: (int)*((unsigned char *)(&sin->sin_addr)+1),
597: (int)*((unsigned char *)(&sin->sin_addr)+2),
598: (int)*((unsigned char *)(&sin->sin_addr)+3),
599: (int)*((unsigned char *)(&sin->sin_port)+0),
600: (int)*((unsigned char *)(&sin->sin_port)+1));
601:
602:
603: /* Inform TCP that we will accept connections
604: */
605: if (listen(master_socket, 1)<0) {
606: master_socket = -1;
607: return HTInetStatus("listen");
608: }
609: CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
610: FD_SET(master_socket, &open_sockets);
611: if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
612:
613: return master_socket; /* Good */
614:
615: } /* get_listen_socket */
616: #endif
617:
618:
619: /* Get Styles from stylesheet
620: ** --------------------------
621: */
622: PRIVATE void get_styles NOARGS
623: {
624: if (!h1Style) h1Style = HTStyleNamed(styleSheet, "Heading1");
625: if (!h2Style) h2Style = HTStyleNamed(styleSheet, "Heading2");
626: if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example");
627: if (!dirStyle) dirStyle = HTStyleNamed(styleSheet, "Dir");
628: }
629:
630:
631: /* Read a directory into an hypertext object from the data socket
632: ** --------------------------------------------------------------
633: **
634: ** On entry,
635: ** anchor Parent anchor to link the HText to
636: ** address Address of the directory
637: ** On exit,
638: ** returns HT_LOADED if OK
639: ** <0 if error.
640: */
641: PRIVATE int read_directory
642: ARGS2 (
643: HTParentAnchor *, parent,
644: CONST char *, address
645: )
646: {
647: HText * HT = HText_new (parent);
648: char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
649: HTChildAnchor * child; /* corresponds to an entry of the directory */
650: char c = 0;
651: #define LASTPATH_LENGTH 150 /* @@@@ Horrible limit on the entry size */
652: char lastpath[LASTPATH_LENGTH + 1];
653: char *p, *entry;
654:
655: HText_beginAppend (HT);
656: get_styles();
657:
658: HTAnchor_setTitle (parent, "FTP Directory of ");
659: HTAnchor_appendTitle (parent, address + 5); /* +5 gets rid of "file:" */
660: HText_setStyle (HT, h1Style);
661: HText_appendText (HT, filename);
662:
663: HText_setStyle (HT, dirStyle);
664: data_read_pointer = data_write_pointer = data_buffer;
665:
666: if (*filename == 0) /* Empty filename : use root */
667: strcpy (lastpath, "/");
668: else {
669: p = filename + strlen (filename) - 2;
670: while (p >= filename && *p != '/')
671: p--;
672: strcpy (lastpath, p + 1); /* relative path */
673: }
674: free (filename);
675: entry = lastpath + strlen (lastpath);
676: if (*(entry-1) != '/')
677: *entry++ = '/'; /* ready to append entries */
678:
679: if (strlen (lastpath) > 1) { /* Current file is not the FTP root */
680: strcpy (entry, "..");
681: child = HTAnchor_findChildAndLink (parent, 0, lastpath, 0);
682: HText_beginAnchor (HT, child);
683: HText_appendText (HT, "Parent Directory");
684: HText_endAnchor (HT);
685: HText_appendCharacter (HT, '\t');
686: }
687: for (;;) {
688: p = entry;
689: while (p - lastpath < LASTPATH_LENGTH) {
690: c = NEXT_DATA_CHAR;
691: if (c == '\r') {
692: c = NEXT_DATA_CHAR;
693: if (c != '\n') {
694: if (TRACE)
695: printf ("Warning: No newline but %d after carriage return.\n", c);
696: break;
697: }
698: }
699: if (c == '\n' || c == (char) EOF)
700: break;
701: *p++ = c;
702: }
703: if (c == (char) EOF && p == entry)
704: break;
705: *p = 0;
706: child = HTAnchor_findChildAndLink (parent, 0, lastpath, 0);
707: HText_beginAnchor (HT, child);
708: HText_appendText (HT, entry);
709: HText_endAnchor (HT);
710: HText_appendCharacter (HT, '\t');
711: }
712:
713: HText_appendParagraph (HT);
714: HText_endAppend (HT);
715: return response(NIL) == 2 ? HT_LOADED : -1;
716: }
717:
718:
719: /* Retrieve File from Server
720: ** -------------------------
721: **
722: ** On entry,
723: ** name WWW address of a file: document, including hostname
724: ** On exit,
725: ** returns Socket number for file if good.
726: ** <0 if bad.
727: */
728: PUBLIC int HTFTP_open_file_read
729: ARGS2 (
730: CONST char *, name,
731: HTParentAnchor *, anchor
732: )
733: {
734: BOOL isDirectory = NO;
735: int status;
736: int retry; /* How many times tried? */
737:
738: for (retry=0; retry<2; retry++) { /* For timed out/broken connections */
739:
740: status = get_connection(name);
741: if (status<0) return status;
742:
743: #ifdef LISTEN
744: status = get_listen_socket();
745: if (status<0) return status;
746:
747: #ifdef REPEAT_PORT
748: /* Inform the server of the port number we will listen on
749: */
750: {
751: status = response(port_command);
752: if (status !=2) { /* Could have timed out */
753: if (status<0) continue; /* try again - net error*/
754: return -status; /* bad reply */
755: }
756: if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
757: }
758: #endif
759: #else /* Use PASV */
760: /* Tell the server to be passive
761: */
762: {
763: char *p;
764: int reply, h0, h1, h2, h3, p0, p1; /* Parts of reply */
765: status = response("PASV\r\n");
766: if (status !=2) {
767: if (status<0) continue; /* retry or Bad return */
768: return -status; /* bad reply */
769: }
770: for(p=response_text; *p; p++)
771: if ((*p<'0')||(*p>'9')) *p = ' '; /* Keep only digits */
772: status = sscanf(response_text, "%d%d%d%d%d%d%d",
773: &reply, &h0, &h1, &h2, &h3, &p0, &p1);
774: if (status<5) {
775: if (TRACE) fprintf(stderr, "FTP: PASV reply has no inet address!\n");
776: return -99;
777: }
778: passive_port = (p0<<8) + p1;
779: if (TRACE) fprintf(stderr, "FTP: Server is listening on port %d\n",
780: passive_port);
781: }
782:
783: /* Open connection for data:
784: */
785: {
786: struct sockaddr_in soc_address;
787: int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
788: if (status<0) {
789: (void) HTInetStatus("socket for data socket");
790: return status;
791: }
792: data_soc = status;
793:
794: soc_address.sin_addr.s_addr = control->addr;
795: soc_address.sin_port = htons(passive_port);
796: soc_address.sin_family = AF_INET; /* Family, host order */
797: if (TRACE) fprintf(stderr,
798: "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
799: (unsigned int)ntohs(soc_address.sin_port),
800: (int)*((unsigned char *)(&soc_address.sin_addr)+0),
801: (int)*((unsigned char *)(&soc_address.sin_addr)+1),
802: (int)*((unsigned char *)(&soc_address.sin_addr)+2),
803: (int)*((unsigned char *)(&soc_address.sin_addr)+3));
804:
805: status = connect(data_soc, (struct sockaddr*)&soc_address,
806: sizeof(soc_address));
807: if (status<0){
808: (void) HTInetStatus("connect for data");
809: NETCLOSE(data_soc);
810: return status; /* Bad return */
811: }
812:
813: if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
814: }
815: #endif /* use PASV */
816: status = 0;
817: break; /* No more retries */
818:
819: } /* for retries */
820: if (status<0) return status; /* Failed with this code */
821:
822: /* Ask for the file:
823: */
824: {
825: char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
826: char command[LINE_LENGTH+1];
827: if (!*filename) StrAllocCopy(filename, "/");
828: sprintf(command, "RETR %s\r\n", filename);
829: status = response(command);
830: if (status != 1) { /* Failed : try to CWD to it */
831: sprintf(command, "CWD %s\r\n", filename);
832: status = response(command);
833: if (status == 2) { /* Successed : let's NAME LIST it */
834: isDirectory = YES;
835: sprintf(command, "NLST\r\n");
836: status = response (command);
837: }
838: }
839: free(filename);
840: if (status != 1) return -status; /* Action not started */
841: }
842:
843: #ifdef LISTEN
844: /* Wait for the connection
845: */
846: {
847: struct sockaddr_in soc_address;
848: int soc_addrlen=sizeof(soc_address);
849: status = accept(master_socket,
850: (struct sockaddr *)&soc_address,
851: &soc_addrlen);
852: if (status<0)
853: return HTInetStatus("accept");
854: CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
855: data_soc = status;
856: }
857: #else
858: /* @@ */
859: #endif
860: if (isDirectory)
861: return read_directory (anchor, name);
862: /* returns HT_LOADED or error */
863: else
864: return data_soc;
865:
866: } /* open_file_read */
867:
868:
869: /* Close socket opened for reading a file, and get final message
870: ** -------------------------------------------------------------
871: **
872: */
873: PUBLIC int HTFTP_close_file
874: ARGS1 (int,soc)
875: {
876: int status;
877:
878: if (soc!=data_soc) {
879: if (TRACE) fprintf(stderr,
880: "HTFTP: close socket %d: (not FTP data socket).\n",
881: soc);
882: return NETCLOSE(soc);
883: }
884:
885: HTInitInput(control->socket);
886: /* Reset buffering to control connection DD 921208 */
887:
888: status = NETCLOSE(data_soc);
889: if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
890: if (status<0) (void) HTInetStatus("close"); /* Comment only */
891: data_soc = -1; /* invalidate it */
892:
893: status = response(NIL);
894: if (status!=2) return -status;
895:
896: return status; /* Good */
897: }
898:
899:
900: /*___________________________________________________________________________
901: */
902: /* Test program only
903: ** -----------------
904: **
905: ** Compiling this with -DTEST produces a test program. Unix only
906: **
907: ** Syntax:
908: ** test <file or option> ...
909: **
910: ** options: -v Verbose: Turn trace on at this point
911: */
912: #ifdef TEST
913:
914: PUBLIC int WWW_TraceFlag;
915:
916: #ifdef __STDC__
917: int main(int argc, char*argv[])
918: #else
919: int main(argc, argv)
920: int argc;
921: char *argv[];
922: #endif
923: {
924:
925: int arg; /* Argument number */
926: int status;
927: connection * con;
928: WWW_TraceFlag = (0==strcmp(argv[1], "-v")); /* diagnostics ? */
929:
930: #ifdef SUPRESS
931: status = get_listen_socket();
932: if (TRACE) fprintf(stderr, "get_listen_socket returns %d\n\n", status);
933: if (status<0) exit(status);
934:
935: status = get_connection(argv[1]); /* test double use */
936: if (TRACE) fprintf(stderr, "get_connection(`%s') returned %d\n\n", argv[1], status);
937: if (status<0) exit(status);
938: #endif
939:
940: for(arg=1; arg<argc; arg++) { /* For each argument */
941: if (0==strcmp(argv[arg], "-v")) {
942: WWW_TraceFlag = 1; /* -v => Trace on */
943:
944: } else { /* Filename: */
945:
946: status = HTTP_open_file_read(argv[arg]);
947: if (TRACE) fprintf(stderr, "open_file_read returned %d\n\n", status);
948: if (status<0) exit(status);
949:
950: /* Copy the file to std out:
951: */
952: {
953: char buffer[INPUT_BUFFER_SIZE];
954: for(;;) {
955: int status = NETREAD(data_soc, buffer, INPUT_BUFFER_SIZE);
956: if (status<=0) {
957: if (status<0) (void) HTInetStatus("read");
958: break;
959: }
960: status = write(1, buffer, status);
961: if (status<0) {
962: fprintf(stderr, "Write failure!\n");
963: break;
964: }
965: }
966: }
967:
968: status = HTTP_close_file(data_soc);
969: if (TRACE) fprintf(stderr, "Close_file returned %d\n\n", status);
970:
971: } /* if */
972: } /* for */
973: status = response("QUIT\r\n"); /* Be good */
974: if (TRACE) fprintf(stderr, "Quit returned %d\n", status);
975:
976: while(connections) close_connection(connections);
977:
978: close_master_socket();
979:
980: } /* main */
981: #endif /* TEST */
982:
Webmaster