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

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.1       timbl      25: ** History:
                     26: **      2 May 91       Written TBL, as a part of the WorldWideWeb project.
                     27: **     15 Jan 92       Bug fix: close() was used for NETCLOSE for control soc
                     28: **     10 Feb 92       Retry if cached connection times out or breaks
                     29: **      8 Dec 92       Bug fix 921208 TBL after DD
                     30: **     17 Dec 92       Anon FTP password now just WWWuser@ suggested by DD
1.2       timbl      31: **                     fails on princeton.edu!
1.22    ! frystyk    32: **     27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
        !            33: **                     must be Unix-style and cannot include the device
        !            34: **                     or top directory.
        !            35: **      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
        !            36: **                     anonymous FTP
        !            37: **      25 Mar 94 (LM)  Added code to recognize different ftp server types
        !            38: **                      and code to parse dates and sizes on most hosts.
        !            39: **     27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
1.1       timbl      40: **
                     41: ** Options:
                     42: **     LISTEN          We listen, the other guy connects for data.
                     43: **                     Otherwise, other way round, but problem finding our
                     44: **                     internet address!
                     45: **
1.22    ! frystyk    46: ** Notes:
        !            47: **                             Portions Copyright 1994 Trustees of Dartmouth College
        !            48: **                     Code for recognizing different FTP servers and
        !            49: **                     parsing "ls -l" output taken from Macintosh Fetch
        !            50: **                     program with permission from Jim Matthews,
        !            51: **                     Dartmouth Software Development Team.
        !            52: **
        !            53: **     BUGS:   @@@     Limit connection cache size!
        !            54: **                     Error reporting to user.
        !            55: **                     400 & 500 errors are acked by user with windows.
        !            56: **                     Use configuration file for user names
        !            57: **             
1.1       timbl      58: */             
                     59: 
1.22    ! frystyk    60: /* Implementation dependent include files */
        !            61: #include "tcp.h"
1.1       timbl      62: 
1.22    ! frystyk    63: /* Library include files */
1.1       timbl      64: #include "HTParse.h"
                     65: #include "HTUtils.h"
                     66: #include "HTTCP.h"
                     67: #include "HTAnchor.h"
1.22    ! frystyk    68: #include "HTFile.h"
1.6       secret     69: #include "HTBTree.h"
                     70: #include "HTChunk.h"
1.22    ! frystyk    71: #include "HTAlert.h"
1.21      frystyk    72: #include "HTDirBrw.h"
1.22    ! frystyk    73: #include "HTFTP.h"                                      /* Implemented here */
        !            74: 
        !            75: /* Macros and other defines */
        !            76: #define REPEAT_PORT                   /* Give the port number for each file */
        !            77: #define REPEAT_LISTEN        /* Close each listen socket and open a new one */
        !            78: /* define POLL_PORTS            If allocation does not work, poll ourselves.*/
        !            79: #define LISTEN_BACKLOG 2       /* Number of pending connect requests (TCP)*/
        !            80: #define LISTEN  /* @@@@ Test LJM */
        !            81: 
        !            82: #define DATA_BUFFER_SIZE 2048
        !            83: #define FIRST_TCP_PORT  1024          /* Region to try for a listening port */
        !            84: #define LAST_TCP_PORT   5999   
        !            85: #define LINE_LENGTH     256
        !            86: #define NEXT_DATA_CHAR next_data_char()
        !            87: 
1.1       timbl      88: #ifndef IPPORT_FTP
1.22    ! frystyk    89: #define IPPORT_FTP      21
1.1       timbl      90: #endif
                     91: 
1.22    ! frystyk    92: /* Globals */
        !            93: 
1.1       timbl      94: 
1.22    ! frystyk    95: /* Type definitions and global variables etc. local to this module */
1.1       timbl      96: typedef struct _connection {
                     97:     struct _connection *       next;   /* Link on list         */
                     98:     u_long                     addr;   /* IP address           */
                     99:     int                                socket; /* Socket number for communication */
                    100:     BOOL                       binary; /* Binary mode? */
1.22    ! frystyk   101:     HTInputSocket *             isoc;
        !           102:     char *                     uid;
        !           103:     char *                     passwd;
1.1       timbl     104: } connection;
                    105: 
1.22    ! frystyk   106: typedef enum _HTFTPServerType {
        !           107:     GENERIC_SERVER = 0,
        !           108:     MACHTEN_SERVER,
        !           109:     UNIX_SERVER,
        !           110:     VMS_SERVER,
        !           111:     CMS_SERVER,
        !           112:     DCTS_SERVER,
        !           113:     TCPC_SERVER,
        !           114:     PETER_LEWIS_SERVER,
        !           115:     NCSA_SERVER
        !           116: } HTFTPServerType;
1.2       timbl     117: 
1.22    ! frystyk   118: PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from FTP host */
        !           119: PRIVATE connection *control;                  /* Current control connection */
1.1       timbl     120: PRIVATE int    data_soc = -1;          /* Socket for data transfer =invalid */
1.22    ! frystyk   121: PRIVATE int     server_type = GENERIC_SERVER;   /* the type of ftp host */
        !           122: PRIVATE int     unsure_type = FALSE;            /* sure about the type? */
        !           123: PRIVATE BOOLEAN use_list = FALSE;              /* use the LIST command? */
        !           124: PRIVATE char data_buffer[DATA_BUFFER_SIZE];            /* Input data buffer */
        !           125: PRIVATE char * data_read_pointer;
        !           126: PRIVATE char * data_write_pointer;
1.1       timbl     127: 
                    128: #ifdef POLL_PORTS
                    129: PRIVATE        unsigned short  port_number = FIRST_TCP_PORT;
                    130: #endif
                    131: 
                    132: #ifdef LISTEN
                    133: PRIVATE int     master_socket = -1;    /* Listening socket = invalid   */
                    134: PRIVATE char   port_command[255];      /* Command for setting the port */
                    135: PRIVATE fd_set open_sockets;           /* Mask of active channels */
                    136: PRIVATE int    num_sockets;            /* Number of sockets to scan */
                    137: #else
                    138: PRIVATE        unsigned short  passive_port;   /* Port server specified for data */
                    139: #endif
                    140: 
1.22    ! frystyk   141: #ifdef NEW_CODE
        !           142: #define NEXT_CHAR HTGetCharacter()     /* Use function in HTFormat.c */
        !           143: #endif
        !           144: 
        !           145: /* TEPORARY!!!!!! */
        !           146: #define HT_INTERRUPTED -2
        !           147: PRIVATE BOOL interrupted_in_htgetcharacter;
        !           148: /* ************* */
        !           149: 
        !           150: /* ------------------------------------------------------------------------- */
        !           151: /*                        Directory Specific Functions                      */
        !           152: /* ------------------------------------------------------------------------- */
        !           153: 
        !           154: /*
        !           155:  * is_ls_date() --
        !           156:  *      Return TRUE if s points to a string of the form:
        !           157:  *              "Sep  1  1990 " or
        !           158:  *              "Sep 11 11:59 " or
        !           159:  *              "Dec 12 1989  " or
        !           160:  *              "FCv 23 1990  " ...
        !           161:  *
        !           162:  * Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews)
        !           163:  */
        !           164: PRIVATE BOOL is_ls_date ARGS1(char *, s)
        !           165: {
        !           166:     /* must start with three alpha characget_striters */
        !           167:     if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
        !           168:        return FALSE;
        !           169:     
        !           170:     /* space */
        !           171:     if (*s++ != ' ')
        !           172:        return FALSE;
        !           173:     
        !           174:     /* space or digit */
        !           175:     if ((*s != ' ') && !isdigit(*s))
        !           176:        return FALSE;
        !           177:     s++;
        !           178:     
        !           179:     /* digit */
        !           180:     if (!isdigit(*s++))
        !           181:        return FALSE;
        !           182:     
        !           183:     /* space */
        !           184:     if (*s++ != ' ')
        !           185:        return FALSE;
        !           186:     
        !           187:     /* space or digit */
        !           188:     if ((*s != ' ') && !isdigit(*s))
        !           189:        return FALSE;
        !           190:     s++;
        !           191:     
        !           192:     /* digit */
        !           193:     if (!isdigit(*s++))
        !           194:        return FALSE;
        !           195:     
        !           196:     /* colon or digit */
        !           197:     if ((*s != ':') && !isdigit(*s))
        !           198:        return FALSE;
        !           199:     s++;
        !           200:     
        !           201:     /* digit */
        !           202:     if (!isdigit(*s++))
        !           203:        return FALSE;
        !           204:     
        !           205:     /* space or digit */
        !           206:     if ((*s != ' ') && !isdigit(*s))
        !           207:        return FALSE;
        !           208:     s++;
        !           209:     
        !           210:     /* space */
        !           211:     if (*s++ != ' ')
        !           212:        return FALSE;
        !           213:     
        !           214:     return TRUE;
        !           215: } /* is_ls_date() */
        !           216: 
        !           217: 
        !           218: /*                                                             HTStrpMonth()
        !           219: **
        !           220: **     Returns the number of the month given or -1 on error.
        !           221: **
        !           222: **     BUG: Handles US dates only!!!
        !           223: */
        !           224: PRIVATE int HTStrpMonth ARGS1(char *, month)
        !           225: {
        !           226:     int ret;
        !           227:     if (!strncmp(month, "JAN", 3))
        !           228:        ret = 0;
        !           229:     else if (!strncmp(month, "FEB", 3))
        !           230:        ret = 1;
        !           231:     else if (!strncmp(month, "MAR", 3))
        !           232:        ret = 2;
        !           233:     else if (!strncmp(month, "APR", 3))
        !           234:        ret = 3;
        !           235:     else if (!strncmp(month, "MAY", 3))
        !           236:        ret = 4;
        !           237:     else if (!strncmp(month, "JUN", 3))
        !           238:        ret = 5;
        !           239:     else if (!strncmp(month, "JUL", 3))
        !           240:        ret = 6;
        !           241:     else if (!strncmp(month, "AUG", 3))
        !           242:        ret = 7;
        !           243:     else if (!strncmp(month, "SEP", 3))
        !           244:        ret = 8;
        !           245:     else if (!strncmp(month, "OCT", 3))
        !           246:        ret = 9;
        !           247:     else if (!strncmp(month, "NOV", 3))
        !           248:        ret = 10;
        !           249:     else if (!strncmp(month, "DEC", 3))
        !           250:        ret = 11;
        !           251:     else {
        !           252:        ret = -1;
        !           253:        if (TRACE) fprintf(stderr, "HTStrpMonth: Couldn't resolve date.\n");
        !           254:     }
        !           255:     return ret;
        !           256: }
        !           257: 
        !           258: 
        !           259: /*                                                             HTStrpTime()
        !           260: **
        !           261: **     Converts a date string from 'ls -l' to a unsigned long number
        !           262: **     This is needed in order to put out the date using the same format
        !           263: **     for all directory listings.
        !           264: **
        !           265: **     Returns 0L on error.
        !           266: */
        !           267: PRIVATE unsigned long HTStrpTime ARGS1(char *, datestr)
        !           268: {
        !           269:     struct tm *time_info;                  /* Points to static tm structure */
        !           270:     char *bcol = datestr;                                   /* Column begin */
        !           271:     char *ecol;                                                       /* Column end */
        !           272:     long tval;
        !           273:     int cnt;
        !           274:     unsigned long curtime = time(NULL);
        !           275:     if ((time_info = gmtime(&curtime)) == NULL) {
        !           276:        if (TRACE)
        !           277:        return 0L;
        !           278:     }
        !           279:     time_info->tm_isdst = -1;                        /* Disable summer time */
        !           280:     for (cnt=0; cnt<3; cnt++)                                      /* Month */
        !           281:        *bcol++ = toupper(*bcol);
        !           282:     if ((time_info->tm_mon = HTStrpMonth(datestr)) < 0)
        !           283:        return 0L;
        !           284:     ecol = bcol;                                                     /* Day */
        !           285:     while (*ecol++ == ' ');                   /* Spool to other side of day */
        !           286:     while (*ecol++ != ' ');
        !           287:     *--ecol = '\0';
        !           288:     time_info->tm_mday = atoi(bcol);
        !           289:     time_info->tm_wday = 0;
        !           290:     time_info->tm_yday = 0;
        !           291:     bcol = ++ecol;                                                  /* Year */
        !           292:     if ((ecol = strchr(bcol, ':')) == NULL) {
        !           293:        time_info->tm_year = atoi(bcol)-1900;
        !           294:        time_info->tm_sec = 0;
        !           295:        time_info->tm_min = 0;
        !           296:        time_info->tm_hour = 0;
        !           297:     } else {                                                        /* Time */
        !           298:        *ecol = '\0';
        !           299:        time_info->tm_sec = 0;
        !           300:        time_info->tm_min = atoi(++ecol);               /* Right side of ':' */
        !           301:        time_info->tm_hour = atoi(bcol);                 /* Left side of ':' */
        !           302:     }
        !           303:     return ((tval = mktime(time_info)) == -1 ? 0L : tval); 
        !           304: }
        !           305: 
        !           306: 
        !           307: /*                                                             HTVMSStrpTime()
        !           308: **
        !           309: **     Converts a date string from vms to a unsigned long number
        !           310: **     This is needed in order to put out the date using the same format
        !           311: **     for all directory listings.
        !           312: **
        !           313: **     Returns 0L on error
        !           314: */
        !           315: PRIVATE unsigned long HTVMSStrpTime ARGS1(char *, datestr)
        !           316: {
        !           317:     struct tm *time_info;                  /* Points to static tm structure */
        !           318:     char *col;
        !           319:     long tval;
        !           320:     unsigned long curtime = time(NULL);
        !           321:     if ((time_info = gmtime(&curtime)) == NULL)
        !           322:        return 0L;
        !           323:     time_info->tm_isdst = -1;                        /* Disable summer time */
        !           324:     if ((col = strtok(datestr, "-")) == NULL)
        !           325:        return 0L;
        !           326:     time_info->tm_mday = atoi(col);                                  /* Day */
        !           327:     time_info->tm_wday = 0;
        !           328:     time_info->tm_yday = 0;
        !           329:     if ((col = strtok(NULL, "-")) == NULL ||
        !           330:        (time_info->tm_mon = HTStrpMonth(col)) < 0)
        !           331:        return 0L;
        !           332:     if ((col = strtok(NULL, " ")) == NULL)                          /* Year */
        !           333:        return 0L;
        !           334:     time_info->tm_year = atoi(col)-1900;
        !           335:     if ((col = strtok(NULL, ":")) == NULL)                          /* Hour */
        !           336:        return 0L;
        !           337:     time_info->tm_hour = atoi(col);
        !           338:     if ((col = strtok(NULL, " ")) == NULL)                          /* Mins */
        !           339:        return 0L;
        !           340:     time_info->tm_min = atoi(col);
        !           341:     time_info->tm_sec = 0;
        !           342:     return ((tval = mktime(time_info)) < 0 ? 0L : tval);
        !           343: }
        !           344: 
        !           345: 
        !           346: /*                                                             HTFTPFilePerm()
        !           347: **
        !           348: **     Converts the file type from 'ls -l' into a long. The reason for
        !           349: **     doing this is to be able to handle the file type and icon selection
        !           350: **     similar to the Unix way used in HTBrowseDirectory().
        !           351: **
        !           352: */
        !           353: PRIVATE long HTFTPFilePerm ARGS1(char *, permission)
        !           354: {
        !           355:     char *strptr = permission;
        !           356:     long mode = 0L;
        !           357:     
        !           358:     /* Special files etc. are all handled like regular files */
        !           359:     switch (*strptr++) {                                       /* File type */
        !           360:       case 'd':        mode = S_IFMT & S_IFDIR; break;
        !           361:       case 'l': mode = S_IFMT & S_IFLNK; break;
        !           362:       default:  mode = S_IFMT & S_IFREG; break;
        !           363:     }
        !           364:     if (*strptr++ == 'r') mode |= S_IRUSR;                          /* User */
        !           365:     if (*strptr++ == 'w') mode |= S_IWUSR;
        !           366:     if (*strptr == 'x')
        !           367:        mode |= S_IXUSR;
        !           368:     else if (*strptr == 's')
        !           369:        mode |= (S_IXUSR | S_ISUID);
        !           370:     else if (*strptr == 'S')
        !           371:        mode |= S_ISUID;
        !           372:     strptr++;
        !           373:     if (*strptr++ == 'r') mode |= S_IRGRP;                         /* Group */
        !           374:     if (*strptr++ == 'w') mode |= S_IWGRP;
        !           375:     if (*strptr == 'x')
        !           376:        mode |= S_IXGRP;
        !           377:     else if (*strptr == 's')
        !           378:        mode |= (S_IXGRP | S_ISGID);
        !           379:     else if (*strptr == 'S')
        !           380:        mode |= S_ISGID;
        !           381:     strptr++;
        !           382:     if (*strptr++ == 'r') mode |= S_IROTH;                         /* Other */
        !           383:     if (*strptr++ == 'w') mode |= S_IWOTH;
        !           384:     if (*strptr == 'x')
        !           385:        mode |= S_IXOTH;
        !           386:     else if (*strptr == 't')
        !           387:        mode |= (S_IXOTH | S_ISVTX);
        !           388:     else if (*strptr == 'T')
        !           389:        mode |= S_ISVTX;
        !           390:     strptr++;
        !           391:     return mode;
        !           392: }
        !           393: 
        !           394: 
        !           395: /*                                                          parse_unix_line()
        !           396: **
        !           397: **     Extract the name, size, and date from an 'ls'. The function expects
        !           398: **     the following format of the ls-line:
        !           399: **
        !           400: **     <permission> <nlink> <owner> [<group>] <size> <date> <filename>
        !           401: **
        !           402: **     The group is not always present and is therefore optional. Both owner
        !           403: **     and group can be numbers.
        !           404: **
        !           405: **     Returns YES if OK, NO on error
        !           406: */
        !           407: PRIVATE BOOL parse_unix_line ARGS2(char *,line, dir_file_info *,f_info)
        !           408: {
        !           409:     char *column;
        !           410:     char *strptr;
        !           411:     int ival;
        !           412:     unsigned long lval;
        !           413:     if (!line || !*line || !f_info)
        !           414:        return NO;
        !           415: 
        !           416:     if ((column = strtok(line, " ")) == NULL)                /* Permissions */
        !           417:        return NO;
        !           418:     f_info->f_mode = HTFTPFilePerm(column);
        !           419:     if ((column = strtok(NULL, " ")) == NULL)                      /* Links */
        !           420:        return NO;
        !           421:     if (sscanf(column, "%d", &ival) == 1)
        !           422:        f_info->f_nlink = ival;
        !           423:     if ((column = strtok(NULL, " ")) == NULL)                      /* Owner */
        !           424:        return NO;
        !           425:     StrAllocCopy(f_info->f_uid, column);
        !           426:     if ((column = strtok(NULL, " ")) == NULL)           /* Group and/or Size */
        !           427:        return NO;
        !           428:     if (sscanf(column, "%lu", &lval) != 1) {
        !           429:        StrAllocCopy(f_info->f_gid, column);
        !           430:        if ((column = strtok(NULL, " ")) == NULL)
        !           431:            return NO;
        !           432:        if (sscanf(column, "%lu", &lval) == 1)
        !           433:            f_info->f_size = lval;
        !           434:     } else {                                      /* Group can be a number! */
        !           435:        strptr = column+strlen(column)+1;
        !           436:        while (*strptr++ == ' ');
        !           437:        if (isdigit(*--strptr)) {          /* Month can't start with a digit */
        !           438:            StrAllocCopy(f_info->f_gid, column);
        !           439:            if ((column = strtok(NULL, " ")) == NULL)
        !           440:                return NO;
        !           441:            if (sscanf(column, "%lu", &lval) == 1)
        !           442:                f_info->f_size = lval;
        !           443:        } else {
        !           444:            f_info->f_gid = "";
        !           445:            f_info->f_size = lval;
        !           446:        }
        !           447:     }
        !           448:     column = column+strlen(column)+1;
        !           449:     while (*column++ == ' ');
        !           450:     strptr = --column+12;                           /* Find the date column */
        !           451:     *strptr++ = '\0';
        !           452:     if ((f_info->f_mtime = HTStrpTime(column)) == 0L)
        !           453:        return NO;
        !           454:     while (*strptr++ == ' ');                          /* Spool to filename */
        !           455:     if ((f_info->f_mode & S_IFMT) == S_IFLNK) {        /* Strip any '->' */
        !           456:        char *link = strstr(strptr-1, " -> ");
        !           457:        if (link)
        !           458:            *link = '\0';
        !           459:     }
        !           460:     StrAllocCopy(f_info->f_name, --strptr);
        !           461:     return YES;                                        /* We have a full structure! */
        !           462: }
        !           463: 
        !           464: 
        !           465: /*                                                           parse_vms_line()
        !           466: **
        !           467: **      Format the name, date, and size from a VMS LIST line
        !           468: **      into the dir_file_info structure
        !           469: **
        !           470: **     Returns YES if OK, NO on error
        !           471: */
        !           472: PRIVATE BOOL parse_vms_line ARGS2(char *, line, dir_file_info *, f_info)
        !           473: {
        !           474:     int i, j, ialloc;
        !           475:     char *cp, *cpd, *cps, *cdir, *sp = " ";
        !           476:     
        !           477:     /**  Get rid of information lines by making them blank too **/
        !           478:     /**  Valid lines have the semi-colon version number token  **/
        !           479:     if (!line || !*line || !f_info || (cp = strchr(line, ';')) == NULL) {
        !           480:        return NO;
        !           481:     }
        !           482: 
        !           483:     /** Cut out file or directory name at VMS version number **/
        !           484:     *cp++ ='\0';
        !           485:     StrAllocCopy(f_info->f_name,line);
        !           486:     
        !           487:     /** Cast VMS file and directory names to lowercase **/
        !           488:     for (i=0; f_info->f_name[i]; i++)
        !           489:        f_info->f_name[i] = tolower(f_info->f_name[i]);
        !           490:     
        !           491:     /** Uppercase terminal .z's or _z's **/
        !           492:     if ((--i > 2) && f_info->f_name[i] == 'z' &&
        !           493:        (f_info->f_name[i-1] == '.' || f_info->f_name[i-1] == '_'))
        !           494:        f_info->f_name[i] = 'Z';
        !           495:     
        !           496:     /* Trim off VMS directory extensions */
        !           497:     if ((cdir = strstr(f_info->f_name, ".dir")) != NULL) { /* Strip any .dir */
        !           498:        f_info->f_mode = S_IFMT & S_IFDIR;
        !           499:        *cdir = '\0';
        !           500:     } else
        !           501:        f_info->f_mode = S_IFMT & S_IFREG;
        !           502: 
        !           503:     /** Convert any tabs in rest of line to spaces **/
        !           504:     cps = cp-1;
        !           505:     while ((cps=strchr(cps+1, '\t')) != NULL)
        !           506:        *cps = ' ';
        !           507:     
        !           508:     /** Collapse serial spaces **/
        !           509:     i = 0; j = 1;
        !           510:     cps = cp;
        !           511:     while (cps[j] != '\0') {
        !           512:        if (cps[i] == ' ' && cps[j] == ' ')
        !           513:            j++;
        !           514:        else
        !           515:            cps[++i] = cps[j++];
        !           516:     }
        !           517:     cps[++i] = '\0';
        !           518:     
        !           519:     /** Track down the date **/
        !           520:     if ((cpd=strchr(cp, '-')) != NULL) {
        !           521:        if (strlen(cpd) > 9 && isdigit(*(cpd-1)) &&
        !           522:            isalpha(*(cpd+1)) && *(cpd+4) == '-') {
        !           523:            if ((f_info->f_mtime = HTVMSStrpTime(cpd-2)) == 0L)
        !           524:                return NO;
        !           525:        }
        !           526:     }
        !           527:     
        !           528:     /** Track down the size **/
        !           529:     if ((cpd = strchr(cp, '/')) != NULL) {
        !           530:        /* Appears be in used/allocated format */
        !           531:        cps = cpd;
        !           532:        while (isdigit(*(cps-1)))
        !           533:            cps--;
        !           534:        if (cps < cpd)
        !           535:            *cpd = '\0';
        !           536:        f_info->f_size = atoi(cps);
        !           537:        cps = cpd+1;
        !           538:        while (isdigit(*cps))
        !           539:            cps++;
        !           540:        *cps = '\0';
        !           541:        ialloc = atoi(cpd+1);
        !           542:        /* Check if used is in blocks or bytes */
        !           543:        if (f_info->f_size <= ialloc)
        !           544:            f_info->f_size *= 512;
        !           545:     }
        !           546:     else if ((cps=strtok(cp, sp)) != NULL) {
        !           547:        /* We just initialized on the version number */
        !           548:        /* Now let's hunt for a lone, size number    */
        !           549:        while ((cps=strtok(NULL, sp)) != NULL) {
        !           550:            cpd = cps;
        !           551:            while (isdigit(*cpd))
        !           552:                cpd++;
        !           553:            if (*cpd == '\0') {
        !           554:                /* Assume it's blocks */
        !           555:                f_info->f_size = atoi(cps) * 512;
        !           556:                break;
        !           557:            }
        !           558:        }
        !           559:     }
        !           560:     return YES;                                        /* We have a full structure! */
        !           561: }
        !           562: 
        !           563: 
        !           564: /*                                                          parse_dir_entry() 
        !           565: **
        !           566: **      Given a line of LIST/NLST output in entry, return results 
        !           567: **      and a file/dir name in f_info struct
        !           568: **
        !           569: **      If first is true, this is the first name in a directory.
        !           570: **     Returns YES if OK, NO on error
        !           571: */
        !           572: PRIVATE BOOL parse_dir_entry ARGS3(char *, entry, BOOL, first,
        !           573:                                   dir_file_info *, f_info)
        !           574: {
        !           575:     BOOL status = YES;
        !           576:     switch (server_type) {
        !           577:       case UNIX_SERVER:
        !           578:       case PETER_LEWIS_SERVER:
        !           579:       case MACHTEN_SERVER:
        !           580:        /* Interpret and edit LIST output from Unix server */
        !           581:        if (first == YES) {
        !           582:            if(!strncmp(entry, "total ", 6) ||
        !           583:               (strstr(entry, "not available") != NULL)) {
        !           584:                return NO;
        !           585:            } else if(unsure_type) {
        !           586:                /* this isn't really a unix server! */
        !           587:                server_type = GENERIC_SERVER;
        !           588:                return NO;
        !           589:            }
        !           590:        }
        !           591:        status = parse_unix_line(entry, f_info);
        !           592:        break;
        !           593:        
        !           594:       case VMS_SERVER:
        !           595:        /* Interpret and edit LIST output from VMS server */
        !           596:        /* and convert information lines to zero length.  */
        !           597:        status = parse_vms_line(entry, f_info);
        !           598:        break;
        !           599:        
        !           600:       case CMS_SERVER:
        !           601:        /* Can't be directory... "entry" already equals the correct f_name */
        !           602:        StrAllocCopy(f_info->f_name, entry);
        !           603:        f_info->f_mode = S_IFMT & S_IFREG;
        !           604:        break;
        !           605:        
        !           606:       case NCSA_SERVER:
        !           607:       case TCPC_SERVER:
        !           608:        /* Directories identified by trailing "/" characters */
        !           609:        StrAllocCopy(f_info->f_name, entry);
        !           610:        {
        !           611:            int len = strlen(entry);
        !           612:            if (*(entry+len-1) == '/') {
        !           613:                *(entry+len-1) = '\0';
        !           614:                f_info->f_mode = S_IFMT & S_IFDIR;
        !           615:            } else {
        !           616:                f_info->f_mode = S_IFMT & S_IFREG;
        !           617:            }
        !           618:        }
        !           619:        break;
        !           620:        
        !           621:       default:
        !           622:        /* We cant tell if it is a directory since we only did an NLST :-( */
        !           623:        StrAllocCopy(f_info->f_name, entry);
        !           624:        f_info->f_mode = S_IFMT & S_IFREG;
        !           625:        break;
        !           626:     }
        !           627:     return status;
        !           628: }
1.1       timbl     629: 
                    630: 
1.22    ! frystyk   631: /* THIS FUNCTION IS TEMPORARY!!!!!!!!!!!!!!! */
1.1       timbl     632: 
                    633: /*     Procedure: Read a character from the data connection
                    634: **     ----------------------------------------------------
1.22    ! frystyk   635: **
        !           636: **     The type is int in order to be able to return EOF
1.1       timbl     637: */
1.22    ! frystyk   638: PRIVATE int next_data_char
1.1       timbl     639: NOARGS
                    640: {
                    641:     int status;
                    642:     if (data_read_pointer >= data_write_pointer) {
                    643:        status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
1.22    ! frystyk   644:        if (status == HT_INTERRUPTED) {
        !           645:            if (TRACE) fprintf(stderr, "next_data_char: Interrupted\n");
1.21      frystyk   646:            return EOF;
1.22    ! frystyk   647:        }
        !           648:        if (status <= 0) {
        !           649:            if (TRACE) fprintf(stderr, "next_data_char: Error: %d\n", status);
1.21      frystyk   650:            return EOF;
                    651:        }
1.22    ! frystyk   652:        data_write_pointer = data_buffer + status;
        !           653:        data_read_pointer = data_buffer;
1.1       timbl     654:     }
                    655: #ifdef NOT_ASCII
                    656:     {
                    657:         char c = *data_read_pointer++;
1.22    ! frystyk   658:        return (int) FROMASCII(c);
1.1       timbl     659:     }
                    660: #else
1.22    ! frystyk   661:     return (int) *data_read_pointer++;
1.1       timbl     662: #endif
                    663: }
                    664: 
                    665: 
1.22    ! frystyk   666: PRIVATE int HTFTP_get_dir_string ARGS1(dir_file_info *, f_info)
1.21      frystyk   667: {
1.22    ! frystyk   668:     int ch;                          /* Must be int in order to contain EOF */
        !           669:     int status;
        !           670:     static BOOL first = YES;               /* Is it the first time through? */
        !           671:     HTChunk *chunk = HTChunkCreate(128);
        !           672: 
        !           673:     for(;;) {                                  /* Until we have a nice line */
1.21      frystyk   674:        if ((ch = NEXT_DATA_CHAR) == EOF) {
1.22    ! frystyk   675:            first = YES;                                /* For the next time */
1.21      frystyk   676:            status = 0;
                    677:            break;
1.22    ! frystyk   678:        }
        !           679:        if (ch == CR || ch == LF) {                           /* Terminator? */
        !           680:            if (chunk->size != 0) {                         /* got some text */
        !           681:                if (server_type == VMS_SERVER) {
        !           682:                    /* Deal with MultiNet's wrapping of long lines - F.M. */
        !           683:                    if (data_read_pointer < data_write_pointer &&
        !           684:                        *(data_read_pointer+1) == ' ')
        !           685:                        data_read_pointer++;
        !           686:                    else if (data_read_pointer >= data_write_pointer) {
        !           687:                        int ret = NETREAD(data_soc, data_buffer,
        !           688:                                          DATA_BUFFER_SIZE);
        !           689:                        if (ret == HT_INTERRUPTED) {
        !           690:                            if (TRACE)
        !           691:                                fprintf(stderr,
        !           692:                                        "HTFTP_get_string: Interrupted\n");
        !           693:                            first = YES;
        !           694:                            status = 0;
        !           695:                            break;
        !           696:                        }
        !           697:                        if (ret <= 0) {
        !           698:                            if (TRACE)
        !           699:                                fprintf(stderr,
        !           700:                                        "HTFTP_get_string: Error %d\n", ret);
        !           701:                            first = YES;
        !           702:                            status = -1;
        !           703:                            break;
        !           704:                        }
        !           705:                        data_write_pointer = data_buffer+ret;
        !           706:                        data_read_pointer = data_buffer;
        !           707:                        if (*data_read_pointer == ' ')
        !           708:                            data_read_pointer++;
        !           709:                    }
        !           710:                }
        !           711:                HTChunkTerminate(chunk);         /* Finish getting one entry */
        !           712:                if (parse_dir_entry(chunk->data, first, f_info) == YES) {
        !           713:                    first = NO;
        !           714:                    status = 1;
        !           715:                    break;
        !           716:                } else {
        !           717:                    HTChunkClear(chunk);
        !           718:                }
        !           719:            }
1.21      frystyk   720:        } else {
                    721:            HTChunkPutc(chunk, ch);
                    722:        }
1.22    ! frystyk   723:     } /* end for(;;) */
        !           724:     HTChunkFree(chunk);
1.21      frystyk   725:     return status;
                    726: }
                    727: 
1.22    ! frystyk   728: /* ------------------------------------------------------------------------- */
        !           729: /*                            FTP Client Functions                          */
        !           730: /* ------------------------------------------------------------------------- */
        !           731: 
        !           732: /*                                                             set_mac_binary
        !           733: **
        !           734: **     This function should try to set the macintosh server into binary mode
        !           735:  */
        !           736: PRIVATE int set_mac_binary NOARGS
        !           737: {
        !           738:     return(2 == response("MACB\r\n"));
        !           739: }
        !           740: 
        !           741: 
        !           742: /* This function gets the current working directory to help
        !           743:  * determine what kind of host it is
        !           744:  */
        !           745: PRIVATE void get_ftp_pwd ARGS2(int *,server_type, BOOLEAN *,use_list) {
        !           746: 
        !           747:     char *cp;
        !           748:     /* get the working directory (to see what it looks like) */
        !           749:     int status = response("PWD\r\n");
        !           750:     if (status < 0)
        !           751:         return;
        !           752:     else 
        !           753:      {
        !           754: 
        !           755:        cp = strchr(response_text+5,'"');
        !           756:        if(cp) *cp = '\0';
        !           757:         if (*server_type == TCPC_SERVER)
        !           758:          {
        !           759:             *server_type = response_text[5] == '/' ? NCSA_SERVER : TCPC_SERVER;
        !           760:          }
        !           761:         else if (response_text[5] == '/')
        !           762:          {
        !           763:             /* path names beginning with / imply Unix,
        !           764:             * right? 
        !           765:             */
        !           766:             if(set_mac_binary())
        !           767:                   *server_type = NCSA_SERVER;
        !           768:             else
        !           769:                {
        !           770:                    *server_type = UNIX_SERVER;
        !           771:                    *use_list = TRUE;
        !           772:                }
        !           773:             return;
        !           774:          }
        !           775:         else if (response_text[strlen(response_text)-1] == ']')
        !           776:          {
        !           777:              /* path names ending with ] imply VMS, right? */
        !           778:              *server_type = VMS_SERVER;
        !           779:             *use_list = TRUE;
        !           780:          }
        !           781:         else
        !           782:              *server_type = GENERIC_SERVER;
        !           783: 
        !           784:         if ((*server_type == NCSA_SERVER) ||
        !           785:                (*server_type == TCPC_SERVER) ||
        !           786:                     (*server_type == PETER_LEWIS_SERVER))
        !           787:             set_mac_binary();
        !           788:      }
        !           789: }
        !           790: 
1.21      frystyk   791: 
1.22    ! frystyk   792: #ifdef NEW_CODE
        !           793: /*                                                          HTFTPServerInfo()
        !           794: **
        !           795: **     This function finds out what server we are talking to. Returns
        !           796: **     -1 on error, else the 'number' of the server.
1.1       timbl     797: **
1.22    ! frystyk   798: **     Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for using
        !           799: **     his code.
1.1       timbl     800: */
1.22    ! frystyk   801: PUBLIC BOOL HTFTServerInfo NOARGS
1.1       timbl     802: {
1.22    ! frystyk   803:     static short    state = FTP_INFO_START;  /* static var to remember state */
        !           804:     char            hold[MAX_LINE_LENGTH];               /* for getting info */
        !           805:     short           osstat;                              /* OS status return */
1.18      frystyk   806:     
1.22    ! frystyk   807:     check_reset();
        !           808:     switch(state) {
        !           809:       case FTP_INFO_START:                                     /* first call */
        !           810:        osstat = send_cmd("SYST", NULL);
        !           811:        if (osstat == noErr)                /* success -- move to next state */
        !           812:            next_state(FTP_INFO_SENT_SYST);
        !           813:        else
        !           814:            return(osstat);
        !           815:        break;
        !           816:        
        !           817:       case FTP_INFO_SENT_SYST:
        !           818:        /* try to get reply from server */
        !           819:        osstat = get_reply(&ftp_server_code, ftp_server_text);
        !           820:        if (osstat == NO_LINE_ERR)
        !           821:            return FTP_NOT_DONE;
        !           822:        else if (osstat != noErr)
        !           823:            err_return(osstat);
        !           824:                
        !           825:        /* Got a line - what kind of server are we talking to? */
        !           826:        if ((ftp_server_code == FTP_RP_SYST_TYPE) ||
        !           827:                    (ftp_server_code == FTP_RP_COMMAND_OK)) {
        !           828:            if (strncmp(ftp_server_text,
        !           829:                        "UNIX Type: L8MAC-OSMachTen", 28) == 0) {
        !           830:                server_type = MACHTEN_SERVER;
        !           831:                use_list = TRUE;
        !           832:            } else if (strstr(ftp_server_text, "UNIX") !=
        !           833:                       NULL) {
        !           834:                server_type = UNIX_SERVER;
        !           835:                use_list = TRUE;
        !           836:            } else if (strncmp(ftp_server_text, "VMS", 3) == 0)
        !           837:                server_type = VMS_SERVER;
        !           838:            else if ((strncmp(ftp_server_text, "VM/CMS", 6) == 0) ||
        !           839:                     (strncmp(ftp_server_text, "VM", 3) == 0))
        !           840:                server_type = CMS_SERVER;
        !           841:            else if (strncmp(ftp_server_text, "DCTS", 4) == 0)
        !           842:                server_type = DCTS_SERVER;
        !           843:            else if (strstr(ftp_server_text,
        !           844:                            "MAC-OS TCP/ConnectII") != NULL) {
        !           845:                server_type = TCPC_SERVER;
        !           846:                unsure_type = TRUE;
        !           847:                unknown_mb_mode = TRUE;
        !           848:            } else if (strncmp(ftp_server_text,
        !           849:                               "MACOS Peter's Server", 20) == 0) {
        !           850:                server_type = PETER_LEWIS_SERVER;
        !           851:                use_list = TRUE;
        !           852:                unknown_mb_mode = TRUE;
        !           853:            }
        !           854:            
        !           855:            can_see_dirs = (server_type == UNIX_SERVER) ||
        !           856:                (server_type == VMS_SERVER) ||
        !           857:                    (server_type == CMS_SERVER) ||
        !           858:                        (server_type == NCSA_SERVER) ||
        !           859:                            (server_type == TCPC_SERVER) ||
        !           860:                                (server_type==MACHTEN_SERVER)||
        !           861:                                    (server_type ==
        !           862:                                     PETER_LEWIS_SERVER);
        !           863:            unsure_type = FALSE;
        !           864:            if ((server_type == TCPC_SERVER) ||
        !           865:                (server_type ==
        !           866:                 GENERIC_SERVER))
        !           867:                /* need to check for old versions of TCP/C
        !           868:                   that use / in pathnames */
        !           869:                next_state(FTP_INFO_GET_WD)
        !           870:            else if ((server_type == NCSA_SERVER) ||
        !           871:                     (server_type ==
        !           872:                      PETER_LEWIS_SERVER))
        !           873:                next_state(FTP_INFO_SET_MACB)
        !           874:            else
        !           875:                err_return(noErr)
        !           876:                }
        !           877:        else if (server_type == GENERIC_SERVER)
        !           878:            /* no SYST command, generic server, so check the work dir */
        !           879:            next_state(FTP_INFO_GET_WD)
        !           880:                break;
        !           881:        
        !           882:       case FTP_INFO_GET_WD:
        !           883:        /* get the working directory (to see what it looks like) */
        !           884:        osstat = ftp_pwd(hold);
        !           885:        if (osstat > noErr)
        !           886:            return FTP_NOT_DONE;
        !           887:        else if (osstat == noErr) {
        !           888:            if (server_type == TCPC_SERVER) {
        !           889:                server_type = hold[0] == '/' ?
        !           890:                    NCSA_SERVER :
        !           891:                        TCPC_SERVER;
        !           892:                unsure_type = FALSE;
        !           893:                unknown_mb_mode = TRUE;
        !           894:            } else if (hold[0] == '/') {
        !           895:                /* path names beginning with / imply Unix, right? */
        !           896:                server_type = UNIX_SERVER;
        !           897:                unsure_type = TRUE;
        !           898:                use_list = TRUE;
        !           899:            } else if (hold[strlen(hold)-1] == ']') {
        !           900:                /* path names ending with ] imply VMS, right? */
        !           901:                server_type = VMS_SERVER;
        !           902:                unsure_type = TRUE;
        !           903:            } else
        !           904:                server_type = GENERIC_SERVER;
        !           905:            can_see_dirs = (server_type == UNIX_SERVER) ||
        !           906:                (server_type == VMS_SERVER) || (server_type == CMS_SERVER) ||
        !           907:                        (server_type == NCSA_SERVER) || 
        !           908:                            (server_type == TCPC_SERVER) ||
        !           909:                                (server_type == MACHTEN_SERVER) ||
        !           910:                                    (server_type == PETER_LEWIS_SERVER);
        !           911:            if ((server_type == NCSA_SERVER) || (server_type == TCPC_SERVER) ||
        !           912:                (server_type == PETER_LEWIS_SERVER))
        !           913:                next_state(FTP_INFO_SET_MACB)
        !           914:            else
        !           915:                err_return(noErr)
        !           916:                }
        !           917:        else if (osstat == FTP_PROTOCOL_ERR)
        !           918:            err_return(noErr)
        !           919:        else
        !           920:            err_return(osstat)
        !           921:                break;
        !           922:        
        !           923:       case FTP_INFO_SET_MACB:
        !           924:        /*turn on MacBinary (to let server know that we know that it's a Mac)*/
        !           925:        osstat = ftp_set_macb(TRUE);
        !           926:        if (osstat > noErr)
        !           927:            return FTP_NOT_DONE;
        !           928:        else
        !           929:            err_return(noErr)
        !           930:                break;
        !           931:     } /* switch state */
        !           932: } /* ftp_info() */
        !           933: #endif /* NEW_CODE */
1.1       timbl     934: 
                    935: 
                    936: /*     Execute Command and get Response
                    937: **     --------------------------------
                    938: **
                    939: **     See the state machine illustrated in RFC959, p57. This implements
                    940: **     one command/reply sequence.  It also interprets lines which are to
                    941: **     be continued, which are marked with a "-" immediately after the
                    942: **     status code.
                    943: **
1.8       timbl     944: **     Continuation then goes on until a line with a matching reply code
                    945: **     an a space after it.
                    946: **
1.1       timbl     947: ** On entry,
                    948: **     con     points to the connection which is established.
1.22    ! frystyk   949: **     cmd     points to a command, or is NULL to just get the response.
1.1       timbl     950: **
                    951: **     The command is terminated with the CRLF pair.
                    952: **
                    953: ** On exit,
                    954: **     returns:  The first digit of the reply type,
                    955: **               or negative for communication failure.
                    956: */
1.13      timbl     957: PRIVATE int response ARGS1(char *, cmd)
1.1       timbl     958: {
                    959:     int result;                                /* Three-digit decimal code */
1.8       timbl     960:     int        continuation_response = -1;
1.1       timbl     961:     int status;
1.22    ! frystyk   962:     extern int interrupted_in_htgetcharacter;
1.1       timbl     963:     
                    964:     if (!control) {
                    965:           if(TRACE) fprintf(stderr, "FTP: No control connection set up!!\n");
                    966:          return -99;
                    967:     }
                    968:     
                    969:     if (cmd) {
                    970:     
                    971:        if (TRACE) fprintf(stderr, "  Tx: %s", cmd);
                    972: 
                    973: #ifdef NOT_ASCII
                    974:        {
                    975:            char * p;
                    976:            for(p=cmd; *p; p++) {
                    977:                *p = TOASCII(*p);
                    978:            }
                    979:        }
                    980: #endif 
                    981:        status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
                    982:        if (status<0) {
                    983:            if (TRACE) fprintf(stderr, 
                    984:                "FTP: Error %d sending command: closing socket %d\n",
                    985:                status, control->socket);
                    986:            close_connection(control);
                    987:            return status;
                    988:        }
                    989:     }
                    990: 
                    991:     do {
                    992:        char *p = response_text;
1.22    ! frystyk   993:        for(;;) {
        !           994: #if NEW_CODE
        !           995:            if (((*p++=NEXT_CHAR) == LF)
        !           996:                        || (p == &response_text[LINE_LENGTH])) {
        !           997: #endif
1.13      timbl     998:            if (((*p++=HTInputSocket_getCharacter(control->isoc)) == LF)
1.1       timbl     999:                        || (p == &response_text[LINE_LENGTH])) {
1.8       timbl    1000:                char continuation;
1.22    ! frystyk  1001: 
        !          1002:                if (interrupted_in_htgetcharacter)
        !          1003:                   {
        !          1004:                     if (TRACE)
        !          1005:                        fprintf(stderr,
        !          1006:                                "FTP: Interrupted in HTGetCharacter, apparently.\n");
        !          1007:                     NETCLOSE (control->socket);
        !          1008:                     control->socket = -1;
        !          1009:                     return HT_INTERRUPTED;
        !          1010:                   }
        !          1011: 
1.1       timbl    1012:                *p++=0;                 /* Terminate the string */
                   1013:                if (TRACE) fprintf(stderr, "    Rx: %s", response_text);
1.22    ! frystyk  1014: 
        !          1015: #if 0
        !          1016:                if(server_type == UNIX_SERVER && 
        !          1017:                                        !strncmp(response_text,"250-",4))
        !          1018:                    help_message_cache_add(response_text+4);
        !          1019: #endif
1.1       timbl    1020:                sscanf(response_text, "%d%c", &result, &continuation);
1.8       timbl    1021:                if  (continuation_response == -1) {
                   1022:                        if (continuation == '-')  /* start continuation */
                   1023:                            continuation_response = result;
                   1024:                } else {        /* continuing */
                   1025:                        if (continuation_response == result
                   1026:                                && continuation == ' ')
                   1027:                            continuation_response = -1; /* ended */
                   1028:                }       
1.1       timbl    1029:                break;      
                   1030:            } /* if end of line */
                   1031:            
1.22    ! frystyk  1032:            if (interrupted_in_htgetcharacter)
        !          1033:                {
        !          1034:                     if (TRACE)
        !          1035:                       fprintf (stderr, "FTP: Interrupted in HTGetCharacter, apparently.\n");
        !          1036:                     NETCLOSE (control->socket);
        !          1037:                     control->socket = -1;
        !          1038:                     return HT_INTERRUPTED;
        !          1039:                }
        !          1040: 
1.12      timbl    1041:            if (*(p-1) == (char) EOF) {
1.1       timbl    1042:                if(TRACE) fprintf(stderr, "Error on rx: closing socket %d\n",
                   1043:                    control->socket);
                   1044:                strcpy(response_text, "000 *** TCP read error on response\n");
                   1045:                close_connection(control);
                   1046:                return -1;      /* End of file on response */
                   1047:            }
                   1048:        } /* Loop over characters */
                   1049: 
1.8       timbl    1050:     } while (continuation_response != -1);
1.1       timbl    1051:     
                   1052:     if (result==421) {
                   1053:        if(TRACE) fprintf(stderr, "FTP: They close so we close socket %d\n",
                   1054:            control->socket);
                   1055:        close_connection(control);
                   1056:        return -1;
                   1057:     }
                   1058:     return result/100;
                   1059: }
                   1060: 
                   1061: 
1.22    ! frystyk  1062: /*     Close an individual connection
        !          1063: **
        !          1064: */
        !          1065: PRIVATE int close_connection ARGS1(connection *, con)
        !          1066: {
        !          1067:     int status;
        !          1068:     if(con->isoc) {
        !          1069:         HTInputSocket_free(con->isoc);
        !          1070:         con->isoc = NULL;
        !          1071:     }
        !          1072:     if (TRACE)
        !          1073:        fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
        !          1074:     status = NETCLOSE(con->socket);
        !          1075:     return status;
        !          1076: }
        !          1077: 
        !          1078: 
1.1       timbl    1079: /*     Get a valid connection to the host
                   1080: **     ----------------------------------
                   1081: **
                   1082: ** On entry,
                   1083: **     arg     points to the name of the host in a hypertext address
                   1084: ** On exit,
                   1085: **     returns <0 if error
                   1086: **             socket number if success
                   1087: **
                   1088: **     This routine takes care of managing timed-out connections, and
                   1089: **     limiting the number of connections in use at any one time.
                   1090: **
                   1091: **     It ensures that all connections are logged in if they exist.
                   1092: **     It ensures they have the port number transferred.
                   1093: */
                   1094: PRIVATE int get_connection ARGS1 (CONST char *,arg)
                   1095: {
1.22    ! frystyk  1096:     int status;
        !          1097:     char * command;
        !          1098:     connection * con = (connection *)malloc(sizeof(*con));
        !          1099: 
        !          1100:     struct sockaddr_in soc_address;     /* Binary network address */
1.1       timbl    1101:     struct sockaddr_in* sin = &soc_address;
                   1102: 
1.22    ! frystyk  1103:     char * username = NULL;
        !          1104:     char * password = NULL;
        !          1105:     static char *user_entered_password=0;
        !          1106:     static char *last_username_and_host=0;
1.1       timbl    1107:     
                   1108:     if (!arg) return -1;               /* Bad if no name sepcified     */
                   1109:     if (!*arg) return -1;              /* Bad if name had zero length  */
                   1110: 
                   1111:     if (TRACE) fprintf(stderr, "FTP: Looking for %s\n", arg);
                   1112: 
                   1113: /* Get node name:
                   1114: */
                   1115:     {
                   1116:        char *p1 = HTParse(arg, "", PARSE_HOST);
1.2       timbl    1117:        char *p2 = strrchr(p1, '@');    /* user? */
1.22    ! frystyk  1118:        char * pw=0;
        !          1119: 
        !          1120:        if (p2!=NULL) {
1.1       timbl    1121:            username = p1;
                   1122:            *p2=0;                      /* terminate */
                   1123:            p1 = p2+1;                  /* point to host */
                   1124:            pw = strchr(username, ':');
                   1125:            if (pw) {
                   1126:                *pw++ = 0;
                   1127:                password = pw;
                   1128:            }
1.22    ! frystyk  1129: 
        !          1130:            /* if the password doesn't exist then we are going to have
        !          1131:             * to ask the user for it.  The only problem is that we
        !          1132:             * don't want to ask for it every time, so we will store
        !          1133:             * away in a primitive fashion.
        !          1134:             */
        !          1135:            if(!password) {
        !          1136:                char tmp[256];
        !          1137: 
        !          1138:                sprintf(tmp,"%s@%s",username,p1);
        !          1139:                /* if the user@host is not equal to the last time through
        !          1140:                 * or user_entered_password has no data then we need
        !          1141:                 * to ask the user for the password
        !          1142:                 */
        !          1143:                if(!last_username_and_host ||
        !          1144:                        strcmp(tmp,last_username_and_host) ||
        !          1145:                                                !user_entered_password) {
        !          1146: 
        !          1147:                    StrAllocCopy(last_username_and_host,tmp);
        !          1148:                    sprintf(tmp,"Enter password for user %s@%s:",username,p1);
        !          1149:                    if(user_entered_password) 
        !          1150:                        free(user_entered_password);
        !          1151:                    user_entered_password = (char *)HTPromptPassword(tmp);
        !          1152: 
        !          1153:                } /* else we already know the password */
        !          1154:                password = user_entered_password;
        !          1155:            }
1.1       timbl    1156:        }
1.22    ! frystyk  1157: /* OLD STUFF */        
1.1       timbl    1158:        if (HTParseInet(sin, p1)) { free(p1); return -1;} /* TBL 920622 */
1.22    ! frystyk  1159: /* END OS */
1.1       timbl    1160:         if (!username) free(p1);
                   1161:     } /* scope of p1 */
1.22    ! frystyk  1162:  
        !          1163: /* THIS IS OLD STUFF */
        !          1164: /*  Set up defaults:
1.1       timbl    1165: */
1.22    ! frystyk  1166:     sin->sin_family = AF_INET;                  /* Family, host order  */
        !          1167:     sin->sin_port = htons(IPPORT_FTP);          /* Well Known Number    */
1.1       timbl    1168: 
1.22    ! frystyk  1169:         if (con == NULL) outofmem(__FILE__, "get_connection");   
        !          1170:         con->addr = sin->sin_addr.s_addr;       /* save it */
        !          1171:         con->binary = NO;
        !          1172:         status = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        !          1173:         if (status<0) {
        !          1174:             (void) HTInetStatus("socket");
        !          1175:             free(con);
        !          1176:             if (username) free(username);
        !          1177:             return status;
        !          1178:         }
        !          1179:         con->socket = status;
        !          1180: 
        !          1181:     status = connect(con->socket, (struct sockaddr*)&soc_address,
        !          1182:                     sizeof(soc_address));
        !          1183: /* END OLD_STUFF */
        !          1184: #ifdef NEW_CODE
        !          1185:     con->socket = -1;
        !          1186:     status = HTDoConnect (arg, "FTP", IPPORT_FTP, &con->socket);
        !          1187: #endif
        !          1188:   if (status < 0)
1.1       timbl    1189:     {
1.22    ! frystyk  1190:       if (TRACE)
        !          1191:         {
        !          1192:           if (status == HT_INTERRUPTED)
        !          1193:             fprintf (stderr,
        !          1194:                      "FTP: Interrupted on connect\n");
        !          1195:           else
        !          1196:             fprintf(stderr,
        !          1197:                 "FTP: Unable to connect to remote host for `%s' (errno %d).\n",
        !          1198:                     arg, errno);
        !          1199:         }
        !          1200:       if (status == HT_INTERRUPTED)
        !          1201:         HTProgress ("Connection interrupted.");
        !          1202:       if (con->socket != -1)
        !          1203:         {
        !          1204:           NETCLOSE(con->socket);
        !          1205:         }
        !          1206:        
        !          1207:       if (username)
        !          1208:           free(username);
        !          1209:       free(con);
        !          1210:       return status;                    /* Bad return */
1.1       timbl    1211:     }
                   1212: 
                   1213:        
1.22    ! frystyk  1214:     if (TRACE) 
        !          1215:        fprintf(stderr, "FTP conneced, socket %d\n", con->socket);
1.1       timbl    1216: 
1.22    ! frystyk  1217:     /* Initialise buffering for contron connection */
        !          1218: #ifdef NEW_CODE
        !          1219:     HTInitInput(control->socket);
        !          1220: #endif
        !          1221:     control = con;             /* Current control connection */
        !          1222:     control->isoc = HTInputSocket_new(control->socket); /* buffering */
1.1       timbl    1223: 
                   1224: /*     Now we log in           Look up username, prompt for pw.
                   1225: */
1.22    ! frystyk  1226:   {
        !          1227:     int status = response((char *)0);  /* Get greeting */
        !          1228: 
        !          1229:     if (status == HT_INTERRUPTED)
        !          1230:       {
        !          1231:         if (TRACE)
        !          1232:           fprintf (stderr,
        !          1233:                    "FTP: Interrupted at beginning of login.\n");
        !          1234:         HTProgress ("Connection interrupted.");
        !          1235:         NETCLOSE(control->socket);
        !          1236:         control->socket = -1;
        !          1237:         return HT_INTERRUPTED;
        !          1238:       }
        !          1239:     if (status == 2) {         /* Send username */
        !          1240:        if (username) {
        !          1241:            command = (char*)malloc(10+strlen(username)+2+1);
        !          1242:            if (command == NULL) outofmem(__FILE__, "get_connection");
        !          1243:            sprintf(command, "USER %s%c%c", username, CR, LF);
        !          1244:        } else {
        !          1245:            command = (char*)malloc(24);
        !          1246:            if (command == NULL) outofmem(__FILE__, "get_connection");
        !          1247:            sprintf(command, "USER anonymous%c%c", CR, LF);
        !          1248:         }
        !          1249:        status = response(command);
        !          1250:        free(command);
        !          1251:         if (status == HT_INTERRUPTED)
        !          1252:           {
        !          1253:             if (TRACE)
        !          1254:               fprintf (stderr,
        !          1255:                        "FTP: Interrupted while sending username.\n");
        !          1256:             HTProgress ("Connection interrupted.");
        !          1257:             NETCLOSE(control->socket);
        !          1258:             control->socket = -1;
        !          1259:             return HT_INTERRUPTED;
        !          1260:           }
        !          1261:     }
        !          1262:     if (status == 3) {         /* Send password */
        !          1263:        if (password) {
        !          1264:            command = (char*)malloc(10+strlen(password)+2+1);
        !          1265:            if (command == NULL) outofmem(__FILE__, "get_connection");
        !          1266:            sprintf(command, "PASS %s%c%c", password, CR, LF);
        !          1267:        } else {
        !          1268:            char * user = getenv("USER");
        !          1269:            CONST char *host = HTHostName();
        !          1270:            if (!user) user = "WWWuser";
        !          1271:            /* If not fully qualified, suppress it as ftp.uu.net
        !          1272:               prefers a blank to a bad name */
        !          1273:            if (!strchr(host, '.')) host = "";
        !          1274: 
        !          1275:            command = (char*)malloc(20+strlen(host)+2+1);
        !          1276:            if (command == NULL) outofmem(__FILE__, "get_connection");
        !          1277:            sprintf(command, "PASS %s@%s%c%c", user ? user : "WWWuser",
        !          1278:                                                        host, CR, LF); /*@@*/
        !          1279:         }
        !          1280:        status = response(command);
        !          1281:        free(command);
        !          1282:         if (status == HT_INTERRUPTED)
        !          1283:           {
        !          1284:             if (TRACE)
        !          1285:               fprintf (stderr,
        !          1286:                        "FTP: Interrupted while sending password.\n");
        !          1287:             HTProgress ("Connection interrupted.");
        !          1288:             NETCLOSE(control->socket);
        !          1289:             control->socket = -1;
        !          1290:             return HT_INTERRUPTED;
        !          1291:           }
        !          1292:     }
        !          1293:     FREE(username);
        !          1294: 
        !          1295:     if (status == 3) {
        !          1296:         char temp[80];
        !          1297:        sprintf(temp, "ACCT noaccount%c%c", CR, LF);
        !          1298:        status = response(temp);
        !          1299:        if (status == HT_INTERRUPTED)
        !          1300:           {
        !          1301:             if (TRACE)
        !          1302:               fprintf (stderr,
        !          1303:                        "FTP: Interrupted while sending password.\n");
        !          1304:             HTProgress ("Connection interrupted.");
        !          1305:             NETCLOSE(control->socket);
        !          1306:             control->socket = -1;
        !          1307:             return HT_INTERRUPTED;
        !          1308:           }
        !          1309: 
        !          1310:     }
        !          1311:     if (status !=2) {
        !          1312:         if (TRACE) fprintf(stderr, "FTP: Login fail: %s", response_text);
        !          1313:        /* if (control->socket > 0) close_connection(control->socket); */
        !          1314:         return -1;             /* Bad return */
        !          1315:     }
        !          1316:     if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
1.1       timbl    1317: 
1.22    ! frystyk  1318:     /** Check for host type **/
        !          1319:     server_type = GENERIC_SERVER;      /* reset */
        !          1320:     use_list = FALSE;                  /* reset */
        !          1321:     {
        !          1322: #if 0
        !          1323:     char temp[80];
        !          1324:     sprintf(temp, "SYST%c%c", CR, LF);
        !          1325: #endif    
        !          1326:     if ((status=response("SYST\r\n")) == 2) {
        !          1327:                 /* we got a line -- what kind of server are we talking to? */
        !          1328:          if (strncmp(response_text+4, "UNIX Type: L8 MAC-OS MachTen", 28) == 0)
        !          1329:           {
        !          1330:              server_type = MACHTEN_SERVER;
        !          1331:             use_list = TRUE;
        !          1332:           }
        !          1333:          else if (strstr(response_text+4, "UNIX") != NULL)
        !          1334:           {
        !          1335:              server_type = UNIX_SERVER;
        !          1336:             use_list = TRUE;
        !          1337:           }
        !          1338:          else if (strncmp(response_text+4, "VMS", 3) == 0)
        !          1339:          {
        !          1340:              server_type = VMS_SERVER;
        !          1341:             use_list = TRUE;
        !          1342:          }
        !          1343:          else if ((strncmp(response_text+4, "VM/CMS", 6) == 0)
        !          1344:                                 || (strncmp(response_text+4, "VM ", 3) == 0))
        !          1345:              server_type = CMS_SERVER;
        !          1346:          else if (strncmp(response_text+4, "DCTS", 4) == 0)
        !          1347:              server_type = DCTS_SERVER;
        !          1348:          else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL)
        !          1349:           {
        !          1350:              server_type = TCPC_SERVER;
        !          1351:              get_ftp_pwd(&server_type, &use_list);
        !          1352:             unsure_type = TRUE;   
        !          1353:           }
        !          1354:          else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0)
        !          1355:           {
        !          1356:              server_type = PETER_LEWIS_SERVER;
        !          1357:              use_list = TRUE;
        !          1358:              set_mac_binary();
        !          1359:           }
        !          1360:         else 
        !          1361:          {
        !          1362:             server_type = GENERIC_SERVER;
        !          1363:              get_ftp_pwd(&server_type, &use_list);
        !          1364:             unsure_type = TRUE;   
        !          1365:          }
        !          1366:     } else {
        !          1367:        /* SYST fails :(  try to get the type from the PWD command */
        !          1368:          get_ftp_pwd(&server_type, &use_list);
        !          1369:     }
        !          1370:     }
1.1       timbl    1371: 
                   1372: /*     Now we inform the server of the port number we will listen on
                   1373: */
1.22    ! frystyk  1374: #ifdef NOTREPEAT_PORT
1.1       timbl    1375:        {
                   1376:            int status = response(port_command);
                   1377:            if (status !=2) {
1.22    ! frystyk  1378:                if (control->socket) close_connection(control->socket);
1.1       timbl    1379:                return -status;         /* Bad return */
                   1380:            }
                   1381:            if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
                   1382:        }
                   1383: #endif
                   1384:        return con->socket;                     /* Good return */
1.22    ! frystyk  1385:   } /* Scope of con */
        !          1386: }
1.1       timbl    1387: 
                   1388: 
                   1389: #ifdef LISTEN
                   1390: 
                   1391: /*     Close Master (listening) socket
                   1392: **     -------------------------------
                   1393: **
                   1394: **
                   1395: */
                   1396: #ifdef __STDC__
                   1397: PRIVATE int close_master_socket(void)
                   1398: #else
                   1399: PRIVATE int close_master_socket()
                   1400: #endif
                   1401: {
                   1402:     int status;
                   1403:     FD_CLR(master_socket, &open_sockets);
                   1404:     status = NETCLOSE(master_socket);
                   1405:     if (TRACE) fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
                   1406:     master_socket = -1;
                   1407:     if (status<0) return HTInetStatus("close master socket");
                   1408:     else return status;
                   1409: }
                   1410: 
                   1411: 
                   1412: /*     Open a master socket for listening on
                   1413: **     -------------------------------------
                   1414: **
                   1415: **     When data is transferred, we open a port, and wait for the server to
                   1416: **     connect with the data.
                   1417: **
                   1418: ** On entry,
                   1419: **     master_socket   Must be negative if not set up already.
                   1420: ** On exit,
                   1421: **     Returns         socket number if good
                   1422: **                     less than zero if error.
                   1423: **     master_socket   is socket number if good, else negative.
                   1424: **     port_number     is valid if good.
                   1425: */
                   1426: #ifdef __STDC__
                   1427: PRIVATE int get_listen_socket(void)
                   1428: #else
                   1429: PRIVATE int get_listen_socket()
                   1430: #endif
                   1431: {
                   1432:     struct sockaddr_in soc_address;    /* Binary network address */
                   1433:     struct sockaddr_in* sin = &soc_address;
                   1434:     int new_socket;                    /* Will be master_socket */
                   1435:     
                   1436:     
                   1437:     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
                   1438:     num_sockets = 0;
                   1439:     
                   1440: #ifndef REPEAT_LISTEN
                   1441:     if (master_socket>=0) return master_socket;  /* Done already */
                   1442: #endif
                   1443: 
                   1444: /*  Create internet socket
                   1445: */
                   1446:     new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                   1447:        
                   1448:     if (new_socket<0)
                   1449:        return HTInetStatus("socket for master socket");
                   1450:     
                   1451:     if (TRACE) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
                   1452:     
                   1453: /*  Search for a free port.
                   1454: */
                   1455:     sin->sin_family = AF_INET;     /* Family = internet, host order  */
                   1456:     sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
                   1457: #ifdef POLL_PORTS
                   1458:     {
                   1459:         unsigned short old_port_number = port_number;
                   1460:        for(port_number=old_port_number+1;;port_number++){ 
                   1461:            int status;
                   1462:            if (port_number > LAST_TCP_PORT)
                   1463:                port_number = FIRST_TCP_PORT;
                   1464:            if (port_number == old_port_number) {
                   1465:                return HTInetStatus("bind");
                   1466:            }
                   1467:            soc_address.sin_port = htons(port_number);
                   1468:            if ((status=bind(new_socket,
                   1469:                    (struct sockaddr*)&soc_address,
                   1470:                            /* Cast to generic sockaddr */
                   1471:                    sizeof(soc_address))) == 0)
                   1472:                break;
                   1473:            if (TRACE) fprintf(stderr, 
                   1474:                "TCP bind attempt to port %d yields %d, errno=%d\n",
1.22    ! frystyk  1475:                port_number, status, SOCKET_ERRNO);
1.1       timbl    1476:        } /* for */
                   1477:     }
                   1478: #else
                   1479:     {
                   1480:         int status;
                   1481:        int address_length = sizeof(soc_address);
                   1482:        status = getsockname(control->socket,
                   1483:                        (struct sockaddr *)&soc_address,
                   1484:                         &address_length);
                   1485:        if (status<0) return HTInetStatus("getsockname");
                   1486:        CTRACE(tfp, "FTP: This host is %s\n",
                   1487:            HTInetString(sin));
                   1488:        
                   1489:        soc_address.sin_port = 0;       /* Unspecified: please allocate */
                   1490:        status=bind(new_socket,
                   1491:                (struct sockaddr*)&soc_address,
                   1492:                        /* Cast to generic sockaddr */
                   1493:                sizeof(soc_address));
                   1494:        if (status<0) return HTInetStatus("bind");
                   1495:        
                   1496:        address_length = sizeof(soc_address);
                   1497:        status = getsockname(new_socket,
                   1498:                        (struct sockaddr*)&soc_address,
                   1499:                        &address_length);
                   1500:        if (status<0) return HTInetStatus("getsockname");
                   1501:     }
                   1502: #endif    
                   1503: 
                   1504:     CTRACE(tfp, "FTP: bound to port %d on %s\n",
                   1505:                (int)ntohs(sin->sin_port),
                   1506:                HTInetString(sin));
                   1507: 
                   1508: #ifdef REPEAT_LISTEN
                   1509:     if (master_socket>=0)
                   1510:         (void) close_master_socket();
                   1511: #endif    
                   1512:     
                   1513:     master_socket = new_socket;
                   1514:     
                   1515: /*     Now we must find out who we are to tell the other guy
                   1516: */
                   1517:     (void)HTHostName();        /* Make address valid - doesn't work*/
1.4       timbl    1518:     sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
1.1       timbl    1519:                    (int)*((unsigned char *)(&sin->sin_addr)+0),
                   1520:                    (int)*((unsigned char *)(&sin->sin_addr)+1),
                   1521:                    (int)*((unsigned char *)(&sin->sin_addr)+2),
                   1522:                    (int)*((unsigned char *)(&sin->sin_addr)+3),
                   1523:                    (int)*((unsigned char *)(&sin->sin_port)+0),
1.4       timbl    1524:                    (int)*((unsigned char *)(&sin->sin_port)+1),
                   1525:                    CR, LF);
1.1       timbl    1526: 
                   1527: 
                   1528: /*     Inform TCP that we will accept connections
                   1529: */
                   1530:     if (listen(master_socket, 1)<0) {
                   1531:        master_socket = -1;
                   1532:        return HTInetStatus("listen");
                   1533:     }
                   1534:     CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
                   1535:     FD_SET(master_socket, &open_sockets);
                   1536:     if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
                   1537: 
                   1538:     return master_socket;              /* Good */
                   1539: 
                   1540: } /* get_listen_socket */
                   1541: #endif
                   1542: 
                   1543: 
                   1544: /*     Retrieve File from Server
                   1545: **     -------------------------
                   1546: **
                   1547: ** On entry,
                   1548: **     name            WWW address of a file: document, including hostname
                   1549: ** On exit,
                   1550: **     returns         Socket number for file if good.
                   1551: **                     <0 if bad.
                   1552: */
1.22    ! frystyk  1553: 
        !          1554: #ifdef NEW_PARS
        !          1555: PUBLIC int HTFTPLoad ARG6 (HTRequest *,                request,
        !          1556:                           void *,                      param,
        !          1557:                           CONST char *,                name,
        !          1558:                           HTParentAnchor *,            anchor,
        !          1559:                           HTFormat,                    format_out,
        !          1560:                           HTStream *,                  sink)
        !          1561: #endif
1.19      luotonen 1562: PUBLIC int HTFTPLoad ARGS1(HTRequest *, request)
1.1       timbl    1563: {
1.22    ! frystyk  1564:     char *name = HTAnchor_physical(request->anchor);
1.1       timbl    1565:     BOOL isDirectory = NO;
                   1566:     int status;
                   1567:     int retry;                 /* How many times tried? */
1.2       timbl    1568:     HTFormat format;
1.22    ! frystyk  1569:     char command[LINE_LENGTH+1];
1.1       timbl    1570:     
1.22    ! frystyk  1571: 
        !          1572:     /* set use_list to NOT since we don't know what kind of server
        !          1573:      * this is yet.  And set the type to GENERIC
        !          1574:      */
        !          1575:     use_list = FALSE;
        !          1576:     server_type = GENERIC_SERVER;
        !          1577: 
1.1       timbl    1578:     for (retry=0; retry<2; retry++) {  /* For timed out/broken connections */
                   1579:     
                   1580:        status = get_connection(name);
                   1581:        if (status<0) return status;
                   1582: 
                   1583: #ifdef LISTEN
                   1584:        status = get_listen_socket();
1.22    ! frystyk  1585:        if (status<0) {
        !          1586:            NETCLOSE (control->socket);
        !          1587:             control->socket = -1;
        !          1588:             close_master_socket ();
        !          1589:             /* HT_INTERRUPTED would fall through, if we could interrupt
        !          1590:                somehow in the middle of it, which we currently can't. */
        !          1591:            return status;
        !          1592:        }
1.1       timbl    1593:     
                   1594: #ifdef REPEAT_PORT
                   1595: /*     Inform the server of the port number we will listen on
                   1596: */
                   1597:        {
                   1598:            status = response(port_command);
1.22    ! frystyk  1599:            if (status == HT_INTERRUPTED) {
        !          1600:               if (TRACE)
        !          1601:                 fprintf (stderr, "FTP: Interrupted in response (port_command)\n");
        !          1602:               HTProgress ("Connection interrupted.");
        !          1603:               NETCLOSE (control->socket);
        !          1604:               control->socket = -1;
        !          1605:               close_master_socket ();
        !          1606:               return HT_INTERRUPTED;
        !          1607:             }
1.1       timbl    1608:            if (status !=2) {           /* Could have timed out */
                   1609:                if (status<0) continue;         /* try again - net error*/
                   1610:                return -status;                 /* bad reply */
                   1611:            }
                   1612:            if (TRACE) fprintf(stderr, "FTP: Port defined.\n");
                   1613:        }
                   1614: #endif
                   1615: #else  /* Use PASV */
                   1616: /*     Tell the server to be passive
                   1617: */
                   1618:        {
                   1619:            char *p;
                   1620:            int reply, h0, h1, h2, h3, p0, p1;  /* Parts of reply */
1.22    ! frystyk  1621:            int status;
        !          1622:            data_soc = status;
        !          1623: 
        !          1624:            sprintf(command, "PASV%c%c", CR, LF);
        !          1625:            status = response(command);
1.1       timbl    1626:            if (status !=2) {
                   1627:                if (status<0) continue;         /* retry or Bad return */
                   1628:                return -status;                 /* bad reply */
                   1629:            }
1.22    ! frystyk  1630:            for(p=response_text; *p && *p != ','; p++)
        !          1631:                ; /* null body */
        !          1632: 
        !          1633:            while (--p > response_text && '0' <= *p && *p <= '9')
        !          1634:                ; /* null body */
        !          1635:        
        !          1636:            status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
        !          1637:                    &h0, &h1, &h2, &h3, &p0, &p1);
        !          1638:            if (status<4) {
        !          1639:                fprintf(stderr, "FTP: PASV reply has no inet address!\n");
        !          1640:                return -99;
        !          1641:            }
        !          1642:            passive_port = (p0<<8) + p1;
        !          1643:           if(TRACE)
        !          1644:                fprintf(stderr, "FTP: Server is listening on port %d\n",
        !          1645:                     passive_port);
        !          1646: 
1.1       timbl    1647: 
                   1648: /*     Open connection for data:
                   1649: */
1.22    ! frystyk  1650:            sprintf(command,
        !          1651:             "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port);
1.1       timbl    1652:            
1.22    ! frystyk  1653:            status = connect(data_soc, (struct sockaddr *) &soc_address,
        !          1654:                             sizeof(soc_address));
        !          1655: #ifdef NEW_CODE
        !          1656:             status = HTDoConnect(name, "FTP", passive_port, &data_soc);
        !          1657: #endif
1.1       timbl    1658:            if (status<0){
                   1659:                (void) HTInetStatus("connect for data");
                   1660:                NETCLOSE(data_soc);
                   1661:                return status;                  /* Bad return */
                   1662:            }
                   1663:            
                   1664:            if (TRACE) fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
                   1665:        }
                   1666: #endif /* use PASV */
                   1667:        status = 0;
                   1668:         break; /* No more retries */
                   1669: 
                   1670:     } /* for retries */
                   1671:     if (status<0) return status;       /* Failed with this code */
                   1672:     
                   1673: /*     Ask for the file:
                   1674: */    
                   1675:     {
                   1676:         char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
1.22    ! frystyk  1677:        char *fname = filename; /** Save for subsequent free() **/
1.17      luotonen 1678:        BOOL binary = NO;
1.22    ! frystyk  1679:        HTAtom * encoding;
1.17      luotonen 1680: 
1.1       timbl    1681:        if (!*filename) StrAllocCopy(filename, "/");
1.22    ! frystyk  1682:        HTUnEscape(filename);
1.15      luotonen 1683:        if (TRACE) fprintf(stderr, "FTP: UnEscaped %s\n", filename);
1.17      luotonen 1684:        format = HTFileFormat(filename,
                   1685:                              &request->content_encoding,
                   1686:                              &request->content_language);
                   1687:        binary = (request->content_encoding != HTAtom_for("8bit")
                   1688:                  && request->content_encoding != HTAtom_for("7bit"));
1.9       timbl    1689:         if (binary != control->binary) {
                   1690:            char * mode = binary ? "I" : "A";
                   1691:            sprintf(command, "TYPE %s%c%c", mode, CR, LF);
                   1692:            status = response(command);
                   1693:            if (status != 2) return -status;
                   1694:            control->binary = binary;
                   1695:        }
1.22    ! frystyk  1696:        if (server_type == VMS_SERVER) {
        !          1697:            char *cp, *cp1, *cp2;
        !          1698:            /** Accept only Unix-style filename **/
        !          1699:            if (strchr(filename, ':') != NULL ||
        !          1700:                strchr(filename, '[') != NULL) {
        !          1701:                free(fname);
        !          1702:                return -1;
        !          1703:            }
        !          1704:            /** Trim trailing slash if filename is not the top directory **/
        !          1705:            if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')
        !          1706:                filename[strlen(filename)-1] = '\0';
        !          1707: 
        !          1708: #ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
        !          1709:            /** Get the current default VMS device:[directory] **/ 
        !          1710:            sprintf(command, "PWD%c%c", CR, LF);
        !          1711:            status = response (command);
        !          1712:            if (status != 2) {
        !          1713:                 free(fname);
        !          1714:                 return -status;
        !          1715:            }
        !          1716:            /** Go to the VMS account's top directory **/
        !          1717:            if ((cp=strchr(response_text, '[')) != NULL &&
        !          1718:                (cp1=strrchr(response_text, ']')) != NULL) {
        !          1719:                sprintf(command, "CWD %s", cp);
        !          1720:                if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1)
        !          1721:                    sprintf(command+(cp2-cp)+4, "]%c%c", CR, LF);
        !          1722:                else
        !          1723:                    sprintf(command+(cp1-cp)+4, "]%c%c", CR, LF);
        !          1724:                status = response (command);
        !          1725:                if (status != 2) {
        !          1726:                    free(fname);
        !          1727:                    return -status;
        !          1728:                }
        !          1729:            }
        !          1730: #endif /* MAINTAIN_CONNECTION */
        !          1731: 
        !          1732:            /** If we want the VMS account's top directory, list it now **/
        !          1733:            if (strlen(filename) == 1 && *filename == '/') {
        !          1734:                isDirectory = YES;
        !          1735:                sprintf(command, "LIST%c%c", CR, LF);
        !          1736:                status = response (command);
        !          1737:                free(fname);
        !          1738:                if (status != 1) return -status;  /* Action not started */
        !          1739: 
        !          1740:                goto listen;  /* big goto */
        !          1741:            }
        !          1742:            /** Otherwise, go to appropriate directory and doctor filename **/
        !          1743:            if ((cp=strchr(filename, '/')) != NULL &&
        !          1744:                (cp1=strrchr(cp, '/')) != NULL && cp != cp1) {
        !          1745:                sprintf(command, "CWD [.%s", cp+1);
        !          1746:                sprintf(command+(cp1-cp)+5, "]%c%c", CR, LF);
        !          1747:                while ((cp2=strrchr(command, '/')) != NULL)
        !          1748:                    *cp2 = '.';
        !          1749:                status = response(command);
        !          1750:                if (status != 2) {
        !          1751:                    free(fname);
        !          1752:                    return -status;
        !          1753:                }
        !          1754:                filename = cp1+1;
        !          1755:            }
        !          1756:            else
        !          1757:                filename += 1;
        !          1758:        }
1.4       timbl    1759:        sprintf(command, "RETR %s%c%c", filename, CR, LF);
1.1       timbl    1760:        status = response(command);
                   1761:        if (status != 1) {  /* Failed : try to CWD to it */
1.22    ! frystyk  1762: 
        !          1763:           /* save those handy help messages */
        !          1764: #if 0
        !          1765:          if(server_type == UNIX_SERVER)
        !          1766:              init_help_message_cache();
        !          1767: #endif
1.4       timbl    1768:          sprintf(command, "CWD %s%c%c", filename, CR, LF);
1.1       timbl    1769:          status = response(command);
1.22    ! frystyk  1770: 
1.1       timbl    1771:          if (status == 2) {  /* Successed : let's NAME LIST it */
                   1772:            isDirectory = YES;
1.22    ! frystyk  1773:            if(use_list)
        !          1774:                sprintf(command, "LIST%c%c", CR, LF);
        !          1775:            else
        !          1776:                sprintf(command, "NLST%c%c", CR, LF);
1.1       timbl    1777:            status = response (command);
                   1778:          }
                   1779:        }
1.22    ! frystyk  1780:        free(fname);
1.1       timbl    1781:        if (status != 1) return -status;                /* Action not started */
                   1782:     }
                   1783: 
1.22    ! frystyk  1784: listen:
1.1       timbl    1785: #ifdef LISTEN
                   1786: /*     Wait for the connection
                   1787: */
                   1788:     {
                   1789:        struct sockaddr_in soc_address;
                   1790:         int    soc_addrlen=sizeof(soc_address);
                   1791:        status = accept(master_socket,
                   1792:                        (struct sockaddr *)&soc_address,
                   1793:                        &soc_addrlen);
                   1794:        if (status<0)
                   1795:            return HTInetStatus("accept");
                   1796:        CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
                   1797:        data_soc = status;
                   1798:     }
                   1799: #else
1.22    ! frystyk  1800: /* @@ */
1.1       timbl    1801: #endif
1.2       timbl    1802:     if (isDirectory) {
1.22    ! frystyk  1803: #ifdef OLD_CODE
        !          1804:         status = read_directory (anchor, name, format_out, sink);
        !          1805: #endif
1.21      frystyk  1806:         char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
1.22    ! frystyk  1807:        HTUnEscape(filename);
        !          1808:         if (TRACE)
        !          1809:            fprintf(stderr, "name: %s\n", filename);
        !          1810:         status = HTFTPBrowseDirectory(request, filename,
        !          1811:                                      (HTDirLineInput) HTFTP_get_dir_string);
        !          1812:         free(filename);
        !          1813: #if 0
        !          1814:         return response(NULL) == 2 ? HT_LOADED : -1;
        !          1815: #endif
        !          1816:        NETCLOSE(data_soc);
        !          1817:        NETCLOSE(control->socket);
        !          1818:         return status;
1.1       timbl    1819:       /* returns HT_LOADED or error */
1.22    ! frystyk  1820:     } else {
        !          1821:         int rv;
        !          1822: 
        !          1823:        HTProgress ("Receiving FTP file.");
        !          1824:        rv = HTParseSocket(format, data_soc, request);
        !          1825: 
        !          1826:        if (rv == HT_INTERRUPTED)
        !          1827:             HTProgress("Data transfer interrupted.");
        !          1828: 
        !          1829: #ifdef NEW_CODE
        !          1830:        HTInitInput(control->socket);
1.21      frystyk  1831: #endif
1.22    ! frystyk  1832:        /* Reset buffering to control connection DD 921208 */
1.2       timbl    1833:     
                   1834:        status = NETCLOSE(data_soc);
                   1835:        if (TRACE) fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
1.22    ! frystyk  1836:        if (status<0 && rv != HT_INTERRUPTED && rv != -1)
        !          1837:            (void) HTInetStatus("close");       /* Comment only */
1.2       timbl    1838:        data_soc = -1;  /* invalidate it */
                   1839:        
1.22    ! frystyk  1840:        status = response(NULL);                /* Pick up final reply */
        !          1841:        if (status!=2 && rv != HT_INTERRUPTED && rv != -1)
        !          1842:            return HTLoadError(request, 500, response_text);
1.2       timbl    1843: 
1.22    ! frystyk  1844:        NETCLOSE(control->socket);
1.2       timbl    1845:        return HT_LOADED;
                   1846:     }       
1.1       timbl    1847: } /* open_file_read */
1.22    ! frystyk  1848: 

Webmaster