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