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