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