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