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