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