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