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