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