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