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