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

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

Webmaster