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

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

Webmaster