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

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

Webmaster