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