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