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