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