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

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

Webmaster