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