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