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