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