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

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>
1.22      frystyk    23: **      LM      Lou Montulli <montulli@ukanaix.cc.ukans.edu>
                     24: **      FM      Foteos Macrides <macrides@sci.wfeb.edu>
1.23      frystyk    25: **     HF      Henrik Frystyk <frystyk@dxcern.cern.ch>
1.30      luotonen   26: **     AL      Ari Luotonen <luotonen@www.cern.ch>
1.23      frystyk    27: **
1.1       timbl      28: ** History:
                     29: **      2 May 91       Written TBL, as a part of the WorldWideWeb project.
                     30: **     15 Jan 92       Bug fix: close() was used for NETCLOSE for control soc
                     31: **     10 Feb 92       Retry if cached connection times out or breaks
                     32: **      8 Dec 92       Bug fix 921208 TBL after DD
                     33: **     17 Dec 92       Anon FTP password now just WWWuser@ suggested by DD
1.2       timbl      34: **                     fails on princeton.edu!
1.22      frystyk    35: **     27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
                     36: **                     must be Unix-style and cannot include the device
                     37: **                     or top directory.
                     38: **      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
                     39: **                     anonymous FTP
                     40: **      25 Mar 94 (LM)  Added code to recognize different ftp server types
                     41: **                      and code to parse dates and sizes on most hosts.
                     42: **     27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
1.23      frystyk    43: **     27 Apr 94 (HF)  The module is basically rewritten to conform with
                     44: **                     rfc 959, 1123 and 1579 and turned into a state 
                     45: **                     machine. New semantics of ftp URLs are supported.
1.30      luotonen   46: **      2 May 94 (AL)  Fixed possible security hole when the URL contains
                     47: **                     a newline, that could cause multiple commands to be
                     48: **                     sent to an FTP server.
1.1       timbl      49: **
                     50: ** Options:
1.23      frystyk    51: **     LISTEN          The default way to open a dats connection is by using
                     52: **                     PASV, but if that fails, we try PORT. If the PORT part
                     53: **                     is unwanted, it can be disabled by undefine LISTEN.
1.1       timbl      54: **
1.22      frystyk    55: ** Notes:
                     56: **                             Portions Copyright 1994 Trustees of Dartmouth College
                     57: **                     Code for recognizing different FTP servers and
                     58: **                     parsing "ls -l" output taken from Macintosh Fetch
                     59: **                     program with permission from Jim Matthews,
                     60: **                     Dartmouth Software Development Team.
                     61: **
1.23      frystyk    62: **     BUGS:   @@@     Use configuration file for user names
                     63: **
                     64: */
1.1       timbl      65: 
1.22      frystyk    66: /* Implementation dependent include files */
                     67: #include "tcp.h"
1.1       timbl      68: 
1.22      frystyk    69: /* Library include files */
1.1       timbl      70: #include "HTParse.h"
                     71: #include "HTUtils.h"
                     72: #include "HTTCP.h"
                     73: #include "HTAnchor.h"
1.22      frystyk    74: #include "HTFile.h"
1.6       secret     75: #include "HTBTree.h"
                     76: #include "HTChunk.h"
1.22      frystyk    77: #include "HTAlert.h"
1.21      frystyk    78: #include "HTDirBrw.h"
1.33    ! frystyk    79: #include "HTError.h"
1.22      frystyk    80: #include "HTFTP.h"                                      /* Implemented here */
                     81: 
                     82: /* Macros and other defines */
1.23      frystyk    83: /* If LISTEN is defined, then first 'PASV' then 'PORT' (if error) is tried,
                     84:    else ONLY 'PASV' is used in order to establish a data connection. */
                     85: #define LISTEN
                     86: #ifdef LISTEN
                     87: /* #define REPEAT_LISTEN */                      /* Reuse the portnumber once found */
                     88: /* #define POLL_PORTS */      /* If allocation does not work, poll ourselves.*/
                     89: #endif
1.22      frystyk    90: 
1.33    ! frystyk    91: #define FTP_DEFAULT_TIMEOUT    1000L                       /* 1/100 seconds */
        !            92: 
1.1       timbl      93: #ifndef IPPORT_FTP
1.33    ! frystyk    94: #define IPPORT_FTP             21
1.1       timbl      95: #endif
                     96: 
1.33    ! frystyk    97: #define WWW_FTP_CLIENT "WWWuser"         /* If can't get user-info, use this */
        !            98: 
1.22      frystyk    99: /* Globals */
1.23      frystyk   100: PUBLIC BOOL HTFTPUserInfo = YES;
1.33    ! frystyk   101: PUBLIC long HTFTPTimeOut = FTP_DEFAULT_TIMEOUT;
1.22      frystyk   102: 
1.23      frystyk   103: /* Type definitions and global variables etc. local to this module */ 
                    104: PRIVATE user_info *old_user;       /* Only used if HT_REUSE_USER_INFO is on */
                    105: PRIVATE HTList *session;        /* List of control connections in a session */
1.1       timbl     106: 
                    107: #ifdef POLL_PORTS
1.23      frystyk   108: #define FIRST_TCP_PORT  1024          /* Region to try for a listening port */
                    109: #define LAST_TCP_PORT   5999
                    110: PRIVATE        unsigned short   port_number = FIRST_TCP_PORT;
1.1       timbl     111: #endif
                    112: 
                    113: #ifdef LISTEN
1.23      frystyk   114: #ifdef REPEAT_LISTEN
                    115: PRIVATE int     master_socket = -1;           /* Listening socket = invalid */
                    116: #endif
                    117: PRIVATE char * this_addr;                                  /* Local address */
1.1       timbl     118: #endif
                    119: 
1.23      frystyk   120: /* ------------------------------------------------------------------------- */
                    121: /*               ************** TEMPORARY STUFF *************               */
                    122: /* ------------------------------------------------------------------------- */
                    123: #define MAX_ACCEPT_POLL                30
                    124: #define FCNTL(r, s, t) fcntl(r, s, t)
                    125: 
1.26      luotonen  126: 
1.32      frystyk   127: /*                                                             HTDoConnect()
                    128: **
                    129: **     TEMPORARY FUNCTION.
                    130: **     Note: Any port indication in URL, e.g., as `host:port' overwrites
                    131: **     the default_port value.
                    132: **
                    133: **     Returns 0 if OK, -1 on error
                    134: */
1.33    ! frystyk   135: PUBLIC int HTDoConnect ARGS5(HTRequest *, request, char *, url,
1.31      frystyk   136:                             u_short, default_port, int *, s,
                    137:                             u_long *, addr)
1.23      frystyk   138: {
                    139:     int status;
                    140:     struct sockaddr_in sock_addr;
                    141:     char *p1 = HTParse(url, "", PARSE_HOST);
                    142:     char *at_sign;
                    143:     char *host;
                    144: 
                    145:     /* if theres an @ then use the stuff after it as a hostname */
                    146:     if((at_sign = strchr(p1,'@')) != NULL)
                    147:        host = at_sign+1;
                    148:     else
                    149:        host = p1;
                    150:     if (TRACE) fprintf(stderr, "HTDoConnect. Looking up `%s\'\n", host);
                    151: 
                    152:    /* Set up defaults: */
1.25      frystyk   153:     memset((void *) &sock_addr, '\0', sizeof(sock_addr));
1.23      frystyk   154:     sock_addr.sin_family = AF_INET;
                    155:     sock_addr.sin_port = htons(default_port);
                    156: 
                    157:     /* Get node name */
                    158:     if (HTParseInet(&sock_addr, host)) {
                    159:        if (TRACE) fprintf(stderr, "HTDoConnect. Can't locate remote host `%s\'\n", host);
1.33    ! frystyk   160:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_NO_REMOTE_HOST,
        !           161:                   (void *) host, strlen(host), "HTDoConnect");
1.23      frystyk   162:        free (p1);
                    163:        *s = -1;
                    164:        return -1;
                    165:     }
                    166: 
                    167:     if ((*s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
1.33    ! frystyk   168:        HTErrorSysAdd(request, ERR_FATAL, NO, "socket");
1.23      frystyk   169:        free (p1);
1.33    ! frystyk   170:        return -1;
1.23      frystyk   171:     }
                    172:     if (addr)
                    173:        *addr = ntohl(sock_addr.sin_addr.s_addr);
                    174: 
                    175:     if (TRACE)
                    176:        fprintf(stderr, "HTDoConnect. Created socket number %d\n", *s);
                    177: 
                    178:     if ((status = connect(*s, (struct sockaddr *) &sock_addr,
                    179:                          sizeof(sock_addr))) < 0) {
1.33    ! frystyk   180:        HTErrorSysAdd(request, ERR_FATAL, NO, "connect");
1.23      frystyk   181:        if (NETCLOSE(*s) < 0)
1.33    ! frystyk   182:            HTErrorSysAdd(request, ERR_FATAL, NO, "close");
1.23      frystyk   183:        free(p1);
                    184:        *s = -1;
                    185:        return -1;
                    186:     }
                    187:     free(p1);
                    188:     return status;
                    189: }
                    190: 
                    191: 
                    192: /*                                                             HTDoAccept()
                    193: **
                    194: **     This function makes a non-blocking accept on a port and polls every
                    195: **     second until MAX_ACCEPT_POLL or interrupted by user.
                    196: **
                    197: **     BUGS Interrupted is not yet implemented!!!
                    198: **
                    199: **     Returns 0 if OK, -1 on error
                    200: */
1.33    ! frystyk   201: PRIVATE int HTDoAccept ARGS2(HTRequest *, request, int, sockfd)
1.23      frystyk   202: {
                    203:     struct sockaddr_in soc_address;
                    204:     int status;
                    205:     int cnt;
                    206:     int soc_addrlen = sizeof(soc_address);
                    207:     if (sockfd < 0) {
                    208:        if (TRACE) fprintf(stderr, "HTDoAccept.. Bad socket number\n");
                    209:        return -1;
                    210:     }
                    211:        
                    212:     /* First make the socket non-blocking */
                    213:     if((status = FCNTL(sockfd, F_GETFL, 0)) != -1) {
1.33    ! frystyk   214:        status |= FNDELAY;
1.23      frystyk   215:        status = FCNTL(sockfd, F_SETFL, status);
                    216:     }
                    217:     if (status == -1) {
1.33    ! frystyk   218:        HTErrorSysAdd(request, ERR_FATAL, NO, "fcntl");
1.23      frystyk   219:        return -1;
                    220:     }
1.22      frystyk   221: 
1.23      frystyk   222:     /* Now poll every sekund */
                    223:     for(cnt=0; cnt<MAX_ACCEPT_POLL; cnt++) {
                    224:        if ((status = accept(sockfd, (struct sockaddr*) &soc_address,
                    225:                             &soc_addrlen)) >= 0) {
                    226:            if (TRACE) fprintf(stderr,
                    227:                               "HTDoAccept.. Accepted new socket %d\n", 
                    228:                               status);
                    229:            return status;
                    230:        } else
1.33    ! frystyk   231:            HTErrorSysAdd(request, ERR_WARNING, YES, "accept");
1.23      frystyk   232:        sleep(1);
                    233:     }  
                    234:     
1.33    ! frystyk   235:     /* If nothing has happened */    
1.23      frystyk   236:     if (TRACE)
                    237:        fprintf(stderr, "HTDoAccept.. Timed out, no connection!\n");
1.33    ! frystyk   238:     HTErrorAdd(request, ERR_FATAL, NO, HTERR_TIME_OUT, NULL, 0, "HTDoAccept");
1.23      frystyk   239:     return -1;
                    240: }
1.22      frystyk   241: 
                    242: /* ------------------------------------------------------------------------- */
                    243: /*                        Directory Specific Functions                      */
                    244: /* ------------------------------------------------------------------------- */
1.25      frystyk   245: 
1.33    ! frystyk   246: 
        !           247: /*                                                           HTFTPParseError
        !           248: **
        !           249: **     This function parses an (multiple line) error message and takes out
        !           250: **     the error codes.
        !           251: **
        !           252: */
        !           253: PRIVATE void HTFTPParseError ARGS1(HTChunk **, error)
        !           254: {
        !           255:     HTChunk *oldtext = *error;
        !           256:     if (!oldtext || !oldtext->data) {
        !           257:        if (TRACE) fprintf(stderr, "FTP......... No error message?\n");
        !           258:        return;
        !           259:     }
        !           260:     {
        !           261:        int result;                    /* The first return code in the chunk */
        !           262:        char *oldp = oldtext->data;
        !           263:        HTChunk *newtext = HTChunkCreate(128);
        !           264:        if (oldtext->size > 4 && sscanf(oldp, "%d", &result) == 1) {
        !           265:            oldp += 4;
        !           266:            while (*oldp) {
        !           267:                if (*oldp == '\n') {
        !           268:                    int tmpres;
        !           269:                    if (sscanf(++oldp, "%d", &tmpres) == 1) {
        !           270:                        if (tmpres == result) {
        !           271:                            HTChunkPutc(newtext, ' ');
        !           272:                            oldp += 3;                     /* Skip this code */
        !           273:                        }
        !           274:                    }
        !           275:                } else
        !           276:                    HTChunkPutc(newtext, *oldp);
        !           277:                oldp++;
        !           278:            }
        !           279:        }
        !           280:        HTChunkFree(oldtext);
        !           281:        HTChunkTerminate(newtext);
        !           282:        *error = newtext;
        !           283:     }
        !           284:     return;
        !           285: }
        !           286: 
        !           287: 
1.25      frystyk   288: /*                                                           HTFTPParseWelcome
1.23      frystyk   289: **
                    290: **     This function parses the welcome message stored in ctrl->welcome.
                    291: **     Only multi-line messages are considered interesting, and the starting
                    292: **     return code is removed.
                    293: **
                    294: */
1.25      frystyk   295: PRIVATE void HTFTPParseWelcome ARGS1(HTChunk **, welcome)
1.23      frystyk   296: {
1.25      frystyk   297:     HTChunk *oldtext = *welcome;
1.33    ! frystyk   298:     if (!oldtext || !oldtext->data) {
1.23      frystyk   299:        if (TRACE) fprintf(stderr, "FTP......... No welcome message?\n");
                    300:        return;
                    301:     }
                    302:     {
1.28      frystyk   303:        int result;                    /* The first return code in the chunk */
                    304:        char cont;                                      /* Either ' ' or '-' */
                    305:        char *oldp = oldtext->data;
1.23      frystyk   306:        HTChunk *newtext = HTChunkCreate(128);
1.28      frystyk   307:        if (oldtext->size > 4 && sscanf(oldp, "%d%c", &result, &cont) == 2) {
                    308:            oldp += 4;
                    309:            while (cont == '-') {
1.23      frystyk   310:                HTChunkPutc(newtext, *oldp);
                    311:                if (*oldp == '\n') {
1.28      frystyk   312:                    int tmpres;
                    313:                    if (isdigit(*++oldp) &&
                    314:                        sscanf(oldp, "%d%c", &tmpres, &cont) == 2) {
                    315:                        if (tmpres == result && cont == ' ')
                    316:                            break;
                    317:                        else
                    318:                            oldp +=3;                      /* Skip this code */
                    319:                    }
1.23      frystyk   320:                }
1.28      frystyk   321:                oldp++;
1.23      frystyk   322:            }
                    323:        }
                    324:        HTChunkTerminate(newtext);
                    325:        HTChunkFree(oldtext);
1.25      frystyk   326:        *welcome = newtext;
                    327:     }
                    328: }
                    329: 
                    330: 
                    331: /*                                                           HTFTPAddWelcome
                    332: **
                    333: **     This function accumulates every welcome messages from the various
                    334: **     states in the login procedure.
                    335: **
                    336: */
                    337: PRIVATE void HTFTPAddWelcome ARGS1(ftp_ctrl_info *, ctrl)
                    338: {
                    339:     if (!ctrl->welcome)                                            /* If first time */
                    340:        ctrl->welcome = HTChunkCreate(128);
                    341: 
                    342:     HTFTPParseWelcome(&ctrl->reply);
                    343:     if (ctrl->reply->size > 1) {
1.28      frystyk   344:        HTChunkPutc(ctrl->welcome, '\n');
1.25      frystyk   345:        HTChunkPuts(ctrl->welcome, ctrl->reply->data);
1.23      frystyk   346:     }
                    347: }
1.22      frystyk   348: 
1.23      frystyk   349: 
                    350: #if 0 /* NOT NEEDED FOR THE MOMENT */
1.22      frystyk   351: /*
                    352:  * is_ls_date() --
                    353:  *      Return TRUE if s points to a string of the form:
                    354:  *              "Sep  1  1990 " or
                    355:  *              "Sep 11 11:59 " or
                    356:  *              "Dec 12 1989  " or
                    357:  *              "FCv 23 1990  " ...
                    358:  *
                    359:  * Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews)
                    360:  */
                    361: PRIVATE BOOL is_ls_date ARGS1(char *, s)
                    362: {
                    363:     /* must start with three alpha characget_striters */
                    364:     if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
                    365:        return FALSE;
                    366:     
                    367:     /* space */
                    368:     if (*s++ != ' ')
                    369:        return FALSE;
                    370:     
                    371:     /* space or digit */
                    372:     if ((*s != ' ') && !isdigit(*s))
                    373:        return FALSE;
                    374:     s++;
                    375:     
                    376:     /* digit */
                    377:     if (!isdigit(*s++))
                    378:        return FALSE;
                    379:     
                    380:     /* space */
                    381:     if (*s++ != ' ')
                    382:        return FALSE;
                    383:     
                    384:     /* space or digit */
                    385:     if ((*s != ' ') && !isdigit(*s))
                    386:        return FALSE;
                    387:     s++;
                    388:     
                    389:     /* digit */
                    390:     if (!isdigit(*s++))
                    391:        return FALSE;
                    392:     
                    393:     /* colon or digit */
                    394:     if ((*s != ':') && !isdigit(*s))
                    395:        return FALSE;
                    396:     s++;
                    397:     
                    398:     /* digit */
                    399:     if (!isdigit(*s++))
                    400:        return FALSE;
                    401:     
                    402:     /* space or digit */
                    403:     if ((*s != ' ') && !isdigit(*s))
                    404:        return FALSE;
                    405:     s++;
                    406:     
                    407:     /* space */
                    408:     if (*s++ != ' ')
                    409:        return FALSE;
                    410:     
                    411:     return TRUE;
                    412: } /* is_ls_date() */
1.23      frystyk   413: #endif
1.22      frystyk   414: 
                    415: /*                                                             HTStrpMonth()
                    416: **
                    417: **     Returns the number of the month given or -1 on error.
                    418: **
                    419: **     BUG: Handles US dates only!!!
                    420: */
                    421: PRIVATE int HTStrpMonth ARGS1(char *, month)
                    422: {
                    423:     int ret;
                    424:     if (!strncmp(month, "JAN", 3))
                    425:        ret = 0;
                    426:     else if (!strncmp(month, "FEB", 3))
                    427:        ret = 1;
                    428:     else if (!strncmp(month, "MAR", 3))
                    429:        ret = 2;
                    430:     else if (!strncmp(month, "APR", 3))
                    431:        ret = 3;
                    432:     else if (!strncmp(month, "MAY", 3))
                    433:        ret = 4;
                    434:     else if (!strncmp(month, "JUN", 3))
                    435:        ret = 5;
                    436:     else if (!strncmp(month, "JUL", 3))
                    437:        ret = 6;
                    438:     else if (!strncmp(month, "AUG", 3))
                    439:        ret = 7;
                    440:     else if (!strncmp(month, "SEP", 3))
                    441:        ret = 8;
                    442:     else if (!strncmp(month, "OCT", 3))
                    443:        ret = 9;
                    444:     else if (!strncmp(month, "NOV", 3))
                    445:        ret = 10;
                    446:     else if (!strncmp(month, "DEC", 3))
                    447:        ret = 11;
                    448:     else {
                    449:        ret = -1;
1.23      frystyk   450:        if (TRACE) fprintf(stderr, "HTStrpMonth. Couldn't resolve date.\n");
1.22      frystyk   451:     }
                    452:     return ret;
                    453: }
                    454: 
                    455: 
                    456: /*                                                             HTStrpTime()
                    457: **
1.23      frystyk   458: **     Converts a date string from 'ls -l' to a time_t number
1.22      frystyk   459: **     This is needed in order to put out the date using the same format
                    460: **     for all directory listings.
                    461: **
1.23      frystyk   462: **     Returns 0 on error.
1.22      frystyk   463: */
1.23      frystyk   464: PRIVATE time_t HTStrpTime ARGS1(char *, datestr)
1.22      frystyk   465: {
                    466:     struct tm *time_info;                  /* Points to static tm structure */
                    467:     char *bcol = datestr;                                   /* Column begin */
                    468:     char *ecol;                                                       /* Column end */
                    469:     long tval;
                    470:     int cnt;
1.23      frystyk   471:     time_t curtime = time(NULL);
1.22      frystyk   472:     if ((time_info = gmtime(&curtime)) == NULL) {
                    473:        if (TRACE)
1.23      frystyk   474:            fprintf(stderr, "HTStrpTime.. Can't get current time.\n");
                    475:        return (time_t) 0;
1.22      frystyk   476:     }
                    477:     time_info->tm_isdst = -1;                        /* Disable summer time */
                    478:     for (cnt=0; cnt<3; cnt++)                                      /* Month */
                    479:        *bcol++ = toupper(*bcol);
                    480:     if ((time_info->tm_mon = HTStrpMonth(datestr)) < 0)
1.23      frystyk   481:        return (time_t) 0;
1.22      frystyk   482:     ecol = bcol;                                                     /* Day */
                    483:     while (*ecol++ == ' ');                   /* Spool to other side of day */
                    484:     while (*ecol++ != ' ');
                    485:     *--ecol = '\0';
                    486:     time_info->tm_mday = atoi(bcol);
                    487:     time_info->tm_wday = 0;
                    488:     time_info->tm_yday = 0;
                    489:     bcol = ++ecol;                                                  /* Year */
                    490:     if ((ecol = strchr(bcol, ':')) == NULL) {
                    491:        time_info->tm_year = atoi(bcol)-1900;
                    492:        time_info->tm_sec = 0;
                    493:        time_info->tm_min = 0;
                    494:        time_info->tm_hour = 0;
                    495:     } else {                                                        /* Time */
1.23      frystyk   496:        /* If the time is given as hh:mm, then the file is less than 1 year
                    497:           old, but we might shift calandar year. This is avoided by checking 
                    498:           if the date parsed is future or not. */
1.22      frystyk   499:        *ecol = '\0';
                    500:        time_info->tm_sec = 0;
1.23      frystyk   501:        time_info->tm_min = atoi(++ecol);               /* Right side of ':' */
1.22      frystyk   502:        time_info->tm_hour = atoi(bcol);                 /* Left side of ':' */
1.23      frystyk   503:        if (mktime(time_info) > curtime)
                    504:            --time_info->tm_year;
1.22      frystyk   505:     }
1.23      frystyk   506:     return ((tval = mktime(time_info)) == -1 ? (time_t) 0 : tval); 
1.22      frystyk   507: }
                    508: 
                    509: 
                    510: /*                                                             HTVMSStrpTime()
                    511: **
1.23      frystyk   512: **     Converts a date string from vms to a time_t number
1.22      frystyk   513: **     This is needed in order to put out the date using the same format
                    514: **     for all directory listings.
                    515: **
1.23      frystyk   516: **     Returns 0 on error
1.22      frystyk   517: */
1.23      frystyk   518: PRIVATE time_t HTVMSStrpTime ARGS1(char *, datestr)
1.22      frystyk   519: {
                    520:     struct tm *time_info;                  /* Points to static tm structure */
                    521:     char *col;
                    522:     long tval;
1.23      frystyk   523:     time_t curtime = time(NULL);
1.22      frystyk   524:     if ((time_info = gmtime(&curtime)) == NULL)
1.23      frystyk   525:        return (time_t) 0;
1.22      frystyk   526:     time_info->tm_isdst = -1;                        /* Disable summer time */
                    527:     if ((col = strtok(datestr, "-")) == NULL)
1.23      frystyk   528:        return (time_t) 0;
1.22      frystyk   529:     time_info->tm_mday = atoi(col);                                  /* Day */
                    530:     time_info->tm_wday = 0;
                    531:     time_info->tm_yday = 0;
                    532:     if ((col = strtok(NULL, "-")) == NULL ||
                    533:        (time_info->tm_mon = HTStrpMonth(col)) < 0)
1.23      frystyk   534:        return (time_t) 0;
1.22      frystyk   535:     if ((col = strtok(NULL, " ")) == NULL)                          /* Year */
1.23      frystyk   536:        return (time_t) 0;
1.22      frystyk   537:     time_info->tm_year = atoi(col)-1900;
                    538:     if ((col = strtok(NULL, ":")) == NULL)                          /* Hour */
1.23      frystyk   539:        return (time_t) 0;
1.22      frystyk   540:     time_info->tm_hour = atoi(col);
                    541:     if ((col = strtok(NULL, " ")) == NULL)                          /* Mins */
1.23      frystyk   542:        return (time_t) 0;
1.22      frystyk   543:     time_info->tm_min = atoi(col);
                    544:     time_info->tm_sec = 0;
1.23      frystyk   545:     return ((tval = mktime(time_info)) < 0 ? (time_t) 0 : tval);
1.22      frystyk   546: }
                    547: 
                    548: 
                    549: /*                                                             HTFTPFilePerm()
                    550: **
                    551: **     Converts the file type from 'ls -l' into a long. The reason for
                    552: **     doing this is to be able to handle the file type and icon selection
                    553: **     similar to the Unix way used in HTBrowseDirectory().
                    554: **
                    555: */
                    556: PRIVATE long HTFTPFilePerm ARGS1(char *, permission)
                    557: {
                    558:     char *strptr = permission;
                    559:     long mode = 0L;
                    560:     
                    561:     /* Special files etc. are all handled like regular files */
                    562:     switch (*strptr++) {                                       /* File type */
                    563:       case 'd':        mode = S_IFMT & S_IFDIR; break;
                    564:       case 'l': mode = S_IFMT & S_IFLNK; break;
                    565:       default:  mode = S_IFMT & S_IFREG; break;
                    566:     }
                    567:     if (*strptr++ == 'r') mode |= S_IRUSR;                          /* User */
                    568:     if (*strptr++ == 'w') mode |= S_IWUSR;
                    569:     if (*strptr == 'x')
                    570:        mode |= S_IXUSR;
                    571:     else if (*strptr == 's')
                    572:        mode |= (S_IXUSR | S_ISUID);
                    573:     else if (*strptr == 'S')
                    574:        mode |= S_ISUID;
                    575:     strptr++;
                    576:     if (*strptr++ == 'r') mode |= S_IRGRP;                         /* Group */
                    577:     if (*strptr++ == 'w') mode |= S_IWGRP;
                    578:     if (*strptr == 'x')
                    579:        mode |= S_IXGRP;
                    580:     else if (*strptr == 's')
                    581:        mode |= (S_IXGRP | S_ISGID);
                    582:     else if (*strptr == 'S')
                    583:        mode |= S_ISGID;
                    584:     strptr++;
                    585:     if (*strptr++ == 'r') mode |= S_IROTH;                         /* Other */
                    586:     if (*strptr++ == 'w') mode |= S_IWOTH;
                    587:     if (*strptr == 'x')
                    588:        mode |= S_IXOTH;
                    589:     else if (*strptr == 't')
                    590:        mode |= (S_IXOTH | S_ISVTX);
                    591:     else if (*strptr == 'T')
                    592:        mode |= S_ISVTX;
                    593:     strptr++;
                    594:     return mode;
                    595: }
                    596: 
                    597: 
                    598: /*                                                          parse_unix_line()
                    599: **
                    600: **     Extract the name, size, and date from an 'ls'. The function expects
                    601: **     the following format of the ls-line:
                    602: **
                    603: **     <permission> <nlink> <owner> [<group>] <size> <date> <filename>
                    604: **
                    605: **     The group is not always present and is therefore optional. Both owner
                    606: **     and group can be numbers.
                    607: **
                    608: **     Returns YES if OK, NO on error
                    609: */
                    610: PRIVATE BOOL parse_unix_line ARGS2(char *,line, dir_file_info *,f_info)
                    611: {
                    612:     char *column;
                    613:     char *strptr;
                    614:     int ival;
                    615:     unsigned long lval;
                    616:     if (!line || !*line || !f_info)
                    617:        return NO;
                    618: 
                    619:     if ((column = strtok(line, " ")) == NULL)                /* Permissions */
                    620:        return NO;
                    621:     f_info->f_mode = HTFTPFilePerm(column);
                    622:     if ((column = strtok(NULL, " ")) == NULL)                      /* Links */
                    623:        return NO;
                    624:     if (sscanf(column, "%d", &ival) == 1)
                    625:        f_info->f_nlink = ival;
                    626:     if ((column = strtok(NULL, " ")) == NULL)                      /* Owner */
                    627:        return NO;
                    628:     StrAllocCopy(f_info->f_uid, column);
                    629:     if ((column = strtok(NULL, " ")) == NULL)           /* Group and/or Size */
                    630:        return NO;
                    631:     if (sscanf(column, "%lu", &lval) != 1) {
                    632:        StrAllocCopy(f_info->f_gid, column);
                    633:        if ((column = strtok(NULL, " ")) == NULL)
                    634:            return NO;
                    635:        if (sscanf(column, "%lu", &lval) == 1)
                    636:            f_info->f_size = lval;
                    637:     } else {                                      /* Group can be a number! */
                    638:        strptr = column+strlen(column)+1;
                    639:        while (*strptr++ == ' ');
                    640:        if (isdigit(*--strptr)) {          /* Month can't start with a digit */
                    641:            StrAllocCopy(f_info->f_gid, column);
                    642:            if ((column = strtok(NULL, " ")) == NULL)
                    643:                return NO;
                    644:            if (sscanf(column, "%lu", &lval) == 1)
                    645:                f_info->f_size = lval;
                    646:        } else {
1.23      frystyk   647:            StrAllocCopy(f_info->f_gid, "");
1.22      frystyk   648:            f_info->f_size = lval;
                    649:        }
                    650:     }
                    651:     column = column+strlen(column)+1;
                    652:     while (*column++ == ' ');
                    653:     strptr = --column+12;                           /* Find the date column */
                    654:     *strptr++ = '\0';
1.23      frystyk   655:     if ((f_info->f_mtime = HTStrpTime(column)) == (time_t) 0)
1.22      frystyk   656:        return NO;
                    657:     while (*strptr++ == ' ');                          /* Spool to filename */
                    658:     if ((f_info->f_mode & S_IFMT) == S_IFLNK) {        /* Strip any '->' */
                    659:        char *link = strstr(strptr-1, " -> ");
                    660:        if (link)
                    661:            *link = '\0';
                    662:     }
                    663:     StrAllocCopy(f_info->f_name, --strptr);
                    664:     return YES;                                        /* We have a full structure! */
                    665: }
                    666: 
                    667: 
                    668: /*                                                           parse_vms_line()
                    669: **
                    670: **      Format the name, date, and size from a VMS LIST line
                    671: **      into the dir_file_info structure
                    672: **
1.23      frystyk   673: **     BUGS: Group, user and permissions are not parsed!
                    674: **
1.22      frystyk   675: **     Returns YES if OK, NO on error
                    676: */
                    677: PRIVATE BOOL parse_vms_line ARGS2(char *, line, dir_file_info *, f_info)
                    678: {
                    679:     int i, j, ialloc;
                    680:     char *cp, *cpd, *cps, *cdir, *sp = " ";
                    681:     
                    682:     /**  Get rid of information lines by making them blank too **/
                    683:     /**  Valid lines have the semi-colon version number token  **/
                    684:     if (!line || !*line || !f_info || (cp = strchr(line, ';')) == NULL) {
                    685:        return NO;
                    686:     }
                    687: 
                    688:     /** Cut out file or directory name at VMS version number **/
                    689:     *cp++ ='\0';
                    690:     StrAllocCopy(f_info->f_name,line);
                    691:     
                    692:     /** Cast VMS file and directory names to lowercase **/
                    693:     for (i=0; f_info->f_name[i]; i++)
                    694:        f_info->f_name[i] = tolower(f_info->f_name[i]);
                    695:     
                    696:     /** Uppercase terminal .z's or _z's **/
                    697:     if ((--i > 2) && f_info->f_name[i] == 'z' &&
                    698:        (f_info->f_name[i-1] == '.' || f_info->f_name[i-1] == '_'))
                    699:        f_info->f_name[i] = 'Z';
                    700:     
                    701:     /* Trim off VMS directory extensions */
                    702:     if ((cdir = strstr(f_info->f_name, ".dir")) != NULL) { /* Strip any .dir */
                    703:        f_info->f_mode = S_IFMT & S_IFDIR;
                    704:        *cdir = '\0';
                    705:     } else
                    706:        f_info->f_mode = S_IFMT & S_IFREG;
                    707: 
                    708:     /** Convert any tabs in rest of line to spaces **/
                    709:     cps = cp-1;
                    710:     while ((cps=strchr(cps+1, '\t')) != NULL)
                    711:        *cps = ' ';
                    712:     
                    713:     /** Collapse serial spaces **/
                    714:     i = 0; j = 1;
                    715:     cps = cp;
                    716:     while (cps[j] != '\0') {
                    717:        if (cps[i] == ' ' && cps[j] == ' ')
                    718:            j++;
                    719:        else
                    720:            cps[++i] = cps[j++];
                    721:     }
                    722:     cps[++i] = '\0';
                    723:     
                    724:     /** Track down the date **/
                    725:     if ((cpd=strchr(cp, '-')) != NULL) {
1.24      luotonen  726:        if ((int)strlen(cpd) > 9 && isdigit(*(cpd-1)) &&
1.22      frystyk   727:            isalpha(*(cpd+1)) && *(cpd+4) == '-') {
1.23      frystyk   728:            if ((f_info->f_mtime = HTVMSStrpTime(cpd-2)) == (time_t) 0)
1.22      frystyk   729:                return NO;
                    730:        }
                    731:     }
                    732:     
                    733:     /** Track down the size **/
                    734:     if ((cpd = strchr(cp, '/')) != NULL) {
                    735:        /* Appears be in used/allocated format */
                    736:        cps = cpd;
                    737:        while (isdigit(*(cps-1)))
                    738:            cps--;
                    739:        if (cps < cpd)
                    740:            *cpd = '\0';
                    741:        f_info->f_size = atoi(cps);
                    742:        cps = cpd+1;
                    743:        while (isdigit(*cps))
                    744:            cps++;
                    745:        *cps = '\0';
                    746:        ialloc = atoi(cpd+1);
                    747:        /* Check if used is in blocks or bytes */
                    748:        if (f_info->f_size <= ialloc)
                    749:            f_info->f_size *= 512;
                    750:     }
                    751:     else if ((cps=strtok(cp, sp)) != NULL) {
                    752:        /* We just initialized on the version number */
                    753:        /* Now let's hunt for a lone, size number    */
                    754:        while ((cps=strtok(NULL, sp)) != NULL) {
                    755:            cpd = cps;
                    756:            while (isdigit(*cpd))
                    757:                cpd++;
                    758:            if (*cpd == '\0') {
                    759:                /* Assume it's blocks */
                    760:                f_info->f_size = atoi(cps) * 512;
                    761:                break;
                    762:            }
                    763:        }
                    764:     }
                    765:     return YES;                                        /* We have a full structure! */
                    766: }
                    767: 
                    768: 
                    769: /*                                                          parse_dir_entry() 
                    770: **
                    771: **      Given a line of LIST/NLST output in entry, return results 
                    772: **      and a file/dir name in f_info struct
                    773: **
1.23      frystyk   774: **      If first_entry is true, this is the first name in a directory.
1.22      frystyk   775: **     Returns YES if OK, NO on error
                    776: */
1.23      frystyk   777: PRIVATE BOOL parse_dir_entry ARGS4(ftp_data_info *, data, char *, entry,
                    778:                                   BOOL, first_entry, dir_file_info *, f_info)
1.22      frystyk   779: {
                    780:     BOOL status = YES;
1.23      frystyk   781:     switch (data->ctrl->server) {
1.22      frystyk   782:       case UNIX_SERVER:
                    783:       case PETER_LEWIS_SERVER:
                    784:       case MACHTEN_SERVER:
1.32      frystyk   785:       case WINDOWS_NT:                             /* This is an experiment */
1.22      frystyk   786:        /* Interpret and edit LIST output from Unix server */
1.23      frystyk   787:        if (first_entry) {
                    788:            if (data->ctrl->unsure_type == YES &&
                    789:                strncmp(entry, "total ", 6) &&
                    790:                (strstr(entry, "not available") != NULL)) {
1.22      frystyk   791:                /* this isn't really a unix server! */
1.23      frystyk   792:                if (TRACE)
                    793:                    fprintf(stderr, "FTP......... No, this isn't a UNIX server anyway :-(\n");
                    794:                data->ctrl->server = GENERIC_SERVER;
1.22      frystyk   795:            }
1.23      frystyk   796:            /* We might as well say that it is not unsure any more, as we
                    797:               can't (or don't) do anything about it, */
                    798:            data->ctrl->unsure_type = NO;
                    799:            status = NO;
                    800:        } else
                    801:            status = parse_unix_line(entry, f_info);
1.22      frystyk   802:        break;
                    803:        
                    804:       case VMS_SERVER:
                    805:        /* Interpret and edit LIST output from VMS server */
                    806:        /* and convert information lines to zero length.  */
                    807:        status = parse_vms_line(entry, f_info);
                    808:        break;
                    809:        
                    810:       case CMS_SERVER:
                    811:        /* Can't be directory... "entry" already equals the correct f_name */
                    812:        StrAllocCopy(f_info->f_name, entry);
                    813:        f_info->f_mode = S_IFMT & S_IFREG;
                    814:        break;
                    815:        
                    816:       case NCSA_SERVER:
                    817:       case TCPC_SERVER:
                    818:        /* Directories identified by trailing "/" characters */
                    819:        StrAllocCopy(f_info->f_name, entry);
                    820:        {
                    821:            int len = strlen(entry);
                    822:            if (*(entry+len-1) == '/') {
                    823:                *(entry+len-1) = '\0';
                    824:                f_info->f_mode = S_IFMT & S_IFDIR;
                    825:            } else {
                    826:                f_info->f_mode = S_IFMT & S_IFREG;
                    827:            }
                    828:        }
                    829:        break;
                    830:        
                    831:       default:
                    832:        /* We cant tell if it is a directory since we only did an NLST :-( */
                    833:        StrAllocCopy(f_info->f_name, entry);
                    834:        f_info->f_mode = S_IFMT & S_IFREG;
                    835:        break;
                    836:     }
                    837:     return status;
                    838: }
1.1       timbl     839: 
                    840: 
1.23      frystyk   841: PRIVATE int HTFTP_get_dir_string ARGS2(ftp_data_info *, data,
                    842:                                       dir_file_info *, f_info)
1.21      frystyk   843: {
1.23      frystyk   844:     int status = 1;
1.22      frystyk   845:     int ch;                          /* Must be int in order to contain EOF */
1.23      frystyk   846:     BOOL got_line = NO;
1.22      frystyk   847:     static BOOL first = YES;               /* Is it the first time through? */
1.23      frystyk   848:     static HTInputSocket *isoc;
1.22      frystyk   849:     HTChunk *chunk = HTChunkCreate(128);
                    850: 
1.23      frystyk   851:     if (first == YES)
                    852:        isoc = HTInputSocket_new(data->socket);          /* Set up buffering */
                    853:     do {                                       /* Until we have a nice line */
                    854:        while ((ch = HTInputSocket_getCharacter(isoc)) >= 0) {
                    855:            if (ch == CR || ch == LF) {                       /* Terminator? */
1.32      frystyk   856:                first = NO;
1.23      frystyk   857:                if (chunk->size != 0) {                     /* got some text */
                    858:                    if (data->ctrl->server == VMS_SERVER) {
                    859:                        /* Deal with MultiNet's wrapping of long lines - F.M.*/
                    860:                        if (isdigit(*(chunk->data+chunk->size-1)))
                    861:                            continue;
                    862:                    }
                    863:                    HTChunkTerminate(chunk);
                    864:                    if (parse_dir_entry(data, chunk->data, first, f_info)) {
                    865:                        got_line = YES;
                    866:                        break;
                    867:                    } else {
                    868:                        HTChunkClear(chunk);
1.22      frystyk   869:                    }
                    870:                }
1.23      frystyk   871:            } else
                    872:                HTChunkPutc(chunk, ch);
1.21      frystyk   873:        }
1.23      frystyk   874:     } while (got_line == NO && ch >= 0);
                    875:     if (ch < 0) {
                    876:        first = YES;
                    877:        status = (ch == EOF) ? 0 : ch;
                    878:     }
                    879:     if (first) {
                    880:        HTInputSocket_free(isoc);
                    881:        isoc = NULL;
                    882:     }
1.22      frystyk   883:     HTChunkFree(chunk);
1.21      frystyk   884:     return status;
                    885: }
                    886: 
1.22      frystyk   887: /* ------------------------------------------------------------------------- */
1.23      frystyk   888: /*       FTP Client Functions for managing control and data connections     */
1.22      frystyk   889: /* ------------------------------------------------------------------------- */
                    890: 
1.23      frystyk   891: /*                                                             HTFTP_send_cmd
1.22      frystyk   892: **
1.23      frystyk   893: **     This function sends a command through the control connection 
                    894: **     specified. The Telnet terminating end of line code <CRLF> is
                    895: **     appended automaticly. The parameter argument is ignored if NULL.
1.1       timbl     896: **
1.23      frystyk   897: **     Returns 0 on OK, else -1 but does NOT close the connection
1.1       timbl     898: */
1.23      frystyk   899: PRIVATE int HTFTP_send_cmd ARGS3(ftp_ctrl_info *, ctrl_info, char *, cmd,
                    900:                                 char *, pars)
1.1       timbl     901: {
1.23      frystyk   902:     char *command;
                    903:     if (!ctrl_info && ctrl_info->socket < 0) {
                    904:        if (TRACE)
                    905:            fprintf(stderr, "HTFTP_send_cmd: Invalid socket\n");
                    906:        return -1;
                    907:     }
                    908:     if ((command = (char *) malloc(strlen(cmd) + (pars ? strlen(pars)+1 : 0) +
                    909:                                   2 + 1)) == NULL)
                    910:        outofmem(__FILE__, "HTFTP_send_cmd");
                    911:     if (pars && *pars)
                    912:        sprintf(command, "%s %s%c%c", cmd, pars, CR, LF);
                    913:     else
                    914:        sprintf(command, "%s%c%c", cmd, CR, LF);
                    915:     if (TRACE) {
                    916:        if (!strcasecomp(cmd, "pass"))
                    917:            fprintf(stderr, "FTP Tx...... PASS ********\n");
1.22      frystyk   918:        else
1.23      frystyk   919:            fprintf(stderr, "FTP Tx...... %s", command);
                    920:     }
                    921: #ifdef NOT_ASCII
                    922:     {
                    923:        char *sp;
                    924:        for (sp = command; *sp; sp++) {
                    925:            *sp = TOASCII(*sp);
                    926:        }
                    927:     }
                    928: #endif
                    929:     {
                    930:        int status = NETWRITE(ctrl_info->socket, command,
                    931:                              (int) strlen(command));
                    932:        if (status < 0) {
                    933:            if (TRACE)
1.33    ! frystyk   934:                fprintf(stderr, "FTP......... Error sending command: %s",
        !           935:                        command);
        !           936:            HTInetStatus("write");
1.23      frystyk   937:            free(command);
                    938:            return -1;
                    939:        }
                    940:        free(command);
                    941:        return 0;
                    942:     }
                    943: }
1.1       timbl     944: 
                    945: 
1.23      frystyk   946: /*                                                        HTFTP_get_response
1.8       timbl     947: **
1.23      frystyk   948: **     This function gets the response from the net to the control connection
                    949: **     specified. If text is not NULL, the response text is put into this 
                    950: **     chunk. In case of OK, the freeing is then left to the callee. The
                    951: **     response is read until a <LF> is encountered, since not all servers
                    952: **     use a full telnet <CRLF> code.
1.1       timbl     953: **
1.23      frystyk   954: **     Returns the 3 digit return code on OK, else -1 but does NOT close
                    955: **     the control connection.
1.1       timbl     956: */
1.23      frystyk   957: PRIVATE int HTFTP_get_response ARGS2(ftp_ctrl_info *, ctrl_info,
                    958:                                     HTChunk **, text)
1.1       timbl     959: {
1.23      frystyk   960:     int result;                                         /* Three-digit decimal code */
                    961:     int offset = 0;                  /* Offset for each newline in response */
                    962:     BOOL first_line = YES;
                    963:     int ch;
                    964:     HTChunk *chunk = HTChunkCreate(128);
                    965: 
                    966:     if (!ctrl_info && ctrl_info->socket < 0) {
                    967:        if (TRACE)
                    968:            fprintf(stderr, "HTFTP_get_response: Invalid socket\n");
                    969:        return -1;
1.1       timbl     970:     }
                    971: 
1.23      frystyk   972:     /* Read response */
                    973:     while ((ch = HTInputSocket_getCharacter(ctrl_info->isoc)) >= 0) {
                    974:        if (ch == LF) {
                    975:            int tmpres;
                    976:            char cont;
                    977:            if (first_line == YES) {
                    978:                if (sscanf(chunk->data, "%d%c", &result, &cont) < 2) {
                    979:                    if (TRACE)
                    980:                        fprintf(stderr,
                    981:                                "FTP Rx...... `%s\' - no code found?\n",
                    982:                                chunk->data);
1.31      frystyk   983:                    HTChunkFree(chunk);
1.23      frystyk   984:                    return -1;
                    985:                }
                    986:                if (cont == '-') {
                    987:                    HTChunkPutc(chunk, '\n');
                    988:                    offset = chunk->size;                 /* Remember offset */
                    989:                    first_line = NO;
                    990:                } else {
                    991:                    HTChunkTerminate(chunk);
                    992:                    break;
                    993:                }
                    994:            } else {
1.28      frystyk   995:                if (isdigit(*(chunk->data+offset)) &&
                    996:                    sscanf(chunk->data+offset, "%d%c", &tmpres, &cont) == 2 &&
1.23      frystyk   997:                    tmpres == result && cont == ' ') {
                    998:                    HTChunkTerminate(chunk);
                    999:                    break;
                   1000:                } else {
                   1001:                    HTChunkPutc(chunk, '\n');
                   1002:                    offset = chunk->size;                   /* Update offset */
                   1003:                }
1.1       timbl    1004:            }
1.23      frystyk  1005:        } else
                   1006:            HTChunkPutc(chunk, (char) ch);
1.1       timbl    1007:     }
1.23      frystyk  1008:     if (!chunk->size || ch < 0) {                      /* No response read? */
1.31      frystyk  1009:        if (TRACE) fprintf(stderr, "FTP Rx...... No response?\n");
1.23      frystyk  1010:        HTChunkFree(chunk);
1.1       timbl    1011:        return -1;
                   1012:     }
1.23      frystyk  1013:     if (TRACE) fprintf(stderr, "FTP Rx...... %s\n", chunk->data);
                   1014:     if (!text)             /* Response text not wanted so we free the chunk */
                   1015:        HTChunkFree(chunk);
                   1016:     else {
                   1017:        if (*text)                                 /* Free old value, if any */
                   1018:            HTChunkFree(*text);
                   1019:        *text = chunk;
                   1020:     }
                   1021:     return result;
1.1       timbl    1022: }
                   1023: 
                   1024: 
1.23      frystyk  1025: 
                   1026: /*                                                     HTFTP_close_data_con
                   1027: **     Closes the data connection and frees memory
                   1028: **     Returns 0 if OK, -1 on error
1.22      frystyk  1029: */
1.23      frystyk  1030: PRIVATE int HTFTP_close_data_con ARGS1(ftp_data_info *, data)
1.22      frystyk  1031: {
1.23      frystyk  1032:     int status = 0;
                   1033:     if (data) {
                   1034:        if (data->socket >= 0) {
                   1035:            if (TRACE)
                   1036:                fprintf(stderr, "FTP......... Closing data socket %d\n",
                   1037:                        data->socket);
                   1038:            if ((status = NETCLOSE(data->socket)) < 0)
                   1039:                HTInetStatus("close data socket");
                   1040: #ifdef REPEAT_LISTEN
                   1041:            if (master_socket == data->socket)
                   1042:                master_socket = -1;
                   1043: #endif
                   1044:        }
                   1045:        FREE(data->datatype);
                   1046:        free(data);
                   1047:     } else {
                   1048:        if (TRACE) fprintf(stderr, "HTFTP_close_data_con: bad argument!");
                   1049:        status = -1;
1.22      frystyk  1050:     }
                   1051:     return status;
                   1052: }
                   1053: 
                   1054: 
1.23      frystyk  1055: /*                                                     HTFTP_close_ctrl_con
                   1056: **     Only if the control connection has no data connection(s) pending
                   1057: **     then it can be closed and the memory freed.
                   1058: **     Returns 0 if OK, -1 on error
1.1       timbl    1059: */
1.23      frystyk  1060: PRIVATE int HTFTP_close_ctrl_con ARGS1(ftp_ctrl_info *, ctrl)
1.1       timbl    1061: {
1.23      frystyk  1062:     int status = 0;
                   1063:     if (ctrl && (!ctrl->data_cons ||
                   1064:                 (ctrl->data_cons && !HTList_count(ctrl->data_cons)))) {
                   1065:        if (TRACE)
                   1066:            fprintf(stderr,
                   1067:                    "FTP......... Closing control socket %d\n", ctrl->socket);
                   1068:        if (ctrl->socket >= 0) {
                   1069:            if ((status = NETCLOSE(ctrl->socket)) < 0)
                   1070:                HTInetStatus("close control socket");
                   1071:        }
                   1072:        if (ctrl->isoc)
                   1073:            HTInputSocket_free(ctrl->isoc);
                   1074:        FREE(ctrl->location);
                   1075:        if (ctrl->user) {
                   1076:            FREE(ctrl->user->domain);
                   1077:            FREE(ctrl->user->id);
                   1078:            FREE(ctrl->user->passwd);
                   1079:            free(ctrl->user);
                   1080:        }
                   1081:        if (ctrl->welcome)
                   1082:            HTChunkFree(ctrl->welcome);
1.25      frystyk  1083:        if (ctrl->reply)
                   1084:            HTChunkFree(ctrl->reply);
1.23      frystyk  1085:        HTList_delete(ctrl->data_cons);
                   1086:        free(ctrl);
                   1087:     }
                   1088:     return status;
                   1089: }
1.22      frystyk  1090: 
1.1       timbl    1091: 
1.23      frystyk  1092: /*                                                     HTFTP_abort_ctrl_con
                   1093: **     Closes the control connection without looking if any data connections
                   1094: **     are pending => they are all removed, so be careful!
                   1095: **     Returns 0 if OK, -1 on error
                   1096: */
                   1097: PRIVATE int HTFTP_abort_ctrl_con ARGS1(ftp_ctrl_info *, ctrl)
                   1098: {
                   1099:     int status = 0;
                   1100:     if (!ctrl) {
                   1101:        if (TRACE)
                   1102:            fprintf(stderr, "HTFTP_abort_ctrl_con called with bad argument\n");
                   1103:        return -1;
                   1104:     }
                   1105:     if (TRACE) fprintf(stderr, "FTP......... Aborting control socket %d\n",
                   1106:                       ctrl->socket);
1.1       timbl    1107: 
1.23      frystyk  1108:     /* Close any pending data connections */
                   1109:     if (ctrl->data_cons && HTList_count(ctrl->data_cons)) {
                   1110:        HTList *cur = ctrl->data_cons;
                   1111:        ftp_data_info *pres;
                   1112:        while ((pres = (ftp_data_info *) HTList_nextObject(cur))) {
                   1113:            HTFTP_close_data_con(pres);
                   1114:        }
                   1115:        HTList_delete(ctrl->data_cons);
                   1116:        ctrl->data_cons = NULL;
                   1117:     }
1.1       timbl    1118: 
1.23      frystyk  1119:     /* If a session is going on, the control connections are closed later */
                   1120:     if (!session) {
                   1121:        if (ctrl->socket >= 0) {
                   1122:            if ((status = NETCLOSE(ctrl->socket)) < 0)
                   1123:                HTInetStatus("abot control socket");
                   1124:        }
                   1125:        if (ctrl->isoc)
                   1126:            HTInputSocket_free(ctrl->isoc);
                   1127:        FREE(ctrl->location);
                   1128:        if (ctrl->user) {
                   1129:            FREE(ctrl->user->domain);
                   1130:            FREE(ctrl->user->id);
                   1131:            FREE(ctrl->user->passwd);
                   1132:            free(ctrl->user);
                   1133:        }
                   1134:        if (ctrl->welcome)
                   1135:            HTChunkFree(ctrl->welcome);
1.25      frystyk  1136:        if (ctrl->reply)
                   1137:            HTChunkFree(ctrl->reply);
1.23      frystyk  1138:        free(ctrl);
1.1       timbl    1139:     }
1.23      frystyk  1140:     return status;
                   1141: }
1.1       timbl    1142: 
                   1143: 
1.23      frystyk  1144: /*                                                        HTFTP_parse_login
                   1145: **
                   1146: **     Scan 'login' part of URL for portnumber, uid and passwd. The
                   1147: **     expected format is [user[:password]@]host[:port]. The 'domain' field
                   1148: **     in the user structure is always filled out together with the
                   1149: **     serv_port. The rest is optional.
                   1150: **
                   1151: **     Returns YES if anything BUT the domain and serv_port is specified,
                   1152: **     else NO 
1.1       timbl    1153: */
1.23      frystyk  1154: PRIVATE BOOL HTFTP_parse_login ARGS3(char *, url, user_info *, user,
                   1155:                                     u_short *, serv_port)
                   1156: {
                   1157:     BOOL status = NO;
                   1158:     char *login = HTParse(url, "", PARSE_HOST);
                   1159:     char *host = strrchr(login, '@');
                   1160:     
                   1161:     if (host) {                                      /* Uid and/or passwd specified */
                   1162:        char *uid = login;
                   1163:        char *passwd;
                   1164:        FREE(user->id);                                   /* Skip old values */
                   1165:        FREE(user->passwd);
                   1166:        *host++ = '\0';
                   1167:        if ((passwd = strrchr(uid, ':')) != NULL) {      /* Passwd specified */
                   1168:            *passwd++ = '\0';    
                   1169:            if (passwd-1 > uid) {                /* Passwd AND uid specified */
                   1170:                StrAllocCopy(user->passwd, passwd);
                   1171:            }
                   1172:        }
                   1173:        StrAllocCopy(user->id, uid);
                   1174:        status = YES;
1.22      frystyk  1175:     } else {
1.23      frystyk  1176:        host = login;
1.22      frystyk  1177:     }
1.23      frystyk  1178:     {
                   1179:        char *portstr;
                   1180:        if ((portstr = strrchr(host, ':')) != NULL) {      /* Port specified */
                   1181:            char *endp = NULL;
                   1182:            *portstr++ = '\0';
                   1183:            *serv_port = (u_short) strtol(portstr, &endp, 10);
                   1184:            if (endp && *endp)   /* If portstr is not good, use default port */
                   1185:                *serv_port = (u_short) IPPORT_FTP;
                   1186:        }
1.22      frystyk  1187:     }
1.23      frystyk  1188:     StrAllocCopy(user->domain, host);                /* This is what's left */
                   1189:     free(login);
                   1190:     return status;
1.22      frystyk  1191: }
1.1       timbl    1192: 
                   1193: 
1.23      frystyk  1194: /*                                                        HTFTP_parse_datatype
1.1       timbl    1195: **
1.23      frystyk  1196: **     Scan 'ftptype' part of URL for the data type with parameters and
                   1197: **     returns the result in accordance with the FTP standard. If nothing is
                   1198: **     found then datatype = NULL.
1.1       timbl    1199: **
1.23      frystyk  1200: **     Returns YES if type is found, else NO 
1.1       timbl    1201: */
1.23      frystyk  1202: PRIVATE BOOL HTFTP_parse_datatype ARGS2(char *, url, char **, datatype)
1.1       timbl    1203: {
1.23      frystyk  1204:     BOOL retour = NO;
                   1205:     char *path = HTParse(url, "", PARSE_PATH);
                   1206:     char *type = strrchr(path, ';');
                   1207:     char dtype[6];
                   1208:     char *tptr = dtype;
                   1209:     
                   1210:     if (type && !strncasecomp(++type, "type=", 5)) {              /* type specified */
                   1211:        *tptr++ = toupper(*(type+5));               /* Look at the type-code */
                   1212:        if (*dtype == 'L') {                 /* We must look for a byte_size */
                   1213:            int cnt;
                   1214:            *tptr++ = ' ';
                   1215:            for (cnt=0; cnt<3 && *(type+6+cnt); cnt++)       /* Max 3 digits */
                   1216:                *tptr++ = *(type+6+cnt);
                   1217:        } else if (*dtype == 'A' || *dtype == 'E') {
                   1218:            *tptr++ = ' ';
                   1219:            *tptr++ = toupper(*(type+6));                   /* Get form-code */
                   1220:        }
                   1221:        *tptr = '\0';
                   1222:        StrAllocCopy(*datatype, dtype);
                   1223:        if (TRACE)
                   1224:            fprintf(stderr, "FTP......... Datatype found: `%s\'\n", *datatype);
                   1225:        retour = YES;
                   1226:     }
                   1227:     free(path);
                   1228:     return retour;
1.1       timbl    1229: }
                   1230: 
                   1231: 
1.23      frystyk  1232: /*                                                        HTFTP_init_con
1.1       timbl    1233: **
1.23      frystyk  1234: **     This function returns a control connection structure linked with a
                   1235: **     data connection structure. The control connection might already be
                   1236: **     open if HTFTPReuseCtrlCon == YES, but that is indicated in the state
                   1237: **     variable. ctrl->user->domain is always filled out but id and passwd
                   1238: **     are optional.
                   1239: **     If error, NULL is returned.
1.1       timbl    1240: */
1.29      frystyk  1241: PRIVATE ftp_ctrl_info *HTFTP_init_con ARGS2(HTRequest *, req, char *, url)
1.1       timbl    1242: {
1.23      frystyk  1243:     int status;
                   1244:     BOOL use_url = NO;    /* YES if uid and passwd are specified in the URL */
                   1245:     u_short serv_port = IPPORT_FTP;            /* Might be changed from URL */
1.1       timbl    1246:     
1.23      frystyk  1247:     ftp_ctrl_info *ctrl;
                   1248:     ftp_data_info *data;
                   1249:     user_info user;                /* Contains userid, passwd etc. from URL */
                   1250: 
                   1251:     if (!url || !*url) {
                   1252:        if (TRACE)
                   1253:            fprintf(stderr, "HTFTP_get_connection: Bad server address!\n");
                   1254:        return NULL;
                   1255:     }
                   1256: 
                   1257:     /* Initiate new data connection structure */
                   1258:     if ((data = (ftp_data_info *) calloc(1, sizeof(ftp_data_info))) == NULL)
                   1259:        outofmem(__FILE__, "HTFTP_get_ctrl_con");
                   1260:     data->socket = -1;                             /* Illigal socket number */
1.33    ! frystyk  1261:     data->passive = 0;                  /* We do the active open pr default */
1.23      frystyk  1262:     
                   1263:     /* Scan URL for uid, pw and portnumber */
1.25      frystyk  1264:     memset((void *) &user, '\0', sizeof(user_info));
1.23      frystyk  1265:     use_url = HTFTP_parse_login(url, &user, &serv_port);
                   1266: 
                   1267:     {
1.25      frystyk  1268:        char *filename = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
1.23      frystyk  1269:        char *strptr;
                   1270: 
                   1271:        /* Check if file name is a directory */
                   1272:        if (!*(strptr = filename) || 
                   1273:            *(strptr = filename+strlen(filename)-1) == '/') {
                   1274:            data->directory = YES;
                   1275:        } else {
                   1276:            /* If data type is not specified in URL let's find it ourselves. */
                   1277:            HTAtom *encoding;
                   1278:            HTAtom *language;
                   1279:            HTUnEscape(filename);
                   1280:            data->fileformat = HTFileFormat(filename, &encoding, &language);
                   1281:            if (HTFTP_parse_datatype(filename, &data->datatype) != YES) {
                   1282:                if ((encoding != HTAtom_for("8bit") &&
                   1283:                     encoding != HTAtom_for("7bit"))) {
                   1284:                    if (TRACE)
                   1285:                        fprintf(stderr, "FTP......... File is binary\n");
                   1286:                    StrAllocCopy(data->datatype, "I");
1.29      frystyk  1287:                    req->content_encoding = HTAtom_for("binary");
1.23      frystyk  1288:                }
                   1289:            } else {
                   1290:                /* Chop off data type */
                   1291:                strptr = strrchr(filename, ';');
                   1292:                *strptr = '\0';
                   1293:            }
                   1294:        }
                   1295:        FREE(filename);
                   1296:     }
                   1297: 
                   1298:     /* Look if control connection already exists else generate new one */
                   1299:     if (session) {
                   1300:        BOOL found = NO;
                   1301:        HTList *cur = session;
                   1302:        struct sockaddr_in sock_addr;
                   1303:        char *host;
                   1304:            
                   1305:        /* if theres an @ then use the stuff after it as a hostname */
                   1306:        {
                   1307:            char *fullhost = HTParse(url, "", PARSE_HOST);
                   1308:            char *at_sign;
                   1309:            if((at_sign = strchr(fullhost, '@')) != NULL)
                   1310:                host = at_sign+1;
                   1311:            else
                   1312:                host = fullhost;
                   1313:        }
                   1314:        
                   1315:        /* Set up defaults: */
1.25      frystyk  1316:        memset((void *) &sock_addr, '\0', sizeof(sock_addr));
1.23      frystyk  1317:        sock_addr.sin_family = AF_INET;
                   1318:        sock_addr.sin_port = htons(serv_port);
                   1319:        
                   1320:        /* Get node name */
                   1321:        if (HTParseInet(&sock_addr, host)) {
                   1322:            if (TRACE) fprintf(stderr,
                   1323:                               "FTP......... Can't locate remote host `%s\'\n", host);
                   1324:            FREE(user.domain);
                   1325:            FREE(user.id);
                   1326:            FREE(user.passwd);
                   1327:            free(host);
                   1328:            return NULL;
                   1329:        }
                   1330:        {
                   1331:            /* Check if host, port and user info is the same */
                   1332:            u_long new_node = ntohl(sock_addr.sin_addr.s_addr);
                   1333:            while ((ctrl = (ftp_ctrl_info *) HTList_nextObject(cur))) {
                   1334:                if (new_node==ctrl->serv_node && serv_port==ctrl->serv_port) {
                   1335:                    if ((user.id && strcmp(user.id, ctrl->user->id)) ||
                   1336:                        (user.passwd && strcmp(user.id, ctrl->user->passwd))) {
                   1337:                        found = NO;
                   1338:                    } else {
                   1339:                        found = YES;
                   1340:                        break;
                   1341:                    }
                   1342:                }
                   1343:            }
                   1344:        }
                   1345:        FREE(host);
                   1346:        if (found) {
                   1347:            if (TRACE) fprintf(stderr,
                   1348:                               "FTP......... Already have connection for %d.%d.%d.%d. on port %d, socket %d at location `%s\'\n",
                   1349:                               (int)*((unsigned char *)(&ctrl->serv_node)+0),
                   1350:                               (int)*((unsigned char *)(&ctrl->serv_node)+1),
                   1351:                               (int)*((unsigned char *)(&ctrl->serv_node)+2),
                   1352:                               (int)*((unsigned char *)(&ctrl->serv_node)+3),
                   1353:                               ctrl->serv_port,
                   1354:                               ctrl->socket,
                   1355:                               ctrl->location);
                   1356:            data->ctrl = ctrl;                         /* Link them together */
                   1357:            HTList_addObject(ctrl->data_cons, (void *) data); /* Add to list */
                   1358:            FREE(user.domain);
                   1359:            FREE(user.id);
                   1360:            FREE(user.passwd);
                   1361:            return ctrl;           /* This should return the found structure */
                   1362:        } else {
                   1363:            if (TRACE)
                   1364:                fprintf(stderr, "FTP......... No existing connection found, so I build a new\n");
                   1365:        }
                   1366:     }
1.1       timbl    1367:     
1.23      frystyk  1368:     /* Set up data structure for control connection */
                   1369:     if ((ctrl = (ftp_ctrl_info *) calloc(1, sizeof(ftp_ctrl_info))) == NULL ||
                   1370:        (ctrl->user = (user_info *) calloc(1, sizeof(user_info))) == NULL)
                   1371:        outofmem(__FILE__, "HTFTP_init_con");
                   1372:     ctrl->serv_port = serv_port;
                   1373:     ctrl->socket = -1;                             /* Illigal socket number */
                   1374:     StrAllocCopy(ctrl->location, "");                    /* Default is root */
                   1375:     ctrl->server = UNKNOWN;
                   1376:     ctrl->unsure_type = YES;
                   1377:     ctrl->use_list = NO;
1.31      frystyk  1378:     ctrl->state = FTP_IDLE;
1.23      frystyk  1379:     if (session)
                   1380:        HTList_addObject(session, (void *) ctrl);
                   1381:     data->ctrl = ctrl;                                /* Link them together */
                   1382:     ctrl->data_cons = HTList_new();
                   1383:     HTList_addObject(ctrl->data_cons, (void *) data);      /* First element */
                   1384: 
                   1385:     /* Initialize user info */
                   1386:     if (HTFTPUserInfo && !old_user) {   /* If first time */
                   1387:        if ((old_user = (user_info *) calloc(1, sizeof(user_info))) == NULL)
                   1388:            outofmem(__FILE__, "HTFTP_init_con");
                   1389:        StrAllocCopy(old_user->domain, "");/* Can't strcmp with NULL, can I? */
                   1390:     }
                   1391:     if (use_url) {
                   1392:        StrAllocCopy(ctrl->user->domain, user.domain);
                   1393:        StrAllocCopy(ctrl->user->id, user.id);
                   1394:        if (user.passwd) {
                   1395:            StrAllocCopy(ctrl->user->passwd, user.passwd);
                   1396:        }
                   1397:     } else if (HTFTPUserInfo && !strcmp(old_user->domain, user.domain)) {
                   1398:        StrAllocCopy(ctrl->user->domain, user.domain);
                   1399:        if (old_user->id) {
                   1400:            StrAllocCopy(ctrl->user->id, old_user->id);
                   1401:            if (old_user->passwd)
                   1402:                StrAllocCopy(ctrl->user->passwd, old_user->passwd);
                   1403:        }
                   1404:     } else {
                   1405:        char *uid = getenv("USER");
                   1406:        StrAllocCopy(ctrl->user->domain, user.domain);
                   1407:        StrAllocCopy(ctrl->user->id, "anonymous");
                   1408:        if (uid)
                   1409:            StrAllocCopy(ctrl->user->passwd, uid);
                   1410:        else
                   1411:            StrAllocCopy(ctrl->user->passwd, WWW_FTP_CLIENT);
                   1412:        StrAllocCat(ctrl->user->passwd, "@");
                   1413:     }
                   1414:     FREE(user.domain);
                   1415:     FREE(user.id);
                   1416:     FREE(user.passwd);
                   1417: 
                   1418:     /* Now get ready for a connect */
                   1419:     if (TRACE) fprintf(stderr, "FTP......... Looking for `%s\'\n", url);
1.33    ! frystyk  1420:     if ((status = HTDoConnect (req, url, serv_port, &ctrl->socket,
1.23      frystyk  1421:                               &ctrl->serv_node)) < 0)
                   1422:     {
                   1423:        if (TRACE)
                   1424:            fprintf(stderr, "HTFTP_init_con: Connection not established!\n");
                   1425:        HTFTP_abort_ctrl_con(ctrl);
                   1426:        return NULL;
                   1427:     }
                   1428:     ctrl->isoc = HTInputSocket_new(ctrl->socket);               /* buffering */
1.1       timbl    1429:     
1.23      frystyk  1430:     if (TRACE) 
                   1431:        fprintf(stderr, "FTP......... Control connected, socket %d\n",
                   1432:                ctrl->socket);
                   1433:     return ctrl;
                   1434: }
                   1435: 
                   1436: 
                   1437: #ifdef LISTEN
                   1438: /*     Open a master socket for listening on
                   1439: **     -------------------------------------
                   1440: **
                   1441: **     When data is transferred, we open a port, and wait for the server to
                   1442: **     connect with the data.
                   1443: **
                   1444: ** On entry,
                   1445: **     master_socket   Must be negative if not set up already.
                   1446: ** On exit,
                   1447: **     Returns         The listening data socket if OK
                   1448: **                     Less than zero if error.
                   1449: **     master_socket   is socket number if good, else negative.
                   1450: **     port_number     is valid if good.
                   1451: */
                   1452: PRIVATE BOOL get_listen_socket ARGS1(ftp_data_info *, data)
                   1453: {
                   1454:     struct sockaddr_in local_addr;                /* Binary network address */
                   1455:     int status = -1;
                   1456: 
                   1457: #ifdef REPEAT_LISTEN
                   1458:     if (master_socket >= 0) {                               /* Done already */
                   1459:        data->socket = master_socket;
                   1460:        if (TRACE)
                   1461:            fprintf(stderr, "FTP......... Reusing passive data socket: %d\n",
                   1462:                    data->socket);
                   1463:        return data->socket;
                   1464:     }
1.33    ! frystyk  1465: #endif /* REPEAT_LISTEN */
1.1       timbl    1466: 
1.23      frystyk  1467:     /* Create internet socket */
                   1468:     if ((data->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
                   1469:        return HTInetStatus("socket for ftp data");
                   1470:     if (TRACE)
1.32      frystyk  1471:        fprintf(stderr, "HTListen.... Created socket number %d\n",
1.23      frystyk  1472:                data->socket);
1.1       timbl    1473:     
1.23      frystyk  1474:     /* Search for a free port. */
1.25      frystyk  1475:     memset((void *) &local_addr, '\0', sizeof(local_addr));
1.23      frystyk  1476:     local_addr.sin_family = AF_INET;       /* Family = internet, host order */
                   1477:     local_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* For multi homed hosts */
                   1478: 
                   1479:     /* Inherit the local address information from the control socket */
                   1480:     {
                   1481:        int addr_size = sizeof(local_addr);
                   1482:        if (getsockname(data->ctrl->socket, (struct sockaddr *)
                   1483:                        &local_addr, &addr_size) < 0) {
                   1484:            status = HTInetStatus("getsockname");
                   1485:            goto errorfin;
                   1486:        }
                   1487:     }
                   1488: 
1.1       timbl    1489: #ifdef POLL_PORTS
                   1490:     {
                   1491:         unsigned short old_port_number = port_number;
1.23      frystyk  1492:        for (port_number=old_port_number+1;;port_number++) {
1.1       timbl    1493:            if (port_number > LAST_TCP_PORT)
                   1494:                port_number = FIRST_TCP_PORT;
                   1495:            if (port_number == old_port_number) {
1.23      frystyk  1496:                if (TRACE)
                   1497:                    fprintf(stderr, "FTP......... No data port available.\n");
                   1498:                goto errorfin;
1.1       timbl    1499:            }
1.23      frystyk  1500:            local_addr.sin_port = htons(port_number);
                   1501: 
                   1502:            /* The socket address is casted to generic sockaddr */
                   1503:            if (bind(data->socket, (struct sockaddr *) &local_addr,
                   1504:                     sizeof(local_addr)) == 0)
                   1505:                break;                               /* We have found a port */
                   1506:            status = HTInetStatus("bind");    /* else, what error did we get */
                   1507:        }
1.1       timbl    1508:     }
                   1509: #else
                   1510:     {
1.23      frystyk  1511:        local_addr.sin_port = 0;             /* Unspecified: please allocate */
                   1512: 
                   1513:        /* The socket address is casted to a generic address */
                   1514:        if (bind(data->socket, (struct sockaddr *) &local_addr,
                   1515:                 sizeof(local_addr)) < 0) {
                   1516:            status = HTInetStatus("bind");
                   1517:            goto errorfin;
                   1518:        }
                   1519:     }
1.33    ! frystyk  1520: #endif /* POLL_PORTS */
1.23      frystyk  1521:     /* Now we must find out who we are to tell the other guy. */
                   1522:     {
                   1523:        int addr_size = sizeof(local_addr);
                   1524:        if (getsockname(data->socket, (struct sockaddr *) &local_addr,
                   1525:                        &addr_size) < 0) {
                   1526:            status = HTInetStatus("getsockname");
                   1527:            goto errorfin;
                   1528:        }
                   1529:        if (TRACE) fprintf(stderr, "FTP......... This host is `%s\'\n",
                   1530:                           HTInetString(&local_addr));
                   1531:     }
                   1532:     if (TRACE) fprintf(stderr, "FTP......... Bound to port %d on %s\n",
                   1533:                       (int) ntohs(local_addr.sin_port),
                   1534:                       HTInetString(&local_addr));
                   1535: 
                   1536:     /* this_addr is a static global, we can refer to later */
                   1537:     if (!this_addr && (this_addr = (char *) malloc(24)) == NULL)
                   1538:        outofmem(__FILE__, "get_listen_socket");
                   1539:     {
                   1540:        u_long addr = ntohl(local_addr.sin_addr.s_addr);
                   1541:        u_short port = ntohs(local_addr.sin_port);
                   1542:        sprintf(this_addr, "%d,%d,%d,%d,%d,%d",
                   1543:                (int)*((unsigned char *)(&addr)+0),
                   1544:                (int)*((unsigned char *)(&addr)+1),
                   1545:                (int)*((unsigned char *)(&addr)+2),
                   1546:                (int)*((unsigned char *)(&addr)+3),
                   1547:                (int)*((unsigned char *)(&port)+0),
                   1548:                (int)*((unsigned char *)(&port)+1));
                   1549:     }
                   1550: 
                   1551:     /* Inform TCP that we will accept connections. Backlog is 1 as we only
                   1552:        want (and expect) one connection. If a 3rd host makes a connect
                   1553:        to this port, we have problems! */
                   1554:     if (listen(data->socket, 1) < 0) {
                   1555:        status = HTInetStatus("listen");
                   1556:        goto errorfin;
                   1557:     }
                   1558:     if (TRACE) fprintf(stderr,
                   1559:                       "FTP......... Data socket number %d listening\n",
                   1560:                       data->socket);
1.1       timbl    1561: 
                   1562: #ifdef REPEAT_LISTEN
1.23      frystyk  1563:     master_socket = data->socket;                   /* Update master_socket */
1.33    ! frystyk  1564: #endif /* REPEAT_LISTEN */
1.23      frystyk  1565:     return data->socket;                                            /* Good */
                   1566: 
                   1567:   errorfin:
                   1568:     NETCLOSE(data->socket);
                   1569:     data->socket = -1;
                   1570:     return -1;
                   1571: }
1.33    ! frystyk  1572: #endif /* LISTEN */
1.23      frystyk  1573: 
                   1574: 
                   1575: /*                                                             HTFTP_login
                   1576: **
                   1577: **     This function makes a login to a ftp-server. It takes the user name
                   1578: **     and passwd specified in ctrl->user and if that fails or an additional
                   1579: **     account is needed, the user is prompted. As it is difficult, when
                   1580: **     the server sends it's welcome message, we receive them all and choose
                   1581: **     the longest.
                   1582: **
                   1583: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
1.1       timbl    1584: */
1.23      frystyk  1585: PRIVATE int HTFTP_login ARGS1(ftp_ctrl_info *, ctrl)
                   1586: {
                   1587:     enum _state {
                   1588:        ERROR  = -2,
                   1589:        FAILURE = -1,
                   1590:        SUCCESS = 0,
                   1591:        BEGIN,
                   1592:        SENT_UID,
                   1593:        SENT_PASSWD,
                   1594:        NEED_USER_INFO,
                   1595:        NEED_PASSWD,
                   1596:        NEED_ACCOUNT,
                   1597:        SENT_ACCOUNT
                   1598:     } state = BEGIN;
                   1599:     BOOL asked = YES;              /* Have we already asked for uid/passwd? */
1.25      frystyk  1600:     int status = HTFTP_get_response(ctrl, &ctrl->reply);     /* Get greeting */
1.23      frystyk  1601:     if (status < 0) {
                   1602:         if (TRACE) fprintf (stderr, "FTP......... Interrupted at beginning of login.\n");
                   1603:        return ERROR;
1.25      frystyk  1604:     } else
                   1605:        HTFTPAddWelcome(ctrl);
1.23      frystyk  1606: 
                   1607:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
                   1608:     while (state > 0) {
                   1609:        switch (state) {
                   1610:          case BEGIN:
                   1611:            if (!HTFTP_send_cmd(ctrl, "USER", ctrl->user->id))
                   1612:                state = SENT_UID;
                   1613:            else
                   1614:                state = ERROR;
                   1615:            break;
                   1616: 
                   1617:          case SENT_UID:
1.25      frystyk  1618:            status = HTFTP_get_response(ctrl, &ctrl->reply);
                   1619:            if (status/100 == 2) {                  /* Logged in w/o passwd! */
1.28      frystyk  1620:                HTFTPAddWelcome(ctrl);
1.23      frystyk  1621:                state = SUCCESS;
1.28      frystyk  1622:            } else if (status/100 == 3) {               /* Password demanded */
                   1623:                HTFTPAddWelcome(ctrl);
1.23      frystyk  1624:                state = NEED_PASSWD;
1.28      frystyk  1625:            } else if (status == 530 && asked == YES)
1.23      frystyk  1626:                state = NEED_USER_INFO;                      /* User unknown */
                   1627:            else if (status/100 == 4)
                   1628:                state = FAILURE;
                   1629:            else
                   1630:                state = ERROR;
                   1631:            break;
1.1       timbl    1632: 
1.23      frystyk  1633:          case NEED_PASSWD:
                   1634:            if (!ctrl->user->passwd) {                  /* Got to ask for it */
                   1635:                char *prompt = NULL;
                   1636:                StrAllocCopy(prompt, "Enter password for user: ");
                   1637:                StrAllocCat(prompt, ctrl->user->id);
                   1638:                StrAllocCat(prompt, "@");
                   1639:                StrAllocCat(prompt, ctrl->user->domain);
                   1640:                StrAllocCat(prompt, ": ");
                   1641:                if ((ctrl->user->passwd = HTPromptPassword(prompt)) == NULL) {
                   1642:                    state = ERROR;
                   1643:                    free(prompt);
                   1644:                    break;
                   1645:                }
                   1646:                free(prompt);
                   1647:            }
                   1648:            /* If userid = "anonymous" then make sure that there is a '@' at
                   1649:               the end of the passwd */
                   1650:            if (!strcasecomp(ctrl->user->id, "anonymous")) {
                   1651:                if (*(ctrl->user->passwd+strlen(ctrl->user->passwd)-1) != '@')
                   1652:                    StrAllocCat(ctrl->user->passwd, "@");
                   1653:            }
                   1654:            if (!HTFTP_send_cmd(ctrl, "PASS", ctrl->user->passwd))
                   1655:                state = SENT_PASSWD;
                   1656:            else
                   1657:                state = ERROR;
                   1658:            break;
1.1       timbl    1659: 
1.23      frystyk  1660:          case SENT_PASSWD:
1.25      frystyk  1661:            status = HTFTP_get_response(ctrl, &ctrl->reply);
                   1662:            if (status/100 == 2) {                  /* Logged in with passwd */
                   1663:                HTFTPAddWelcome(ctrl);
1.23      frystyk  1664:                state = SUCCESS;
1.28      frystyk  1665:            } else if (status/100 == 3) {                /* Account demanded */
                   1666:                HTFTPAddWelcome(ctrl);
1.23      frystyk  1667:                state = NEED_ACCOUNT;
1.28      frystyk  1668:            } else if (status == 530 && asked == YES)
1.23      frystyk  1669:                state = NEED_USER_INFO;                      /* User unknown */
                   1670:            else if (status/100 == 4)
                   1671:                state = FAILURE;
                   1672:            else
                   1673:                state = ERROR;
                   1674:            break;
                   1675:            
                   1676:          case NEED_ACCOUNT:
                   1677:            {
                   1678:                char *prompt = NULL;
                   1679:                char *account = NULL;
                   1680:                StrAllocCopy(prompt, "Enter account for user: ");
                   1681:                StrAllocCat(prompt, ctrl->user->domain);
                   1682:                StrAllocCat(prompt, "@");
                   1683:                StrAllocCat(prompt, ctrl->user->id);
                   1684:                if ((account = HTPrompt(prompt, NULL)) != NULL &&
                   1685:                    !HTFTP_send_cmd(ctrl, "ACCT", account)) {
                   1686:                    state = SENT_ACCOUNT;
                   1687:                } else {
                   1688:                    state = ERROR;
                   1689:                }
                   1690:                free(prompt);
                   1691:                free(account);
                   1692:            }
                   1693:            break;
1.1       timbl    1694: 
1.23      frystyk  1695:          case SENT_ACCOUNT:
1.25      frystyk  1696:            status = HTFTP_get_response(ctrl, &ctrl->reply);
                   1697:            if (status/100 == 2) {
                   1698:                HTFTPAddWelcome(ctrl);
1.23      frystyk  1699:                state = SUCCESS;
1.25      frystyk  1700:            } else if (status/100 == 4)
1.23      frystyk  1701:                state = FAILURE;
                   1702:            else
                   1703:                state = ERROR;
                   1704:            break;
                   1705:            
                   1706:          case NEED_USER_INFO:
                   1707:            {
                   1708:                char *prompt = NULL;
                   1709:                StrAllocCopy(prompt, "Enter username and password for: ");
                   1710:                StrAllocCat(prompt, ctrl->user->domain);
                   1711:                FREE(ctrl->user->id);
                   1712:                FREE(ctrl->user->passwd);
                   1713:                HTPromptUsernameAndPassword(prompt, &ctrl->user->id,
                   1714:                                             &ctrl->user->passwd);
                   1715:                if (ctrl->user->id && ctrl->user->passwd &&
                   1716:                    !HTFTP_send_cmd(ctrl, "USER", ctrl->user->id))
                   1717:                    state = SENT_UID;
                   1718:                else
                   1719:                    state = ERROR;
                   1720:                free(prompt);
                   1721:            }
                   1722:            asked = NO;
                   1723:            break;
1.1       timbl    1724: 
1.23      frystyk  1725:          case FAILURE:                       /* Otherwise gcc complains :-( */
                   1726:          case ERROR:
                   1727:          case SUCCESS:
                   1728:            break;
                   1729:        } /* end of switch */
                   1730:     }
                   1731:     if (state == SUCCESS) {
                   1732:        if (TRACE)
                   1733:            fprintf(stderr, "FTP......... Logged in at `%s\' as `%s\'\n",
                   1734:                    ctrl->user->domain, ctrl->user->id);
                   1735:     }
1.1       timbl    1736: 
1.23      frystyk  1737:     /* This is a real pain this reuse user stuff :-( */
                   1738:     if (HTFTPUserInfo) {
                   1739:        StrAllocCopy(old_user->domain, ctrl->user->domain);
                   1740:        StrAllocCopy(old_user->id, ctrl->user->id);
                   1741:        StrAllocCopy(old_user->passwd, ctrl->user->passwd);
                   1742:     }
                   1743:     return state;
                   1744: }
1.1       timbl    1745: 
1.23      frystyk  1746: /*                                                             HTFTP_logout
1.1       timbl    1747: **
1.23      frystyk  1748: **     This function logs out from a ftp-server.
                   1749: **
                   1750: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
1.1       timbl    1751: */
1.23      frystyk  1752: PRIVATE int HTFTP_logout ARGS1(ftp_ctrl_info *, ctrl)
                   1753: {
                   1754:     enum _state {
                   1755:        ERROR  = -2,
                   1756:        FAILURE = -1,
                   1757:        SUCCESS = 0,
                   1758:        BEGIN,
                   1759:        SENT_QUIT
                   1760:     } state = BEGIN;
                   1761:     int status;
                   1762: 
                   1763:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
                   1764:     while (state > 0) {
                   1765:        switch (state) {
                   1766:          case BEGIN:
                   1767:            if (!HTFTP_send_cmd(ctrl, "QUIT", NULL))
                   1768:                state = SENT_QUIT;
                   1769:            else
                   1770:                state = ERROR;
                   1771:            break;
                   1772: 
                   1773:          case SENT_QUIT:
1.28      frystyk  1774:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  1775:            if (status/100 == 2)
                   1776:                state = SUCCESS;
                   1777:            else if (status/100 == 4)
                   1778:                state = FAILURE;
                   1779:            else
                   1780:                state = ERROR;
                   1781:            break;
                   1782: 
                   1783:          case FAILURE:                       /* Otherwise gcc complains :-( */
                   1784:          case ERROR:
                   1785:          case SUCCESS:
                   1786:            break;
                   1787:        }
                   1788:     }
                   1789:     return state;
                   1790: }
                   1791: 
1.22      frystyk  1792: 
1.23      frystyk  1793: /*                                                         HTFTP_get_data_con
                   1794:  **
                   1795:  **            Gets a valid data connection to the server and initializes the
                   1796:  **    transfer mode.
                   1797:  **
                   1798:  **    Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
                   1799:  */
1.33    ! frystyk  1800: PRIVATE int HTFTP_get_data_con ARGS3(HTRequest *, request,
        !          1801:                                     ftp_data_info *, data, char *, url)
1.1       timbl    1802: {
1.23      frystyk  1803:     enum _state {
                   1804:        ERROR = -2,
                   1805:        FAILURE = -1,
                   1806:        SUCCESS = 0,
                   1807:        BEGIN,
                   1808:        SENT_TYPE,
                   1809:        SENT_PASV,
                   1810:        SENT_PORT,
1.33    ! frystyk  1811:        NEED_ACTIVE,             /* We are the active ones in the connection */
        !          1812:        NEED_PASSIVE                                       /* We are passive */
1.23      frystyk  1813:     } state = BEGIN;
                   1814:     int serv_port;
1.1       timbl    1815:     int status;
1.28      frystyk  1816:     ftp_ctrl_info *ctrl = data->ctrl;
1.1       timbl    1817:     
1.23      frystyk  1818:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
                   1819:     while (state > 0) {
                   1820:        switch (state) {
                   1821:          case BEGIN:
1.33    ! frystyk  1822: 
1.23      frystyk  1823:            /* First check if it is necessary to send TYPE, else send PASV */
                   1824:            if (data->datatype) {
1.28      frystyk  1825:                if (!HTFTP_send_cmd(ctrl, "TYPE", data->datatype))
1.23      frystyk  1826:                    state = SENT_TYPE;
                   1827:                else
                   1828:                    state = ERROR;
                   1829:            } else {
1.28      frystyk  1830:                if (!HTFTP_send_cmd(ctrl, "PASV", NULL))
1.23      frystyk  1831:                    state = SENT_PASV;
                   1832:                else
                   1833:                    state = ERROR;
                   1834:            }
                   1835:            break;
                   1836:            
                   1837:          case SENT_PASV:
                   1838:            {
1.28      frystyk  1839:                status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  1840:                if (status == 227) {                
                   1841:                    /* If succes, we have to scan for the returned port number.
                   1842:                       However, the format for the response is not standard, so
                   1843:                       the best thing to do is to scan for the first digit
                   1844:                       after the status code, see RFC1123 */
                   1845:                    char *portstr;
                   1846:                    int h0, h1, h2, h3, p0, p1;
1.28      frystyk  1847:                    portstr = ctrl->reply->data+3;
1.23      frystyk  1848:                    while (*portstr && !isdigit(*portstr++));
                   1849:                    if (!*portstr || sscanf(--portstr, "%d,%d,%d,%d,%d,%d",
                   1850:                                            &h0, &h1, &h2, &h3, &p0, &p1)<4) {
                   1851:                        if (TRACE) fprintf(stderr,
                   1852:                            "FTP......... PASV reply has no inet address!\n");
                   1853:                        state = ERROR;
                   1854:                    } else {
                   1855:                        serv_port = (p0<<8)+p1;
                   1856:                        state = NEED_ACTIVE;
                   1857:                    }
                   1858:                } else if (status/100 == 4)
                   1859:                    state = FAILURE;
                   1860:                else if (state == 530)                   /* Not logged in??? */
                   1861:                    state = ERROR;
                   1862:                else
                   1863:                    state = NEED_PASSIVE;      /* If error, try PORT instead */
                   1864:            }
                   1865:            break;
1.22      frystyk  1866: 
1.23      frystyk  1867:          case NEED_ACTIVE:
                   1868:            /* Now get ready for a connect */
                   1869:            if (TRACE) fprintf(stderr,
                   1870:                               "FTP......... Server is listening on port %d\n",
                   1871:                               serv_port);
1.32      frystyk  1872:            
                   1873:            /* Got to strip any host port indication as this is for the control
                   1874:               connection. Thanks to ramey@jello.csc.ti.com (Joe Ramey) */
                   1875:            {
                   1876:                char *host=HTParse(url, "",
                   1877:                                   PARSE_ACCESS+PARSE_HOST+PARSE_PUNCTUATION);
                   1878:                char *portptr = strrchr(host, ':');
                   1879:                if (portptr && strncmp(portptr, "://", 3))
                   1880:                    *portptr = '\0';
1.33    ! frystyk  1881:                status = HTDoConnect(request, host, serv_port,
1.32      frystyk  1882:                                     &data->socket, (u_long *) NULL);
                   1883:                free(host);
                   1884:            }
1.33    ! frystyk  1885:            if (status < 0) {
1.23      frystyk  1886:                if (TRACE) fprintf(stderr,
                   1887:                                   "FTP......... Data connection failed using PASV, let's try PORT instead\n");
1.33    ! frystyk  1888:                HTErrorIgnore(request);      /* Don't generate error message */
1.23      frystyk  1889:                state = NEED_PASSIVE;
                   1890:            } else if (status >= 0) {
                   1891:                if (TRACE) fprintf(stderr, "FTP......... Data connected using PASV, socket %d\n", data->socket);
                   1892:                state = SUCCESS;
                   1893:            } else {
                   1894:                state = ERROR;                                /* Interrupted */
                   1895:            }
                   1896:            break;
1.22      frystyk  1897: 
1.23      frystyk  1898:          case NEED_PASSIVE:
                   1899: #ifdef LISTEN
                   1900:            /* The server didn't accept our PASV so now we try ourselves to be
                   1901:               passive using PORT */
                   1902:            if (get_listen_socket(data) < 0 ||
1.28      frystyk  1903:                HTFTP_send_cmd(ctrl, "PORT", this_addr))
1.23      frystyk  1904:                state = ERROR;
                   1905:            else
                   1906:                state = SENT_PORT;
                   1907: #else
                   1908:            /* If PORT is not compiled, then there is nothing we can do! */
                   1909:            if (TRACE) fprintf(stderr, "FTP......... PORT is not possible!\n");
                   1910:            state = ERROR;
                   1911: #endif
                   1912:            break;
                   1913: 
                   1914:          case SENT_PORT:
1.28      frystyk  1915:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  1916:            if (status/100 == 2) {
1.33    ! frystyk  1917:                data->passive = 1;
1.23      frystyk  1918:                state = SUCCESS;
                   1919:            } else if (status/100 == 4)
                   1920:                state = FAILURE;
                   1921:            else
                   1922:                state = ERROR;
                   1923:            break;
1.1       timbl    1924: 
1.23      frystyk  1925:          case SENT_TYPE:
1.28      frystyk  1926:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  1927:            /* If OK, then tell the server to be passive */
                   1928:            if (status/100 == 2) {
1.28      frystyk  1929:                if (!HTFTP_send_cmd(ctrl, "PASV", NULL))
1.23      frystyk  1930:                    state = SENT_PASV;
                   1931:                else
                   1932:                    state = ERROR;
                   1933:            } else if (status/100 == 4)
                   1934:                state = FAILURE;
                   1935:            else
                   1936:                state = ERROR;
                   1937:            break;
                   1938:                        
                   1939:          case ERROR:                         /* Otherwise gcc complains :-( */
                   1940:          case FAILURE:
                   1941:          case SUCCESS:
                   1942:            break;
1.22      frystyk  1943:        }
1.23      frystyk  1944:     }
                   1945:     return state;
                   1946: }
                   1947: 
                   1948: 
1.33    ! frystyk  1949: #ifdef LISTEN
        !          1950: /*                                                       HTFTP_switch_to_port
        !          1951: **
        !          1952: **     This function changes the current data connection from being PASV to
        !          1953: **     PORT. This is to handle servers that doesn't tell if they can handle
        !          1954: **     a PASV data connection before very late in the state diagram.
        !          1955: **
        !          1956: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
        !          1957: */
        !          1958: PRIVATE int HTFTP_switch_to_port ARGS2(ftp_data_info *, data,
        !          1959:                                       HTRequest *,     req)
        !          1960: {
        !          1961:     enum _state {
        !          1962:        ERROR = -2,
        !          1963:        SUCCESS = 0,
        !          1964:     } state = ERROR;
        !          1965:     int status;
        !          1966:     ftp_ctrl_info *ctrl = data->ctrl;
        !          1967: 
        !          1968:     if (data->passive) {
        !          1969:        if (TRACE)
        !          1970:            fprintf(stderr, "FTP Switch.. We are already passive, so PORT won't help :-(\n");
        !          1971:        return state;
        !          1972:     }
        !          1973: 
        !          1974:     if (TRACE)
        !          1975:        fprintf(stderr, "FTP Switch.. Closing PASV data connection number %d, and try to reopen it on the fly using PORT\n",
        !          1976:                data->socket);
        !          1977:     if ((status = NETCLOSE(data->socket)) < 0) {
        !          1978:        HTInetStatus("close data socket");
        !          1979:     } else
        !          1980:        data->socket = -1;         /* Invalid socket */
        !          1981:     
        !          1982:     /* Now get new data connection using PORT */
        !          1983:     if (status >= 0 && get_listen_socket(data) >= 0 &&
        !          1984:        !HTFTP_send_cmd(ctrl, "PORT", this_addr)) {
        !          1985:        status = HTFTP_get_response(ctrl,&ctrl->reply);
        !          1986:        if (status/100 == 2) {
        !          1987:            data->passive = 1;
        !          1988:            state = SUCCESS;
        !          1989:        }
        !          1990:     }
        !          1991:     return state;
        !          1992: }
        !          1993: #endif /* LISTEN */
        !          1994: 
        !          1995: 
        !          1996: /*                                                       HTFTP_look_for_data
        !          1997: **
        !          1998: **     This function puts up a select on the data connetcion and the control
        !          1999: **     connection in order to find out on which one data is transmitted.
        !          2000: **
        !          2001: **     Returns -1 on ERROR, 0 if data-connection, 1 if control connection.
        !          2002: */
        !          2003: PRIVATE int HTFTP_look_for_data ARGS2(HTRequest *,     request,
        !          2004:                                      ftp_data_info *,  data)
        !          2005: {
        !          2006:     int status = -1;
        !          2007:     fd_set read_socks;
        !          2008:     struct timeval max_wait;
        !          2009:     ftp_ctrl_info *ctrl = data->ctrl;
        !          2010:     int maxfdpl = HTMAX(data->socket, ctrl->socket) + 1;
        !          2011:     if (data->socket < 0 || ctrl->socket < 0) {
        !          2012:        if (TRACE)
        !          2013:            fprintf(stderr, "FTP Select.. Invalid socket\n");
        !          2014:        return -1;
        !          2015:     }
        !          2016: 
        !          2017:     /* Initialize the set of sockets */
        !          2018:     FD_ZERO(&read_socks);
        !          2019:     FD_SET(data->socket, &read_socks);       /* Turn on bit for data socket */
        !          2020:     FD_SET(ctrl->socket, &read_socks);    /* Turn on bit for control socket */
        !          2021: 
        !          2022:     /* Set up timer */
        !          2023:     if (HTFTPTimeOut <= 0)
        !          2024:        HTFTPTimeOut = FTP_DEFAULT_TIMEOUT;
        !          2025:     max_wait.tv_sec = HTFTPTimeOut/100;
        !          2026:     max_wait.tv_usec = (HTFTPTimeOut%100)*10000;
        !          2027: 
        !          2028:     /* This sleep is necessary as data is usually arriving more slow on control
        !          2029:        connection than on data connection. Even on an error, the data socket 
        !          2030:        might indicate that data is ready, even though they are not :-( */
        !          2031:     sleep(1);
        !          2032: 
        !          2033:     /* Now make the select */
        !          2034:     if ((status = select(maxfdpl, &read_socks, (fd_set *) NULL,
        !          2035:                         (fd_set *) NULL, &max_wait)) < 0)
        !          2036:        HTInetStatus("select");
        !          2037:     else if (!status) {
        !          2038:        if (TRACE) fprintf(stderr, "FTP Select.. Connections timed out\n");
        !          2039:        status = -1;
        !          2040:     } else if (status > 1) {
        !          2041:        if (TRACE) fprintf(stderr, "FTP Select.. Both data connetion and control data connection has data, let's grab the control\n");
        !          2042:        status = 1;
        !          2043:     } else if (FD_ISSET(data->socket, &read_socks)) {
        !          2044:        if (TRACE)
        !          2045:            fprintf(stderr, "FTP Select.. Data connection %d ready\n",
        !          2046:                    data->socket);
        !          2047:        status = 0;
        !          2048:     } else if (FD_ISSET(ctrl->socket, &read_socks)) {
        !          2049:        if (TRACE)
        !          2050:            fprintf(stderr, "FTP Select.. Control connection %d ready\n",
        !          2051:                    ctrl->socket);
        !          2052:        status = 1;
        !          2053:     } else {
        !          2054:        if (TRACE)
        !          2055:            fprintf(stderr, "FTP Select.. Unknown socket returned\n");
        !          2056:        status = -1;
        !          2057:     }
        !          2058:     return status;
        !          2059: }
        !          2060: 
        !          2061: 
1.23      frystyk  2062: /*                                                          HTFTPServerInfo()
                   2063: **
                   2064: **     This function finds out what server we are talking to.
                   2065: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
                   2066: **
                   2067: **     Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for making
                   2068: **     his code available.
1.1       timbl    2069: */
1.23      frystyk  2070: PRIVATE int HTFTPServerInfo ARGS1(ftp_ctrl_info *, ctrl)
                   2071: {
                   2072:     enum _state {
                   2073:        ERROR  = -2,
                   2074:        FAILURE = -1,
                   2075:        SUCCESS = 0,
                   2076:        BEGIN,
                   2077:        SENT_SYST,
                   2078:        NEED_PWD,
                   2079:        SENT_PWD
                   2080:     } state = BEGIN;
                   2081:     int status;
                   2082: 
                   2083:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
                   2084:     while (state > 0) {
                   2085:        switch (state) {
                   2086:          case BEGIN:
                   2087:            if (!HTFTP_send_cmd(ctrl, "SYST", NULL))
                   2088:                state = SENT_SYST;
                   2089:            else
                   2090:                state = ERROR;
                   2091:            break;
                   2092:            
                   2093:          case SENT_SYST:
                   2094:            {
                   2095:                char *info;
1.28      frystyk  2096:                status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2097:                if (status/100 != 2) {
                   2098:                    state = NEED_PWD;
                   2099:                    break;
                   2100:                }
                   2101: 
                   2102:                /* Got a line - what kind of server are we talking to? */
1.28      frystyk  2103:                info = ctrl->reply->data+3;            /* Skip response code */
1.23      frystyk  2104:                while (*info && *info++ == ' ');
                   2105:                if (!*info) {
                   2106:                    if (TRACE)
                   2107:                        fprintf(stderr, "FTP......... No server info?\n");
                   2108:                    state = NEED_PWD;
                   2109:                    break;
                   2110:                }
                   2111:                --info;
                   2112:                if (strncmp(info, "UNIX Type: L8MAC-OSMachTen", 28) == 0) {
                   2113:                    ctrl->server = MACHTEN_SERVER;
                   2114:                    ctrl->use_list = YES;
                   2115:                    ctrl->unsure_type = NO;
                   2116:                } else if (strstr(info, "UNIX") != NULL) {
                   2117:                    ctrl->server = UNIX_SERVER;
                   2118:                    ctrl->use_list = YES;
                   2119:                    ctrl->unsure_type = NO;
                   2120:                } else if (strncmp(info, "VMS", 3) == 0) {
                   2121:                    ctrl->server = VMS_SERVER;
                   2122:                    ctrl->use_list = YES;
                   2123:                    ctrl->unsure_type = NO;
                   2124:                } else if ((strncmp(info, "VM/CMS", 6) == 0) ||
                   2125:                           (strncmp(info, "VM", 2) == 0)) {
                   2126:                    ctrl->server = CMS_SERVER;
                   2127:                    ctrl->unsure_type = NO;
                   2128:                } else if (strncmp(info, "DCTS", 4) == 0) {
                   2129:                    ctrl->server = DCTS_SERVER;
                   2130:                    ctrl->unsure_type = NO;
                   2131:                } else if (strstr(info, "MAC-OS TCP/ConnectII") != NULL) {
                   2132:                    ctrl->server = TCPC_SERVER;
                   2133:                    /* Check old versions of TCP/C using / in pathnames */
                   2134:                    ctrl->unsure_type = YES;
                   2135:                } else if (strncmp(info, "MACOS Peter's Server", 20) == 0) {
                   2136:                    ctrl->server = PETER_LEWIS_SERVER;
                   2137:                    ctrl->use_list = YES;
                   2138:                    ctrl->unsure_type = NO;
1.32      frystyk  2139:                } else if (strncmp(info, "Windows_NT", 10) == 0) {
                   2140:                    ctrl->server = WINDOWS_NT;
                   2141:                    ctrl->use_list = YES;
                   2142:                    ctrl->unsure_type = NO;
1.23      frystyk  2143:                }
                   2144:                
                   2145:                /* If we are unsure, try PWD to get more information */
                   2146:                if (ctrl->server == UNKNOWN || ctrl->unsure_type == YES)
                   2147:                    state = NEED_PWD;
                   2148:                else
                   2149:                    state = SUCCESS;
1.1       timbl    2150:            }
1.23      frystyk  2151:            break;
                   2152: 
                   2153:          case NEED_PWD:
                   2154:            /* get the working directory (to see what it looks like) */
                   2155:            if (!HTFTP_send_cmd(ctrl, "PWD", NULL))
                   2156:                state = SENT_PWD;
                   2157:            else
                   2158:                state = ERROR;
                   2159:            break;
                   2160: 
                   2161:          case SENT_PWD:
                   2162:            {
1.28      frystyk  2163:                status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2164:                if (status/100 != 2)
                   2165:                    state = ERROR;
                   2166:                else {
                   2167:                    char *start, *end;
                   2168: 
                   2169:                    /* Now analyze response information between "'s */
1.28      frystyk  2170:                    if ((start = strchr(ctrl->reply->data, '"')) == NULL ||
1.23      frystyk  2171:                        (end = strchr(++start, '"')) == NULL) {
                   2172:                        if (TRACE)
                   2173:                            fprintf(stderr,
                   2174:                                    "FTP......... No current directory?\n");
                   2175:                        ctrl->server = GENERIC_SERVER;
                   2176:                    } else {
                   2177:                        *end = '\0';
                   2178:                        if (ctrl->server == TCPC_SERVER) {
                   2179:                            ctrl->server = *start == '/' ?
                   2180:                                NCSA_SERVER : TCPC_SERVER;
                   2181:                            ctrl->unsure_type = NO;
                   2182:                        } else if (*start == '/') {
                   2183:                            /* path names starting with / imply Unix, right? */
                   2184:                            ctrl->server = UNIX_SERVER;
                   2185:                            ctrl->use_list = YES;
                   2186:                        } else if (*(end-1) == ']') {
                   2187:                            /* path names ending with ] imply VMS, right? */
                   2188:                            ctrl->server = VMS_SERVER;
                   2189:                        } else
                   2190:                            ctrl->server = GENERIC_SERVER;
                   2191:                    }
                   2192:                    state = SUCCESS;
                   2193:                }
                   2194:            }               
                   2195:            break;
                   2196:            
                   2197:          case FAILURE:                       /* Otherwise gcc complains :-( */
                   2198:          case ERROR:
                   2199:          case SUCCESS:
                   2200:            break;
1.1       timbl    2201:        }
1.23      frystyk  2202:     }
                   2203:     if (TRACE) {
                   2204:        static char *servers[] = {
                   2205:            "UNKNOWN",
                   2206:            "GENERIC",
                   2207:            "MACHTEN",
                   2208:            "UNIX",
                   2209:            "VMS",
                   2210:            "CMS",
                   2211:            "DCTS",
                   2212:            "TCPC",
1.32      frystyk  2213:            "PETER LEWIS",
                   2214:            "NCSA",
                   2215:            "WINDOWS NT"
1.23      frystyk  2216:            };
                   2217:        if (ctrl->unsure_type == YES)
                   2218:            fprintf(stderr, "FTP......... This might be a %s server\n",
                   2219:                    *(servers+ctrl->server+1));
                   2220:        else
                   2221:            fprintf(stderr, "FTP......... We are talking to a %s server\n",
                   2222:                    *(servers+ctrl->server+1));
                   2223:     }
                   2224:     return state;
                   2225: }
                   2226: 
                   2227: 
                   2228: /*                                                          HTFTPLocation()
                   2229: **
                   2230: **     This function compares the current directory location in the
                   2231: **     ftp-session to the new URL and returns the relative position. If
                   2232: **     the new URL is at a higher location, the function performs CDUP's
                   2233: **     until this location is reached so that the relative position is '.'
                   2234: **
                   2235: **     Returns relative path name if OK, else current location
                   2236: **
1.1       timbl    2237: */
1.23      frystyk  2238: PRIVATE char *HTFTPLocation ARGS2(ftp_ctrl_info *, ctrl, char *, url)
                   2239: {
                   2240:     unsigned char getup = 0;
                   2241:     char *current;
                   2242:     char *new;
                   2243:     char *relative;
                   2244:     char *result = NULL;
                   2245:     char *strptr;
                   2246: 
                   2247:     /* Make a full URL out of current location */ 
                   2248:     current = HTParse(url, "", PARSE_ACCESS+PARSE_HOST+PARSE_PUNCTUATION);
                   2249:     StrAllocCat(current, "/");
                   2250:     StrAllocCat(current, ctrl->location);
                   2251:     if (*(current+strlen(current)-1) != '/')
                   2252:        StrAllocCat(current, "/");
                   2253: 
                   2254:     /* Make a temporary URL without any type indication */
                   2255:     new = HTParse(url, "", PARSE_ALL);
                   2256:     if ((strptr = strrchr(new, ';')) != NULL)
                   2257:        *strptr = '\0';
                   2258: 
                   2259:     /* Compare those two URLs */
                   2260:     relative = HTRelative(new, current);
                   2261:     {
                   2262:        /* Now send any CDUP if necessary */
                   2263:        char *tail = relative;
                   2264:        int status;
                   2265:        while (tail && (strptr = strstr(tail, "../")) != NULL) {
                   2266:            if (HTFTP_send_cmd(ctrl, "CDUP", NULL)) {
                   2267:                break;
                   2268:            }
1.28      frystyk  2269:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2270:            if (status/100 != 2) {
                   2271:                break;
1.1       timbl    2272:            }
1.23      frystyk  2273:            ++getup;
                   2274:            tail += 3;
                   2275:        }
                   2276:     }
                   2277: 
                   2278:     /* Now update current location if we have used CDUP and make relative 
                   2279:        return value. */
                   2280:     if (getup) {
                   2281:        char *location = HTParse(new, "", PARSE_PATH);
                   2282:        free(ctrl->location);
                   2283:        if (*location == '/')
                   2284:            ctrl->location = ++location;
                   2285:        else
                   2286:            ctrl->location = location;
                   2287:        if (*ctrl->location &&
                   2288:            *(ctrl->location+strlen(ctrl->location)-1) == '/')
                   2289:            *(ctrl->location+strlen(ctrl->location)-1) = '\0';
                   2290:        StrAllocCopy(result, "");
                   2291:        free(relative);
                   2292:     } else {
                   2293:        if (*relative == '/') {
                   2294:            StrAllocCopy(result, relative+1);
                   2295:            free(relative);
                   2296:        } else
                   2297:            result = relative;
1.25      frystyk  2298:        if (*relative && *(relative+strlen(relative)-1) == '/')
1.23      frystyk  2299:            *(relative+strlen(relative)-1) = '\0';
                   2300:     }
                   2301:     if (TRACE)
                   2302:        fprintf(stderr, "FTP......... current location on server: `%s\'\n",
                   2303:                ctrl->location);
                   2304:     free(current);
                   2305:     free(new);
                   2306:     return result;
                   2307: }
1.22      frystyk  2308: 
                   2309: 
1.23      frystyk  2310: /* ------------------------------------------------------------------------- */
                   2311: /*                   FTP Client Functions for receiving data                */
                   2312: /* ------------------------------------------------------------------------- */
1.1       timbl    2313: 
1.23      frystyk  2314: /*                                                             HTFTP_get_dir
                   2315: **
                   2316: **     This function asks for the directory specified and calls
                   2317: **     HTFTPBrowseDirectory. First we try in one go, but if that doesn't
                   2318: **     work, then we use CWD for each segment. The directory is searched
                   2319: **     relative to the login directory, that is without a starting '/'.
                   2320: **
                   2321: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
1.1       timbl    2322: */
1.33    ! frystyk  2323: PRIVATE int HTFTP_get_dir ARGS3(ftp_ctrl_info *, ctrl, HTRequest *, req,
        !          2324:                                char *, relative)
1.23      frystyk  2325: {
                   2326:     enum _state {
                   2327:        ERROR  = -2,
                   2328:        FAILURE = -1,
                   2329:        SUCCESS = 0,
                   2330:        BEGIN,
                   2331:        NEED_LIST,
                   2332:        SENT_LIST,
                   2333:        SENT_CWD,
                   2334:         MULTIPLE_CWD,
                   2335:        READY_FOR_DATA,
                   2336:        SENT_ABOR,
1.33    ! frystyk  2337:        WAIT_FOR_CONNECTION,
        !          2338:        HANDLE_CTRL,
1.23      frystyk  2339:        GOT_DATA
                   2340:     } state = BEGIN;
1.33    ! frystyk  2341:     BOOL handle_ctrl = NO;
1.23      frystyk  2342:     ftp_data_info *data = (ftp_data_info *) ctrl->data_cons->next->object; 
                   2343:     int status;
                   2344:     char *unescaped = NULL;
                   2345:     StrAllocCopy(unescaped, relative);
                   2346:     HTUnEscape(unescaped);
1.30      luotonen 2347:     HTCleanTelnetString(unescaped);    /* Prevent security holes */
1.23      frystyk  2348: 
                   2349:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
                   2350:     while (state > 0) {
                   2351:        switch (state) {
                   2352:          case BEGIN:
                   2353:            /* Only if the root directory is requested, we can use LIST right
                   2354:               away, else we must first use CWD */
                   2355:            if (!*unescaped || !strcmp(unescaped, "/"))
                   2356:                state = NEED_LIST;
                   2357:            else {
                   2358:                /* We first try to CWD to the location in one go. */
                   2359:                if (!HTFTP_send_cmd(ctrl, "CWD", unescaped))
                   2360:                    state = SENT_CWD;
                   2361:                else
                   2362:                    state = ERROR;
                   2363:            }
                   2364:            break;
                   2365: 
                   2366:          case NEED_LIST:
                   2367:            if (ctrl->use_list == YES) {
                   2368:                if (!HTFTP_send_cmd(ctrl, "LIST", NULL))
                   2369:                    state = SENT_LIST;
                   2370:                else
                   2371:                    state = ERROR;
                   2372:            } else {
                   2373:                if (!HTFTP_send_cmd(ctrl, "NLST", NULL))
                   2374:                    state = SENT_LIST;
                   2375:                else
                   2376:                    state = ERROR;
                   2377:            }
                   2378:            break;
                   2379: 
                   2380:          case SENT_LIST:
1.33    ! frystyk  2381: #ifdef SEQUENT
        !          2382:            
        !          2383:            /* If we are listening, do a non-blocking accept now, as the
1.23      frystyk  2384:               accept on some systems (DYNIX) completes the connection. On
                   2385:               BSD systems, the completion is done independently of the
                   2386:               accept. (thanks to Bill Rushka, wcr@aps.org) */
1.33    ! frystyk  2387:            if (data->passive == 1) {
1.23      frystyk  2388:                int newfd;
1.33    ! frystyk  2389:                if ((newfd = HTDoAccept(req, data->socket)) >= 0) {
1.23      frystyk  2390: #ifdef REPEAT_LISTEN
                   2391:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d stays open.\n", data->socket);
                   2392: #else
                   2393:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d closed.\n", data->socket);
                   2394:                    if (NETCLOSE(data->socket) < 0) {
                   2395:                        HTInetStatus("close");
                   2396:                        state = ERROR;
                   2397:                        break;
                   2398:                    }
1.22      frystyk  2399: #endif
1.23      frystyk  2400:                    data->socket = newfd;        /* Switch to new socket */
1.33    ! frystyk  2401:                    data->passive = 2;
1.23      frystyk  2402:                    if (TRACE)
                   2403:                        fprintf(stderr, "FTP......... New data socket: %d\n",
                   2404:                                data->socket);
                   2405:                } else {
1.31      frystyk  2406:                    HTChunkClear(ctrl->reply);
1.33    ! frystyk  2407:                    ctrl->reply = NULL;
1.23      frystyk  2408:                    state = ERROR;
                   2409:                    break;
                   2410:                }
1.1       timbl    2411:            }
1.33    ! frystyk  2412: #endif /* SEQUENT */
        !          2413: 
1.28      frystyk  2414:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33    ! frystyk  2415:            if (status == 150)                   /* About to open connection */
        !          2416:                state = WAIT_FOR_CONNECTION;
        !          2417:            else if (status == 125)                     /* Transfer starting */
1.23      frystyk  2418:                state = READY_FOR_DATA;
                   2419:            else if (status/100 == 4)
                   2420:                state = FAILURE;
                   2421:            else
                   2422:                state = ERROR;
                   2423:            break;
1.1       timbl    2424:            
1.23      frystyk  2425:          case SENT_CWD:
1.28      frystyk  2426:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2427:            if (status/100 == 2) {
                   2428:                /* Update current location */
                   2429:                if (*ctrl->location)
                   2430:                    StrAllocCat(ctrl->location, "/");
                   2431:                StrAllocCat(ctrl->location, relative);
1.28      frystyk  2432:                HTChunkClear(ctrl->welcome);
                   2433:                HTFTPAddWelcome(ctrl);
1.23      frystyk  2434:                state = NEED_LIST;
                   2435:            } else if (status/100 == 4)
                   2436:                state = FAILURE;
                   2437:            else
                   2438:                state = MULTIPLE_CWD;
                   2439:            break;
1.1       timbl    2440: 
1.23      frystyk  2441:          case MULTIPLE_CWD:
                   2442:            /* We must use the escaped version when looking for '/' as
                   2443:               delimiter between segments, and then unescape each segment */
                   2444:            if (TRACE) fprintf(stderr, "FTP......... Can't jump directly to location, try multiple CD's instead\n");
                   2445:            state = NEED_LIST;               /* This is overwritten if error */
                   2446:            {
                   2447:                char *path = NULL;
                   2448:                char *segment;
                   2449:                StrAllocCopy(path, relative);
                   2450:                segment = strtok(path, "/");
                   2451:                while (segment && *segment) {
                   2452:                    HTUnEscape(segment);
1.30      luotonen 2453:                    HTCleanTelnetString(segment);  /* Prevent security holes */
1.23      frystyk  2454:                    if (HTFTP_send_cmd(ctrl, "CWD", segment)) {
                   2455:                        state = ERROR;
                   2456:                        break;
                   2457:                    }
1.28      frystyk  2458:                    status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2459:                    if (status/100 != 2) {
                   2460:                        if (status/100 == 4)
                   2461:                            state = FAILURE;
                   2462:                        else
                   2463:                            state = ERROR;
                   2464:                        break;
                   2465:                    } else {                      /* Update current location */
                   2466:                        char *new_seg = HTEscape(segment, URL_XPALPHAS);
                   2467:                        if (*ctrl->location)
                   2468:                            StrAllocCat(ctrl->location, "/");
                   2469:                        StrAllocCat(ctrl->location, new_seg);
                   2470:                        free(new_seg);
1.28      frystyk  2471:                        HTChunkClear(ctrl->welcome);
                   2472:                        HTFTPAddWelcome(ctrl);
1.23      frystyk  2473:                    }
                   2474:                    segment = strtok(NULL, "/");           /* Get next token */
1.22      frystyk  2475:                }
1.23      frystyk  2476:                free(path);
1.22      frystyk  2477:            }
1.23      frystyk  2478:            break;
1.22      frystyk  2479: 
1.23      frystyk  2480:          case READY_FOR_DATA:
1.33    ! frystyk  2481:            if (data->passive == 1) {
        !          2482:                int newfd;
        !          2483:                if ((newfd = HTDoAccept(req, data->socket)) >= 0) {
        !          2484: #ifdef REPEAT_LISTEN
        !          2485:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d stays open.\n", data->socket);
        !          2486: #else
        !          2487:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d closed.\n", data->socket);
        !          2488:                    if (NETCLOSE(data->socket) < 0) {
        !          2489:                        HTInetStatus("close");
        !          2490:                        state = ERROR;
        !          2491:                        break;
        !          2492:                    }
        !          2493: #endif
        !          2494:                    data->socket = newfd;            /* Switch to new socket */
        !          2495:                    data->passive = 2;
        !          2496:                    if (TRACE)
        !          2497:                        fprintf(stderr, "FTP......... New data socket: %d\n",
        !          2498:                                data->socket);
        !          2499:                } else {
        !          2500:                    HTChunkClear(ctrl->reply);
        !          2501:                    ctrl->reply = NULL;
        !          2502:                    state = ERROR;
        !          2503:                    break;
        !          2504:                }
        !          2505:            }
        !          2506: 
1.23      frystyk  2507:            /* Now, the browsing module can be called */
                   2508:            {
                   2509:                char *url = HTAnchor_physical(req->anchor);
                   2510:                char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
                   2511:                HTUnEscape(path);
                   2512:                if (TRACE)
                   2513:                    fprintf(stderr, "FTP......... Receiving directory `%s\'\n",
                   2514:                            path);
                   2515:                status = HTFTPBrowseDirectory(req, path, data,
                   2516:                                              HTFTP_get_dir_string);
                   2517:                if (status == -1)
                   2518:                    state = ERROR;
                   2519:                else if (status == HT_INTERRUPTED) {
                   2520:                    if (!HTFTP_send_cmd(ctrl, "ABOR", NULL))
                   2521:                        state = SENT_ABOR;
                   2522:                    else
                   2523:                        state = ERROR;
                   2524:                } else
                   2525:                    state = GOT_DATA;
                   2526:                free(path);
1.22      frystyk  2527:            }
1.23      frystyk  2528:            break;
                   2529: 
                   2530:          case GOT_DATA:
1.33    ! frystyk  2531:            if (!handle_ctrl) {
        !          2532:                status = HTFTP_get_response(ctrl, &ctrl->reply);
        !          2533:                if (status/100 == 2)
        !          2534:                    state = SUCCESS;                       /* Directory read */
        !          2535:                else if (status/100 == 4)
        !          2536:                    state = FAILURE;
        !          2537:                else
        !          2538:                    state = ERROR;
        !          2539:            } else
        !          2540:                state = SUCCESS;
        !          2541:            break;
        !          2542: 
        !          2543:          case SENT_ABOR:
1.28      frystyk  2544:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2545:            if (status/100 == 2)
1.33    ! frystyk  2546:                state = SUCCESS;
1.23      frystyk  2547:            else if (status/100 == 4)
                   2548:                state = FAILURE;
                   2549:            else
                   2550:                state = ERROR;
                   2551:            break;
                   2552: 
1.33    ! frystyk  2553:          case WAIT_FOR_CONNECTION:
        !          2554:            /* Now we have to wait to see whether the FTP-server sends on the
        !          2555:               data port or the control port */
        !          2556:            status = HTFTP_look_for_data(req, data);
        !          2557:            if (!status)
        !          2558:                state = READY_FOR_DATA;
        !          2559:            else
        !          2560:                state = HANDLE_CTRL;
        !          2561:            break;
        !          2562: 
        !          2563:          case HANDLE_CTRL:
1.28      frystyk  2564:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33    ! frystyk  2565:            if (status/100 == 2) {
        !          2566:                state = READY_FOR_DATA;
        !          2567: #ifdef LISTEN
        !          2568:            } else if (status == 425) {    /* Connection could not be opened */
        !          2569:                if (HTFTP_switch_to_port(data, req))
        !          2570:                    state = ERROR;
        !          2571:                else
        !          2572:                    state = NEED_LIST;
        !          2573: #endif /* LISTEN */
        !          2574:            } else if (status/100 == 4)
1.23      frystyk  2575:                state = FAILURE;
1.22      frystyk  2576:            else
1.23      frystyk  2577:                state = ERROR;
1.33    ! frystyk  2578:            handle_ctrl = YES;
1.23      frystyk  2579:            break;
                   2580: 
                   2581:          case FAILURE:                       /* Otherwise gcc complains :-( */
                   2582:          case ERROR:
                   2583:          case SUCCESS:
                   2584:            break;
1.22      frystyk  2585:        }
1.23      frystyk  2586:     }
                   2587:     FREE(unescaped);
                   2588:     return state;
                   2589: }
                   2590: 
                   2591: 
                   2592: /*                                                             HTFTP_get_file
                   2593: **
                   2594: **     This function asks for the file specified. First we try in one go,
                   2595: **     but if that doesn't work, then we use CWD for each segment and then
                   2596: **     try to retrieve it. If that also fails, then we try if it is a
                   2597: **     directory. This procedure causes that directory links generated in
                   2598: **     HTFTPBrowseDirectory should have a '/' at the end in order to go
                   2599: **     directly to HTFTP_get_dir. The relative is searched relative to
                   2600: **     the login directory, that is without a starting '/'.
                   2601: **
                   2602: **     Returns -2 on ERROR, -1 on FAILURE, 0 on SUCCESS.
                   2603: */
1.33    ! frystyk  2604: PRIVATE int HTFTP_get_file ARGS3(ftp_ctrl_info *, ctrl, HTRequest *, req,
        !          2605:                                 char *, relative)
1.23      frystyk  2606: {
                   2607:     enum _state {
                   2608:        ERROR  = -2,
                   2609:        FAILURE = -1,
                   2610:        SUCCESS = 0,
                   2611:        BEGIN,
                   2612:        SENT_RETR,
                   2613:         MULTIPLE_CWD,
                   2614:        READY_FOR_DATA,
                   2615:        SENT_ABOR,
1.33    ! frystyk  2616:        WAIT_FOR_CONNECTION,
        !          2617:        HANDLE_CTRL,
1.23      frystyk  2618:        GOT_DATA
                   2619:     } state = BEGIN;
1.33    ! frystyk  2620:     BOOL handle_ctrl = NO;
1.23      frystyk  2621:     BOOL multiple = NO;                      /* Have we already tried multiple CWD? */
                   2622:     ftp_data_info *data = (ftp_data_info *) ctrl->data_cons->next->object; 
                   2623:     int status;
                   2624:     char *unescaped = NULL;
                   2625:     StrAllocCopy(unescaped, relative);
                   2626:     HTUnEscape(unescaped);
1.30      luotonen 2627:     HTCleanTelnetString(unescaped);  /* Prevent security holes */
1.23      frystyk  2628: 
1.33    ! frystyk  2629:     /* This loop only stops if state is ERROR, FAILURE or SUCCESS */
1.23      frystyk  2630:     while (state > 0) {
                   2631:        switch (state) {
                   2632:          case BEGIN:
                   2633:            /* First we try to retrieve the file in one go. */
                   2634:            if (!HTFTP_send_cmd(ctrl, "RETR", unescaped))
                   2635:                state = SENT_RETR;
                   2636:            else
                   2637:                state = ERROR;
                   2638:            break;
                   2639: 
                   2640:          case SENT_RETR:
1.33    ! frystyk  2641: #ifdef SEQUENT     
        !          2642:            /* If we are listening, do a non-blocking accept now, as the
1.23      frystyk  2643:               accept on some systems (DYNIX) completes the connection. On
                   2644:               BSD systems, the completion is done independently of the
                   2645:               accept. (thanks to Bill Rushka, wcr@aps.org) */
1.33    ! frystyk  2646:            if (data->passive == 1) {
1.23      frystyk  2647:                int newfd;
1.33    ! frystyk  2648:                if ((newfd = HTDoAccept(req, data->socket)) >= 0) {
1.23      frystyk  2649: #ifdef REPEAT_LISTEN
                   2650:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d stays open.\n", data->socket);
                   2651: #else
                   2652:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d closed.\n", data->socket);
                   2653:                    if (NETCLOSE(data->socket) < 0) {
                   2654:                        HTInetStatus("close");
                   2655:                        state = ERROR;
                   2656:                        break;
                   2657:                    }
1.22      frystyk  2658: #endif
1.33    ! frystyk  2659:                    data->socket = newfd;            /* Switch to new socket */
        !          2660:                    data->passive = 2;
1.23      frystyk  2661:                    if (TRACE)
                   2662:                        fprintf(stderr, "FTP......... New data socket: %d\n",
                   2663:                                data->socket);
                   2664:                } else {
1.31      frystyk  2665:                    HTChunkClear(ctrl->reply);
1.33    ! frystyk  2666:                    ctrl->reply = NULL;
1.23      frystyk  2667:                    state = ERROR;
                   2668:                    break;
                   2669:                }
                   2670:            }
1.33    ! frystyk  2671: #endif /* SEQUENT */
        !          2672: 
1.28      frystyk  2673:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33    ! frystyk  2674:            if (status == 150)                   /* About to open connection */
        !          2675:                state = WAIT_FOR_CONNECTION;
        !          2676:            else if (status == 125)
        !          2677:                state = READY_FOR_DATA;                 /* Transfer starting */
1.23      frystyk  2678:            else if (status/100 == 4)
                   2679:                state = FAILURE;
1.33    ! frystyk  2680: 
1.23      frystyk  2681:            /* If there is no '/' in unescaped, it won't help to try
                   2682:               multiple CWD's, as it either doesn't exist or is a directory */
                   2683:            else if (multiple == NO && strchr(unescaped, '/') != NULL)
                   2684:                state = MULTIPLE_CWD;
                   2685:            else {
                   2686:                data->directory = YES;
                   2687:                state = FAILURE;
                   2688:            }
                   2689:            break;
                   2690:            
                   2691:          case MULTIPLE_CWD:
                   2692:            /* We must use the escaped version when looking for '/' as
                   2693:               delimiter between segments, and then unescape each segment */
                   2694:            if (TRACE) fprintf(stderr, "FTP......... Can't jump directly to location, try multiple CD's instead\n");
                   2695:            multiple = YES;
                   2696:            {
                   2697:                char *path = NULL;
                   2698:                char *segment;
                   2699:                char *last_slash;       /* Used to identify the last segment */
                   2700:                StrAllocCopy(path, relative);
                   2701:                if ((last_slash = strrchr(path, '/')) == NULL)
                   2702:                    last_slash = path;
                   2703:                else
                   2704:                    last_slash++;
                   2705:                segment = strtok(path, "/");
                   2706:                while (segment && *segment && segment != last_slash) {
                   2707:                    HTUnEscape(segment);
1.30      luotonen 2708:                    HTCleanTelnetString(segment);  /* Prevent security holes */
1.23      frystyk  2709:                    if (HTFTP_send_cmd(ctrl, "CWD", segment)) {
                   2710:                        state = ERROR;
                   2711:                        break;
                   2712:                    }
1.28      frystyk  2713:                    status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2714:                    if (status/100 != 2) {
                   2715:                        if (status/100 == 4)
                   2716:                            state = FAILURE;
                   2717:                        else
                   2718:                            state = ERROR;
                   2719:                        break;
                   2720:                    } else {                      /* Update current location */
                   2721:                        char *new_seg = HTEscape(segment, URL_XPALPHAS);
                   2722:                        if (*ctrl->location)
                   2723:                            StrAllocCat(ctrl->location, "/");
                   2724:                        StrAllocCat(ctrl->location, new_seg);
                   2725:                        free(new_seg);
                   2726:                    }
                   2727:                    segment = strtok(NULL, "/");           /* Get next token */
                   2728:                }
                   2729:                /* Now try to retrieve the last segment */
                   2730:                if (segment == last_slash) {
                   2731:                    HTUnEscape(segment);
1.30      luotonen 2732:                    HTCleanTelnetString(segment);  /* Prevent security holes */
1.33    ! frystyk  2733:                    if (!HTFTP_send_cmd(ctrl, "RETR", segment)) {
        !          2734:                        StrAllocCopy(unescaped, segment);
1.23      frystyk  2735:                        state = SENT_RETR;
1.33    ! frystyk  2736:                    } else
1.23      frystyk  2737:                        state = ERROR;
                   2738:                } else {
                   2739:                    if (TRACE) fprintf(stderr, "FTP......... Strange error, filename not found?\n");
                   2740:                    state = ERROR;
                   2741:                }
                   2742:                free(path);
                   2743:            }
                   2744:            break;
                   2745: 
                   2746:          case READY_FOR_DATA:
1.33    ! frystyk  2747:            if (data->passive == 1) {
        !          2748:                int newfd;
        !          2749:                if ((newfd = HTDoAccept(req, data->socket)) >= 0) {
        !          2750: #ifdef REPEAT_LISTEN
        !          2751:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d stays open.\n", data->socket);
        !          2752: #else
        !          2753:                    if (TRACE) fprintf(stderr, "FTP......... Passive data channel number %d closed.\n", data->socket);
        !          2754:                    if (NETCLOSE(data->socket) < 0) {
        !          2755:                        HTInetStatus("close");
        !          2756:                        state = ERROR;
        !          2757:                        break;
        !          2758:                    }
        !          2759: #endif
        !          2760:                    data->socket = newfd;        /* Switch to new socket */
        !          2761:                    data->passive = 2;
        !          2762:                    if (TRACE)
        !          2763:                        fprintf(stderr, "FTP......... New data socket: %d\n",
        !          2764:                                data->socket);
        !          2765:                } else {
        !          2766:                    HTChunkClear(ctrl->reply);
        !          2767:                    ctrl->reply = NULL;
        !          2768:                    state = ERROR;
        !          2769:                    break;
        !          2770:                }
        !          2771:            }
        !          2772: 
1.23      frystyk  2773:            /* Now, the browsing module can be called */
                   2774:            if (TRACE) fprintf(stderr, "FTP......... Receiving file `%s\'\n",
                   2775:                               unescaped);
                   2776:            status = HTParseSocket(data->fileformat, data->socket, req);
                   2777:            if (status != HT_LOADED) {
                   2778:                if (status == HT_INTERRUPTED) {
                   2779:                    if (!HTFTP_send_cmd(ctrl, "ABOR", NULL))
                   2780:                        state = SENT_ABOR;
                   2781:                    else
                   2782:                        state = ERROR;
                   2783:                } else
                   2784:                    state = ERROR;
                   2785:            } else
                   2786:                state = GOT_DATA;
                   2787:            break;
                   2788: 
                   2789:          case GOT_DATA:
1.33    ! frystyk  2790:            if (!handle_ctrl) {
        !          2791:                status = HTFTP_get_response(ctrl, &ctrl->reply);
        !          2792:                if (status/100 == 2)
        !          2793:                    state = SUCCESS;                            /* File read */
        !          2794:                else if (status/100 == 4)
        !          2795:                    state = FAILURE;
        !          2796:                else
        !          2797:                    state = ERROR;
        !          2798:            } else
        !          2799:                state = SUCCESS;
1.23      frystyk  2800:            break;
1.22      frystyk  2801: 
1.23      frystyk  2802:          case SENT_ABOR:
1.28      frystyk  2803:            status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23      frystyk  2804:            if (status/100 == 2)
                   2805:                state = SUCCESS;
                   2806:            else if (status/100 == 4)
                   2807:                state = FAILURE;
1.22      frystyk  2808:            else
1.23      frystyk  2809:                state = ERROR;
                   2810:            break;
                   2811: 
1.33    ! frystyk  2812:           case WAIT_FOR_CONNECTION:
        !          2813:             /* Now we have to wait to see whether the FTP-server sends on the
        !          2814:                data port or the control port */
        !          2815:             status = HTFTP_look_for_data(req, data);
        !          2816:             if (!status)
        !          2817:                 state = READY_FOR_DATA;
        !          2818:             else
        !          2819:                 state = HANDLE_CTRL;
        !          2820:             break;
        !          2821: 
        !          2822:           case HANDLE_CTRL:
        !          2823:             status = HTFTP_get_response(ctrl, &ctrl->reply);
        !          2824:             if (status/100 == 2) {
        !          2825:                 state = READY_FOR_DATA;
        !          2826: #ifdef LISTEN
        !          2827:             } else if (status == 425) {    /* Connection could not be opened */
        !          2828:                 if (HTFTP_switch_to_port(data, req))
        !          2829:                     state = ERROR;
        !          2830:                 else {
        !          2831:                    if (!HTFTP_send_cmd(ctrl, "RETR", unescaped))
        !          2832:                        state = SENT_RETR;
        !          2833:                    else
        !          2834:                        state = ERROR;
        !          2835:                }
        !          2836: #endif /* LISTEN */
        !          2837:             } else if (status/100 == 4)
        !          2838:                 state = FAILURE;
        !          2839:             else
        !          2840:                 state = ERROR;
        !          2841:             handle_ctrl = YES;
        !          2842:             break;
        !          2843: 
1.23      frystyk  2844:          case FAILURE:                       /* Otherwise gcc complains :-( */
                   2845:          case ERROR:
                   2846:          case SUCCESS:
                   2847:            break;
1.1       timbl    2848:        }
                   2849:     }
1.23      frystyk  2850:     FREE(unescaped);
                   2851:     return state;
                   2852: }
1.1       timbl    2853: 
1.23      frystyk  2854: 
                   2855: /* ------------------------------------------------------------------------- */
                   2856: /*                            PUBLIC FTP functions                          */
                   2857: /* ------------------------------------------------------------------------- */
                   2858: 
                   2859: /*                                                        HTFTP_enable_session
                   2860: **
                   2861: **     This function makes it possible to reuse the same control connections
                   2862: **     until they are either timed out by the server, or that the session
1.31      frystyk  2863: **     is closed by HTFTP_end_session. Note that HTLoadFTP can run
1.23      frystyk  2864: **     independently of start and end session, and then each load runs like
                   2865: **     an atomic action.
                   2866: */
                   2867: PUBLIC void HTFTP_enable_session NOARGS
                   2868: {
                   2869:     if (session) {
                   2870:        if (TRACE)
                   2871:            fprintf(stderr, "FTP......... Session is already enabled?\n");
                   2872:        return;
                   2873:     }
                   2874:     session = HTList_new();
                   2875: }
                   2876: 
                   2877: 
                   2878: /*                                                       HTFTP_disable_session
                   2879: **
                   2880: **     This function is the closing function for HTFTP_enable_session.
                   2881: **
                   2882: **     Returns YES if OK, else NO
1.1       timbl    2883: */
1.23      frystyk  2884: PUBLIC BOOL HTFTP_disable_session NOARGS
                   2885: {
                   2886:     BOOL status = YES;
                   2887:     if (!session) {
                   2888:        if (TRACE)
                   2889:            fprintf(stderr, "FTP......... No session to disable?\n");
                   2890:        return NO;
                   2891:     }
1.1       timbl    2892:     {
1.23      frystyk  2893:        HTList *cur = session;
                   2894:        ftp_ctrl_info *pres;
                   2895:        while ((pres = (ftp_ctrl_info *) HTList_nextObject(cur))) {
                   2896:            if (HTFTP_close_ctrl_con(pres))
                   2897:                status = NO;
                   2898:        }
                   2899:        HTList_delete(session);
1.1       timbl    2900:     }
1.23      frystyk  2901:     session = NULL;
                   2902:     return status;
                   2903: }
                   2904: 
                   2905: 
                   2906: /*     Retrieve File from Server as an atomic action. 
                   2907: **     -----------------------------------------------
                   2908: **
                   2909: ** On entry,
                   2910: **     request         This is the request structure
                   2911: ** On exit,
                   2912: **     returns         <0              Error has occured
                   2913: **                     HT_LOADED       OK
                   2914: */
1.31      frystyk  2915: PUBLIC int HTLoadFTP ARGS1(HTRequest *, request)
1.23      frystyk  2916: {
                   2917:     char *url = HTAnchor_physical(request->anchor);
                   2918:     int status = -1;
1.33    ! frystyk  2919:     int retry;                                     /* How many times tried? */
1.23      frystyk  2920:     ftp_ctrl_info *ctrl;
                   2921: 
1.33    ! frystyk  2922:     if (!request || !url || !*url) {
        !          2923:        if (TRACE) fprintf(stderr, "HTLoadFTP... Bad argument\n");
        !          2924:        return -1;
        !          2925:     }
        !          2926: 
1.23      frystyk  2927:     /* Initiate a (possibly already exsisting) control connection and a
                   2928:        corresponding data connection */
                   2929:     HTSimplify(url);
1.29      frystyk  2930:     if((ctrl = HTFTP_init_con(request, url)) == NULL) {
1.33    ! frystyk  2931: #ifdef OLD_CODE
1.25      frystyk  2932:        HTLoadError(request, 500,
1.32      frystyk  2933:                    "Could not establish connection to FTP-server");
1.33    ! frystyk  2934: #endif /* OLD_CODE */
        !          2935:        goto endfunc;
1.25      frystyk  2936:     }
1.23      frystyk  2937: 
                   2938:     /* Only if the control connection is in IDLE state, a new
                   2939:        transfer can be started. The control connection can be in another
                   2940:        mode if (session), and then the request is getting queued in
                   2941:        ctrl->data_cons. */
1.31      frystyk  2942:     if (ctrl->state == FTP_IDLE || (session && ctrl->state == FTP_LOGGED_IN)) {
1.23      frystyk  2943:        ftp_data_info *data = ctrl->data_cons->next->object;
1.31      frystyk  2944:        if (ctrl->state == FTP_IDLE)
                   2945:            ctrl->state = FTP_BEGIN;
                   2946:        while (ctrl->state != FTP_IDLE) {               /* Do until finished */
1.23      frystyk  2947:            switch (ctrl->state) {
1.31      frystyk  2948:              case FTP_BEGIN:
1.23      frystyk  2949:                if (!HTFTP_login(ctrl))
1.31      frystyk  2950:                    ctrl->state = FTP_LOGGED_IN;
1.23      frystyk  2951:                else
1.31      frystyk  2952:                    ctrl->state = FTP_ERROR;
1.23      frystyk  2953:                break;
                   2954: 
1.31      frystyk  2955:              case FTP_LOGGED_IN:
1.33    ! frystyk  2956:                if (!HTFTP_get_data_con(request, data, url))
1.31      frystyk  2957:                    ctrl->state = FTP_GOT_DATA_CON;
1.23      frystyk  2958:                else
1.31      frystyk  2959:                    ctrl->state = FTP_ERROR;
1.23      frystyk  2960:                break;
                   2961: 
1.31      frystyk  2962:              case FTP_GOT_DATA_CON:
1.23      frystyk  2963:                {
                   2964:                    /* Now we must ask for the URL requested. If FAILURE, then
                   2965:                       we try twice to see, if it helps */
1.33    ! frystyk  2966:                    char *rel = NULL;
1.23      frystyk  2967:                    for (retry=0; retry<2; retry++) {
                   2968:                        if ((rel = HTFTPLocation(ctrl, url)) == NULL) {
1.31      frystyk  2969:                            ctrl->state = FTP_ERROR;
1.23      frystyk  2970:                            break;
                   2971:                        }
1.33    ! frystyk  2972:                        if (retry == 1 && TRACE)
        !          2973:                            fprintf(stderr,
        !          2974:                                    "FTP......... First attempt to get URL failed, let's try again\n");
1.23      frystyk  2975: 
                   2976:                        if (data->directory == YES) {
                   2977:                            /* If we haven't already got server-info */
                   2978:                            if (ctrl->server == UNKNOWN) {
                   2979:                                if (HTFTPServerInfo(ctrl)) {
1.31      frystyk  2980:                                    ctrl->state = FTP_ERROR;
1.23      frystyk  2981:                                    break;
                   2982:                                }
                   2983:                            }
1.33    ! frystyk  2984:                            status = HTFTP_get_dir(ctrl, request, rel);
1.23      frystyk  2985:                        }
                   2986:                        else
1.33    ! frystyk  2987:                            status = HTFTP_get_file(ctrl, request, rel);
1.23      frystyk  2988:                        if (!status) {
1.31      frystyk  2989:                            ctrl->state = FTP_GOT_DATA;
1.23      frystyk  2990:                            break;
                   2991:                        } else if (status == -2) {                  /* Error */
1.31      frystyk  2992:                            ctrl->state = FTP_ERROR;
1.23      frystyk  2993:                            break;
                   2994:                        } else {
1.33    ! frystyk  2995:                            FREE(rel);
1.31      frystyk  2996:                            ctrl->state = FTP_FAILURE;          /* Try twice */
1.23      frystyk  2997:                        }
                   2998:                    }
1.33    ! frystyk  2999:                    FREE(rel);
1.23      frystyk  3000:                }
1.31      frystyk  3001:                if (retry == 2 && ctrl->state == FTP_FAILURE)
                   3002:                    ctrl->state = FTP_ERROR;
1.23      frystyk  3003:                break;
                   3004: 
1.31      frystyk  3005:              case FTP_GOT_DATA:
1.23      frystyk  3006:                if (HTFTP_close_data_con(data))
1.31      frystyk  3007:                    ctrl->state = FTP_ERROR;
1.23      frystyk  3008:                else {
                   3009:                    HTList_removeLastObject(ctrl->data_cons);
                   3010:                    if (!session) {
                   3011:                        if (!HTFTP_logout(ctrl)) {
1.31      frystyk  3012:                            ctrl->state = FTP_IDLE;
1.23      frystyk  3013:                            status = HT_LOADED;
                   3014:                        } else
1.31      frystyk  3015:                            ctrl->state = FTP_ERROR;
1.23      frystyk  3016:                    } else {
1.31      frystyk  3017:                        ctrl->state = FTP_LOGGED_IN; /*Ready for next request*/
1.23      frystyk  3018:                        return HT_LOADED;
                   3019:                    }
                   3020:                    break;
                   3021:                }
                   3022:                break;
                   3023: 
1.31      frystyk  3024:              case FTP_ERROR:
1.30      luotonen 3025:                {
1.32      frystyk  3026:                    if (ctrl->reply && ctrl->reply->data) {
1.33    ! frystyk  3027:                        HTFTPParseError(&ctrl->reply);
        !          3028:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_FTP_SERVER,
        !          3029:                                   (void *) ctrl->reply->data,
        !          3030:                                   ctrl->reply->size-1, "HTLoadFTP");
1.32      frystyk  3031:                    } else {
1.33    ! frystyk  3032:                        char *hoststr = HTParse(url, "", PARSE_HOST);
        !          3033:                        HTUnEscape(hoststr);
        !          3034:                        HTErrorAdd(request, ERR_FATAL, NO,
        !          3035:                                   HTERR_FTP_NO_RESPONSE,
        !          3036:                                   (void *) hoststr, strlen(hoststr),
        !          3037:                                   "HTLoadFTP");
        !          3038:                        free(hoststr);
1.32      frystyk  3039:                    }
1.30      luotonen 3040:                    HTFTP_abort_ctrl_con(ctrl);
1.33    ! frystyk  3041:                    status = -1;
        !          3042:                    goto endfunc;
1.30      luotonen 3043:                }
1.23      frystyk  3044:                break;
                   3045: 
                   3046:              default:
                   3047:                if (TRACE) fprintf(stderr, "FTP......... Unknown state, what happened?\n");
                   3048:                break;
                   3049:            }
                   3050:        }
1.22      frystyk  3051: 
1.23      frystyk  3052:        /* The control connection is only closed if the load is atomic */
                   3053:        if (!session && HTFTP_close_ctrl_con(ctrl))
                   3054:            status = -1;
                   3055:     }
1.33    ! frystyk  3056: 
        !          3057:   endfunc:
1.23      frystyk  3058:     if (status < 0 && status != HT_INTERRUPTED)
1.33    ! frystyk  3059:        HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, NULL, 0, 
        !          3060:                   "HTLoadFTP");
        !          3061: #ifdef OLD_CODE
1.23      frystyk  3062:        return HTLoadError(request, 500,
1.32      frystyk  3063:               "Document not loaded due to strange behavior from FTP-server");
1.33    ! frystyk  3064: #endif
        !          3065:     if (request->error_stack)
        !          3066:        HTErrorMsg(request);
1.23      frystyk  3067:     return status;
                   3068: }
1.31      frystyk  3069: 
                   3070: /* Protocol descriptors */
                   3071: GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFTP, 0 , 0 };
1.22      frystyk  3072: 
1.23      frystyk  3073: /* END OF MODULE */
1.22      frystyk  3074: 
                   3075: 

Webmaster