Annotation of libwww/Library/src/HTFTP.c, revision 1.2

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

Webmaster