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

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.60    ! frystyk    51: **     Sep 95  HFN     Rewritten to support streams and persistent conenctions
        !            52: **                     and multiplexed IO
1.22      frystyk    53: ** Notes:
                     54: **                             Portions Copyright 1994 Trustees of Dartmouth College
                     55: **                     Code for recognizing different FTP servers and
                     56: **                     parsing "ls -l" output taken from Macintosh Fetch
                     57: **                     program with permission from Jim Matthews,
                     58: **                     Dartmouth Software Development Team.
1.23      frystyk    59: */
1.1       timbl      60: 
1.22      frystyk    61: /* Library include files */
1.48      frystyk    62: #include "tcp.h"
                     63: #include "HTUtils.h"
                     64: #include "HTString.h"
1.1       timbl      65: #include "HTParse.h"
                     66: #include "HTTCP.h"
                     67: #include "HTAnchor.h"
1.51      frystyk    68: #include "HTBind.h"
1.53      frystyk    69: #include "HTSocket.h"
1.60    ! frystyk    70: #include "HTWriter.h"
1.6       secret     71: #include "HTChunk.h"
1.22      frystyk    72: #include "HTAlert.h"
1.60    ! frystyk    73: #include "HTFormat.h"
        !            74: #include "HTStream.h"
1.33      frystyk    75: #include "HTError.h"
1.58      frystyk    76: #include "HTReqMan.h"
1.60    ! frystyk    77: #include "HTNetMan.h"
1.22      frystyk    78: #include "HTFTP.h"                                      /* Implemented here */
                     79: 
1.48      frystyk    80: #ifdef VMS
                     81: #include "HTVMSUtils.h"
                     82: #endif /* VMS */
                     83: 
1.22      frystyk    84: /* Macros and other defines */
1.60    ! frystyk    85: #if 0
        !            86: /* Only use this if ABSOLUTELY necessary! */
        !            87: #define POLL_PORTS           /* If allocation does not work, poll ourselves.*/
1.1       timbl      88: #endif
                     89: 
                     90: #ifdef POLL_PORTS
1.23      frystyk    91: #define FIRST_TCP_PORT  1024          /* Region to try for a listening port */
                     92: #define LAST_TCP_PORT   5999
1.60    ! frystyk    93: PRIVATE        int DataPort = FIRST_TCP_PORT;
1.1       timbl      94: #endif
                     95: 
1.60    ! frystyk    96: #ifndef FTP_PORT
        !            97: #define FTP_PORT       21
        !            98: #define FTP_DATA       20
1.1       timbl      99: #endif
1.60    ! frystyk   100: 
        !           101: #define WWW_FTP_CLIENT "libwww@"         /* If can't get user-info, use this */
        !           102: #define FTP_DIR(me)    ((me)->type=='L' || (me)->type=='N')
1.1       timbl     103: 
1.58      frystyk   104: /*
                    105: ** Local context structure used in the HTNet object.
                    106: */
1.39      frystyk   107: typedef enum _HTFTPServerType {
1.60    ! frystyk   108:     FTP_GENERIC        = 0x1,
        !           109:     FTP_MACHTEN                = 0x2,
        !           110:     FTP_UNIX           = 0x4,
        !           111:     FTP_VMS            = 0x8,
        !           112:     FTP_CMS            = 0x10,
        !           113:     FTP_DCTS           = 0x20,
        !           114:     FTP_TCPC           = 0x40,
        !           115:     FTP_PETER_LEWIS    = 0x80,
        !           116:     FTP_NCSA           = 0x200,
        !           117:     FTP_WINNT          = 0x400,
        !           118:     FTP_UNSURE         = 0x8000
1.39      frystyk   119: } HTFTPServerType;
                    120: 
1.45      frystyk   121: typedef enum _HTFTPState {
1.60    ! frystyk   122:     FTP_SUCCESS        = -2,
        !           123:     FTP_ERROR = -1,
1.58      frystyk   124:     FTP_BEGIN = 0,
                    125:     FTP_NEED_CCON,                                    /* Control connection */
1.60    ! frystyk   126:     FTP_NEED_LOGIN,
1.58      frystyk   127:     FTP_NEED_DCON,                                       /* Data connection */
1.60    ! frystyk   128:     FTP_NEED_DATA,
        !           129:     FTP_NEED_SERVER                               /* For directory listings */
1.39      frystyk   130: } HTFTPState;
                    131: 
1.58      frystyk   132: typedef struct _ftp_ctrl {
1.60    ! frystyk   133:     HTChunk *          cmd;
        !           134:     int                        repcode;
        !           135:     char *             reply;
        !           136:     char *             uid;
1.58      frystyk   137:     char *             passwd;
1.60    ! frystyk   138:     char *             account;
1.58      frystyk   139:     HTFTPState         state;                    /* State of the connection */
1.60    ! frystyk   140:     int                substate;                 /* For hierarchical states */
        !           141:     BOOL               sent;                       /* Read or write command */
        !           142:     BOOL               cwd;                                     /* Done cwd */
        !           143:     BOOL               reset;                            /* Expect greeting */
1.45      frystyk   144:     HTFTPServerType    server;                            /* Type of server */
1.60    ! frystyk   145:     HTNet *            dnet;                             /* Data connection */
1.58      frystyk   146: } ftp_ctrl;
1.39      frystyk   147: 
1.58      frystyk   148: typedef struct _ftp_data {
1.60    ! frystyk   149:     char               host[30];                /* Host to contact for data */
        !           150:     char *             file;                            /* File or dir name */
        !           151:     char *             offset;                          /* offset into file */
        !           152:     BOOL               pasv;                           /* Active or passive */
        !           153:     char               type;                /* 'A', 'I', 'L'(IST), 'N'(LST) */
1.58      frystyk   154: } ftp_data;
1.39      frystyk   155: 
1.60    ! frystyk   156: #define MAX_STATUS_LEN         100   /* Max nb of chars to check StatusLine */
        !           157: 
        !           158: struct _HTStream {
        !           159:     CONST HTStreamClass *      isa;
        !           160:     HTStream *                 target;
        !           161:     HTRequest *                        request;
        !           162:     ftp_ctrl *                 ctrl;
        !           163:     HTSocketEOL                        state;
        !           164:     int                                status;
        !           165:     HTChunk *                  welcome;
        !           166:     BOOL                       first_line;
        !           167:     char                       buffer[MAX_STATUS_LEN+1];
        !           168:     int                                buflen;
        !           169: };
        !           170: 
        !           171: typedef enum _FTPDataCon {
        !           172:     FTP_DATA_PASV = 0x1,
        !           173:     FTP_DATA_PORT = 0x2
        !           174: } FTPDataCon;
        !           175: 
        !           176: PRIVATE FTPDataCon FTPMode = FTP_DATA_PASV;
1.39      frystyk   177: 
1.23      frystyk   178: /* ------------------------------------------------------------------------- */
1.60    ! frystyk   179: /*                         FTP Status Line Stream                           */
1.22      frystyk   180: /* ------------------------------------------------------------------------- */
1.60    ! frystyk   181: 
        !           182: /*     FTPCleanup
        !           183: **     ----------
        !           184: **      This function closes the connection and frees memory.
        !           185: **      Returns YES on OK, else NO
        !           186: */
        !           187: PRIVATE int FTPCleanup (HTRequest *request, int status)
        !           188: {
        !           189:     HTNet *cnet = request->net;
        !           190:     ftp_ctrl *ctrl = (ftp_ctrl *) cnet->context;
        !           191:     HTNet *dnet = ctrl->dnet;
        !           192:     ftp_data *data = (ftp_data *) dnet->context;
        !           193: 
        !           194:     /* Free stream with data TO network */
        !           195:     if (!HTRequest_isDestination(request) && request->input_stream) {
        !           196:        if (status == HT_INTERRUPTED)
        !           197:            (*request->input_stream->isa->abort)(request->input_stream, NULL);
        !           198:        else
        !           199:            (*request->input_stream->isa->_free)(request->input_stream);
        !           200:     }
        !           201: 
        !           202:     /* Remove the request object and our own context structure for http */
        !           203:     HTNet_delete(dnet, HT_IGNORE);
        !           204:     HTNet_delete(cnet, status);
        !           205: 
        !           206:     HTChunkFree(ctrl->cmd);
        !           207:     FREE(ctrl->reply);
        !           208:     FREE(ctrl->uid);
        !           209:     FREE(ctrl->passwd);
        !           210:     FREE(ctrl->account);
        !           211:     FREE(data->file);
        !           212:     FREE(ctrl);
        !           213:     FREE(data);
        !           214:     return YES;
        !           215: }
        !           216: 
        !           217: /*     ScanResponse
        !           218: **     ------------
        !           219: **     Analyzes the response from the FTP server.
        !           220: **     Returns HT_LOADED if OK, HT_OK if more, HT_ERROR if error
        !           221: **     the control connection.
1.33      frystyk   222: */
1.60    ! frystyk   223: PRIVATE int ScanResponse (HTStream *me)
1.33      frystyk   224: {
1.60    ! frystyk   225:     int reply = 0;
        !           226:     char cont = '\0';
        !           227:     char *ptr = me->buffer+4;
        !           228:     *(me->buffer+me->buflen) = '\0';
        !           229:     if (isdigit(*(me->buffer))) sscanf(me->buffer, "%d%c", &reply, &cont);
        !           230:     if (me->first_line) {
        !           231:        if (PROT_TRACE) fprintf(TDEST, "FTP Rx...... `%s\'\n", me->buffer);
        !           232:        if (!reply) return HT_ERROR;
        !           233:        me->first_line = NO;
        !           234:        me->ctrl->repcode = reply;
        !           235:        StrAllocCopy(me->ctrl->reply, ptr);
        !           236:     } else {
        !           237:        HTChunkPuts(me->welcome, ptr);
        !           238:        HTChunkPutc(me->welcome, '\n');
1.33      frystyk   239:     }
1.60    ! frystyk   240:     me->state = EOL_BEGIN;
        !           241:     me->buflen = 0;
        !           242:     if (cont != '-') {
        !           243:        me->first_line = YES;
        !           244:        return HT_LOADED;
1.33      frystyk   245:     }
1.60    ! frystyk   246:     return HT_OK;
1.33      frystyk   247: }
                    248: 
1.60    ! frystyk   249: /*
        !           250: **     Searches for FTP header line until buffer fills up or a CRLF or LF
        !           251: **     is found
1.23      frystyk   252: */
1.60    ! frystyk   253: PRIVATE int FTPStatus_put_block (HTStream * me, CONST char * b, int l)
        !           254: {    
        !           255:     int status;
        !           256:     while (l-- > 0) {
        !           257:        if (me->state == EOL_FCR) {
        !           258:            if (*b == LF) {
        !           259:                if ((status = ScanResponse(me)) != HT_OK)
        !           260:                    return status;
        !           261:            } else {
        !           262:                me->state = EOL_BEGIN;
        !           263:            }
        !           264:        } else if (*b == CR) {
        !           265:            me->state = EOL_FCR;
        !           266:        } else if (*b == LF) {
        !           267:            if ((status = ScanResponse(me)) != HT_OK)
        !           268:                return status;
        !           269:        } else {
        !           270:            *(me->buffer+me->buflen++) = *b;
        !           271:            if (me->buflen >= MAX_STATUS_LEN) {
        !           272:                if (PROT_TRACE)
        !           273:                    fprintf(TDEST, "FTP Status.. Line too long - chopped\n");
        !           274:                if ((status = ScanResponse(me)) != HT_OK)
        !           275:                    return status;
1.23      frystyk   276:            }
                    277:        }
1.60    ! frystyk   278:        b++;
1.25      frystyk   279:     }
1.60    ! frystyk   280:     return HT_OK;
1.25      frystyk   281: }
                    282: 
1.60    ! frystyk   283: PRIVATE int FTPStatus_put_string ARGS2(HTStream *, me, CONST char*, s)
        !           284: {
        !           285:     return FTPStatus_put_block(me, s, (int) strlen(s));
        !           286: }
1.25      frystyk   287: 
1.60    ! frystyk   288: PRIVATE int FTPStatus_put_character ARGS2(HTStream *, me, char, c)
1.25      frystyk   289: {
1.60    ! frystyk   290:     return FTPStatus_put_block(me, &c, 1);
        !           291: }
1.25      frystyk   292: 
1.60    ! frystyk   293: PRIVATE int FTPStatus_flush ARGS1(HTStream *, me)
        !           294: {
        !           295:     return (*me->target->isa->flush)(me->target);
1.23      frystyk   296: }
1.22      frystyk   297: 
1.60    ! frystyk   298: PRIVATE int FTPStatus_free ARGS1(HTStream *, me)
1.22      frystyk   299: {
1.60    ! frystyk   300:     int status = HT_OK;
        !           301:     if (me->target) {
        !           302:        if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
        !           303:            return HT_WOULD_BLOCK;
1.22      frystyk   304:     }
1.60    ! frystyk   305:     HTChunkFree(me->welcome);
        !           306:     free(me);
        !           307:     return HT_OK;
1.22      frystyk   308: }
                    309: 
1.60    ! frystyk   310: PRIVATE int FTPStatus_abort ARGS2(HTStream *, me, HTError, e)
1.22      frystyk   311: {
1.60    ! frystyk   312:     if (me->target)
        !           313:        (*me->target->isa->abort)(me->target, e);
        !           314:     HTChunkFree(me->welcome);
        !           315:     free(me);
        !           316:     if (PROT_TRACE)
        !           317:        fprintf(TDEST, "FTPStatus... ABORTING...\n");
        !           318:     return HT_ERROR;
1.22      frystyk   319: }
                    320: 
1.60    ! frystyk   321: /*     FTPStatus Stream
        !           322: **     -----------------
1.22      frystyk   323: */
1.60    ! frystyk   324: PRIVATE CONST HTStreamClass FTPStatusClass =
        !           325: {              
        !           326:     "FTPStatus",
        !           327:     FTPStatus_flush,
        !           328:     FTPStatus_free,
        !           329:     FTPStatus_abort,
        !           330:     FTPStatus_put_character,
        !           331:     FTPStatus_put_string,
        !           332:     FTPStatus_put_block
        !           333: };
        !           334: 
        !           335: PRIVATE HTStream * FTPStatus_new ARGS2(HTRequest *, request,
        !           336:                                       ftp_ctrl *, ctrl)
        !           337: {
        !           338:     HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
        !           339:     if (!me) outofmem(__FILE__, "FTPStatus_new");
        !           340:     me->isa = &FTPStatusClass;
        !           341:     me->request = request;
        !           342:     me->first_line = YES;
        !           343:     me->welcome = HTChunkCreate(256);
        !           344:     me->ctrl = ctrl;
        !           345:     me->state = EOL_BEGIN;
        !           346:     return me;
1.22      frystyk   347: }
                    348: 
1.60    ! frystyk   349: /* ------------------------------------------------------------------------- */
        !           350: /*       FTP Client Functions for managing control and data connections     */
        !           351: /* ------------------------------------------------------------------------- */
1.22      frystyk   352: 
1.60    ! frystyk   353: PRIVATE int SendCommand (HTRequest *request, ftp_ctrl *ctrl,
        !           354:                         char *token, char *pars)
        !           355: {
        !           356:     int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
        !           357:     HTChunkClear(ctrl->cmd);
        !           358:     HTChunkEnsure(ctrl->cmd, len);
        !           359:     if (pars && *pars)
        !           360:        sprintf(HTChunkData(ctrl->cmd), "%s %s%c%c", token, pars, CR, LF);
        !           361:     else
        !           362:        sprintf(HTChunkData(ctrl->cmd), "%s%c%c", token, CR, LF);
        !           363:     if (PROT_TRACE) fprintf(TDEST, "FTP Tx...... %s", HTChunkData(ctrl->cmd));
        !           364:     return (*request->input_stream->isa->put_block)
        !           365:        (request->input_stream, HTChunkData(ctrl->cmd), len);
        !           366: }
        !           367: 
        !           368: /*     HTFTPParseURL
        !           369: **     -------------
        !           370: **     Scan URL for uid and passwd, and any data type indication. The
        !           371: **     expected format is [user[:password]@]host[:port].
        !           372: **     If no values are found then use defaults.
        !           373: **     Returns YES if OK, else NO
1.22      frystyk   374: */
1.60    ! frystyk   375: PRIVATE BOOL HTFTPParseURL (char *url, ftp_ctrl *ctrl, ftp_data *data)
1.22      frystyk   376: {
1.60    ! frystyk   377:     char *login = HTParse(url, "", PARSE_HOST);
        !           378:     char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
        !           379:     char *ptr = strchr(login, '@');
        !           380:     if (ptr) {                               /* Uid and/or passwd specified */
        !           381:        char *passwd;
        !           382:        *ptr = '\0';
        !           383:        if ((passwd = strchr(login, ':'))) {             /* Passwd specified */
        !           384:            *passwd++ = '\0';    
        !           385:            HTUnEscape(passwd);
        !           386:            StrAllocCopy(ctrl->passwd, passwd);
1.22      frystyk   387:        }
1.60    ! frystyk   388:        HTUnEscape(login);
        !           389:        StrAllocCopy(ctrl->uid, login);
        !           390:     } else {                                               /* Use anonymous */
        !           391:        CONST char *mailaddress = HTGetMailAddress();
        !           392:        StrAllocCopy(ctrl->uid, "anonymous");
        !           393:        if (mailaddress)
        !           394:            StrAllocCopy(ctrl->passwd, mailaddress);
        !           395:        else
        !           396:            StrAllocCopy(ctrl->passwd, WWW_FTP_CLIENT);
1.22      frystyk   397:     }
1.60    ! frystyk   398:     if (PROT_TRACE) fprintf(TDEST, "FTPParse.... uid `%s\' pw `%s\'\n",
        !           399:                            ctrl->uid, ctrl->passwd);   
        !           400: 
        !           401:     ptr = strchr(path, ';');
        !           402:     if (ptr) {
        !           403:        *ptr = '\0';
        !           404:        if (strncasecomp(ptr, ";type=", 6))                 /* Look for type */
        !           405:            data->type = TOUPPER(*(ptr+6));
        !           406:        else if (*(ptr-1) == '/')
        !           407:            data->type = 'N';
        !           408:     } else if (*(path+strlen(path)-1) == '/') {
        !           409:        *(path+strlen(path)-1) = '\0';
        !           410:        data->type = 'N';
        !           411:     }
        !           412:     if (data->type && PROT_TRACE)
        !           413:        fprintf(TDEST, "FTPParse.... Datatype %c\n", data->type);       
        !           414:     StrAllocCopy(data->file, path);
        !           415:     data->offset = data->file;
        !           416:     free(login);
        !           417:     free(path);
        !           418:     return YES;
1.22      frystyk   419: }
                    420: 
1.60    ! frystyk   421: /*     Use LIST or NLST
        !           422: **     ----------------
        !           423: **     This function sets the type field for what type of list we can use
        !           424: **     Returns YES if OK, else NO
        !           425: */
        !           426: PRIVATE BOOL FTPListType (ftp_data * data, HTFTPServerType type)
        !           427: {
        !           428:     if (!data) return NO;
        !           429:     switch (type) {
        !           430:       case FTP_GENERIC:        data->type='N'; break;
        !           431:       case FTP_MACHTEN:        data->type='L'; break;
        !           432:       case FTP_UNIX:           data->type='L'; break;
        !           433:       case FTP_VMS:            data->type='L'; break;
        !           434:       case FTP_CMS:            data->type='N'; break;
        !           435:       case FTP_DCTS:           data->type='N'; break;
        !           436:       case FTP_TCPC:           data->type='N'; break;
        !           437:       case FTP_PETER_LEWIS:    data->type='L'; break;
        !           438:       case FTP_NCSA:           data->type='N'; break;
        !           439:       case FTP_WINNT:          data->type='L'; break;
        !           440:       default:                         data->type='N'; break;
        !           441:     }
        !           442:     return YES;
        !           443: }
        !           444: 
        !           445: /*     Open a Data socket for listening on
        !           446: **     -----------------------------------
        !           447: **     Set up a port to listen for data
        !           448: **     Returns YES if OK, else NO
1.22      frystyk   449: */
1.60    ! frystyk   450: PRIVATE BOOL ListenSocket (HTNet *cnet, HTNet *dnet, ftp_data *data)
1.22      frystyk   451: {
1.60    ! frystyk   452: #ifdef POLL_PORTS
        !           453:     unsigned short old_DataPort = DataPort;
        !           454:     for (DataPort=old_DataPort+1;; DataPort++) {
        !           455:        if (DataPort > LAST_TCP_PORT)
        !           456:            DataPort = FIRST_TCP_PORT;
        !           457:        if (DataPort == old_DataPort) {
        !           458:            if (PROT_TRACE) fprintf(TDEST,"FTP......... No data port found\n");
        !           459:            return NO;
        !           460:        }
        !           461:        if (HTDoListen(dnet, DataPort) == HT_OK)
        !           462:            break;
        !           463:        if (dnet->sockfd != INVSOC) {
        !           464:            NETCLOSE(dnet->sockfd);
        !           465:            dnet->sockfd = INVSOC:
        !           466:        }
1.22      frystyk   467:     }
1.60    ! frystyk   468: #else
        !           469:     if (HTDoListen(dnet, 0, cnet->sockfd) != HT_OK) return NO;
        !           470: #endif /* POLL_PORTS */
1.22      frystyk   471: 
1.60    ! frystyk   472:     /* Now we must find out who we are to tell the other guy */
        !           473:     {
        !           474:        SockA local_addr;
        !           475:        int addr_size = sizeof(local_addr);
        !           476:        memset((void *) &local_addr, '\0', sizeof(local_addr));
        !           477:        if (getsockname(dnet->sockfd, (struct sockaddr *) &local_addr,
        !           478:                        &addr_size) < 0) {
        !           479:            HTErrorSysAdd(dnet->request, ERR_FATAL, socerrno,NO,"getsockname");
        !           480:            return NO;
1.22      frystyk   481:        }
1.60    ! frystyk   482:        if (PROT_TRACE) fprintf(TDEST, "FTP......... This host is `%s\'\n",
        !           483:                                HTInetString(&local_addr));
        !           484:        {
        !           485:            u_long addr = local_addr.sin_addr.s_addr;
        !           486:            u_short port = local_addr.sin_port;
        !           487:            sprintf(data->host, "%d,%d,%d,%d,%d,%d",
        !           488:                    (int)*((unsigned char *)(&addr)+0),
        !           489:                    (int)*((unsigned char *)(&addr)+1),
        !           490:                    (int)*((unsigned char *)(&addr)+2),
        !           491:                    (int)*((unsigned char *)(&addr)+3),
        !           492:                    (int)*((unsigned char *)(&port)+0),
        !           493:                    (int)*((unsigned char *)(&port)+1));
1.22      frystyk   494:        }
                    495:     }
1.60    ! frystyk   496:     return YES;
1.22      frystyk   497: }
                    498: 
1.60    ! frystyk   499: /*     HTFTPLogin
        !           500: **     -----------
        !           501: **     This function makes a login to a ftp-server. It takes the user name
        !           502: **     and passwd specified in ctrl->user and if that fails or an additional
        !           503: **     account is needed, the user is prompted.
        !           504: **     Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
1.22      frystyk   505: */
1.60    ! frystyk   506: PRIVATE int HTFTPLogin (HTRequest *request, HTNet *cnet, ftp_ctrl *ctrl)
1.22      frystyk   507: {
1.60    ! frystyk   508:     int status;
        !           509:     typedef enum _state {
        !           510:        SUB_ERROR = -2,
        !           511:        SUB_SUCCESS = -1,
        !           512:        NEED_SELECT = 0,
        !           513:        NEED_GREETING,
        !           514:        NEED_REIN,
        !           515:        NEED_UID,
        !           516:        NEED_PASSWD,
        !           517:        NEED_ACCOUNT,
        !           518:        PROMPT_USER
        !           519:     } state;
        !           520: 
        !           521:     /* Jump into a second level state machine */
        !           522:     while (1) {
        !           523:        switch ((state) ctrl->substate) {
        !           524:          case NEED_SELECT:
        !           525:            ctrl->substate = ctrl->reset ? NEED_REIN : NEED_GREETING;
        !           526:            break;
        !           527: 
        !           528:          case NEED_GREETING:
        !           529:            status = HTSocketRead(request, cnet);
        !           530:            if (status == HT_WOULD_BLOCK)
        !           531:                return HT_WOULD_BLOCK;
        !           532:            else if (status == HT_LOADED) {
        !           533:                if (ctrl->repcode/100 == 2)
        !           534:                    ctrl->substate = NEED_UID;
        !           535:                else
        !           536:                    ctrl->substate = SUB_ERROR;
        !           537:            } else
        !           538:                ctrl->substate = SUB_ERROR;
        !           539:            break;
        !           540: 
        !           541:          case NEED_REIN:
        !           542:            if (!ctrl->sent) {
        !           543:                status = SendCommand(request, ctrl, "REIN", NULL);
        !           544:                if (status == HT_WOULD_BLOCK)
        !           545:                    return HT_WOULD_BLOCK;
        !           546:                else if (status == HT_ERROR)
        !           547:                    ctrl->substate = SUB_ERROR;
        !           548:                ctrl->sent = YES;
        !           549:            } else {
        !           550:                status = HTSocketRead(request, cnet);
        !           551:                if (status == HT_WOULD_BLOCK)
        !           552:                    return HT_WOULD_BLOCK;
        !           553:                else if (status == HT_LOADED) {
        !           554:                    if (ctrl->repcode/100 == 2)
        !           555:                        ctrl->substate = NEED_UID;
        !           556:                    else
        !           557:                        ctrl->substate = SUB_SUCCESS;       /* hope the best */
        !           558:                } else
        !           559:                    ctrl->substate = SUB_ERROR;
        !           560:                ctrl->sent = NO;
1.22      frystyk   561:            }
1.60    ! frystyk   562:            break;
        !           563: 
        !           564:          case NEED_UID:
        !           565:            if (!ctrl->sent) {
        !           566:                status = SendCommand(request, ctrl, "USER", ctrl->uid);
        !           567:                if (status == HT_WOULD_BLOCK)
        !           568:                    return HT_WOULD_BLOCK;
        !           569:                else if (status == HT_ERROR)
        !           570:                    ctrl->substate = SUB_ERROR;
        !           571:                ctrl->sent = YES;
1.22      frystyk   572:            } else {
1.60    ! frystyk   573:                status = HTSocketRead(request, cnet);
        !           574:                if (status == HT_WOULD_BLOCK)
        !           575:                    return HT_WOULD_BLOCK;
        !           576:                else if (status == HT_LOADED) {
        !           577:                    int code = ctrl->repcode/100;
        !           578:                    if (code == 2)                  /* Logged in w/o passwd! */
        !           579:                        ctrl->substate = SUB_SUCCESS;
        !           580:                    else if (code == 3)                 /* Password demanded */
        !           581:                        ctrl->substate = NEED_PASSWD;
        !           582:                    else if (ctrl->repcode == 530)
        !           583:                        ctrl->substate = PROMPT_USER;        /* User unknown */
        !           584:                    else
        !           585:                        ctrl->substate = SUB_ERROR;
        !           586:                } else
        !           587:                    ctrl->substate = SUB_ERROR;
        !           588:                ctrl->sent = NO;
1.22      frystyk   589:            }
1.60    ! frystyk   590:            break;
1.1       timbl     591: 
1.60    ! frystyk   592:          case NEED_PASSWD:
        !           593:            if (!ctrl->sent) {
        !           594:                status = SendCommand(request, ctrl, "PASS", ctrl->passwd);
        !           595:                if (status == HT_WOULD_BLOCK)
        !           596:                    return HT_WOULD_BLOCK;
        !           597:                else if (status == HT_ERROR)
        !           598:                    ctrl->substate = SUB_ERROR;
        !           599:                ctrl->sent = YES;
        !           600:            } else {
        !           601:                status = HTSocketRead(request, cnet);
        !           602:                if (status == HT_WOULD_BLOCK)
        !           603:                    return HT_WOULD_BLOCK;
        !           604:                else if (status == HT_LOADED) {
        !           605:                    int code = ctrl->repcode/100;
        !           606:                    if (code == 2)                  /* Logged in with passwd */
        !           607:                        ctrl->substate = SUB_SUCCESS;
        !           608:                    else if (code == 3) {                /* Account demanded */
        !           609:                        char *prompt = NULL;
        !           610:                        StrAllocCopy(prompt, "Account required for ");
        !           611:                        StrAllocCat(prompt, ctrl->uid);
        !           612:                        if ((ctrl->account = HTPrompt(request, prompt, NULL)))
        !           613:                            ctrl->substate = NEED_ACCOUNT;
        !           614:                        else
        !           615:                            ctrl->substate = SUB_ERROR;
        !           616:                    } else if (ctrl->repcode == 530)
        !           617:                        ctrl->substate = PROMPT_USER;
        !           618:                    else
        !           619:                        ctrl->substate = SUB_ERROR;
        !           620:                } else
        !           621:                    ctrl->substate = SUB_ERROR;
        !           622:                ctrl->sent = NO;
        !           623:            }
        !           624:            break;
1.1       timbl     625: 
1.60    ! frystyk   626:          case NEED_ACCOUNT:
        !           627:            if (!ctrl->sent) {
        !           628:                status = SendCommand(request, ctrl, "ACCT", ctrl->account);
        !           629:                if (status == HT_WOULD_BLOCK)
        !           630:                    return HT_WOULD_BLOCK;
        !           631:                else if (status == HT_ERROR)
        !           632:                    ctrl->substate = SUB_ERROR;
        !           633:                ctrl->sent = YES;
1.23      frystyk   634:            } else {
1.60    ! frystyk   635:                status = HTSocketRead(request, cnet);
        !           636:                if (status == HT_WOULD_BLOCK)
        !           637:                    return HT_WOULD_BLOCK;
        !           638:                else if (status == HT_LOADED) {
        !           639:                    int code = ctrl->repcode/100;
        !           640:                    if (code == 2)                 /* Logged in with account */
        !           641:                        ctrl->substate = SUB_SUCCESS;
        !           642:                    else
        !           643:                        ctrl->substate = SUB_ERROR;              /* hopeless */
        !           644:                } else
        !           645:                    ctrl->substate = SUB_ERROR;
        !           646:                ctrl->sent = NO;
1.1       timbl     647:            }
1.60    ! frystyk   648:            break;
1.1       timbl     649: 
1.60    ! frystyk   650:          case PROMPT_USER:
        !           651:            {
        !           652:                char *prompt = NULL;
        !           653:                StrAllocCopy(prompt, "Enter username and password");
        !           654:                FREE(ctrl->uid);
        !           655:                FREE(ctrl->passwd);
        !           656:                HTPromptUsernameAndPassword(request, prompt,
        !           657:                                            &ctrl->uid, &ctrl->passwd);
        !           658:                if (ctrl->uid && ctrl->passwd)
        !           659:                    ctrl->substate = NEED_UID;
        !           660:                else
        !           661:                    ctrl->substate = SUB_ERROR;
        !           662:                free(prompt);
        !           663:            }
        !           664:            break;
1.1       timbl     665: 
1.60    ! frystyk   666:          case SUB_ERROR:
        !           667:            if (PROT_TRACE)
        !           668:                fprintf(TDEST, "FTP......... Login failed\n");
        !           669:            ctrl->substate = 0;
        !           670:            return HT_ERROR;
        !           671:            break;
1.23      frystyk   672: 
1.60    ! frystyk   673:          case SUB_SUCCESS:
1.43      frystyk   674:            if (PROT_TRACE)
1.60    ! frystyk   675:                fprintf(TDEST, "FTP......... Logged in as `%s\'\n", ctrl->uid);
        !           676:            ctrl->substate = 0;
        !           677:            return HT_OK;
        !           678:            break;
1.23      frystyk   679:        }
1.22      frystyk   680:     }
                    681: }
                    682: 
1.60    ! frystyk   683: /*     HTFTPDataConnection
        !           684: **     -------------------
        !           685: **     Prepares a data connection to the server and initializes the
        !           686: **     transfer mode.
        !           687: **     Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
1.1       timbl     688: */
1.60    ! frystyk   689: PRIVATE int HTFTPDataConnection (HTRequest * request, HTNet *cnet,
        !           690:                                 ftp_ctrl *ctrl, ftp_data *data)
1.1       timbl     691: {
1.60    ! frystyk   692:     int status;
        !           693:     HTNet *dnet = ctrl->dnet;
        !           694:     typedef enum _state {
        !           695:        SUB_ERROR = -2,
        !           696:        SUB_SUCCESS = -1,
        !           697:        NEED_TYPE = 0,
        !           698:        NEED_SELECT,
        !           699:        NEED_PASV,
        !           700:        NEED_PORT
        !           701:     } state;
        !           702:     
        !           703:     /* Jump into a second level state machine */
        !           704:     while (1) {
        !           705:        switch ((state) ctrl->substate) {
        !           706:          case NEED_TYPE:
        !           707:            if(!data->type|| data->pasv || data->type=='N' || data->type=='L'){
        !           708:                ctrl->substate = NEED_SELECT;
        !           709:                break;
        !           710:            }
        !           711:            if (!ctrl->sent) {
        !           712:                char type[2];
        !           713:                *type = data->type;
        !           714:                *(type+1) = '\0';
        !           715:                status = SendCommand(request, ctrl, "TYPE", type);
        !           716:                if (status == HT_WOULD_BLOCK)
        !           717:                    return HT_WOULD_BLOCK;
        !           718:                else if (status == HT_ERROR)
        !           719:                    ctrl->substate = SUB_ERROR;
        !           720:                ctrl->sent = YES;
        !           721:            } else {
        !           722:                status = HTSocketRead(request, cnet);
        !           723:                if (status == HT_WOULD_BLOCK)
        !           724:                    return HT_WOULD_BLOCK;
        !           725:                else if (status == HT_LOADED) {
        !           726:                    if (ctrl->repcode/100 == 2)
        !           727:                        ctrl->substate = NEED_SELECT;
        !           728:                    else
        !           729:                        ctrl->substate = SUB_ERROR;
        !           730:                } else
        !           731:                    ctrl->substate = SUB_ERROR;
        !           732:                ctrl->sent = NO;
        !           733:            }
        !           734:            break;
        !           735:            
        !           736:          case NEED_SELECT:
        !           737:            if (FTPMode & FTP_DATA_PASV && !data->pasv)
        !           738:                ctrl->substate = NEED_PASV;
        !           739:            else if (ListenSocket(cnet, dnet, data))
        !           740:                ctrl->substate = NEED_PORT;
        !           741:            else
        !           742:                ctrl->substate = SUB_ERROR;
        !           743:            break;
        !           744: 
        !           745:          case NEED_PASV:
        !           746:            if (!ctrl->sent) {
        !           747:                status = SendCommand(request, ctrl, "PASV", NULL);
        !           748:                if (status == HT_WOULD_BLOCK)
        !           749:                    return HT_WOULD_BLOCK;
        !           750:                else if (status == HT_ERROR)
        !           751:                    ctrl->substate = SUB_ERROR;
        !           752:                ctrl->sent = YES;
        !           753:            } else {
        !           754:                status = HTSocketRead(request, cnet);
        !           755:                if (status == HT_WOULD_BLOCK)
        !           756:                    return HT_WOULD_BLOCK;
        !           757:                else if (status == HT_LOADED) {
        !           758:                    if (ctrl->repcode == 227) {             
        !           759:                        /*
        !           760:                        ** If succes, we have to scan for the returned number.
        !           761:                        ** As the format for the response isn't standard,
        !           762:                        ** the best thing to do is to scan for the first digit
        !           763:                        ** after the status code, see RFC1123
        !           764:                        */
        !           765:                        char *host = ctrl->reply;
        !           766:                        int h0, h1, h2, h3, p0=0, p1=0;
        !           767:                        while (*host && !isdigit(*host++));
        !           768:                        if (!*host || sscanf(--host, "%d,%d,%d,%d,%d,%d",
        !           769:                                             &h0,&h1,&h2,&h3,&p0,&p1) < 6) {
        !           770:                            if (PROT_TRACE)
        !           771:                                fprintf(TDEST, "FTP......... PASV No addr\n");
        !           772:                            ctrl->substate = SUB_ERROR;
        !           773:                            break;
        !           774:                        } else {
        !           775:                            int port = (p0<<8)+p1;
        !           776:                            sprintf(data->host, "ftp://%d.%d.%d.%d:%d/",
        !           777:                                    h0, h1, h2, h3, port);
        !           778:                            data->pasv = YES;
        !           779:                            ctrl->substate = SUB_SUCCESS;
        !           780:                        }
        !           781:                    } else {
        !           782:                        ctrl->substate = ListenSocket(cnet, dnet, data) ?
        !           783:                            NEED_PORT : SUB_ERROR;
        !           784:                    }
        !           785:                } else
        !           786:                    ctrl->substate = SUB_ERROR;
        !           787:                ctrl->sent = NO;
        !           788:            }
        !           789:            break;
1.22      frystyk   790: 
1.60    ! frystyk   791:          case NEED_PORT:
        !           792:            if (!ctrl->sent) {
        !           793:                status = SendCommand(request, ctrl, "PORT", data->host);
        !           794:                if (status == HT_WOULD_BLOCK)
        !           795:                    return HT_WOULD_BLOCK;
        !           796:                else if (status == HT_ERROR)
        !           797:                    ctrl->substate = SUB_ERROR;
        !           798:                ctrl->sent = YES;
        !           799:            } else {
        !           800:                status = HTSocketRead(request, cnet);
        !           801:                if (status == HT_WOULD_BLOCK)
        !           802:                    return HT_WOULD_BLOCK;
        !           803:                else if (status == HT_LOADED) {
        !           804:                    data->pasv = NO;
        !           805:                    ctrl->substate = (ctrl->repcode/100 == 2) ?
        !           806:                        SUB_SUCCESS : SUB_ERROR;
        !           807:                } else
        !           808:                    ctrl->substate = SUB_ERROR;
        !           809:                ctrl->sent = NO;
        !           810:            }
        !           811:            break;
1.1       timbl     812: 
1.60    ! frystyk   813:          case SUB_ERROR:
        !           814:            if (PROT_TRACE)
        !           815:                fprintf(TDEST, "FTP......... Can't setup data connection\n");
        !           816:            ctrl->substate = 0;
        !           817:            return HT_ERROR;
        !           818:            break;
1.1       timbl     819: 
1.60    ! frystyk   820:          case SUB_SUCCESS:
        !           821:            if (PROT_TRACE)
        !           822:                fprintf(TDEST, "FTP......... Data connection negotiated\n");
        !           823:            ctrl->substate = 0;
        !           824:            return HT_OK;
        !           825:            break;
1.23      frystyk   826:        }
1.58      frystyk   827:     }
1.23      frystyk   828: }
1.1       timbl     829: 
                    830: 
1.60    ! frystyk   831: /*     HTFTPServerInfo
        !           832: **     ---------------
        !           833: **     This function finds out what server we are talking to.
        !           834: **     Maybe we can upgrade from NLST to LIST.
        !           835: **     Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
        !           836: **     Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for making
        !           837: **     his code available.
1.1       timbl     838: */
1.60    ! frystyk   839: PRIVATE int HTFTPServerInfo (HTRequest *request, HTNet *cnet,
        !           840:                             ftp_ctrl *ctrl, ftp_data *data)
1.23      frystyk   841: {
1.60    ! frystyk   842:     int status;
        !           843:     typedef enum _state {
        !           844:        SUB_ERROR = -2,
        !           845:        SUB_SUCCESS = -1,
        !           846:        NEED_SYST = 0,
        !           847:        CHECK_SYST,
        !           848:        NEED_PWD,
        !           849:        CHECK_PWD
        !           850:     } state;
        !           851: 
        !           852:     /* Jump into a second level state machine */
        !           853:     while (1) {
        !           854:        switch ((state) ctrl->substate) {
        !           855:          case NEED_SYST:
        !           856:            if (!ctrl->sent) {          
        !           857:                if ((ctrl->server = HTDNS_serverVersion(cnet->dns))) {
        !           858:                    if (PROT_TRACE)
        !           859:                        fprintf(TDEST,"FTP Server.. We know from cache that this is a type %d server\n",
        !           860:                               ctrl->server);
        !           861:                    FTPListType(data, ctrl->server);
        !           862:                    return HT_OK;
        !           863:                }
        !           864:                status = SendCommand(request, ctrl, "SYST", NULL);
        !           865:                if (status == HT_WOULD_BLOCK)
        !           866:                    return HT_WOULD_BLOCK;
        !           867:                else if (status == HT_ERROR)
        !           868:                    ctrl->substate = SUB_ERROR;
        !           869:                ctrl->sent = YES;
        !           870:            } else {
        !           871:                status = HTSocketRead(request, cnet);
        !           872:                if (status == HT_WOULD_BLOCK)
        !           873:                    return HT_WOULD_BLOCK;
        !           874:                else if (status == HT_LOADED) {
        !           875:                    ctrl->substate=ctrl->repcode==215 ? CHECK_SYST : NEED_PWD;
        !           876:                } else
        !           877:                    ctrl->substate = SUB_ERROR;
        !           878:                ctrl->sent = NO;
1.23      frystyk   879:            }
                    880:            break;
                    881: 
1.60    ! frystyk   882:          case CHECK_SYST:
1.23      frystyk   883:            {
1.60    ! frystyk   884:                char *reply = ctrl->reply;
        !           885:                if (!*reply) {
1.43      frystyk   886:                    if (PROT_TRACE)
1.48      frystyk   887:                        fprintf(TDEST, "FTP......... No server info?\n");
1.60    ! frystyk   888:                    ctrl->substate = NEED_PWD;
1.23      frystyk   889:                    break;
                    890:                }
1.60    ! frystyk   891:                if (strncmp(reply, "UNIX Type: L8MAC-OSMachTen", 28) == 0) {
        !           892:                    ctrl->server = FTP_MACHTEN;
        !           893:                } else if (strstr(reply, "UNIX") != NULL) {
        !           894:                    ctrl->server = FTP_UNIX;
        !           895:                } else if (strncmp(reply, "VMS", 3) == 0) {
        !           896:                    ctrl->server = FTP_VMS;
        !           897:                } else if ((strncmp(reply, "VM/CMS", 6) == 0) ||
        !           898:                           (strncmp(reply, "VM", 2) == 0)) {
        !           899:                    ctrl->server = FTP_CMS;
        !           900:                } else if (strncmp(reply, "DCTS", 4) == 0) {
        !           901:                    ctrl->server = FTP_DCTS;
        !           902:                } else if (strstr(reply, "MAC-OS TCP/ConnectII") != NULL) {
        !           903:                    ctrl->server = FTP_TCPC;
1.23      frystyk   904:                    /* Check old versions of TCP/C using / in pathnames */
1.60    ! frystyk   905:                    ctrl->server |= FTP_UNSURE;
        !           906:                } else if (strncmp(reply, "MACOS Peter's Server", 20) == 0) {
        !           907:                    ctrl->server = FTP_PETER_LEWIS;
        !           908:                } else if (strncmp(reply, "Windows_NT", 10) == 0) {
        !           909:                    ctrl->server = FTP_WINNT;
1.23      frystyk   910:                }
                    911:                
                    912:                /* If we are unsure, try PWD to get more information */
1.60    ! frystyk   913:                if (ctrl->server & FTP_UNSURE)
        !           914:                    ctrl->substate = NEED_PWD;
1.23      frystyk   915:                else
1.60    ! frystyk   916:                    ctrl->substate = SUB_SUCCESS;
1.1       timbl     917:            }
1.23      frystyk   918:            break;
                    919: 
                    920:          case NEED_PWD:
1.60    ! frystyk   921:            if (!ctrl->sent) {
        !           922:                status = SendCommand(request, ctrl, "PWD", NULL);
        !           923:                if (status == HT_WOULD_BLOCK)
        !           924:                    return HT_WOULD_BLOCK;
        !           925:                else if (status == HT_ERROR)
        !           926:                    ctrl->substate = SUB_ERROR;
        !           927:                ctrl->sent = YES;
1.23      frystyk   928:            } else {
1.60    ! frystyk   929:                status = HTSocketRead(request, cnet);
        !           930:                if (status == HT_WOULD_BLOCK)
        !           931:                    return HT_WOULD_BLOCK;
        !           932:                else if (status == HT_LOADED) {
        !           933:                    ctrl->substate = (ctrl->repcode/100 == 2) ?
        !           934:                        CHECK_PWD : SUB_ERROR;
        !           935:                } else
        !           936:                    ctrl->substate = SUB_ERROR;
        !           937:                ctrl->sent = NO;
1.23      frystyk   938:            }
                    939:            break;
                    940: 
1.60    ! frystyk   941:          case CHECK_PWD:
        !           942:            {
        !           943:                char *start = strchr(ctrl->reply, '"');
        !           944:                char *end;
        !           945:                if (!start || (end = strchr(++start, '"')) == NULL) {
1.43      frystyk   946:                    if (PROT_TRACE)
1.60    ! frystyk   947:                        fprintf(TDEST, "FTP......... No current directory?\n");
        !           948:                    ctrl->server = FTP_GENERIC;
1.23      frystyk   949:                } else {
1.60    ! frystyk   950:                    *end = '\0';
        !           951:                    if (ctrl->server == FTP_TCPC) {
        !           952:                        ctrl->server = *start == '/' ? FTP_NCSA : FTP_TCPC;
        !           953:                    } else if (*start == '/') {
        !           954:                        /* path names starting with / imply Unix, right? */
        !           955:                        ctrl->server = FTP_UNIX;
        !           956:                    } else if (*(end-1) == ']') {
        !           957:                        /* path names ending with ] imply VMS, right? */
        !           958:                        ctrl->server = FTP_VMS;
        !           959:                    } else
        !           960:                        ctrl->server = FTP_GENERIC;
1.23      frystyk   961:                }
1.60    ! frystyk   962:                ctrl->substate = SUB_SUCCESS;
1.1       timbl     963:            }
1.23      frystyk   964:            break;
1.1       timbl     965: 
1.60    ! frystyk   966:          case SUB_ERROR:
        !           967:            if (PROT_TRACE)
        !           968:                fprintf(TDEST, "FTP......... Can't get server information\n");
        !           969:            ctrl->substate = 0;
        !           970:            return HT_ERROR;
1.23      frystyk   971:            break;
1.22      frystyk   972: 
1.60    ! frystyk   973:          case SUB_SUCCESS:
        !           974:            if (PROT_TRACE) {
        !           975:                fprintf(TDEST, "FTP Server.. %s server %d\n",
        !           976:                        ctrl->server & FTP_UNSURE ? "Looks like" : "Guessed",
        !           977:                        ctrl->server & ~(FTP_UNSURE));
1.22      frystyk   978:            }
1.60    ! frystyk   979:            HTDNS_setServerVersion(cnet->dns, ctrl->server);
        !           980:            FTPListType(data, ctrl->server);
        !           981:            ctrl->substate = 0;
        !           982:            return HT_OK;
1.23      frystyk   983:            break;
1.22      frystyk   984:        }
1.23      frystyk   985:     }
                    986: }
                    987: 
1.60    ! frystyk   988: /*     HTFTPGetData
        !           989: **     ------------
        !           990: **     This function asks for the file or a directory. First we try in one go,
1.23      frystyk   991: **     but if that doesn't work, then we use CWD for each segment and then
                    992: **     try to retrieve it. If that also fails, then we try if it is a
1.60    ! frystyk   993: **     directory.
        !           994: **     Returns HT_OK, HT_LOADED, HT_ERROR, or HT_WOULD_BLOCK
1.23      frystyk   995: */
1.60    ! frystyk   996: PRIVATE int HTFTPGetData (HTRequest *request, HTNet *cnet,
        !           997:                          ftp_ctrl *ctrl, ftp_data *data)
1.23      frystyk   998: {
                    999:     int status;
1.60    ! frystyk  1000:     char *segment = NULL;
        !          1001:     HTNet *dnet = ctrl->dnet;
        !          1002:     typedef enum _state {
        !          1003:        SUB_ERROR = -2,
        !          1004:        SUB_SUCCESS = -1,
        !          1005:        NEED_SELECT = 0,
        !          1006:        NEED_CONNECT,
        !          1007:        NEED_ACCEPT,
        !          1008:        NEED_ACTION,
        !          1009:         NEED_CWD,
        !          1010:        NEED_SEGMENT,
        !          1011:        NEED_STREAM,
        !          1012:        NEED_BODY,
        !          1013:        NEED_EPILOG
        !          1014:     } state;
        !          1015: 
        !          1016:     /* Jump into a second level state machine */
        !          1017:     while (1) {
        !          1018:        switch ((state) ctrl->substate) {
        !          1019:          case NEED_SELECT:
        !          1020:            ctrl->substate = data->pasv ? NEED_CONNECT : NEED_ACTION;
        !          1021:            break;
        !          1022: 
        !          1023:          case NEED_CONNECT:
        !          1024:            status = HTDoConnect(dnet, data->host, FTP_DATA);
        !          1025:            if (status == HT_WOULD_BLOCK)
        !          1026:                return HT_WOULD_BLOCK;
        !          1027:            else if (status == HT_OK) {
        !          1028:                if (PROT_TRACE)
        !          1029:                    fprintf(TDEST, "FTP Data.... Active data socket %d\n",
        !          1030:                            dnet->sockfd);
        !          1031:                ctrl->substate = NEED_ACTION;
        !          1032:            } else {                              /* Swap to PORT on the fly */
        !          1033:                NETCLOSE(dnet->sockfd);
        !          1034:                dnet->sockfd = INVSOC;
        !          1035:                if (PROT_TRACE)
        !          1036:                    fprintf(TDEST, "FTP......... Swap to PORT on the fly\n");
        !          1037:                ctrl->substate = 0;
        !          1038:                FREE(segment);
        !          1039:                return HT_OK;
        !          1040:            }
1.23      frystyk  1041:            break;
                   1042: 
1.60    ! frystyk  1043:          case NEED_ACCEPT:
        !          1044:            {
        !          1045:                SOCKFD newfd = INVSOC;
        !          1046:                status = HTDoAccept(dnet, &newfd);
        !          1047:                if (status == HT_WOULD_BLOCK)
        !          1048:                    return HT_WOULD_BLOCK;
        !          1049:                else if (status == HT_OK) {
        !          1050:                    if (PROT_TRACE)
        !          1051:                        fprintf(TDEST, "FTP Data.... Passive data socket %d\n",
        !          1052:                                dnet->sockfd);
        !          1053:                    NETCLOSE(dnet->sockfd);
        !          1054:                    dnet->sockfd = newfd;
1.43      frystyk  1055:                    if (PROT_TRACE)
1.60    ! frystyk  1056:                        fprintf(TDEST, "FTP Data.... New data socket %d\n",
        !          1057:                                dnet->sockfd);
        !          1058:                    ctrl->substate = NEED_STREAM;
        !          1059:                } else
        !          1060:                    ctrl->substate = SUB_ERROR;
1.23      frystyk  1061:            }
1.60    ! frystyk  1062:            break;
1.33      frystyk  1063: 
1.60    ! frystyk  1064:          case NEED_ACTION:
        !          1065:            if (!ctrl->sent) {
        !          1066:                char *cmd = (data->type=='L') ? "LIST" :
        !          1067:                    (data->type=='N') ? "NLST" : "RETR";
        !          1068:                StrAllocCopy(segment, data->offset);
        !          1069:                HTUnEscape(segment);
        !          1070:                HTCleanTelnetString(segment);
        !          1071:                status = SendCommand(request, ctrl, cmd, segment);
        !          1072:                if (status == HT_WOULD_BLOCK)
        !          1073:                    return HT_WOULD_BLOCK;
        !          1074:                else if (status == HT_ERROR)
        !          1075:                    ctrl->substate = SUB_ERROR;
        !          1076:                ctrl->sent = YES;
        !          1077:            } else {
        !          1078:                status = HTSocketRead(request, cnet);
        !          1079:                if (status == HT_WOULD_BLOCK)
        !          1080:                    return HT_WOULD_BLOCK;
        !          1081:                else if (status == HT_LOADED) {
        !          1082:                    int code = ctrl->repcode;
        !          1083:                    if (code==125 || code==150 || code==225)
        !          1084:                        ctrl->substate=data->pasv ? NEED_STREAM : NEED_ACCEPT;
        !          1085:                    else if (code/100==5 && !ctrl->cwd)
        !          1086:                        ctrl->substate = NEED_SEGMENT;
        !          1087:                    else
        !          1088:                        ctrl->substate = SUB_ERROR;                     
        !          1089:                } else
        !          1090:                    ctrl->substate = SUB_ERROR;
        !          1091:                ctrl->sent = NO;
1.23      frystyk  1092:            }
                   1093:            break;
1.60    ! frystyk  1094: 
        !          1095:          case NEED_SEGMENT:
1.23      frystyk  1096:            {
1.60    ! frystyk  1097:                char *ptr;
        !          1098:                if (data->offset == data->file) {
        !          1099:                    StrAllocCopy(segment, "/");
        !          1100:                    data->offset++;
        !          1101:                    ctrl->substate = NEED_CWD;
        !          1102:                } else {
        !          1103:                    if ((ptr = strchr(data->offset, '/'))) {
        !          1104:                        *ptr='\0';
        !          1105:                        StrAllocCopy(segment, data->offset);
        !          1106:                        *ptr='/';
        !          1107:                        data->offset = ++ptr;
        !          1108:                        HTUnEscape(segment);
        !          1109:                        HTCleanTelnetString(segment);
        !          1110:                        ctrl->substate = NEED_CWD;
1.33      frystyk  1111:                    } else
1.60    ! frystyk  1112:                        ctrl->substate = NEED_ACTION;
1.23      frystyk  1113:                }
                   1114:            }
                   1115:            break;
                   1116: 
1.60    ! frystyk  1117:          case NEED_CWD:
        !          1118:            if (!ctrl->sent) {
        !          1119:                status = SendCommand(request, ctrl, "CWD", segment);
        !          1120:                if (status == HT_WOULD_BLOCK)
        !          1121:                    return HT_WOULD_BLOCK;
        !          1122:                else if (status == HT_ERROR)
        !          1123:                    ctrl->substate = SUB_ERROR;
        !          1124:                ctrl->cwd = YES;
        !          1125:                ctrl->sent = YES;
        !          1126:            } else {
        !          1127:                status = HTSocketRead(request, cnet);
        !          1128:                if (status == HT_WOULD_BLOCK)
        !          1129:                    return HT_WOULD_BLOCK;
        !          1130:                else if (status == HT_LOADED) {
        !          1131:                    if (ctrl->repcode/100 == 2)
        !          1132:                        ctrl->substate = NEED_SEGMENT;
        !          1133:                    else
        !          1134:                        ctrl->substate = SUB_ERROR;
        !          1135:                } else
        !          1136:                    ctrl->substate = SUB_ERROR;
        !          1137:                ctrl->sent = NO;
1.33      frystyk  1138:            }
1.60    ! frystyk  1139:            break;
1.33      frystyk  1140: 
1.60    ! frystyk  1141:          case NEED_STREAM:
        !          1142:            {
        !          1143:                HTFormat format;
        !          1144:                dnet->isoc = HTInputSocket_new(dnet->sockfd);
        !          1145:                if (data->type == 'L')                               /* LIST */
        !          1146:                    format = HTAtom_for("text/x-ftplist");
        !          1147:                else if (data->type == 'N')
        !          1148:                    format = HTAtom_for("text/x-ftpnlst");           /* NLST */
        !          1149:                else
        !          1150:                    format = HTAnchor_format(request->anchor);
        !          1151:                dnet->target = HTStreamStack(format, request->output_format,
        !          1152:                                             request->output_stream,
        !          1153:                                             request,YES);
        !          1154:                ctrl->substate = NEED_BODY;
        !          1155:            }
1.23      frystyk  1156:            break;
                   1157: 
1.60    ! frystyk  1158:          case NEED_BODY:
        !          1159:            status = HTSocketRead(request, dnet);
        !          1160:            if (status == HT_WOULD_BLOCK)
        !          1161:                return HT_WOULD_BLOCK;
        !          1162:            else if (status == HT_LOADED)
        !          1163:                ctrl->substate = NEED_EPILOG;
        !          1164:            else
        !          1165:                ctrl->substate = SUB_ERROR;
        !          1166:            break;
        !          1167: 
        !          1168:          case NEED_EPILOG:
        !          1169:            status = HTSocketRead(request, cnet);
        !          1170:            if (status == HT_WOULD_BLOCK)
        !          1171:                return HT_WOULD_BLOCK;
        !          1172:            else if (status == HT_LOADED) {
        !          1173:                if (ctrl->repcode/100 == 2)
        !          1174:                    ctrl->substate = SUB_SUCCESS;
1.33      frystyk  1175:                else
1.60    ! frystyk  1176:                    ctrl->substate = SUB_ERROR;
1.33      frystyk  1177:            } else
1.60    ! frystyk  1178:                ctrl->substate = SUB_ERROR;
1.23      frystyk  1179:            break;
1.22      frystyk  1180: 
1.60    ! frystyk  1181:          case SUB_ERROR:
        !          1182:            if (PROT_TRACE)
        !          1183:                fprintf(TDEST, "FTP......... Can't retrieve file\n");
        !          1184:            ctrl->substate = 0;
        !          1185:            FREE(segment);
        !          1186:            return HT_ERROR;
1.23      frystyk  1187:            break;
                   1188: 
1.60    ! frystyk  1189:          case SUB_SUCCESS:
        !          1190:            if (PROT_TRACE)
        !          1191:                fprintf(TDEST, "FTP......... File's loaded\n");
        !          1192:            ctrl->substate = 0;
        !          1193:            FREE(segment);
        !          1194:            return HT_LOADED;
1.23      frystyk  1195:            break;
1.1       timbl    1196:        }
                   1197:     }
1.23      frystyk  1198: }
1.1       timbl    1199: 
1.23      frystyk  1200: /* ------------------------------------------------------------------------- */
                   1201: 
                   1202: /*     Retrieve File from Server as an atomic action. 
                   1203: **     -----------------------------------------------
1.58      frystyk  1204: **     Given a hypertext address, this routine loads a document.
1.23      frystyk  1205: **
                   1206: ** On entry,
1.58      frystyk  1207: **      request                This is the request structure
                   1208: **     returns         HT_ERROR        Error has occured in call back
                   1209: **                     HT_OK           Call back was OK
                   1210: */
                   1211: PUBLIC int HTLoadFTP ARGS3(SOCKET, soc, HTRequest *, request, SockOps, ops)
                   1212: {
                   1213:     int status = HT_ERROR;
1.60    ! frystyk  1214:     HTNet *cnet = request->net;
1.58      frystyk  1215:     ftp_ctrl *ctrl;
1.60    ! frystyk  1216:     ftp_data *data;
1.58      frystyk  1217:     char *url = HTAnchor_physical(request->anchor);
1.23      frystyk  1218: 
1.58      frystyk  1219:     /*
1.60    ! frystyk  1220:     ** Initiate a new FTP ctrl and data structure and bind to request structure
1.58      frystyk  1221:     ** This is actually state FTP_BEGIN, but it can't be in the state
                   1222:     ** machine as we need the structure first.
                   1223:     */
                   1224:     if (ops == FD_NONE) {
                   1225:        if (PROT_TRACE) fprintf(TDEST, "FTP......... Looking for `%s\'\n",url);
1.60    ! frystyk  1226:        if ((ctrl = (ftp_ctrl *) calloc(1, sizeof(ftp_ctrl))) == NULL ||
        !          1227:            (data = (ftp_data *) calloc(1, sizeof(ftp_data))) == NULL)
1.58      frystyk  1228:            outofmem(__FILE__, "HTLoadFTP");
1.60    ! frystyk  1229:        ctrl->cmd = HTChunkCreate(128);
1.58      frystyk  1230:        ctrl->state = FTP_BEGIN;
1.60    ! frystyk  1231:        ctrl->server = FTP_UNSURE;
        !          1232:        HTNet_dup(cnet, &ctrl->dnet);
        !          1233:        cnet->preemtive = NO;     /* Control connection must be non-blocking */
        !          1234:        cnet->context = ctrl;              /* Context for control connection */
        !          1235:        ctrl->dnet->context = data;           /* Context for data connection */
        !          1236:     } else if (ops == FD_CLOSE) {                            /* Interrupted */
        !          1237:        if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request))
        !          1238:            FTPCleanup(request, HT_IGNORE);
        !          1239:        else
        !          1240:            FTPCleanup(request, HT_INTERRUPTED);
        !          1241:        return HT_OK;
        !          1242:     } else {
        !          1243:        ctrl = (ftp_ctrl *) cnet->context;              /* Get existing copy */
        !          1244:        data = (ftp_data *) ctrl->dnet->context;
        !          1245:     }
1.58      frystyk  1246: 
                   1247:     /* Now jump into the machine. We know the state from the previous run */
                   1248:     while (1) {
                   1249:        switch (ctrl->state) {
1.60    ! frystyk  1250:          case FTP_BEGIN:
        !          1251:            HTFTPParseURL(url, ctrl, data);
        !          1252:            if (data->type != 'N') {
        !          1253:                HTBind_getBindings(request->anchor);
        !          1254:                if (HTAnchor_encoding(request->anchor) != HTAtom_for("7bit"))
        !          1255:                    data->type = 'I';
        !          1256:            }
        !          1257:            ctrl->state = FTP_NEED_CCON;
        !          1258:            break;
        !          1259: 
        !          1260:          case FTP_NEED_CCON:
        !          1261:            status = HTDoConnect(cnet, url, FTP_PORT);
        !          1262:            if (status == HT_OK) {
        !          1263:                char *s_class = HTDNS_serverClass(cnet->dns);
        !          1264:                if (s_class && strcasecomp(s_class, "ftp")) {
        !          1265:                    HTErrorAdd(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0,
        !          1266:                               "HTLoadFTP");
        !          1267:                    ctrl->state = FTP_ERROR;
        !          1268:                    break;
        !          1269:                }
        !          1270:                HTDNS_setServerClass(cnet->dns, "ftp");
        !          1271:                if (HTDNS_socket(cnet->dns) != INVSOC)
        !          1272:                    ctrl->reset = 1;
1.23      frystyk  1273:                else
1.60    ! frystyk  1274:                    HTDNS_setSocket(cnet->dns, cnet->sockfd);
        !          1275:                if (PROT_TRACE)
        !          1276:                    fprintf(TDEST, "FTP Ctrl.... Connected, socket %d\n",
        !          1277:                            cnet->sockfd);
1.23      frystyk  1278: 
1.60    ! frystyk  1279:                /* Set up stream TO network */
        !          1280:                request->input_stream = HTWriter_new(cnet, YES);
1.23      frystyk  1281: 
1.60    ! frystyk  1282:                /*
        !          1283:                ** Set up concurrent read/write if this request isn't the
        !          1284:                ** source for a PUT or POST. As source we don't start reading
        !          1285:                ** before all destinations are ready. If destination then
        !          1286:                ** register the input stream and get ready for read
        !          1287:                */
        !          1288:                if (HTRequest_isPostWeb(request)) {
        !          1289:                    HTEvent_Register(cnet->sockfd, request, (SockOps) FD_READ,
        !          1290:                                     HTLoadFTP, cnet->priority);
        !          1291:                    HTRequest_linkDestination(request);
        !          1292:                }
        !          1293: 
        !          1294:                /* Set up stream FROM network and corresponding read buffer */
        !          1295:                cnet->isoc = HTInputSocket_new(cnet->sockfd);
        !          1296:                cnet->target = FTPStatus_new(request, ctrl);
        !          1297:                ctrl->state = FTP_NEED_LOGIN;
        !          1298:            } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT)
        !          1299:                return HT_OK;
        !          1300:            else
        !          1301:                ctrl->state = FTP_ERROR;               /* Error or interrupt */
        !          1302:            break;
        !          1303: 
        !          1304:          case FTP_NEED_LOGIN:
        !          1305:            status = HTFTPLogin(request, cnet, ctrl);
        !          1306:            if (status == HT_WOULD_BLOCK) return HT_OK;
        !          1307:            ctrl->state = (status == HT_OK) ? FTP_NEED_DCON : FTP_ERROR;
        !          1308:            break;
        !          1309: 
        !          1310:          case FTP_NEED_DCON:
        !          1311:            status = HTFTPDataConnection(request, cnet, ctrl, data);
        !          1312:            if (status == HT_WOULD_BLOCK) return HT_OK;
        !          1313:            if (status == HT_OK)
        !          1314:                ctrl->state = (data->type=='N') ?
        !          1315:                    FTP_NEED_SERVER : FTP_NEED_DATA;
        !          1316:            else
        !          1317:                ctrl->state = FTP_ERROR;
        !          1318:            break;
        !          1319: 
        !          1320:          case FTP_NEED_DATA:
        !          1321:            status = HTFTPGetData(request, cnet, ctrl, data);
        !          1322:            if (status == HT_WOULD_BLOCK) return HT_OK;
        !          1323:            if (status == HT_LOADED)
        !          1324:                ctrl->state = FTP_SUCCESS;
        !          1325:            else if (status == HT_OK)
        !          1326:                ctrl->state = FTP_NEED_DCON;
        !          1327:            else if (!FTP_DIR(data)) {
        !          1328:                FTPListType(data, ctrl->server);
        !          1329:                ctrl->state = FTP_NEED_SERVER;         /* Try a dir instead? */
        !          1330:            } else
        !          1331:                ctrl->state = FTP_ERROR;
        !          1332:            break;
1.23      frystyk  1333: 
1.60    ! frystyk  1334:          case FTP_NEED_SERVER:
        !          1335:            status = HTFTPServerInfo(request, cnet, ctrl, data);
        !          1336:            if (status == HT_WOULD_BLOCK) return HT_OK;
        !          1337:            ctrl->state = FTP_NEED_DATA;
        !          1338:            break;
        !          1339: 
        !          1340:          case FTP_SUCCESS:
        !          1341:            if (HTRequest_isPostWeb(request)) {
        !          1342:                BOOL main = HTRequest_isMainDestination(request);
        !          1343:                if (HTRequest_isDestination(request)) {
        !          1344:                    HTLink *link =
        !          1345:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
        !          1346:                                          (HTAnchor *) request->anchor);
        !          1347:                    HTAnchor_setLinkResult(link, HT_LINK_OK);
1.23      frystyk  1348:                }
1.60    ! frystyk  1349:                HTRequest_removeDestination(request);
        !          1350:                FTPCleanup(request, main ? HT_LOADED : HT_IGNORE);
        !          1351:            } else
        !          1352:                FTPCleanup(request, HT_LOADED);
        !          1353:            return HT_OK;
        !          1354:            break;
        !          1355:            
        !          1356:          case FTP_ERROR:
        !          1357:            /* Clean up the other connections or just this one */
        !          1358:            if (HTRequest_isPostWeb(request)) {
        !          1359:                BOOL main = HTRequest_isMainDestination(request);
        !          1360:                HTRequest_killPostWeb(request);
        !          1361:                if (HTRequest_isDestination(request)) {
        !          1362:                    HTLink *link =
        !          1363:                        HTAnchor_findLink((HTAnchor *) request->source->anchor,
        !          1364:                                          (HTAnchor *) request->anchor);
        !          1365:                    HTAnchor_setLinkResult(link, HT_LINK_ERROR);
1.30      luotonen 1366:                }
1.60    ! frystyk  1367:                HTRequest_removeDestination(request);
        !          1368:                FTPCleanup(request, main ? HT_ERROR : HT_IGNORE);
        !          1369:            } else
        !          1370:                FTPCleanup(request, HT_ERROR);
        !          1371:            return HT_OK;
        !          1372:            break;
1.23      frystyk  1373:        }
1.60    ! frystyk  1374:     } /* End of while(1) */
1.23      frystyk  1375: }
1.22      frystyk  1376: 

Webmaster