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