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