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