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