Annotation of libwww/Library/src/HTFTP.c, revision 1.13
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
1.2 timbl 29: ** fails on princeton.edu!
1.1 timbl 30: **
31: ** Options:
32: ** LISTEN We listen, the other guy connects for data.
33: ** Otherwise, other way round, but problem finding our
34: ** internet address!
35: **
36: ** Bugs:
37: ** No binary mode! Always uses ASCII!
38: */
39:
40: #define LISTEN /* @@@@ Test */
41:
42: /*
43: BUGS: @@@ Limit connection cache size!
44: Error reporting to user.
45: 400 & 500 errors are acked by user with windows.
46: Use configuration file for user names
47: Prompt user for password
48:
49: ** Note for portablility this version does not use select() and
50: ** so does not watch the control and data channels at the
51: ** same time.
52: */
53:
1.2 timbl 54: #include "HTFTP.h" /* Implemented here */
1.13 ! timbl 55: #include "HTFormat.h"
1.2 timbl 56:
1.4 timbl 57: #define CR FROMASCII('\015') /* Must be converted to ^M for transmission */
58: #define LF FROMASCII('\012') /* Must be converted to ^J for transmission */
59:
1.1 timbl 60: #define REPEAT_PORT /* Give the port number for each file */
61: #define REPEAT_LISTEN /* Close each listen socket and open a new one */
62:
63: /* define POLL_PORTS If allocation does not work, poll ourselves.*/
64: #define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/
65:
66: #define FIRST_TCP_PORT 1024 /* Region to try for a listening port */
67: #define LAST_TCP_PORT 5999
68:
69: #define LINE_LENGTH 256
70: #define COMMAND_LENGTH 256
71:
72: #include "HTParse.h"
73: #include "HTUtils.h"
74: #include "tcp.h"
75: #include "HTTCP.h"
76: #include "HTAnchor.h"
1.2 timbl 77: #include "HTFile.h" /* For HTFileFormat() */
1.6 secret 78: #include "HTBTree.h"
79: #include "HTChunk.h"
1.1 timbl 80: #ifndef IPPORT_FTP
81: #define IPPORT_FTP 21
82: #endif
83:
84: #ifdef REMOVED_CODE
85: extern char *malloc();
86: extern void free();
87: extern char *strncpy();
88: #endif
89:
90: typedef struct _connection {
91: struct _connection * next; /* Link on list */
92: u_long addr; /* IP address */
93: int socket; /* Socket number for communication */
94: BOOL binary; /* Binary mode? */
1.13 ! timbl 95: HTInputSocket * isoc;
1.1 timbl 96: } connection;
97:
98: #ifndef NIL
99: #define NIL 0
100: #endif
101:
1.2 timbl 102: /* Hypertext object building machinery
103: */
104: #include "HTML.h"
105:
106: #define PUTC(c) (*targetClass.put_character)(target, c)
107: #define PUTS(s) (*targetClass.put_string)(target, s)
108: #define START(e) (*targetClass.start_element)(target, e, 0, 0)
109: #define END(e) (*targetClass.end_element)(target, e)
110: #define FREE_TARGET (*targetClass.free)(target)
111: struct _HTStructured {
112: CONST HTStructuredClass * isa;
113: /* ... */
114: };
1.1 timbl 115:
1.2 timbl 116:
1.1 timbl 117: /* Module-Wide Variables
118: ** ---------------------
119: */
120: PRIVATE connection * connections =0; /* Linked list of connections */
121: PRIVATE char response_text[LINE_LENGTH+1];/* Last response from NewsHost */
122: PRIVATE connection * control; /* Current connection */
123: PRIVATE int data_soc = -1; /* Socket for data transfer =invalid */
124:
125: #ifdef POLL_PORTS
126: PRIVATE unsigned short port_number = FIRST_TCP_PORT;
127: #endif
128:
129: #ifdef LISTEN
130: PRIVATE int master_socket = -1; /* Listening socket = invalid */
131: PRIVATE char port_command[255]; /* Command for setting the port */
132: PRIVATE fd_set open_sockets; /* Mask of active channels */
133: PRIVATE int num_sockets; /* Number of sockets to scan */
134: #else
135: PRIVATE unsigned short passive_port; /* Port server specified for data */
136: #endif
137:
138:
139: #define DATA_BUFFER_SIZE 2048
140: PRIVATE char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */
141: PRIVATE char * data_read_pointer;
142: PRIVATE char * data_write_pointer;
143: #define NEXT_DATA_CHAR next_data_char()
144:
145:
146: /* Procedure: Read a character from the data connection
147: ** ----------------------------------------------------
148: */
149: PRIVATE char next_data_char
150: NOARGS
151: {
152: int status;
153: if (data_read_pointer >= data_write_pointer) {
154: status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
155: /* Get some more data */
156: if (status <= 0) return (char)-1;
157: data_write_pointer = data_buffer + status;
158: data_read_pointer = data_buffer;
159: }
160: #ifdef NOT_ASCII
161: {
162: char c = *data_read_pointer++;
163: return FROMASCII(c);
164: }
165: #else
166: return *data_read_pointer++;
167: #endif
168: }
169:
170:
171: /* Close an individual connection
172: **
173: */
174: #ifdef __STDC__
175: PRIVATE int close_connection(connection * con)
176: #else
177: PRIVATE int close_connection(con)
178: connection *con;
179: #endif
180: {
181: connection * scan;
182: int status = NETCLOSE(con->socket);
1.13 ! timbl 183: HTInputSocket_free(con->isoc);
1.1 timbl 184: if (TRACE) fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
185: if (connections==con) {
186: connections = con->next;
187: return status;
188: }
189: for(scan=connections; scan; scan=scan->next) {
190: if (scan->next == con) {
191: scan->next = con->next; /* Unlink */
192: if (control==con) control = (connection*)0;
193: return status;
194: } /*if */
195: } /* for */
196: return -1; /* very strange -- was not on list. */
197: }
198:
199:
200: /* Execute Command and get Response
201: ** --------------------------------
202: **
203: ** See the state machine illustrated in RFC959, p57. This implements
204: ** one command/reply sequence. It also interprets lines which are to
205: ** be continued, which are marked with a "-" immediately after the
206: ** status code.
207: **
1.8 timbl 208: ** Continuation then goes on until a line with a matching reply code
209: ** an a space after it.
210: **
1.1 timbl 211: ** On entry,
212: ** con points to the connection which is established.
213: ** cmd points to a command, or is NIL to just get the response.
214: **
215: ** The command is terminated with the CRLF pair.
216: **
217: ** On exit,
218: ** returns: The first digit of the reply type,
219: ** or negative for communication failure.
220: */
1.13 ! timbl 221: PRIVATE int response ARGS1(char *, cmd)
! 222:
1.1 timbl 223: {
224: int result; /* Three-digit decimal code */
1.8 timbl 225: int continuation_response = -1;
1.1 timbl 226: int status;
227:
228: if (!control) {
229: if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
230: return -99;
231: }
232:
233: if (cmd) {
234:
235: if (TRACE) fprintf(stderr, " Tx: %s", cmd);
236:
237: #ifdef NOT_ASCII
238: {
239: char * p;
240: for(p=cmd; *p; p++) {
241: *p = TOASCII(*p);
242: }
243: }
244: #endif
245: status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
246: if (status<0) {
247: if (TRACE) fprintf(stderr,
248: "FTP: Error %d sending command: closing socket %d\n",
249: status, control->socket);
250: close_connection(control);
251: return status;
252: }
253: }
254:
255: do {
256: char *p = response_text;
257: for(;;) {
1.13 ! timbl 258: if (((*p++=HTInputSocket_getCharacter(control->isoc)) == LF)
1.1 timbl 259: || (p == &response_text[LINE_LENGTH])) {
1.8 timbl 260: char continuation;
1.1 timbl 261: *p++=0; /* Terminate the string */
262: if (TRACE) fprintf(stderr, " Rx: %s", response_text);
263: sscanf(response_text, "%d%c", &result, &continuation);
1.8 timbl 264: if (continuation_response == -1) {
265: if (continuation == '-') /* start continuation */
266: continuation_response = result;
267: } else { /* continuing */
268: if (continuation_response == result
269: && continuation == ' ')
270: continuation_response = -1; /* ended */
271: }
1.1 timbl 272: break;
273: } /* if end of line */
274:
1.12 timbl 275: if (*(p-1) == (char) EOF) {
1.1 timbl 276: if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
277: control->socket);
278: strcpy(response_text, "000 *** TCP read error on response\n");
279: close_connection(control);
280: return -1; /* End of file on response */
281: }
282: } /* Loop over characters */
283:
1.8 timbl 284: } while (continuation_response != -1);
1.1 timbl 285:
286: if (result==421) {
287: if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
288: control->socket);
289: close_connection(control);
290: return -1;
291: }
292: return result/100;
293: }
294:
295:
296: /* Get a valid connection to the host
297: ** ----------------------------------
298: **
299: ** On entry,
300: ** arg points to the name of the host in a hypertext address
301: ** On exit,
302: ** returns <0 if error
303: ** socket number if success
304: **
305: ** This routine takes care of managing timed-out connections, and
306: ** limiting the number of connections in use at any one time.
307: **
308: ** It ensures that all connections are logged in if they exist.
309: ** It ensures they have the port number transferred.
310: */
311: PRIVATE int get_connection ARGS1 (CONST char *,arg)
312: {
313: struct sockaddr_in soc_address; /* Binary network address */
314: struct sockaddr_in* sin = &soc_address;
315:
316: char * username=0;
317: char * password=0;
318:
319: if (!arg) return -1; /* Bad if no name sepcified */
320: if (!*arg) return -1; /* Bad if name had zero length */
321:
322: /* Set up defaults:
323: */
324: sin->sin_family = AF_INET; /* Family, host order */
325: sin->sin_port = htons(IPPORT_FTP); /* Well Known Number */
326:
327: if (TRACE) fprintf(stderr, "FTP: Looking for %s\n", arg);
328:
329: /* Get node name:
330: */
331: {
332: char *p1 = HTParse(arg, "", PARSE_HOST);
1.2 timbl 333: char *p2 = strrchr(p1, '@'); /* user? */
1.1 timbl 334: char * pw;
335: if (p2) {
336: username = p1;
337: *p2=0; /* terminate */
338: p1 = p2+1; /* point to host */
339: pw = strchr(username, ':');
340: if (pw) {
341: *pw++ = 0;
342: password = pw;
343: }
344: }
345: if (HTParseInet(sin, p1)) { free(p1); return -1;} /* TBL 920622 */
346:
347: if (!username) free(p1);
348: } /* scope of p1 */
349:
350:
351: /* Now we check whether we already have a connection to that port.
352: */
353:
354: {
355: connection * scan;
356: for (scan=connections; scan; scan=scan->next) {
357: if (sin->sin_addr.s_addr == scan->addr) {
358: if (TRACE) fprintf(stderr,
359: "FTP: Already have connection for %d.%d.%d.%d.\n",
360: (int)*((unsigned char *)(&scan->addr)+0),
361: (int)*((unsigned char *)(&scan->addr)+1),
362: (int)*((unsigned char *)(&scan->addr)+2),
363: (int)*((unsigned char *)(&scan->addr)+3));
364: if (username) free(username);
365: return scan->socket; /* Good return */
366: } else {
367: if (TRACE) fprintf(stderr,
368: "FTP: Existing connection is %d.%d.%d.%d\n",
369: (int)*((unsigned char *)(&scan->addr)+0),
370: (int)*((unsigned char *)(&scan->addr)+1),
371: (int)*((unsigned char *)(&scan->addr)+2),
372: (int)*((unsigned char *)(&scan->addr)+3));
373: }
374: }
375: }
376:
377:
378: /* Now, let's get a socket set up from the server:
379: */
380: {
381: int status;
382: connection * con = (connection *)malloc(sizeof(*con));
383: if (con == NULL) outofmem(__FILE__, "get_connection");
384: con->addr = sin->sin_addr.s_addr; /* save it */
385: con->binary = NO;
386: status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
387: if (status<0) {
388: (void) HTInetStatus("socket");
389: free(con);
390: if (username) free(username);
391: return status;
392: }
393: con->socket = status;
394:
395: status = connect(con->socket, (struct sockaddr*)&soc_address,
396: sizeof(soc_address));
397: if (status<0){
398: (void) HTInetStatus("connect");
399: if (TRACE) fprintf(stderr,
400: "FTP: Unable to connect to remote host for `%s'.\n",
401: arg);
402: NETCLOSE(con->socket);
403: free(con);
404: if (username) free(username);
405: return status; /* Bad return */
406: }
407:
408: if (TRACE) fprintf(stderr, "FTP connected, socket %d\n", con->socket);
409: control = con; /* Current control connection */
410: con->next = connections; /* Link onto list of good ones */
411: connections = con;
1.13 ! timbl 412: con->isoc = HTInputSocket_new(con->socket); /* buffering */
1.1 timbl 413:
414:
415: /* Now we log in Look up username, prompt for pw.
416: */
417: {
418: int status = response(NIL); /* Get greeting */
419:
420: if (status == 2) { /* Send username */
421: char * command;
422: if (username) {
423: command = (char*)malloc(10+strlen(username)+2+1);
424: if (command == NULL) outofmem(__FILE__, "get_connection");
1.4 timbl 425: sprintf(command, "USER %s%c%c", username, CR, LF);
1.1 timbl 426: } else {
427: command = (char*)malloc(25);
428: if (command == NULL) outofmem(__FILE__, "get_connection");
1.4 timbl 429: sprintf(command, "USER anonymous%c%c", CR, LF);
1.1 timbl 430: }
431: status = response(command);
432: free(command);
433: }
434: if (status == 3) { /* Send password */
435: char * command;
436: if (password) {
437: command = (char*)malloc(10+strlen(password)+2+1);
438: if (command == NULL) outofmem(__FILE__, "get_connection");
1.4 timbl 439: sprintf(command, "PASS %s%c%c", password, CR, LF);
1.1 timbl 440: } else {
1.10 timbl 441: char * user = getenv("USER");
442: CONST char *host = HTHostName();
443: if (!user) user = "WWWuser";
444: /* If not fully qualified, suppress it as ftp.uu.net
445: prefers a blank to a bad name */
446: if (!strchr(host, '.')) host = "";
447:
448: command = (char*)malloc(20+strlen(host)+2+1);
1.1 timbl 449: if (command == NULL) outofmem(__FILE__, "get_connection");
450: sprintf(command,
1.10 timbl 451: "PASS %s@%s%c%c", user ? user : "WWWuser",
452: host, CR, LF); /*@@*/
1.1 timbl 453: }
454: status = response(command);
455: free(command);
456: }
457: if (username) free(username);
458:
1.4 timbl 459: if (status == 3) {
460: char temp[80];
461: sprintf(temp, "ACCT noaccount%c%c", CR, LF);
462: status = response(temp);
463: }
1.1 timbl 464: if (status !=2) {
465: if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
466: if (control) close_connection(control);
467: return -1; /* Bad return */
468: }
469: if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
470: }
471:
472: /* Now we inform the server of the port number we will listen on
473: */
474: #ifndef REPEAT_PORT
475: {
476: int status = response(port_command);
477: if (status !=2) {
478: if (control) close_connection(control);
479: return -status; /* Bad return */
480: }
481: if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
482: }
483: #endif
484: return con->socket; /* Good return */
485: } /* Scope of con */
1.13 ! timbl 486: } /* get_connection */
1.1 timbl 487:
488:
489: #ifdef LISTEN
490:
491: /* Close Master (listening) socket
492: ** -------------------------------
493: **
494: **
495: */
496: #ifdef __STDC__
497: PRIVATE int close_master_socket(void)
498: #else
499: PRIVATE int close_master_socket()
500: #endif
501: {
502: int status;
503: FD_CLR(master_socket, &open_sockets);
504: status = NETCLOSE(master_socket);
505: if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
506: master_socket = -1;
507: if (status<0) return HTInetStatus("close master socket");
508: else return status;
509: }
510:
511:
512: /* Open a master socket for listening on
513: ** -------------------------------------
514: **
515: ** When data is transferred, we open a port, and wait for the server to
516: ** connect with the data.
517: **
518: ** On entry,
519: ** master_socket Must be negative if not set up already.
520: ** On exit,
521: ** Returns socket number if good
522: ** less than zero if error.
523: ** master_socket is socket number if good, else negative.
524: ** port_number is valid if good.
525: */
526: #ifdef __STDC__
527: PRIVATE int get_listen_socket(void)
528: #else
529: PRIVATE int get_listen_socket()
530: #endif
531: {
532: struct sockaddr_in soc_address; /* Binary network address */
533: struct sockaddr_in* sin = &soc_address;
534: int new_socket; /* Will be master_socket */
535:
536:
537: FD_ZERO(&open_sockets); /* Clear our record of open sockets */
538: num_sockets = 0;
539:
540: #ifndef REPEAT_LISTEN
541: if (master_socket>=0) return master_socket; /* Done already */
542: #endif
543:
544: /* Create internet socket
545: */
546: new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
547:
548: if (new_socket<0)
549: return HTInetStatus("socket for master socket");
550:
551: if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
552:
553: /* Search for a free port.
554: */
555: sin->sin_family = AF_INET; /* Family = internet, host order */
556: sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
557: #ifdef POLL_PORTS
558: {
559: unsigned short old_port_number = port_number;
560: for(port_number=old_port_number+1;;port_number++){
561: int status;
562: if (port_number > LAST_TCP_PORT)
563: port_number = FIRST_TCP_PORT;
564: if (port_number == old_port_number) {
565: return HTInetStatus("bind");
566: }
567: soc_address.sin_port = htons(port_number);
568: if ((status=bind(new_socket,
569: (struct sockaddr*)&soc_address,
570: /* Cast to generic sockaddr */
571: sizeof(soc_address))) == 0)
572: break;
573: if (TRACE) fprintf(stderr,
574: "TCP bind attempt to port %d yields %d, errno=%d\n",
575: port_number, status, errno);
576: } /* for */
577: }
578: #else
579: {
580: int status;
581: int address_length = sizeof(soc_address);
582: status = getsockname(control->socket,
583: (struct sockaddr *)&soc_address,
584: &address_length);
585: if (status<0) return HTInetStatus("getsockname");
586: CTRACE(tfp, "FTP: This host is %s\n",
587: HTInetString(sin));
588:
589: soc_address.sin_port = 0; /* Unspecified: please allocate */
590: status=bind(new_socket,
591: (struct sockaddr*)&soc_address,
592: /* Cast to generic sockaddr */
593: sizeof(soc_address));
594: if (status<0) return HTInetStatus("bind");
595:
596: address_length = sizeof(soc_address);
597: status = getsockname(new_socket,
598: (struct sockaddr*)&soc_address,
599: &address_length);
600: if (status<0) return HTInetStatus("getsockname");
601: }
602: #endif
603:
604: CTRACE(tfp, "FTP: bound to port %d on %s\n",
605: (int)ntohs(sin->sin_port),
606: HTInetString(sin));
607:
608: #ifdef REPEAT_LISTEN
609: if (master_socket>=0)
610: (void) close_master_socket();
611: #endif
612:
613: master_socket = new_socket;
614:
615: /* Now we must find out who we are to tell the other guy
616: */
617: (void)HTHostName(); /* Make address valid - doesn't work*/
1.4 timbl 618: sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
1.1 timbl 619: (int)*((unsigned char *)(&sin->sin_addr)+0),
620: (int)*((unsigned char *)(&sin->sin_addr)+1),
621: (int)*((unsigned char *)(&sin->sin_addr)+2),
622: (int)*((unsigned char *)(&sin->sin_addr)+3),
623: (int)*((unsigned char *)(&sin->sin_port)+0),
1.4 timbl 624: (int)*((unsigned char *)(&sin->sin_port)+1),
625: CR, LF);
1.1 timbl 626:
627:
628: /* Inform TCP that we will accept connections
629: */
630: if (listen(master_socket, 1)<0) {
631: master_socket = -1;
632: return HTInetStatus("listen");
633: }
634: CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
635: FD_SET(master_socket, &open_sockets);
636: if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
637:
638: return master_socket; /* Good */
639:
640: } /* get_listen_socket */
641: #endif
642:
643:
644:
645: /* Read a directory into an hypertext object from the data socket
646: ** --------------------------------------------------------------
647: **
648: ** On entry,
1.2 timbl 649: ** anchor Parent anchor to link the this node to
1.1 timbl 650: ** address Address of the directory
651: ** On exit,
652: ** returns HT_LOADED if OK
653: ** <0 if error.
654: */
655: PRIVATE int read_directory
1.13 ! timbl 656: ARGS6 (
! 657: HTRequest *, request,
! 658: void *, param,
1.2 timbl 659: HTParentAnchor *, parent,
660: CONST char *, address,
1.4 timbl 661: HTFormat, format_out,
1.6 secret 662: HTStream *, sink )
1.1 timbl 663: {
1.13 ! timbl 664: HTStructured* target = HTML_new(request, NULL, WWW_HTML, format_out, sink);
1.6 secret 665: HTStructuredClass targetClass;
666: char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
667:
668: char c = 0;
669:
670: char *lastpath; /* prefix for link, either "" (for root) or xxx */
671: char *entry; /* pointer into lastpath to bit after last slash */
672:
673: targetClass = *(target->isa);
674:
675: HTDirTitles(target, (HTAnchor*)parent);
1.2 timbl 676:
1.6 secret 677: data_read_pointer = data_write_pointer = data_buffer;
678:
679: if (*filename == 0) /* Empty filename : use root */
680: strcpy (lastpath, "/");
681: else
682: {
683: char * p = strrchr(filename, '/'); /* find lastslash */
684: lastpath = (char*)malloc(strlen(p));
685: if (!lastpath) outofmem(__FILE__, "read_directory");
686: strcpy(lastpath, p+1); /* take slash off the beginning */
687: }
688: free (filename);
689:
690:
691: {
1.7 secret 692: HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
1.6 secret 693: char c;
694: HTChunk * chunk = HTChunkCreate(128);
695: START(HTML_DIR);
696: for (c=0; c!=(char)EOF;) /* For each entry in the directory */
697: {
698: char * filename = NULL;
699: char * p = entry;
700: HTChunkClear(chunk);
701: /* read directory entry
702: */
703: for(;;) { /* Read in one line as filename */
704: c = NEXT_DATA_CHAR;
705: if (c == '\r' || c == LF) { /* Terminator? */
706: if (chunk->size != 0) /* got some text */
707: break; /* finish getting one entry */
708: } else if (c == (char)EOF) {
709: break; /* End of file */
710: } else {
711: HTChunkPutc(chunk, c);
712: }
713: }
714: HTChunkTerminate(chunk);
715: if (c == (char) EOF && chunk->size == 1) /* 1 means empty: includes terminating 0 */
716: break;
717: if(TRACE) fprintf(stderr, "HTFTP: file name in %s is %s\n", lastpath, chunk->data);
718: StrAllocCopy(filename, chunk->data);
719: HTBTree_add(bt,filename); /* sort filename in the tree bt */
720:
721: } /* next entry */
722: HTChunkFree(chunk);
1.1 timbl 723:
1.6 secret 724: /* Run through tree printing out in order
725: */
726: {
727: HTBTElement * ele;
728: for (ele = HTBTree_next(bt, NULL);
729: ele != NULL;
730: ele = HTBTree_next(bt, ele))
731: {
732: START(HTML_LI);
733: HTDirEntry(target, lastpath, (char *)HTBTree_object(ele));
734: }
735: }
736: END(HTML_DIR);
737: FREE_TARGET;
738: HTBTreeAndObject_free(bt);
739: }
1.2 timbl 740:
1.6 secret 741: if (lastpath) free(lastpath);
742: return response(NIL) == 2 ? HT_LOADED : -1;
1.1 timbl 743: }
744:
745:
746: /* Retrieve File from Server
747: ** -------------------------
748: **
749: ** On entry,
750: ** name WWW address of a file: document, including hostname
751: ** On exit,
752: ** returns Socket number for file if good.
753: ** <0 if bad.
754: */
1.2 timbl 755: PUBLIC int HTFTPLoad
1.13 ! timbl 756: ARGS6 (
! 757: HTRequest *, request,
! 758: void *, param,
1.1 timbl 759: CONST char *, name,
1.2 timbl 760: HTParentAnchor *, anchor,
761: HTFormat, format_out,
762: HTStream *, sink
1.1 timbl 763: )
764: {
765: BOOL isDirectory = NO;
766: int status;
767: int retry; /* How many times tried? */
1.2 timbl 768: HTFormat format;
1.1 timbl 769:
770: for (retry=0; retry<2; retry++) { /* For timed out/broken connections */
771:
772: status = get_connection(name);
773: if (status<0) return status;
774:
775: #ifdef LISTEN
776: status = get_listen_socket();
777: if (status<0) return status;
778:
779: #ifdef REPEAT_PORT
780: /* Inform the server of the port number we will listen on
781: */
782: {
783: status = response(port_command);
784: if (status !=2) { /* Could have timed out */
785: if (status<0) continue; /* try again - net error*/
786: return -status; /* bad reply */
787: }
788: if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
789: }
790: #endif
791: #else /* Use PASV */
792: /* Tell the server to be passive
793: */
794: {
795: char *p;
796: int reply, h0, h1, h2, h3, p0, p1; /* Parts of reply */
1.4 timbl 797: status = response("PASV%c%c", CR, LF);
1.1 timbl 798: if (status !=2) {
799: if (status<0) continue; /* retry or Bad return */
800: return -status; /* bad reply */
801: }
802: for(p=response_text; *p; p++)
803: if ((*p<'0')||(*p>'9')) *p = ' '; /* Keep only digits */
804: status = sscanf(response_text, "%d%d%d%d%d%d%d",
805: &reply, &h0, &h1, &h2, &h3, &p0, &p1);
806: if (status<5) {
807: if (TRACE) fprintf(stderr, "FTP: PASV reply has no inet address!\n");
808: return -99;
809: }
810: passive_port = (p0<<8) + p1;
811: if (TRACE) fprintf(stderr, "FTP: Server is listening on port %d\n",
812: passive_port);
813: }
814:
815: /* Open connection for data:
816: */
817: {
818: struct sockaddr_in soc_address;
819: int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
820: if (status<0) {
821: (void) HTInetStatus("socket for data socket");
822: return status;
823: }
824: data_soc = status;
825:
826: soc_address.sin_addr.s_addr = control->addr;
827: soc_address.sin_port = htons(passive_port);
828: soc_address.sin_family = AF_INET; /* Family, host order */
829: if (TRACE) fprintf(stderr,
830: "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
831: (unsigned int)ntohs(soc_address.sin_port),
832: (int)*((unsigned char *)(&soc_address.sin_addr)+0),
833: (int)*((unsigned char *)(&soc_address.sin_addr)+1),
834: (int)*((unsigned char *)(&soc_address.sin_addr)+2),
835: (int)*((unsigned char *)(&soc_address.sin_addr)+3));
836:
837: status = connect(data_soc, (struct sockaddr*)&soc_address,
838: sizeof(soc_address));
839: if (status<0){
840: (void) HTInetStatus("connect for data");
841: NETCLOSE(data_soc);
842: return status; /* Bad return */
843: }
844:
845: if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
846: }
847: #endif /* use PASV */
848: status = 0;
849: break; /* No more retries */
850:
851: } /* for retries */
852: if (status<0) return status; /* Failed with this code */
853:
854: /* Ask for the file:
855: */
856: {
857: char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
858: char command[LINE_LENGTH+1];
1.9 timbl 859: BOOL binary;
860: HTAtom * encoding;
1.1 timbl 861: if (!*filename) StrAllocCopy(filename, "/");
1.9 timbl 862: format = HTFileFormat(filename, &encoding);
863: binary = (encoding != HTAtom_for("8bit")
864: && encoding != HTAtom_for("7bit"));
865: if (binary != control->binary) {
866: char * mode = binary ? "I" : "A";
867: sprintf(command, "TYPE %s%c%c", mode, CR, LF);
868: status = response(command);
869: if (status != 2) return -status;
870: control->binary = binary;
871: }
1.4 timbl 872: sprintf(command, "RETR %s%c%c", filename, CR, LF);
1.1 timbl 873: status = response(command);
874: if (status != 1) { /* Failed : try to CWD to it */
1.4 timbl 875: sprintf(command, "CWD %s%c%c", filename, CR, LF);
1.1 timbl 876: status = response(command);
877: if (status == 2) { /* Successed : let's NAME LIST it */
878: isDirectory = YES;
1.4 timbl 879: sprintf(command, "NLST%c%c", CR, LF);
1.1 timbl 880: status = response (command);
881: }
882: }
883: free(filename);
884: if (status != 1) return -status; /* Action not started */
885: }
886:
887: #ifdef LISTEN
888: /* Wait for the connection
889: */
890: {
891: struct sockaddr_in soc_address;
892: int soc_addrlen=sizeof(soc_address);
893: status = accept(master_socket,
894: (struct sockaddr *)&soc_address,
895: &soc_addrlen);
896: if (status<0)
897: return HTInetStatus("accept");
898: CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
899: data_soc = status;
900: }
901: #else
902: /* @@ */
903: #endif
1.2 timbl 904: if (isDirectory) {
1.13 ! timbl 905: return read_directory(request, NULL, anchor, name, format_out, sink);
1.1 timbl 906: /* returns HT_LOADED or error */
1.2 timbl 907: } else {
1.13 ! timbl 908: HTParseSocket(format, data_soc, request);
1.2 timbl 909:
910: status = NETCLOSE(data_soc);
911: if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
912: if (status<0) (void) HTInetStatus("close"); /* Comment only */
913: data_soc = -1; /* invalidate it */
914:
915: status = response(NIL); /* Pick up final reply */
1.6 secret 916: if (status!=2) return HTLoadError(sink, 500, response_text);
1.2 timbl 917:
918: return HT_LOADED;
919: }
1.1 timbl 920: } /* open_file_read */
Webmaster