Annotation of libwww/Library/src/HTFTP.c, revision 1.59
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.59 ! frystyk 1086: if ((status = HTDoConnect((HTNet *) ctrl, url, FTP_PORT)) < 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 */
1.59 ! frystyk 1550: status = HTDoConnect((HTNet *) data, data->host, serv_port);
1.33 frystyk 1551: if (status < 0) {
1.48 frystyk 1552: if (PROT_TRACE) fprintf(TDEST,
1.23 frystyk 1553: "FTP......... Data connection failed using PASV, let's try PORT instead\n");
1.35 frystyk 1554: HTErrorFree(request); /* Don't generate error message */
1.23 frystyk 1555: state = NEED_PASSIVE;
1556: } else if (status >= 0) {
1.48 frystyk 1557: if (PROT_TRACE) fprintf(TDEST, "FTP......... Data connected using PASV, socket %d\n", data->sockfd);
1.23 frystyk 1558: state = SUCCESS;
1559: } else {
1.48 frystyk 1560: state = P_ERROR; /* Interrupted */
1.23 frystyk 1561: }
1562: break;
1.22 frystyk 1563:
1.23 frystyk 1564: case NEED_PASSIVE:
1.50 frystyk 1565: #ifndef HT_FTP_NO_PORT
1.23 frystyk 1566: /* The server didn't accept our PASV so now we try ourselves to be
1567: passive using PORT */
1568: if (get_listen_socket(data) < 0 ||
1.28 frystyk 1569: HTFTP_send_cmd(ctrl, "PORT", this_addr))
1.48 frystyk 1570: state = P_ERROR;
1.23 frystyk 1571: else
1572: state = SENT_PORT;
1573: #else
1574: /* If PORT is not compiled, then there is nothing we can do! */
1.48 frystyk 1575: if (PROT_TRACE) fprintf(TDEST, "FTP......... PORT is not possible!\n");
1576: state = P_ERROR;
1.23 frystyk 1577: #endif
1578: break;
1579:
1580: case SENT_PORT:
1.28 frystyk 1581: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 1582: if (status/100 == 2) {
1.33 frystyk 1583: data->passive = 1;
1.23 frystyk 1584: state = SUCCESS;
1585: } else if (status/100 == 4)
1586: state = FAILURE;
1587: else
1.48 frystyk 1588: state = P_ERROR;
1.23 frystyk 1589: break;
1.1 timbl 1590:
1.23 frystyk 1591: case SENT_TYPE:
1.28 frystyk 1592: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 1593: /* If OK, then tell the server to be passive */
1594: if (status/100 == 2) {
1.28 frystyk 1595: if (!HTFTP_send_cmd(ctrl, "PASV", NULL))
1.23 frystyk 1596: state = SENT_PASV;
1597: else
1.48 frystyk 1598: state = P_ERROR;
1.23 frystyk 1599: } else if (status/100 == 4)
1600: state = FAILURE;
1601: else
1.48 frystyk 1602: state = P_ERROR;
1.23 frystyk 1603: break;
1604:
1.48 frystyk 1605: case P_ERROR: /* Otherwise gcc complains :-( */
1.23 frystyk 1606: case FAILURE:
1607: case SUCCESS:
1608: break;
1.22 frystyk 1609: }
1.23 frystyk 1610: }
1611: return state;
1612: }
1613:
1614:
1.50 frystyk 1615: #ifndef HT_FTP_NO_PORT
1.33 frystyk 1616: /* HTFTP_switch_to_port
1617: **
1618: ** This function changes the current data connection from being PASV to
1619: ** PORT. This is to handle servers that doesn't tell if they can handle
1620: ** a PASV data connection before very late in the state diagram.
1621: **
1.48 frystyk 1622: ** Returns -2 on P_ERROR, -1 on FAILURE, 0 on SUCCESS.
1.33 frystyk 1623: */
1.58 frystyk 1624: PRIVATE int HTFTP_switch_to_port ARGS2(ftp_data *, data,
1.33 frystyk 1625: HTRequest *, req)
1626: {
1627: enum _state {
1.48 frystyk 1628: P_ERROR = -2,
1.41 frystyk 1629: SUCCESS = 0
1.48 frystyk 1630: } state = P_ERROR;
1.33 frystyk 1631: int status;
1.58 frystyk 1632: ftp_ctrl *ctrl = data->ctrl;
1.33 frystyk 1633:
1634: if (data->passive) {
1.43 frystyk 1635: if (PROT_TRACE)
1.48 frystyk 1636: fprintf(TDEST, "FTP Switch.. We are already passive, so PORT won't help :-(\n");
1.33 frystyk 1637: return state;
1638: }
1639:
1.43 frystyk 1640: if (PROT_TRACE)
1.48 frystyk 1641: fprintf(TDEST, "FTP Switch.. Closing PASV data connection number %d, and try to reopen it on the fly using PORT\n",
1.39 frystyk 1642: data->sockfd);
1643: if ((status = NETCLOSE(data->sockfd)) < 0) {
1.48 frystyk 1644: HTErrorSysAdd(req, ERR_FATAL, NO, socerrno, "NETCLOSE");
1.33 frystyk 1645: } else
1.48 frystyk 1646: data->sockfd = INVSOC; /* Invalid socket */
1.33 frystyk 1647:
1648: /* Now get new data connection using PORT */
1649: if (status >= 0 && get_listen_socket(data) >= 0 &&
1650: !HTFTP_send_cmd(ctrl, "PORT", this_addr)) {
1651: status = HTFTP_get_response(ctrl,&ctrl->reply);
1652: if (status/100 == 2) {
1653: data->passive = 1;
1654: state = SUCCESS;
1655: }
1656: }
1657: return state;
1658: }
1.50 frystyk 1659: #endif /* HT_FTP_NO_PORT */
1.33 frystyk 1660:
1661:
1662: /* HTFTP_look_for_data
1663: **
1664: ** This function puts up a select on the data connetcion and the control
1665: ** connection in order to find out on which one data is transmitted.
1666: **
1.48 frystyk 1667: ** Returns -1 on P_ERROR, 0 if data-connection, 1 if control connection.
1.33 frystyk 1668: */
1669: PRIVATE int HTFTP_look_for_data ARGS2(HTRequest *, request,
1.58 frystyk 1670: ftp_data *, data)
1.33 frystyk 1671: {
1672: int status = -1;
1673: fd_set read_socks;
1674: struct timeval max_wait;
1.58 frystyk 1675: ftp_ctrl *ctrl = data->ctrl;
1.39 frystyk 1676: int maxfdpl = HTMAX(data->sockfd, ctrl->sockfd) + 1;
1.48 frystyk 1677: if (data->sockfd==INVSOC || ctrl->sockfd==INVSOC) {
1.43 frystyk 1678: if (PROT_TRACE)
1.48 frystyk 1679: fprintf(TDEST, "FTP Select.. Invalid socket\n");
1.33 frystyk 1680: return -1;
1681: }
1682:
1683: /* Initialize the set of sockets */
1684: FD_ZERO(&read_socks);
1.39 frystyk 1685: FD_SET(data->sockfd, &read_socks); /* Turn on bit for data socket */
1686: FD_SET(ctrl->sockfd, &read_socks); /* Turn on bit for control socket */
1.33 frystyk 1687:
1688: /* Set up timer */
1689: if (HTFTPTimeOut <= 0)
1690: HTFTPTimeOut = FTP_DEFAULT_TIMEOUT;
1691: max_wait.tv_sec = HTFTPTimeOut/100;
1692: max_wait.tv_usec = (HTFTPTimeOut%100)*10000;
1693:
1694: /* This sleep is necessary as data is usually arriving more slow on control
1695: connection than on data connection. Even on an error, the data socket
1696: might indicate that data is ready, even though they are not :-( */
1.49 frystyk 1697: SLEEP(1);
1.33 frystyk 1698:
1699: /* Now make the select */
1.48 frystyk 1700: #ifdef __hpux
1701: if ((status = select(maxfdpl, (int *) &read_socks, (int *) NULL,
1702: (int *) NULL, &max_wait)) < 0)
1703: #else
1704: if ((status = select(maxfdpl, &read_socks, (fd_set *) NULL,
1.33 frystyk 1705: (fd_set *) NULL, &max_wait)) < 0)
1.48 frystyk 1706: #endif /* __hpux */
1707: HTErrorSysAdd(request, ERR_FATAL, socerrno, NO, "select");
1.33 frystyk 1708: else if (!status) {
1.43 frystyk 1709: HTErrorAdd(request, ERR_FATAL, NO,
1710: HTERR_FTP_NO_RESPONSE, NULL, 0, "HTFTP_look_for_data");
1.33 frystyk 1711: status = -1;
1712: } else if (status > 1) {
1.48 frystyk 1713: 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 1714: status = 1;
1.39 frystyk 1715: } else if (FD_ISSET(data->sockfd, &read_socks)) {
1.43 frystyk 1716: if (PROT_TRACE)
1.48 frystyk 1717: fprintf(TDEST, "FTP Select.. Data connection %d ready\n",
1.39 frystyk 1718: data->sockfd);
1.33 frystyk 1719: status = 0;
1.39 frystyk 1720: } else if (FD_ISSET(ctrl->sockfd, &read_socks)) {
1.43 frystyk 1721: if (PROT_TRACE)
1.48 frystyk 1722: fprintf(TDEST, "FTP Select.. Control connection %d ready\n",
1.39 frystyk 1723: ctrl->sockfd);
1.33 frystyk 1724: status = 1;
1725: } else {
1.43 frystyk 1726: if (PROT_TRACE)
1.48 frystyk 1727: fprintf(TDEST, "FTP Select.. Unknown socket returned\n");
1.33 frystyk 1728: status = -1;
1729: }
1730: return status;
1731: }
1732:
1733:
1.23 frystyk 1734: /* HTFTPServerInfo()
1735: **
1736: ** This function finds out what server we are talking to.
1.48 frystyk 1737: ** Returns -2 on P_ERROR, -1 on FAILURE, 0 on SUCCESS.
1.23 frystyk 1738: **
1739: ** Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for making
1740: ** his code available.
1.1 timbl 1741: */
1.58 frystyk 1742: PRIVATE int HTFTPServerInfo ARGS1(ftp_ctrl *, ctrl)
1.23 frystyk 1743: {
1744: enum _state {
1.48 frystyk 1745: P_ERROR = -2,
1.23 frystyk 1746: FAILURE = -1,
1747: SUCCESS = 0,
1748: BEGIN,
1749: SENT_SYST,
1750: NEED_PWD,
1751: SENT_PWD
1752: } state = BEGIN;
1753: int status;
1754:
1.48 frystyk 1755: /* This loop only stops if state is P_ERROR, FAILURE or SUCCESS */
1.23 frystyk 1756: while (state > 0) {
1757: switch (state) {
1758: case BEGIN:
1759: if (!HTFTP_send_cmd(ctrl, "SYST", NULL))
1760: state = SENT_SYST;
1761: else
1.48 frystyk 1762: state = P_ERROR;
1.23 frystyk 1763: break;
1764:
1765: case SENT_SYST:
1766: {
1767: char *info;
1.28 frystyk 1768: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 1769: if (status/100 != 2) {
1770: state = NEED_PWD;
1771: break;
1772: }
1773:
1774: /* Got a line - what kind of server are we talking to? */
1.28 frystyk 1775: info = ctrl->reply->data+3; /* Skip response code */
1.23 frystyk 1776: while (*info && *info++ == ' ');
1777: if (!*info) {
1.43 frystyk 1778: if (PROT_TRACE)
1.48 frystyk 1779: fprintf(TDEST, "FTP......... No server info?\n");
1.23 frystyk 1780: state = NEED_PWD;
1781: break;
1782: }
1783: --info;
1784: if (strncmp(info, "UNIX Type: L8MAC-OSMachTen", 28) == 0) {
1785: ctrl->server = MACHTEN_SERVER;
1786: ctrl->use_list = YES;
1787: ctrl->unsure_type = NO;
1788: } else if (strstr(info, "UNIX") != NULL) {
1789: ctrl->server = UNIX_SERVER;
1790: ctrl->use_list = YES;
1791: ctrl->unsure_type = NO;
1792: } else if (strncmp(info, "VMS", 3) == 0) {
1793: ctrl->server = VMS_SERVER;
1794: ctrl->use_list = YES;
1795: ctrl->unsure_type = NO;
1796: } else if ((strncmp(info, "VM/CMS", 6) == 0) ||
1797: (strncmp(info, "VM", 2) == 0)) {
1798: ctrl->server = CMS_SERVER;
1799: ctrl->unsure_type = NO;
1800: } else if (strncmp(info, "DCTS", 4) == 0) {
1801: ctrl->server = DCTS_SERVER;
1802: ctrl->unsure_type = NO;
1803: } else if (strstr(info, "MAC-OS TCP/ConnectII") != NULL) {
1804: ctrl->server = TCPC_SERVER;
1805: /* Check old versions of TCP/C using / in pathnames */
1806: ctrl->unsure_type = YES;
1807: } else if (strncmp(info, "MACOS Peter's Server", 20) == 0) {
1808: ctrl->server = PETER_LEWIS_SERVER;
1809: ctrl->use_list = YES;
1810: ctrl->unsure_type = NO;
1.32 frystyk 1811: } else if (strncmp(info, "Windows_NT", 10) == 0) {
1812: ctrl->server = WINDOWS_NT;
1813: ctrl->use_list = YES;
1814: ctrl->unsure_type = NO;
1.23 frystyk 1815: }
1816:
1817: /* If we are unsure, try PWD to get more information */
1818: if (ctrl->server == UNKNOWN || ctrl->unsure_type == YES)
1819: state = NEED_PWD;
1820: else
1821: state = SUCCESS;
1.1 timbl 1822: }
1.23 frystyk 1823: break;
1824:
1825: case NEED_PWD:
1826: /* get the working directory (to see what it looks like) */
1827: if (!HTFTP_send_cmd(ctrl, "PWD", NULL))
1828: state = SENT_PWD;
1829: else
1.48 frystyk 1830: state = P_ERROR;
1.23 frystyk 1831: break;
1832:
1833: case SENT_PWD:
1834: {
1.28 frystyk 1835: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 1836: if (status/100 != 2)
1.48 frystyk 1837: state = P_ERROR;
1.23 frystyk 1838: else {
1839: char *start, *end;
1840:
1841: /* Now analyze response information between "'s */
1.28 frystyk 1842: if ((start = strchr(ctrl->reply->data, '"')) == NULL ||
1.23 frystyk 1843: (end = strchr(++start, '"')) == NULL) {
1.43 frystyk 1844: if (PROT_TRACE)
1.48 frystyk 1845: fprintf(TDEST,
1.23 frystyk 1846: "FTP......... No current directory?\n");
1847: ctrl->server = GENERIC_SERVER;
1848: } else {
1849: *end = '\0';
1850: if (ctrl->server == TCPC_SERVER) {
1851: ctrl->server = *start == '/' ?
1852: NCSA_SERVER : TCPC_SERVER;
1853: ctrl->unsure_type = NO;
1854: } else if (*start == '/') {
1855: /* path names starting with / imply Unix, right? */
1856: ctrl->server = UNIX_SERVER;
1857: ctrl->use_list = YES;
1858: } else if (*(end-1) == ']') {
1859: /* path names ending with ] imply VMS, right? */
1860: ctrl->server = VMS_SERVER;
1861: } else
1862: ctrl->server = GENERIC_SERVER;
1863: }
1864: state = SUCCESS;
1865: }
1866: }
1867: break;
1868:
1869: case FAILURE: /* Otherwise gcc complains :-( */
1.48 frystyk 1870: case P_ERROR:
1.23 frystyk 1871: case SUCCESS:
1872: break;
1.1 timbl 1873: }
1.23 frystyk 1874: }
1.43 frystyk 1875: if (PROT_TRACE) {
1.23 frystyk 1876: static char *servers[] = {
1877: "UNKNOWN",
1878: "GENERIC",
1879: "MACHTEN",
1880: "UNIX",
1881: "VMS",
1882: "CMS",
1883: "DCTS",
1884: "TCPC",
1.32 frystyk 1885: "PETER LEWIS",
1886: "NCSA",
1887: "WINDOWS NT"
1.23 frystyk 1888: };
1889: if (ctrl->unsure_type == YES)
1.48 frystyk 1890: fprintf(TDEST, "FTP......... This might be a %s server\n",
1.23 frystyk 1891: *(servers+ctrl->server+1));
1892: else
1.48 frystyk 1893: fprintf(TDEST, "FTP......... We are talking to a %s server\n",
1.23 frystyk 1894: *(servers+ctrl->server+1));
1895: }
1896: return state;
1897: }
1898:
1899:
1.58 frystyk 1900: #if 0
1.23 frystyk 1901: /* HTFTPLocation()
1902: **
1903: ** This function compares the current directory location in the
1904: ** ftp-session to the new URL and returns the relative position. If
1905: ** the new URL is at a higher location, the function performs CDUP's
1906: ** until this location is reached so that the relative position is '.'
1907: **
1908: ** Returns relative path name if OK, else current location
1909: **
1.1 timbl 1910: */
1.58 frystyk 1911: PRIVATE char *HTFTPLocation ARGS2(ftp_ctrl *, ctrl, char *, url)
1.23 frystyk 1912: {
1913: unsigned char getup = 0;
1914: char *current;
1.44 frystyk 1915: char *newloc;
1.23 frystyk 1916: char *relative;
1917: char *result = NULL;
1918: char *strptr;
1919:
1920: /* Make a full URL out of current location */
1921: current = HTParse(url, "", PARSE_ACCESS+PARSE_HOST+PARSE_PUNCTUATION);
1922: StrAllocCat(current, "/");
1923: StrAllocCat(current, ctrl->location);
1924: if (*(current+strlen(current)-1) != '/')
1925: StrAllocCat(current, "/");
1926:
1927: /* Make a temporary URL without any type indication */
1.44 frystyk 1928: newloc = HTParse(url, "", PARSE_ALL);
1929: if ((strptr = strrchr(newloc, ';')) != NULL)
1.23 frystyk 1930: *strptr = '\0';
1931:
1932: /* Compare those two URLs */
1.44 frystyk 1933: relative = HTRelative(newloc, current);
1.23 frystyk 1934: {
1935: /* Now send any CDUP if necessary */
1936: char *tail = relative;
1937: int status;
1938: while (tail && (strptr = strstr(tail, "../")) != NULL) {
1939: if (HTFTP_send_cmd(ctrl, "CDUP", NULL)) {
1940: break;
1941: }
1.28 frystyk 1942: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 1943: if (status/100 != 2) {
1944: break;
1.1 timbl 1945: }
1.23 frystyk 1946: ++getup;
1947: tail += 3;
1948: }
1949: }
1950:
1951: /* Now update current location if we have used CDUP and make relative
1952: return value. */
1953: if (getup) {
1.44 frystyk 1954: char *location = HTParse(newloc, "", PARSE_PATH);
1.23 frystyk 1955: free(ctrl->location);
1956: if (*location == '/')
1957: ctrl->location = ++location;
1958: else
1959: ctrl->location = location;
1960: if (*ctrl->location &&
1961: *(ctrl->location+strlen(ctrl->location)-1) == '/')
1962: *(ctrl->location+strlen(ctrl->location)-1) = '\0';
1963: StrAllocCopy(result, "");
1964: free(relative);
1965: } else {
1966: if (*relative == '/') {
1967: StrAllocCopy(result, relative+1);
1968: free(relative);
1969: } else
1970: result = relative;
1.25 frystyk 1971: if (*relative && *(relative+strlen(relative)-1) == '/')
1.23 frystyk 1972: *(relative+strlen(relative)-1) = '\0';
1973: }
1.43 frystyk 1974: if (PROT_TRACE)
1.48 frystyk 1975: fprintf(TDEST, "FTP......... current location on server: `%s\'\n",
1.23 frystyk 1976: ctrl->location);
1977: free(current);
1.44 frystyk 1978: free(newloc);
1.23 frystyk 1979: return result;
1980: }
1.58 frystyk 1981: #endif
1.22 frystyk 1982:
1983:
1.23 frystyk 1984: /* ------------------------------------------------------------------------- */
1985: /* FTP Client Functions for receiving data */
1986: /* ------------------------------------------------------------------------- */
1.1 timbl 1987:
1.23 frystyk 1988: /* HTFTP_get_dir
1989: **
1990: ** This function asks for the directory specified and calls
1991: ** HTFTPBrowseDirectory. First we try in one go, but if that doesn't
1992: ** work, then we use CWD for each segment. The directory is searched
1993: ** relative to the login directory, that is without a starting '/'.
1994: **
1.48 frystyk 1995: ** Returns -2 on P_ERROR, -1 on FAILURE, 0 on SUCCESS.
1.1 timbl 1996: */
1.58 frystyk 1997: PRIVATE int HTFTP_get_dir ARGS3(ftp_ctrl *, ctrl, HTRequest *, req,
1.33 frystyk 1998: char *, relative)
1.23 frystyk 1999: {
2000: enum _state {
1.48 frystyk 2001: P_ERROR = -2,
1.23 frystyk 2002: FAILURE = -1,
2003: SUCCESS = 0,
2004: BEGIN,
2005: NEED_LIST,
2006: SENT_LIST,
2007: SENT_CWD,
2008: MULTIPLE_CWD,
2009: READY_FOR_DATA,
2010: SENT_ABOR,
1.33 frystyk 2011: WAIT_FOR_CONNECTION,
2012: HANDLE_CTRL,
1.23 frystyk 2013: GOT_DATA
2014: } state = BEGIN;
1.33 frystyk 2015: BOOL handle_ctrl = NO;
1.58 frystyk 2016: ftp_data *data = (ftp_data *) ctrl->data_cons->next->object;
1.23 frystyk 2017: int status;
2018: char *unescaped = NULL;
2019: StrAllocCopy(unescaped, relative);
2020: HTUnEscape(unescaped);
1.30 luotonen 2021: HTCleanTelnetString(unescaped); /* Prevent security holes */
1.23 frystyk 2022:
1.48 frystyk 2023: /* This loop only stops if state is P_ERROR, FAILURE or SUCCESS */
1.23 frystyk 2024: while (state > 0) {
2025: switch (state) {
2026: case BEGIN:
2027: /* Only if the root directory is requested, we can use LIST right
2028: away, else we must first use CWD */
2029: if (!*unescaped || !strcmp(unescaped, "/"))
2030: state = NEED_LIST;
2031: else {
2032: /* We first try to CWD to the location in one go. */
2033: if (!HTFTP_send_cmd(ctrl, "CWD", unescaped))
2034: state = SENT_CWD;
2035: else
1.48 frystyk 2036: state = P_ERROR;
1.23 frystyk 2037: }
2038: break;
2039:
2040: case NEED_LIST:
2041: if (ctrl->use_list == YES) {
2042: if (!HTFTP_send_cmd(ctrl, "LIST", NULL))
2043: state = SENT_LIST;
2044: else
1.48 frystyk 2045: state = P_ERROR;
1.23 frystyk 2046: } else {
2047: if (!HTFTP_send_cmd(ctrl, "NLST", NULL))
2048: state = SENT_LIST;
2049: else
1.48 frystyk 2050: state = P_ERROR;
1.23 frystyk 2051: }
2052: break;
2053:
2054: case SENT_LIST:
1.48 frystyk 2055: #ifdef SEQUENT
1.33 frystyk 2056:
2057: /* If we are listening, do a non-blocking accept now, as the
1.23 frystyk 2058: accept on some systems (DYNIX) completes the connection. On
2059: BSD systems, the completion is done independently of the
2060: accept. (thanks to Bill Rushka, wcr@aps.org) */
1.33 frystyk 2061: if (data->passive == 1) {
1.23 frystyk 2062: int newfd;
1.48 frystyk 2063: if ((newfd = HTDoAccept(req, data->sockfd)) >= 0) {
1.23 frystyk 2064: #ifdef REPEAT_LISTEN
1.48 frystyk 2065: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d stays open.\n", data->sockfd);
1.23 frystyk 2066: #else
1.48 frystyk 2067: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d closed.\n", data->sockfd);
1.39 frystyk 2068: if (NETCLOSE(data->sockfd) < 0) {
1.48 frystyk 2069: HTErrorSysAdd(data->request, ERR_FATAL, socerrno, NO,
2070: "NETCLOSE");
2071: state = P_ERROR;
1.23 frystyk 2072: break;
2073: }
1.22 frystyk 2074: #endif
1.39 frystyk 2075: data->sockfd = newfd; /* Switch to new socket */
1.33 frystyk 2076: data->passive = 2;
1.43 frystyk 2077: if (PROT_TRACE)
1.48 frystyk 2078: fprintf(TDEST, "FTP......... New data socket: %d\n",
1.39 frystyk 2079: data->sockfd);
1.23 frystyk 2080: } else {
1.31 frystyk 2081: HTChunkClear(ctrl->reply);
1.33 frystyk 2082: ctrl->reply = NULL;
1.48 frystyk 2083: state = P_ERROR;
1.23 frystyk 2084: break;
2085: }
1.1 timbl 2086: }
1.48 frystyk 2087: #endif /* SEQUENT */
1.33 frystyk 2088:
1.28 frystyk 2089: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33 frystyk 2090: if (status == 150) /* About to open connection */
2091: state = WAIT_FOR_CONNECTION;
2092: else if (status == 125) /* Transfer starting */
1.23 frystyk 2093: state = READY_FOR_DATA;
2094: else if (status/100 == 4)
2095: state = FAILURE;
2096: else
1.48 frystyk 2097: state = P_ERROR;
1.23 frystyk 2098: break;
1.1 timbl 2099:
1.23 frystyk 2100: case SENT_CWD:
1.28 frystyk 2101: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 2102: if (status/100 == 2) {
2103: /* Update current location */
2104: if (*ctrl->location)
2105: StrAllocCat(ctrl->location, "/");
2106: StrAllocCat(ctrl->location, relative);
1.28 frystyk 2107: HTChunkClear(ctrl->welcome);
2108: HTFTPAddWelcome(ctrl);
1.23 frystyk 2109: state = NEED_LIST;
2110: } else if (status/100 == 4)
2111: state = FAILURE;
2112: else
2113: state = MULTIPLE_CWD;
2114: break;
1.1 timbl 2115:
1.23 frystyk 2116: case MULTIPLE_CWD:
2117: /* We must use the escaped version when looking for '/' as
2118: delimiter between segments, and then unescape each segment */
1.48 frystyk 2119: if (PROT_TRACE) fprintf(TDEST, "FTP......... Can't jump directly to location, try multiple CD's instead\n");
1.23 frystyk 2120: state = NEED_LIST; /* This is overwritten if error */
2121: {
2122: char *path = NULL;
2123: char *segment;
2124: StrAllocCopy(path, relative);
2125: segment = strtok(path, "/");
2126: while (segment && *segment) {
2127: HTUnEscape(segment);
1.30 luotonen 2128: HTCleanTelnetString(segment); /* Prevent security holes */
1.23 frystyk 2129: if (HTFTP_send_cmd(ctrl, "CWD", segment)) {
1.48 frystyk 2130: state = P_ERROR;
1.23 frystyk 2131: break;
2132: }
1.28 frystyk 2133: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 2134: if (status/100 != 2) {
2135: if (status/100 == 4)
2136: state = FAILURE;
2137: else
1.48 frystyk 2138: state = P_ERROR;
1.23 frystyk 2139: break;
2140: } else { /* Update current location */
2141: char *new_seg = HTEscape(segment, URL_XPALPHAS);
2142: if (*ctrl->location)
2143: StrAllocCat(ctrl->location, "/");
2144: StrAllocCat(ctrl->location, new_seg);
2145: free(new_seg);
1.28 frystyk 2146: HTChunkClear(ctrl->welcome);
2147: HTFTPAddWelcome(ctrl);
1.23 frystyk 2148: }
2149: segment = strtok(NULL, "/"); /* Get next token */
1.22 frystyk 2150: }
1.23 frystyk 2151: free(path);
1.22 frystyk 2152: }
1.23 frystyk 2153: break;
1.22 frystyk 2154:
1.23 frystyk 2155: case READY_FOR_DATA:
1.33 frystyk 2156: if (data->passive == 1) {
2157: int newfd;
1.58 frystyk 2158: if ((newfd = HTDoAccept((HTNet *) data)) >= 0) {
1.33 frystyk 2159: #ifdef REPEAT_LISTEN
1.48 frystyk 2160: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d stays open.\n", data->sockfd);
1.33 frystyk 2161: #else
1.48 frystyk 2162: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d closed.\n", data->sockfd);
1.39 frystyk 2163: if (NETCLOSE(data->sockfd) < 0) {
1.48 frystyk 2164: HTErrorSysAdd(data->request, ERR_FATAL, socerrno, NO,
2165: "NETCLOSE");
2166: state = P_ERROR;
1.33 frystyk 2167: break;
2168: }
2169: #endif
1.39 frystyk 2170: data->sockfd = newfd; /* Switch to new socket */
1.33 frystyk 2171: data->passive = 2;
1.43 frystyk 2172: if (PROT_TRACE)
1.48 frystyk 2173: fprintf(TDEST, "FTP......... New data socket: %d\n",
1.39 frystyk 2174: data->sockfd);
1.33 frystyk 2175: } else {
2176: HTChunkClear(ctrl->reply);
2177: ctrl->reply = NULL;
1.48 frystyk 2178: state = P_ERROR;
1.33 frystyk 2179: break;
2180: }
2181: }
2182:
1.23 frystyk 2183: /* Now, the browsing module can be called */
2184: {
2185: char *url = HTAnchor_physical(req->anchor);
2186: char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
2187: HTUnEscape(path);
1.43 frystyk 2188: if (PROT_TRACE)
1.48 frystyk 2189: fprintf(TDEST, "FTP......... Receiving directory `%s\'\n",
1.23 frystyk 2190: path);
1.39 frystyk 2191: data->isoc = HTInputSocket_new(data->sockfd);
2192: status = HTFTPBrowseDirectory(req, path, HTFTP_get_dir_string);
2193: HTInputSocket_free(data->isoc);
1.23 frystyk 2194: if (status == -1)
1.48 frystyk 2195: state = P_ERROR;
1.58 frystyk 2196: else
1.23 frystyk 2197: state = GOT_DATA;
2198: free(path);
1.22 frystyk 2199: }
1.23 frystyk 2200: break;
2201:
2202: case GOT_DATA:
1.33 frystyk 2203: if (!handle_ctrl) {
2204: status = HTFTP_get_response(ctrl, &ctrl->reply);
2205: if (status/100 == 2)
2206: state = SUCCESS; /* Directory read */
2207: else if (status/100 == 4)
2208: state = FAILURE;
2209: else
1.48 frystyk 2210: state = P_ERROR;
1.33 frystyk 2211: } else
2212: state = SUCCESS;
2213: break;
2214:
2215: case SENT_ABOR:
1.28 frystyk 2216: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 2217: if (status/100 == 2)
1.33 frystyk 2218: state = SUCCESS;
1.23 frystyk 2219: else if (status/100 == 4)
2220: state = FAILURE;
2221: else
1.48 frystyk 2222: state = P_ERROR;
1.23 frystyk 2223: break;
2224:
1.33 frystyk 2225: case WAIT_FOR_CONNECTION:
2226: /* Now we have to wait to see whether the FTP-server sends on the
2227: data port or the control port */
2228: status = HTFTP_look_for_data(req, data);
2229: if (!status)
2230: state = READY_FOR_DATA;
1.43 frystyk 2231: else if (status == 1)
2232: state = HANDLE_CTRL;
1.33 frystyk 2233: else
1.48 frystyk 2234: state = P_ERROR;
1.33 frystyk 2235: break;
2236:
2237: case HANDLE_CTRL:
1.28 frystyk 2238: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33 frystyk 2239: if (status/100 == 2) {
2240: state = READY_FOR_DATA;
1.50 frystyk 2241: #ifndef HT_FTP_NO_PORT
1.33 frystyk 2242: } else if (status == 425) { /* Connection could not be opened */
2243: if (HTFTP_switch_to_port(data, req))
1.48 frystyk 2244: state = P_ERROR;
1.33 frystyk 2245: else
2246: state = NEED_LIST;
1.50 frystyk 2247: #endif /* HT_FTP_NO_PORT */
1.33 frystyk 2248: } else if (status/100 == 4)
1.23 frystyk 2249: state = FAILURE;
1.22 frystyk 2250: else
1.48 frystyk 2251: state = P_ERROR;
1.33 frystyk 2252: handle_ctrl = YES;
1.23 frystyk 2253: break;
2254:
2255: case FAILURE: /* Otherwise gcc complains :-( */
1.48 frystyk 2256: case P_ERROR:
1.23 frystyk 2257: case SUCCESS:
2258: break;
1.22 frystyk 2259: }
1.23 frystyk 2260: }
2261: FREE(unescaped);
2262: return state;
2263: }
2264:
2265:
2266: /* HTFTP_get_file
2267: **
2268: ** This function asks for the file specified. First we try in one go,
2269: ** but if that doesn't work, then we use CWD for each segment and then
2270: ** try to retrieve it. If that also fails, then we try if it is a
2271: ** directory. This procedure causes that directory links generated in
2272: ** HTFTPBrowseDirectory should have a '/' at the end in order to go
2273: ** directly to HTFTP_get_dir. The relative is searched relative to
2274: ** the login directory, that is without a starting '/'.
2275: **
1.48 frystyk 2276: ** Returns -2 on P_ERROR, -1 on FAILURE, 0 on SUCCESS.
1.23 frystyk 2277: */
1.58 frystyk 2278: PRIVATE int HTFTP_get_file ARGS3(ftp_ctrl *, ctrl, HTRequest *, req,
1.33 frystyk 2279: char *, relative)
1.23 frystyk 2280: {
2281: enum _state {
1.48 frystyk 2282: P_ERROR = -2,
1.23 frystyk 2283: FAILURE = -1,
2284: SUCCESS = 0,
2285: BEGIN,
2286: SENT_RETR,
2287: MULTIPLE_CWD,
2288: READY_FOR_DATA,
2289: SENT_ABOR,
1.33 frystyk 2290: WAIT_FOR_CONNECTION,
2291: HANDLE_CTRL,
1.23 frystyk 2292: GOT_DATA
2293: } state = BEGIN;
1.33 frystyk 2294: BOOL handle_ctrl = NO;
1.23 frystyk 2295: BOOL multiple = NO; /* Have we already tried multiple CWD? */
1.58 frystyk 2296: ftp_data *data = (ftp_data *) ctrl->data_cons->next->object;
1.23 frystyk 2297: int status;
2298: char *unescaped = NULL;
2299: StrAllocCopy(unescaped, relative);
2300: HTUnEscape(unescaped);
1.30 luotonen 2301: HTCleanTelnetString(unescaped); /* Prevent security holes */
1.23 frystyk 2302:
1.48 frystyk 2303: /* This loop only stops if state is P_ERROR, FAILURE or SUCCESS */
1.23 frystyk 2304: while (state > 0) {
2305: switch (state) {
2306: case BEGIN:
2307: /* First we try to retrieve the file in one go. */
2308: if (!HTFTP_send_cmd(ctrl, "RETR", unescaped))
2309: state = SENT_RETR;
2310: else
1.48 frystyk 2311: state = P_ERROR;
1.23 frystyk 2312: break;
2313:
2314: case SENT_RETR:
1.48 frystyk 2315: #ifdef SEQUENT
1.33 frystyk 2316: /* If we are listening, do a non-blocking accept now, as the
1.23 frystyk 2317: accept on some systems (DYNIX) completes the connection. On
2318: BSD systems, the completion is done independently of the
2319: accept. (thanks to Bill Rushka, wcr@aps.org) */
1.33 frystyk 2320: if (data->passive == 1) {
1.23 frystyk 2321: int newfd;
1.48 frystyk 2322: if ((newfd = HTDoAccept(req, data->sockfd)) >= 0) {
1.23 frystyk 2323: #ifdef REPEAT_LISTEN
1.48 frystyk 2324: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d stays open.\n", data->sockfd);
1.23 frystyk 2325: #else
1.48 frystyk 2326: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d closed.\n", data->sockfd);
1.39 frystyk 2327: if (NETCLOSE(data->sockfd) < 0) {
1.48 frystyk 2328: HTErrorSysAdd(data->request, ERR_FATAL, socerrno,
2329: NO, "NETCLOSE");
2330: state = P_ERROR;
1.23 frystyk 2331: break;
2332: }
1.22 frystyk 2333: #endif
1.39 frystyk 2334: data->sockfd = newfd; /* Switch to new socket */
1.33 frystyk 2335: data->passive = 2;
1.43 frystyk 2336: if (PROT_TRACE)
1.48 frystyk 2337: fprintf(TDEST, "FTP......... New data socket: %d\n",
1.39 frystyk 2338: data->sockfd);
1.23 frystyk 2339: } else {
1.31 frystyk 2340: HTChunkClear(ctrl->reply);
1.33 frystyk 2341: ctrl->reply = NULL;
1.48 frystyk 2342: state = P_ERROR;
1.23 frystyk 2343: break;
2344: }
2345: }
1.48 frystyk 2346: #endif /* SEQUENT */
1.33 frystyk 2347:
1.28 frystyk 2348: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.33 frystyk 2349: if (status == 150) /* About to open connection */
2350: state = WAIT_FOR_CONNECTION;
2351: else if (status == 125)
2352: state = READY_FOR_DATA; /* Transfer starting */
1.23 frystyk 2353: else if (status/100 == 4)
2354: state = FAILURE;
1.33 frystyk 2355:
1.23 frystyk 2356: /* If there is no '/' in unescaped, it won't help to try
2357: multiple CWD's, as it either doesn't exist or is a directory */
2358: else if (multiple == NO && strchr(unescaped, '/') != NULL)
2359: state = MULTIPLE_CWD;
2360: else {
2361: data->directory = YES;
2362: state = FAILURE;
2363: }
2364: break;
2365:
2366: case MULTIPLE_CWD:
2367: /* We must use the escaped version when looking for '/' as
2368: delimiter between segments, and then unescape each segment */
1.48 frystyk 2369: if (PROT_TRACE) fprintf(TDEST, "FTP......... Can't jump directly to location, try multiple CD's instead\n");
1.23 frystyk 2370: multiple = YES;
2371: {
2372: char *path = NULL;
2373: char *segment;
2374: char *last_slash; /* Used to identify the last segment */
2375: StrAllocCopy(path, relative);
2376: if ((last_slash = strrchr(path, '/')) == NULL)
2377: last_slash = path;
2378: else
2379: last_slash++;
2380: segment = strtok(path, "/");
2381: while (segment && *segment && segment != last_slash) {
2382: HTUnEscape(segment);
1.30 luotonen 2383: HTCleanTelnetString(segment); /* Prevent security holes */
1.23 frystyk 2384: if (HTFTP_send_cmd(ctrl, "CWD", segment)) {
1.48 frystyk 2385: state = P_ERROR;
1.23 frystyk 2386: break;
2387: }
1.28 frystyk 2388: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 2389: if (status/100 != 2) {
2390: if (status/100 == 4)
2391: state = FAILURE;
2392: else
1.48 frystyk 2393: state = P_ERROR;
1.23 frystyk 2394: break;
2395: } else { /* Update current location */
2396: char *new_seg = HTEscape(segment, URL_XPALPHAS);
2397: if (*ctrl->location)
2398: StrAllocCat(ctrl->location, "/");
2399: StrAllocCat(ctrl->location, new_seg);
2400: free(new_seg);
2401: }
2402: segment = strtok(NULL, "/"); /* Get next token */
2403: }
2404: /* Now try to retrieve the last segment */
2405: if (segment == last_slash) {
2406: HTUnEscape(segment);
1.30 luotonen 2407: HTCleanTelnetString(segment); /* Prevent security holes */
1.33 frystyk 2408: if (!HTFTP_send_cmd(ctrl, "RETR", segment)) {
2409: StrAllocCopy(unescaped, segment);
1.23 frystyk 2410: state = SENT_RETR;
1.33 frystyk 2411: } else
1.48 frystyk 2412: state = P_ERROR;
1.23 frystyk 2413: } else {
1.48 frystyk 2414: if (PROT_TRACE) fprintf(TDEST, "FTP......... Strange error, filename not found?\n");
2415: state = P_ERROR;
1.23 frystyk 2416: }
2417: free(path);
2418: }
2419: break;
2420:
2421: case READY_FOR_DATA:
1.33 frystyk 2422: if (data->passive == 1) {
2423: int newfd;
1.58 frystyk 2424: if ((newfd = HTDoAccept((HTNet *) data)) >= 0) {
1.33 frystyk 2425: #ifdef REPEAT_LISTEN
1.48 frystyk 2426: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d stays open.\n", data->sockfd);
1.33 frystyk 2427: #else
1.48 frystyk 2428: if (PROT_TRACE) fprintf(TDEST, "FTP......... Passive data channel number %d closed.\n", data->sockfd);
1.39 frystyk 2429: if (NETCLOSE(data->sockfd) < 0) {
1.48 frystyk 2430: HTErrorSysAdd(data->request, ERR_FATAL, socerrno, NO,
2431: "NETCLOSE");
2432: state = P_ERROR;
1.33 frystyk 2433: break;
2434: }
2435: #endif
1.39 frystyk 2436: data->sockfd = newfd; /* Switch to new socket */
1.33 frystyk 2437: data->passive = 2;
1.43 frystyk 2438: if (PROT_TRACE)
1.48 frystyk 2439: fprintf(TDEST, "FTP......... New data socket: %d\n",
1.39 frystyk 2440: data->sockfd);
1.33 frystyk 2441: } else {
2442: HTChunkClear(ctrl->reply);
2443: ctrl->reply = NULL;
1.48 frystyk 2444: state = P_ERROR;
1.33 frystyk 2445: break;
2446: }
2447: }
2448:
1.35 frystyk 2449: /* Now, the net parse module can be called */
1.51 frystyk 2450: if (PROT_TRACE)
2451: fprintf(TDEST,"FTP......... Receiving file `%s\'\n",unescaped);
2452: status = HTParseSocket(HTAnchor_format(req->anchor),
2453: data->sockfd, req);
1.58 frystyk 2454: state = (status != HT_LOADED) ? P_ERROR : GOT_DATA;
1.23 frystyk 2455: break;
2456:
2457: case GOT_DATA:
1.33 frystyk 2458: if (!handle_ctrl) {
2459: status = HTFTP_get_response(ctrl, &ctrl->reply);
2460: if (status/100 == 2)
2461: state = SUCCESS; /* File read */
2462: else if (status/100 == 4)
2463: state = FAILURE;
2464: else
1.48 frystyk 2465: state = P_ERROR;
1.33 frystyk 2466: } else
2467: state = SUCCESS;
1.23 frystyk 2468: break;
1.22 frystyk 2469:
1.23 frystyk 2470: case SENT_ABOR:
1.28 frystyk 2471: status = HTFTP_get_response(ctrl, &ctrl->reply);
1.23 frystyk 2472: if (status/100 == 2)
2473: state = SUCCESS;
2474: else if (status/100 == 4)
2475: state = FAILURE;
1.22 frystyk 2476: else
1.48 frystyk 2477: state = P_ERROR;
1.23 frystyk 2478: break;
2479:
1.33 frystyk 2480: case WAIT_FOR_CONNECTION:
2481: /* Now we have to wait to see whether the FTP-server sends on the
2482: data port or the control port */
2483: status = HTFTP_look_for_data(req, data);
2484: if (!status)
2485: state = READY_FOR_DATA;
1.43 frystyk 2486: else if (status == 1)
1.33 frystyk 2487: state = HANDLE_CTRL;
1.43 frystyk 2488: else
1.48 frystyk 2489: state = P_ERROR;
1.33 frystyk 2490: break;
2491:
2492: case HANDLE_CTRL:
2493: status = HTFTP_get_response(ctrl, &ctrl->reply);
2494: if (status/100 == 2) {
2495: state = READY_FOR_DATA;
1.50 frystyk 2496: #ifndef HT_FTP_NO_PORT
1.33 frystyk 2497: } else if (status == 425) { /* Connection could not be opened */
2498: if (HTFTP_switch_to_port(data, req))
1.48 frystyk 2499: state = P_ERROR;
1.33 frystyk 2500: else {
2501: if (!HTFTP_send_cmd(ctrl, "RETR", unescaped))
2502: state = SENT_RETR;
2503: else
1.48 frystyk 2504: state = P_ERROR;
1.33 frystyk 2505: }
1.50 frystyk 2506: #endif /* HT_FTP_NO_PORT */
1.33 frystyk 2507: } else if (status/100 == 4)
2508: state = FAILURE;
2509: else
1.48 frystyk 2510: state = P_ERROR;
1.33 frystyk 2511: handle_ctrl = YES;
2512: break;
2513:
1.23 frystyk 2514: case FAILURE: /* Otherwise gcc complains :-( */
1.48 frystyk 2515: case P_ERROR:
1.23 frystyk 2516: case SUCCESS:
2517: break;
1.1 timbl 2518: }
2519: }
1.23 frystyk 2520: FREE(unescaped);
2521: return state;
2522: }
1.1 timbl 2523:
1.23 frystyk 2524:
2525: /* ------------------------------------------------------------------------- */
2526: /* PUBLIC FTP functions */
2527: /* ------------------------------------------------------------------------- */
1.58 frystyk 2528: #endif
1.39 frystyk 2529: /* HTFTPWelcomeMsg
2530: **
2531: ** Returns the welcome message from the login sequence
2532: */
1.58 frystyk 2533: PUBLIC HTChunk *HTFTPWelcomeMsg ARGS1(HTNet *, data)
1.39 frystyk 2534: {
1.58 frystyk 2535: return ((ftp_data *) data)->ctrl->welcome;
1.39 frystyk 2536: }
2537:
2538: /* HTFTPUseList
2539: **
2540: ** Can we use long listings in HTDirBrw.c?
2541: */
1.58 frystyk 2542: PUBLIC BOOL HTFTUseList ARGS1(HTNet *, data)
1.39 frystyk 2543: {
1.58 frystyk 2544: return ((ftp_data *) data)->ctrl->use_list;
1.39 frystyk 2545: }
2546:
1.23 frystyk 2547:
2548:
2549: /* Retrieve File from Server as an atomic action.
2550: ** -----------------------------------------------
1.58 frystyk 2551: ** Given a hypertext address, this routine loads a document.
1.23 frystyk 2552: **
2553: ** On entry,
1.58 frystyk 2554: ** request This is the request structure
2555: ** returns HT_ERROR Error has occured in call back
2556: ** HT_OK Call back was OK
2557: */
2558: PUBLIC int HTLoadFTP ARGS3(SOCKET, soc, HTRequest *, request, SockOps, ops)
2559: {
2560: int status = HT_ERROR;
2561: #if 0
2562: HTNet *net = request->net;
2563: ftp_ctrl *ctrl;
2564: char *url = HTAnchor_physical(request->anchor);
1.33 frystyk 2565: int retry; /* How many times tried? */
1.23 frystyk 2566:
1.58 frystyk 2567: /*
2568: ** Initiate a new FTP data structure and bind to request structure
2569: ** This is actually state FTP_BEGIN, but it can't be in the state
2570: ** machine as we need the structure first.
2571: */
2572: if (ops == FD_NONE) {
2573: if (PROT_TRACE) fprintf(TDEST, "FTP......... Looking for `%s\'\n",url);
2574:
2575: /* The data HTNet object is hooked up to the request object and the
2576: ctrl HTNet object is floating around */
2577: if ((data = (ftp_data *) calloc(1, sizeof(ftp_data))) == NULL)
2578: outofmem(__FILE__, "HTLoadFTP");
2579: ctrl->state = FTP_BEGIN;
2580: net->context = ctrl; /* Context for control connection */
2581:
2582: } if (ops == FD_CLOSE) { /* Interrupted */
2583: GopherCleanup(request, HT_INTERRUPTED);
2584: } else
2585: gopher = (gopher_info *) net->context; /* Get existing copy */
2586:
2587: /* Now jump into the machine. We know the state from the previous run */
2588: while (1) {
2589: switch (ctrl->state) {
2590:
1.33 frystyk 2591:
1.23 frystyk 2592: /* Initiate a (possibly already exsisting) control connection and a
2593: corresponding data connection */
1.29 frystyk 2594: if((ctrl = HTFTP_init_con(request, url)) == NULL) {
1.33 frystyk 2595: goto endfunc;
1.25 frystyk 2596: }
1.23 frystyk 2597:
2598: /* Only if the control connection is in IDLE state, a new
2599: transfer can be started. The control connection can be in another
2600: mode if (session), and then the request is getting queued in
2601: ctrl->data_cons. */
1.58 frystyk 2602: if (ctrl->state == FTP_IDLE) {
2603: ftp_data *data = (ftp_data *) ctrl->data_cons->next->object;
1.31 frystyk 2604: if (ctrl->state == FTP_IDLE)
2605: ctrl->state = FTP_BEGIN;
2606: while (ctrl->state != FTP_IDLE) { /* Do until finished */
1.23 frystyk 2607: switch (ctrl->state) {
1.31 frystyk 2608: case FTP_BEGIN:
1.23 frystyk 2609: if (!HTFTP_login(ctrl))
1.31 frystyk 2610: ctrl->state = FTP_LOGGED_IN;
1.23 frystyk 2611: else
1.31 frystyk 2612: ctrl->state = FTP_ERROR;
1.23 frystyk 2613: break;
2614:
1.31 frystyk 2615: case FTP_LOGGED_IN:
1.33 frystyk 2616: if (!HTFTP_get_data_con(request, data, url))
1.31 frystyk 2617: ctrl->state = FTP_GOT_DATA_CON;
1.23 frystyk 2618: else
1.31 frystyk 2619: ctrl->state = FTP_ERROR;
1.23 frystyk 2620: break;
2621:
1.31 frystyk 2622: case FTP_GOT_DATA_CON:
1.23 frystyk 2623: {
2624: /* Now we must ask for the URL requested. If FAILURE, then
2625: we try twice to see, if it helps */
1.33 frystyk 2626: char *rel = NULL;
1.23 frystyk 2627: for (retry=0; retry<2; retry++) {
2628: if ((rel = HTFTPLocation(ctrl, url)) == NULL) {
1.31 frystyk 2629: ctrl->state = FTP_ERROR;
1.23 frystyk 2630: break;
2631: }
1.33 frystyk 2632: if (retry == 1 && TRACE)
1.48 frystyk 2633: fprintf(TDEST,
1.33 frystyk 2634: "FTP......... First attempt to get URL failed, let's try again\n");
1.23 frystyk 2635:
2636: if (data->directory == YES) {
2637: /* If we haven't already got server-info */
2638: if (ctrl->server == UNKNOWN) {
2639: if (HTFTPServerInfo(ctrl)) {
1.31 frystyk 2640: ctrl->state = FTP_ERROR;
1.23 frystyk 2641: break;
2642: }
2643: }
1.33 frystyk 2644: status = HTFTP_get_dir(ctrl, request, rel);
1.23 frystyk 2645: }
2646: else
1.33 frystyk 2647: status = HTFTP_get_file(ctrl, request, rel);
1.23 frystyk 2648: if (!status) {
1.31 frystyk 2649: ctrl->state = FTP_GOT_DATA;
1.23 frystyk 2650: break;
2651: } else if (status == -2) { /* Error */
1.31 frystyk 2652: ctrl->state = FTP_ERROR;
1.23 frystyk 2653: break;
2654: } else {
1.33 frystyk 2655: FREE(rel);
1.31 frystyk 2656: ctrl->state = FTP_FAILURE; /* Try twice */
1.23 frystyk 2657: }
2658: }
1.33 frystyk 2659: FREE(rel);
1.23 frystyk 2660: }
1.31 frystyk 2661: if (retry == 2 && ctrl->state == FTP_FAILURE)
2662: ctrl->state = FTP_ERROR;
1.23 frystyk 2663: break;
2664:
1.31 frystyk 2665: case FTP_GOT_DATA:
1.23 frystyk 2666: if (HTFTP_close_data_con(data))
1.31 frystyk 2667: ctrl->state = FTP_ERROR;
1.23 frystyk 2668: else {
2669: HTList_removeLastObject(ctrl->data_cons);
1.58 frystyk 2670: if (!HTFTP_logout(ctrl)) {
2671: ctrl->state = FTP_IDLE;
2672: status = HT_LOADED;
2673: } else
2674: ctrl->state = FTP_ERROR;
1.23 frystyk 2675: break;
2676: }
2677: break;
2678:
1.31 frystyk 2679: case FTP_ERROR:
1.30 luotonen 2680: {
1.32 frystyk 2681: if (ctrl->reply && ctrl->reply->data) {
1.33 frystyk 2682: HTFTPParseError(&ctrl->reply);
2683: HTErrorAdd(request, ERR_FATAL, NO, HTERR_FTP_SERVER,
2684: (void *) ctrl->reply->data,
2685: ctrl->reply->size-1, "HTLoadFTP");
1.32 frystyk 2686: } else {
1.33 frystyk 2687: char *hoststr = HTParse(url, "", PARSE_HOST);
2688: HTUnEscape(hoststr);
2689: HTErrorAdd(request, ERR_FATAL, NO,
2690: HTERR_FTP_NO_RESPONSE,
2691: (void *) hoststr, strlen(hoststr),
2692: "HTLoadFTP");
2693: free(hoststr);
1.32 frystyk 2694: }
1.30 luotonen 2695: HTFTP_abort_ctrl_con(ctrl);
1.33 frystyk 2696: status = -1;
2697: goto endfunc;
1.30 luotonen 2698: }
1.23 frystyk 2699: break;
2700:
2701: default:
1.43 frystyk 2702: if (PROT_TRACE)
1.48 frystyk 2703: fprintf(TDEST, "FTP......... Unknown state, what happened?\n");
1.23 frystyk 2704: break;
2705: }
2706: }
1.22 frystyk 2707:
1.58 frystyk 2708: if (HTFTP_close_ctrl_con(ctrl))
1.23 frystyk 2709: status = -1;
2710: }
1.33 frystyk 2711:
2712: endfunc:
1.58 frystyk 2713: if (status < 0) {
1.35 frystyk 2714: char *unescaped = NULL;
2715: StrAllocCopy(unescaped, url);
2716: HTUnEscape(unescaped);
2717: HTErrorAdd(request, ERR_FATAL, NO, HTERR_INTERNAL, (void *) unescaped,
2718: (int) strlen(unescaped), "HTLoadFTP");
2719: free(unescaped);
2720: }
1.58 frystyk 2721: #endif
1.23 frystyk 2722: return status;
2723: }
1.22 frystyk 2724:
Webmaster