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