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