Annotation of libwww/Library/src/HTFTP.c, revision 1.3
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;
1.3 ! timbl 689: (*targetClass.start_element)(target, HTML_A, present,
! 690: (CONST char**) value); /* typecast for think c */
1.2 timbl 691: PUTS("Up to Parent Directory");
692: END(HTML_A);
1.1 timbl 693: }
1.2 timbl 694:
695: START(HTML_DIR);
1.1 timbl 696: for (;;) {
697: p = entry;
698: while (p - lastpath < LASTPATH_LENGTH) {
699: c = NEXT_DATA_CHAR;
700: if (c == '\r') {
701: c = NEXT_DATA_CHAR;
702: if (c != '\n') {
703: if (TRACE)
704: printf ("Warning: No newline but %d after carriage return.\n", c);
705: break;
706: }
707: }
708: if (c == '\n' || c == (char) EOF)
709: break;
710: *p++ = c;
711: }
712: if (c == (char) EOF && p == entry)
713: break;
714: *p = 0;
1.2 timbl 715: START(HTML_LI);
716: value[HTML_A_HREF] = lastpath;
1.3 ! timbl 717: (*targetClass.start_element)(target, HTML_A, present,
! 718: (CONST char**)value); /* typecast for think c */
1.2 timbl 719: PUTS(entry);
720: END(HTML_A);
1.1 timbl 721: }
722:
1.2 timbl 723: END(HTML_DIR);
724: END_TARGET;
725: FREE_TARGET;
726:
1.1 timbl 727: return response(NIL) == 2 ? HT_LOADED : -1;
728: }
729:
730:
731: /* Retrieve File from Server
732: ** -------------------------
733: **
734: ** On entry,
735: ** name WWW address of a file: document, including hostname
736: ** On exit,
737: ** returns Socket number for file if good.
738: ** <0 if bad.
739: */
1.2 timbl 740: PUBLIC int HTFTPLoad
741: ARGS4 (
1.1 timbl 742: CONST char *, name,
1.2 timbl 743: HTParentAnchor *, anchor,
744: HTFormat, format_out,
745: HTStream *, sink
1.1 timbl 746: )
747: {
748: BOOL isDirectory = NO;
749: int status;
750: int retry; /* How many times tried? */
1.2 timbl 751: HTFormat format;
1.1 timbl 752:
753: for (retry=0; retry<2; retry++) { /* For timed out/broken connections */
754:
755: status = get_connection(name);
756: if (status<0) return status;
757:
758: #ifdef LISTEN
759: status = get_listen_socket();
760: if (status<0) return status;
761:
762: #ifdef REPEAT_PORT
763: /* Inform the server of the port number we will listen on
764: */
765: {
766: status = response(port_command);
767: if (status !=2) { /* Could have timed out */
768: if (status<0) continue; /* try again - net error*/
769: return -status; /* bad reply */
770: }
771: if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
772: }
773: #endif
774: #else /* Use PASV */
775: /* Tell the server to be passive
776: */
777: {
778: char *p;
779: int reply, h0, h1, h2, h3, p0, p1; /* Parts of reply */
780: status = response("PASV\r\n");
781: if (status !=2) {
782: if (status<0) continue; /* retry or Bad return */
783: return -status; /* bad reply */
784: }
785: for(p=response_text; *p; p++)
786: if ((*p<'0')||(*p>'9')) *p = ' '; /* Keep only digits */
787: status = sscanf(response_text, "%d%d%d%d%d%d%d",
788: &reply, &h0, &h1, &h2, &h3, &p0, &p1);
789: if (status<5) {
790: if (TRACE) fprintf(stderr, "FTP: PASV reply has no inet address!\n");
791: return -99;
792: }
793: passive_port = (p0<<8) + p1;
794: if (TRACE) fprintf(stderr, "FTP: Server is listening on port %d\n",
795: passive_port);
796: }
797:
798: /* Open connection for data:
799: */
800: {
801: struct sockaddr_in soc_address;
802: int status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
803: if (status<0) {
804: (void) HTInetStatus("socket for data socket");
805: return status;
806: }
807: data_soc = status;
808:
809: soc_address.sin_addr.s_addr = control->addr;
810: soc_address.sin_port = htons(passive_port);
811: soc_address.sin_family = AF_INET; /* Family, host order */
812: if (TRACE) fprintf(stderr,
813: "FTP: Data remote address is port %d, inet %d.%d.%d.%d\n",
814: (unsigned int)ntohs(soc_address.sin_port),
815: (int)*((unsigned char *)(&soc_address.sin_addr)+0),
816: (int)*((unsigned char *)(&soc_address.sin_addr)+1),
817: (int)*((unsigned char *)(&soc_address.sin_addr)+2),
818: (int)*((unsigned char *)(&soc_address.sin_addr)+3));
819:
820: status = connect(data_soc, (struct sockaddr*)&soc_address,
821: sizeof(soc_address));
822: if (status<0){
823: (void) HTInetStatus("connect for data");
824: NETCLOSE(data_soc);
825: return status; /* Bad return */
826: }
827:
828: if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
829: }
830: #endif /* use PASV */
831: status = 0;
832: break; /* No more retries */
833:
834: } /* for retries */
835: if (status<0) return status; /* Failed with this code */
836:
837: /* Ask for the file:
838: */
839: {
840: char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
841: char command[LINE_LENGTH+1];
842: if (!*filename) StrAllocCopy(filename, "/");
843: sprintf(command, "RETR %s\r\n", filename);
1.2 timbl 844: format = HTFileFormat(filename);
1.1 timbl 845: status = response(command);
846: if (status != 1) { /* Failed : try to CWD to it */
847: sprintf(command, "CWD %s\r\n", filename);
848: status = response(command);
849: if (status == 2) { /* Successed : let's NAME LIST it */
850: isDirectory = YES;
851: sprintf(command, "NLST\r\n");
852: status = response (command);
853: }
854: }
855: free(filename);
856: if (status != 1) return -status; /* Action not started */
857: }
858:
859: #ifdef LISTEN
860: /* Wait for the connection
861: */
862: {
863: struct sockaddr_in soc_address;
864: int soc_addrlen=sizeof(soc_address);
865: status = accept(master_socket,
866: (struct sockaddr *)&soc_address,
867: &soc_addrlen);
868: if (status<0)
869: return HTInetStatus("accept");
870: CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
871: data_soc = status;
872: }
873: #else
874: /* @@ */
875: #endif
1.2 timbl 876: if (isDirectory) {
877: return read_directory (anchor, name, sink);
1.1 timbl 878: /* returns HT_LOADED or error */
1.2 timbl 879: } else {
880: HTParseSocket(format, format_out,
881: anchor, data_soc, sink);
882:
883: HTInitInput(control->socket);
884: /* Reset buffering to control connection DD 921208 */
885:
886: status = NETCLOSE(data_soc);
887: if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
888: if (status<0) (void) HTInetStatus("close"); /* Comment only */
889: data_soc = -1; /* invalidate it */
890:
891: status = response(NIL); /* Pick up final reply */
892: if (status!=2) return -status;
893:
894: return HT_LOADED;
895: }
1.1 timbl 896: } /* open_file_read */
897:
898:
899: /* Close socket opened for reading a file, and get final message
900: ** -------------------------------------------------------------
901: **
902: */
1.2 timbl 903: #ifdef OLD_CODE
1.1 timbl 904: PUBLIC int HTFTP_close_file
905: ARGS1 (int,soc)
906: {
907: int status;
908:
909: if (soc!=data_soc) {
910: if (TRACE) fprintf(stderr,
911: "HTFTP: close socket %d: (not FTP data socket).\n",
912: soc);
913: return NETCLOSE(soc);
914: }
915:
916: HTInitInput(control->socket);
917: /* Reset buffering to control connection DD 921208 */
918:
919: status = NETCLOSE(data_soc);
920: if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
921: if (status<0) (void) HTInetStatus("close"); /* Comment only */
922: data_soc = -1; /* invalidate it */
923:
924: status = response(NIL);
925: if (status!=2) return -status;
926:
927: return status; /* Good */
928: }
1.2 timbl 929: #endif /* old code */
1.1 timbl 930:
931:
932: /*___________________________________________________________________________
933: */
934: /* Test program only
935: ** -----------------
936: **
937: ** Compiling this with -DTEST produces a test program. Unix only
1.2 timbl 938: ** OBSOLETE -- WON'T COMPILE,
1.1 timbl 939: **
940: ** Syntax:
941: ** test <file or option> ...
942: **
943: ** options: -v Verbose: Turn trace on at this point
944: */
945: #ifdef TEST
946:
947: PUBLIC int WWW_TraceFlag;
948:
949: #ifdef __STDC__
950: int main(int argc, char*argv[])
951: #else
952: int main(argc, argv)
953: int argc;
954: char *argv[];
955: #endif
956: {
957:
958: int arg; /* Argument number */
959: int status;
960: connection * con;
961: WWW_TraceFlag = (0==strcmp(argv[1], "-v")); /* diagnostics ? */
962:
963: #ifdef SUPRESS
964: status = get_listen_socket();
965: if (TRACE) fprintf(stderr, "get_listen_socket returns %d\n\n", status);
966: if (status<0) exit(status);
967:
968: status = get_connection(argv[1]); /* test double use */
969: if (TRACE) fprintf(stderr, "get_connection(`%s') returned %d\n\n", argv[1], status);
970: if (status<0) exit(status);
971: #endif
972:
973: for(arg=1; arg<argc; arg++) { /* For each argument */
974: if (0==strcmp(argv[arg], "-v")) {
975: WWW_TraceFlag = 1; /* -v => Trace on */
976:
977: } else { /* Filename: */
978:
979: status = HTTP_open_file_read(argv[arg]);
980: if (TRACE) fprintf(stderr, "open_file_read returned %d\n\n", status);
981: if (status<0) exit(status);
982:
983: /* Copy the file to std out:
984: */
985: {
986: char buffer[INPUT_BUFFER_SIZE];
987: for(;;) {
988: int status = NETREAD(data_soc, buffer, INPUT_BUFFER_SIZE);
989: if (status<=0) {
990: if (status<0) (void) HTInetStatus("read");
991: break;
992: }
993: status = write(1, buffer, status);
994: if (status<0) {
995: fprintf(stderr, "Write failure!\n");
996: break;
997: }
998: }
999: }
1000:
1001: status = HTTP_close_file(data_soc);
1002: if (TRACE) fprintf(stderr, "Close_file returned %d\n\n", status);
1003:
1004: } /* if */
1005: } /* for */
1006: status = response("QUIT\r\n"); /* Be good */
1007: if (TRACE) fprintf(stderr, "Quit returned %d\n", status);
1008:
1009: while(connections) close_connection(connections);
1010:
1011: close_master_socket();
1012:
1013: } /* main */
1014: #endif /* TEST */
1015:
Webmaster