Annotation of libwww/Library/src/HTFTP.c, revision 1.1.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