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

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

Webmaster