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

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

Webmaster