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