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

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

Webmaster