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