Annotation of libwww/Library/src/HTFTP.c, revision 1.65
1.46 frystyk 1: /* HTFTP.c
2: ** FILE TRANSFER PROTOCOL (FTP) CLIENT
3: **
1.52 frystyk 4: ** (c) COPYRIGHT MIT 1995.
1.46 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.1 timbl 6: **
7: ** A cache of control connections is kept.
8: **
9: ** Note: Port allocation
10: **
11: ** It is essential that the port is allocated by the system, rather
12: ** than chosen in rotation by us (POLL_PORTS), or the following
13: ** problem occurs.
14: **
15: ** It seems that an attempt by the server to connect to a port which has
16: ** been used recently by a listen on the same socket, or by another
17: ** socket this or another process causes a hangup of (almost exactly)
18: ** one minute. Therefore, we have to use a rotating port number.
19: ** The problem remains that if the application is run twice in quick
20: ** succession, it will hang for what remains of a minute.
21: **
22: ** Authors
1.54 frystyk 23: ** TBL Tim Berners-lee <timbl@w3.org>
1.1 timbl 24: ** DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
1.22 frystyk 25: ** LM Lou Montulli <montulli@ukanaix.cc.ukans.edu>
26: ** FM Foteos Macrides <macrides@sci.wfeb.edu>
1.54 frystyk 27: ** HF Henrik Frystyk <frystyk@w3.org>
1.30 luotonen 28: ** AL Ari Luotonen <luotonen@www.cern.ch>
1.23 frystyk 29: **
1.1 timbl 30: ** History:
31: ** 2 May 91 Written TBL, as a part of the WorldWideWeb project.
32: ** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc
33: ** 10 Feb 92 Retry if cached connection times out or breaks
34: ** 8 Dec 92 Bug fix 921208 TBL after DD
35: ** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD
1.2 timbl 36: ** fails on princeton.edu!
1.22 frystyk 37: ** 27 Dec 93 (FM) Fixed up so FTP now works with VMS hosts. Path
38: ** must be Unix-style and cannot include the device
39: ** or top directory.
40: ** ?? ??? ?? (LM) Added code to prompt and send passwords for non
41: ** anonymous FTP
42: ** 25 Mar 94 (LM) Added code to recognize different ftp server types
43: ** and code to parse dates and sizes on most hosts.
44: ** 27 Mar 93 (FM) Added code for getting dates and sizes on VMS hosts.
1.23 frystyk 45: ** 27 Apr 94 (HF) The module is basically rewritten to conform with
46: ** rfc 959, 1123 and 1579 and turned into a state
47: ** machine. New semantics of ftp URLs are supported.
1.30 luotonen 48: ** 2 May 94 (AL) Fixed possible security hole when the URL contains
49: ** a newline, that could cause multiple commands to be
50: ** sent to an FTP server.
1.60 frystyk 51: ** Sep 95 HFN Rewritten to support streams and persistent conenctions
52: ** and multiplexed IO
1.22 frystyk 53: ** Notes:
54: ** Portions Copyright 1994 Trustees of Dartmouth College
55: ** Code for recognizing different FTP servers and
56: ** parsing "ls -l" output taken from Macintosh Fetch
57: ** program with permission from Jim Matthews,
58: ** Dartmouth Software Development Team.
1.23 frystyk 59: */
1.1 timbl 60:
1.22 frystyk 61: /* Library include files */
1.48 frystyk 62: #include "tcp.h"
63: #include "HTUtils.h"
64: #include "HTString.h"
1.1 timbl 65: #include "HTParse.h"
66: #include "HTTCP.h"
67: #include "HTAnchor.h"
1.51 frystyk 68: #include "HTBind.h"
1.53 frystyk 69: #include "HTSocket.h"
1.60 frystyk 70: #include "HTWriter.h"
1.6 secret 71: #include "HTChunk.h"
1.22 frystyk 72: #include "HTAlert.h"
1.60 frystyk 73: #include "HTFormat.h"
74: #include "HTStream.h"
1.33 frystyk 75: #include "HTError.h"
1.58 frystyk 76: #include "HTReqMan.h"
1.60 frystyk 77: #include "HTNetMan.h"
1.63 frystyk 78: #include "HTFTPDir.h"
1.22 frystyk 79: #include "HTFTP.h" /* Implemented here */
80:
1.48 frystyk 81: #ifdef VMS
82: #include "HTVMSUtils.h"
83: #endif /* VMS */
84:
1.22 frystyk 85: /* Macros and other defines */
1.60 frystyk 86: #if 0
87: /* Only use this if ABSOLUTELY necessary! */
88: #define POLL_PORTS /* If allocation does not work, poll ourselves.*/
1.1 timbl 89: #endif
90:
91: #ifdef POLL_PORTS
1.23 frystyk 92: #define FIRST_TCP_PORT 1024 /* Region to try for a listening port */
93: #define LAST_TCP_PORT 5999
1.60 frystyk 94: PRIVATE int DataPort = FIRST_TCP_PORT;
1.1 timbl 95: #endif
96:
1.60 frystyk 97: #ifndef FTP_PORT
98: #define FTP_PORT 21
99: #define FTP_DATA 20
1.1 timbl 100: #endif
1.60 frystyk 101:
102: #define WWW_FTP_CLIENT "libwww@" /* If can't get user-info, use this */
103: #define FTP_DIR(me) ((me)->type=='L' || (me)->type=='N')
1.1 timbl 104:
1.58 frystyk 105: /*
106: ** Local context structure used in the HTNet object.
107: */
1.45 frystyk 108: typedef enum _HTFTPState {
1.60 frystyk 109: FTP_SUCCESS = -2,
110: FTP_ERROR = -1,
1.58 frystyk 111: FTP_BEGIN = 0,
112: FTP_NEED_CCON, /* Control connection */
1.60 frystyk 113: FTP_NEED_LOGIN,
1.58 frystyk 114: FTP_NEED_DCON, /* Data connection */
1.60 frystyk 115: FTP_NEED_DATA,
116: FTP_NEED_SERVER /* For directory listings */
1.39 frystyk 117: } HTFTPState;
118:
1.58 frystyk 119: typedef struct _ftp_ctrl {
1.60 frystyk 120: HTChunk * cmd;
121: int repcode;
122: char * reply;
123: char * uid;
1.58 frystyk 124: char * passwd;
1.60 frystyk 125: char * account;
1.58 frystyk 126: HTFTPState state; /* State of the connection */
1.60 frystyk 127: int substate; /* For hierarchical states */
128: BOOL sent; /* Read or write command */
129: BOOL cwd; /* Done cwd */
130: BOOL reset; /* Expect greeting */
1.63 frystyk 131: FTPServerType server; /* Type of server */
1.60 frystyk 132: HTNet * dnet; /* Data connection */
1.58 frystyk 133: } ftp_ctrl;
1.39 frystyk 134:
1.58 frystyk 135: typedef struct _ftp_data {
1.60 frystyk 136: char host[30]; /* Host to contact for data */
137: char * file; /* File or dir name */
138: char * offset; /* offset into file */
139: BOOL pasv; /* Active or passive */
140: char type; /* 'A', 'I', 'L'(IST), 'N'(LST) */
1.63 frystyk 141: BOOL ready; /* True if either ctrl or data is HT_LOADED */
1.58 frystyk 142: } ftp_data;
1.39 frystyk 143:
1.60 frystyk 144: struct _HTStream {
145: CONST HTStreamClass * isa;
146: HTStream * target;
147: HTRequest * request;
148: ftp_ctrl * ctrl;
149: HTSocketEOL state;
150: HTChunk * welcome;
1.62 frystyk 151: BOOL junk; /* For too long lines */
1.60 frystyk 152: BOOL first_line;
1.63 frystyk 153: char buffer[MAX_FTP_LINE+1];
1.60 frystyk 154: int buflen;
155: };
156:
157: typedef enum _FTPDataCon {
158: FTP_DATA_PASV = 0x1,
159: FTP_DATA_PORT = 0x2
160: } FTPDataCon;
161:
162: PRIVATE FTPDataCon FTPMode = FTP_DATA_PASV;
1.39 frystyk 163:
1.23 frystyk 164: /* ------------------------------------------------------------------------- */
1.60 frystyk 165: /* FTP Status Line Stream */
1.22 frystyk 166: /* ------------------------------------------------------------------------- */
1.60 frystyk 167:
168: /* FTPCleanup
169: ** ----------
170: ** This function closes the connection and frees memory.
171: ** Returns YES on OK, else NO
172: */
1.65 ! frystyk 173: PRIVATE int FTPCleanup (HTRequest * request, int status)
1.60 frystyk 174: {
175: HTNet *cnet = request->net;
176: ftp_ctrl *ctrl = (ftp_ctrl *) cnet->context;
177: HTNet *dnet = ctrl->dnet;
178: ftp_data *data = (ftp_data *) dnet->context;
179:
180: /* Free stream with data TO network */
181: if (!HTRequest_isDestination(request) && request->input_stream) {
182: if (status == HT_INTERRUPTED)
183: (*request->input_stream->isa->abort)(request->input_stream, NULL);
184: else
185: (*request->input_stream->isa->_free)(request->input_stream);
186: }
187:
188: /* Remove the request object and our own context structure for http */
189: HTNet_delete(dnet, HT_IGNORE);
190: HTNet_delete(cnet, status);
191:
192: HTChunkFree(ctrl->cmd);
193: FREE(ctrl->reply);
194: FREE(ctrl->uid);
195: FREE(ctrl->passwd);
196: FREE(ctrl->account);
197: FREE(data->file);
198: FREE(ctrl);
199: FREE(data);
200: return YES;
201: }
202:
203: /* ScanResponse
204: ** ------------
205: ** Analyzes the response from the FTP server.
206: ** Returns HT_LOADED if OK, HT_OK if more, HT_ERROR if error
207: ** the control connection.
1.33 frystyk 208: */
1.65 ! frystyk 209: PRIVATE int ScanResponse (HTStream * me)
1.33 frystyk 210: {
1.60 frystyk 211: int reply = 0;
212: char cont = '\0';
213: char *ptr = me->buffer+4;
214: *(me->buffer+me->buflen) = '\0';
215: if (isdigit(*(me->buffer))) sscanf(me->buffer, "%d%c", &reply, &cont);
216: if (me->first_line) {
217: if (PROT_TRACE) fprintf(TDEST, "FTP Rx...... `%s\'\n", me->buffer);
218: if (!reply) return HT_ERROR;
219: me->first_line = NO;
220: me->ctrl->repcode = reply;
221: StrAllocCopy(me->ctrl->reply, ptr);
222: } else {
223: HTChunkPuts(me->welcome, ptr);
224: HTChunkPutc(me->welcome, '\n');
1.33 frystyk 225: }
1.60 frystyk 226: me->buflen = 0;
227: if (cont != '-') {
228: me->first_line = YES;
229: return HT_LOADED;
1.33 frystyk 230: }
1.60 frystyk 231: return HT_OK;
1.33 frystyk 232: }
233:
1.60 frystyk 234: /*
235: ** Searches for FTP header line until buffer fills up or a CRLF or LF
236: ** is found
1.23 frystyk 237: */
1.60 frystyk 238: PRIVATE int FTPStatus_put_block (HTStream * me, CONST char * b, int l)
239: {
240: int status;
241: while (l-- > 0) {
242: if (me->state == EOL_FCR) {
243: if (*b == LF) {
244: me->state = EOL_BEGIN;
1.62 frystyk 245: if (!me->junk) {
246: if ((status = ScanResponse(me)) != HT_OK)
247: return status;
248: } else {
249: me->buflen = 0;
250: me->junk = NO;
251: }
1.60 frystyk 252: }
253: } else if (*b == CR) {
254: me->state = EOL_FCR;
255: } else if (*b == LF) {
1.62 frystyk 256: me->state = EOL_BEGIN;
257: if (!me->junk) {
258: if ((status = ScanResponse(me)) != HT_OK)
259: return status;
260: } else {
261: me->buflen = 0;
262: me->junk = NO;
263: }
1.60 frystyk 264: } else {
265: *(me->buffer+me->buflen++) = *b;
1.63 frystyk 266: if (me->buflen >= MAX_FTP_LINE) {
1.60 frystyk 267: if (PROT_TRACE)
268: fprintf(TDEST, "FTP Status.. Line too long - chopped\n");
1.62 frystyk 269: me->junk = YES;
270: if ((status = ScanResponse(me)) != HT_OK) {
271: me->junk = NO;
272: return status;
273: }
1.23 frystyk 274: }
275: }
1.60 frystyk 276: b++;
1.25 frystyk 277: }
1.60 frystyk 278: return HT_OK;
1.25 frystyk 279: }
280:
1.65 ! frystyk 281: PRIVATE int FTPStatus_put_string (HTStream * me, CONST char * s)
1.60 frystyk 282: {
283: return FTPStatus_put_block(me, s, (int) strlen(s));
284: }
1.25 frystyk 285:
1.65 ! frystyk 286: PRIVATE int FTPStatus_put_character (HTStream * me, char c)
1.25 frystyk 287: {
1.60 frystyk 288: return FTPStatus_put_block(me, &c, 1);
289: }
1.25 frystyk 290:
1.65 ! frystyk 291: PRIVATE int FTPStatus_flush (HTStream * me)
1.60 frystyk 292: {
293: return (*me->target->isa->flush)(me->target);
1.23 frystyk 294: }
1.22 frystyk 295:
1.65 ! frystyk 296: PRIVATE int FTPStatus_free (HTStream * me)
1.22 frystyk 297: {
1.60 frystyk 298: int status = HT_OK;
299: if (me->target) {
300: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
301: return HT_WOULD_BLOCK;
1.22 frystyk 302: }
1.60 frystyk 303: HTChunkFree(me->welcome);
304: free(me);
305: return HT_OK;
1.22 frystyk 306: }
307:
1.65 ! frystyk 308: PRIVATE int FTPStatus_abort (HTStream * me, HTError e)
1.22 frystyk 309: {
1.60 frystyk 310: if (me->target)
311: (*me->target->isa->abort)(me->target, e);
312: HTChunkFree(me->welcome);
313: free(me);
314: if (PROT_TRACE)
315: fprintf(TDEST, "FTPStatus... ABORTING...\n");
316: return HT_ERROR;
1.22 frystyk 317: }
318:
1.60 frystyk 319: /* FTPStatus Stream
320: ** -----------------
1.22 frystyk 321: */
1.60 frystyk 322: PRIVATE CONST HTStreamClass FTPStatusClass =
323: {
324: "FTPStatus",
325: FTPStatus_flush,
326: FTPStatus_free,
327: FTPStatus_abort,
328: FTPStatus_put_character,
329: FTPStatus_put_string,
330: FTPStatus_put_block
331: };
332:
1.65 ! frystyk 333: PRIVATE HTStream * FTPStatus_new (HTRequest * request, ftp_ctrl * ctrl)
1.60 frystyk 334: {
335: HTStream * me = (HTStream *) calloc(1, sizeof(HTStream));
336: if (!me) outofmem(__FILE__, "FTPStatus_new");
337: me->isa = &FTPStatusClass;
338: me->request = request;
339: me->first_line = YES;
340: me->welcome = HTChunkCreate(256);
341: me->ctrl = ctrl;
342: me->state = EOL_BEGIN;
343: return me;
1.22 frystyk 344: }
345:
1.60 frystyk 346: /* ------------------------------------------------------------------------- */
347: /* FTP Client Functions for managing control and data connections */
348: /* ------------------------------------------------------------------------- */
1.22 frystyk 349:
1.60 frystyk 350: PRIVATE int SendCommand (HTRequest *request, ftp_ctrl *ctrl,
351: char *token, char *pars)
352: {
353: int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2;
354: HTChunkClear(ctrl->cmd);
355: HTChunkEnsure(ctrl->cmd, len);
356: if (pars && *pars)
357: sprintf(HTChunkData(ctrl->cmd), "%s %s%c%c", token, pars, CR, LF);
358: else
359: sprintf(HTChunkData(ctrl->cmd), "%s%c%c", token, CR, LF);
360: if (PROT_TRACE) fprintf(TDEST, "FTP Tx...... %s", HTChunkData(ctrl->cmd));
361: return (*request->input_stream->isa->put_block)
362: (request->input_stream, HTChunkData(ctrl->cmd), len);
363: }
364:
365: /* HTFTPParseURL
366: ** -------------
367: ** Scan URL for uid and passwd, and any data type indication. The
368: ** expected format is [user[:password]@]host[:port].
369: ** If no values are found then use defaults.
370: ** Returns YES if OK, else NO
1.22 frystyk 371: */
1.60 frystyk 372: PRIVATE BOOL HTFTPParseURL (char *url, ftp_ctrl *ctrl, ftp_data *data)
1.22 frystyk 373: {
1.60 frystyk 374: char *login = HTParse(url, "", PARSE_HOST);
375: char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
376: char *ptr = strchr(login, '@');
377: if (ptr) { /* Uid and/or passwd specified */
378: char *passwd;
379: *ptr = '\0';
380: if ((passwd = strchr(login, ':'))) { /* Passwd specified */
381: *passwd++ = '\0';
382: HTUnEscape(passwd);
383: StrAllocCopy(ctrl->passwd, passwd);
1.22 frystyk 384: }
1.60 frystyk 385: HTUnEscape(login);
386: StrAllocCopy(ctrl->uid, login);
387: } else { /* Use anonymous */
388: CONST char *mailaddress = HTGetMailAddress();
389: StrAllocCopy(ctrl->uid, "anonymous");
390: if (mailaddress)
391: StrAllocCopy(ctrl->passwd, mailaddress);
392: else
393: StrAllocCopy(ctrl->passwd, WWW_FTP_CLIENT);
1.22 frystyk 394: }
1.60 frystyk 395: if (PROT_TRACE) fprintf(TDEST, "FTPParse.... uid `%s\' pw `%s\'\n",
396: ctrl->uid, ctrl->passwd);
397:
398: ptr = strchr(path, ';');
399: if (ptr) {
400: *ptr = '\0';
401: if (strncasecomp(ptr, ";type=", 6)) /* Look for type */
402: data->type = TOUPPER(*(ptr+6));
403: else if (*(ptr-1) == '/')
404: data->type = 'N';
405: } else if (*(path+strlen(path)-1) == '/') {
406: *(path+strlen(path)-1) = '\0';
407: data->type = 'N';
408: }
409: if (data->type && PROT_TRACE)
410: fprintf(TDEST, "FTPParse.... Datatype %c\n", data->type);
411: StrAllocCopy(data->file, path);
412: data->offset = data->file;
413: free(login);
414: free(path);
415: return YES;
1.22 frystyk 416: }
417:
1.60 frystyk 418: /* Use LIST or NLST
419: ** ----------------
420: ** This function sets the type field for what type of list we can use
421: ** Returns YES if OK, else NO
422: */
1.63 frystyk 423: PRIVATE BOOL FTPListType (ftp_data * data, FTPServerType type)
1.60 frystyk 424: {
425: if (!data) return NO;
426: switch (type) {
427: case FTP_GENERIC: data->type='N'; break;
428: case FTP_MACHTEN: data->type='L'; break;
429: case FTP_UNIX: data->type='L'; break;
430: case FTP_VMS: data->type='L'; break;
431: case FTP_CMS: data->type='N'; break;
432: case FTP_DCTS: data->type='N'; break;
433: case FTP_TCPC: data->type='N'; break;
434: case FTP_PETER_LEWIS: data->type='L'; break;
435: case FTP_NCSA: data->type='N'; break;
436: case FTP_WINNT: data->type='L'; break;
437: default: data->type='N'; break;
438: }
439: return YES;
440: }
441:
442: /* Open a Data socket for listening on
443: ** -----------------------------------
444: ** Set up a port to listen for data
445: ** Returns YES if OK, else NO
1.22 frystyk 446: */
1.60 frystyk 447: PRIVATE BOOL ListenSocket (HTNet *cnet, HTNet *dnet, ftp_data *data)
1.22 frystyk 448: {
1.60 frystyk 449: #ifdef POLL_PORTS
450: unsigned short old_DataPort = DataPort;
451: for (DataPort=old_DataPort+1;; DataPort++) {
452: if (DataPort > LAST_TCP_PORT)
453: DataPort = FIRST_TCP_PORT;
454: if (DataPort == old_DataPort) {
455: if (PROT_TRACE) fprintf(TDEST,"FTP......... No data port found\n");
456: return NO;
457: }
458: if (HTDoListen(dnet, DataPort) == HT_OK)
459: break;
460: if (dnet->sockfd != INVSOC) {
461: NETCLOSE(dnet->sockfd);
462: dnet->sockfd = INVSOC:
463: }
1.22 frystyk 464: }
1.60 frystyk 465: #else
466: if (HTDoListen(dnet, 0, cnet->sockfd) != HT_OK) return NO;
467: #endif /* POLL_PORTS */
1.22 frystyk 468:
1.60 frystyk 469: /* Now we must find out who we are to tell the other guy */
470: {
471: SockA local_addr;
472: int addr_size = sizeof(local_addr);
473: memset((void *) &local_addr, '\0', sizeof(local_addr));
474: if (getsockname(dnet->sockfd, (struct sockaddr *) &local_addr,
475: &addr_size) < 0) {
476: HTErrorSysAdd(dnet->request, ERR_FATAL, socerrno,NO,"getsockname");
477: return NO;
1.22 frystyk 478: }
1.60 frystyk 479: if (PROT_TRACE) fprintf(TDEST, "FTP......... This host is `%s\'\n",
480: HTInetString(&local_addr));
481: {
482: u_long addr = local_addr.sin_addr.s_addr;
483: u_short port = local_addr.sin_port;
484: sprintf(data->host, "%d,%d,%d,%d,%d,%d",
485: (int)*((unsigned char *)(&addr)+0),
486: (int)*((unsigned char *)(&addr)+1),
487: (int)*((unsigned char *)(&addr)+2),
488: (int)*((unsigned char *)(&addr)+3),
489: (int)*((unsigned char *)(&port)+0),
490: (int)*((unsigned char *)(&port)+1));
1.22 frystyk 491: }
492: }
1.60 frystyk 493: return YES;
1.22 frystyk 494: }
495:
1.60 frystyk 496: /* HTFTPLogin
497: ** -----------
498: ** This function makes a login to a ftp-server. It takes the user name
499: ** and passwd specified in ctrl->user and if that fails or an additional
500: ** account is needed, the user is prompted.
501: ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
1.22 frystyk 502: */
1.60 frystyk 503: PRIVATE int HTFTPLogin (HTRequest *request, HTNet *cnet, ftp_ctrl *ctrl)
1.22 frystyk 504: {
1.60 frystyk 505: int status;
506: typedef enum _state {
507: SUB_ERROR = -2,
508: SUB_SUCCESS = -1,
509: NEED_SELECT = 0,
510: NEED_GREETING,
511: NEED_REIN,
512: NEED_UID,
513: NEED_PASSWD,
514: NEED_ACCOUNT,
515: PROMPT_USER
516: } state;
517:
518: /* Jump into a second level state machine */
519: while (1) {
520: switch ((state) ctrl->substate) {
521: case NEED_SELECT:
522: ctrl->substate = ctrl->reset ? NEED_REIN : NEED_GREETING;
523: break;
524:
525: case NEED_GREETING:
526: status = HTSocketRead(request, cnet);
527: if (status == HT_WOULD_BLOCK)
528: return HT_WOULD_BLOCK;
529: else if (status == HT_LOADED) {
530: if (ctrl->repcode/100 == 2)
531: ctrl->substate = NEED_UID;
532: else
533: ctrl->substate = SUB_ERROR;
534: } else
535: ctrl->substate = SUB_ERROR;
536: break;
537:
538: case NEED_REIN:
539: if (!ctrl->sent) {
540: status = SendCommand(request, ctrl, "REIN", NULL);
541: if (status == HT_WOULD_BLOCK)
542: return HT_WOULD_BLOCK;
543: else if (status == HT_ERROR)
544: ctrl->substate = SUB_ERROR;
545: ctrl->sent = YES;
546: } else {
547: status = HTSocketRead(request, cnet);
548: if (status == HT_WOULD_BLOCK)
549: return HT_WOULD_BLOCK;
550: else if (status == HT_LOADED) {
551: if (ctrl->repcode/100 == 2)
552: ctrl->substate = NEED_UID;
553: else
554: ctrl->substate = SUB_SUCCESS; /* hope the best */
555: } else
556: ctrl->substate = SUB_ERROR;
557: ctrl->sent = NO;
1.22 frystyk 558: }
1.60 frystyk 559: break;
560:
561: case NEED_UID:
562: if (!ctrl->sent) {
563: status = SendCommand(request, ctrl, "USER", ctrl->uid);
564: if (status == HT_WOULD_BLOCK)
565: return HT_WOULD_BLOCK;
566: else if (status == HT_ERROR)
567: ctrl->substate = SUB_ERROR;
568: ctrl->sent = YES;
1.22 frystyk 569: } else {
1.60 frystyk 570: status = HTSocketRead(request, cnet);
571: if (status == HT_WOULD_BLOCK)
572: return HT_WOULD_BLOCK;
573: else if (status == HT_LOADED) {
574: int code = ctrl->repcode/100;
575: if (code == 2) /* Logged in w/o passwd! */
576: ctrl->substate = SUB_SUCCESS;
577: else if (code == 3) /* Password demanded */
578: ctrl->substate = NEED_PASSWD;
579: else if (ctrl->repcode == 530)
580: ctrl->substate = PROMPT_USER; /* User unknown */
581: else
582: ctrl->substate = SUB_ERROR;
583: } else
584: ctrl->substate = SUB_ERROR;
585: ctrl->sent = NO;
1.22 frystyk 586: }
1.60 frystyk 587: break;
1.1 timbl 588:
1.60 frystyk 589: case NEED_PASSWD:
590: if (!ctrl->sent) {
591: status = SendCommand(request, ctrl, "PASS", ctrl->passwd);
592: if (status == HT_WOULD_BLOCK)
593: return HT_WOULD_BLOCK;
594: else if (status == HT_ERROR)
595: ctrl->substate = SUB_ERROR;
596: ctrl->sent = YES;
597: } else {
598: status = HTSocketRead(request, cnet);
599: if (status == HT_WOULD_BLOCK)
600: return HT_WOULD_BLOCK;
601: else if (status == HT_LOADED) {
602: int code = ctrl->repcode/100;
603: if (code == 2) /* Logged in with passwd */
604: ctrl->substate = SUB_SUCCESS;
605: else if (code == 3) { /* Account demanded */
606: char *prompt = NULL;
607: StrAllocCopy(prompt, "Account required for ");
608: StrAllocCat(prompt, ctrl->uid);
609: if ((ctrl->account = HTPrompt(request, prompt, NULL)))
610: ctrl->substate = NEED_ACCOUNT;
611: else
612: ctrl->substate = SUB_ERROR;
613: } else if (ctrl->repcode == 530)
614: ctrl->substate = PROMPT_USER;
615: else
616: ctrl->substate = SUB_ERROR;
617: } else
618: ctrl->substate = SUB_ERROR;
619: ctrl->sent = NO;
620: }
621: break;
1.1 timbl 622:
1.60 frystyk 623: case NEED_ACCOUNT:
624: if (!ctrl->sent) {
625: status = SendCommand(request, ctrl, "ACCT", ctrl->account);
626: if (status == HT_WOULD_BLOCK)
627: return HT_WOULD_BLOCK;
628: else if (status == HT_ERROR)
629: ctrl->substate = SUB_ERROR;
630: ctrl->sent = YES;
1.23 frystyk 631: } else {
1.60 frystyk 632: status = HTSocketRead(request, cnet);
633: if (status == HT_WOULD_BLOCK)
634: return HT_WOULD_BLOCK;
635: else if (status == HT_LOADED) {
636: int code = ctrl->repcode/100;
637: if (code == 2) /* Logged in with account */
638: ctrl->substate = SUB_SUCCESS;
639: else
640: ctrl->substate = SUB_ERROR; /* hopeless */
641: } else
642: ctrl->substate = SUB_ERROR;
643: ctrl->sent = NO;
1.1 timbl 644: }
1.60 frystyk 645: break;
1.1 timbl 646:
1.60 frystyk 647: case PROMPT_USER:
648: {
649: char *prompt = NULL;
650: StrAllocCopy(prompt, "Enter username and password");
651: FREE(ctrl->uid);
652: FREE(ctrl->passwd);
653: HTPromptUsernameAndPassword(request, prompt,
654: &ctrl->uid, &ctrl->passwd);
655: if (ctrl->uid && ctrl->passwd)
656: ctrl->substate = NEED_UID;
657: else
658: ctrl->substate = SUB_ERROR;
659: free(prompt);
660: }
661: break;
1.1 timbl 662:
1.60 frystyk 663: case SUB_ERROR:
664: if (PROT_TRACE)
665: fprintf(TDEST, "FTP......... Login failed\n");
666: ctrl->substate = 0;
667: return HT_ERROR;
668: break;
1.23 frystyk 669:
1.60 frystyk 670: case SUB_SUCCESS:
1.43 frystyk 671: if (PROT_TRACE)
1.60 frystyk 672: fprintf(TDEST, "FTP......... Logged in as `%s\'\n", ctrl->uid);
673: ctrl->substate = 0;
674: return HT_OK;
675: break;
1.23 frystyk 676: }
1.22 frystyk 677: }
678: }
679:
1.60 frystyk 680: /* HTFTPDataConnection
681: ** -------------------
682: ** Prepares a data connection to the server and initializes the
683: ** transfer mode.
684: ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
1.1 timbl 685: */
1.60 frystyk 686: PRIVATE int HTFTPDataConnection (HTRequest * request, HTNet *cnet,
687: ftp_ctrl *ctrl, ftp_data *data)
1.1 timbl 688: {
1.60 frystyk 689: int status;
690: HTNet *dnet = ctrl->dnet;
691: typedef enum _state {
692: SUB_ERROR = -2,
693: SUB_SUCCESS = -1,
694: NEED_TYPE = 0,
695: NEED_SELECT,
696: NEED_PASV,
697: NEED_PORT
698: } state;
699:
700: /* Jump into a second level state machine */
701: while (1) {
702: switch ((state) ctrl->substate) {
703: case NEED_TYPE:
704: if(!data->type|| data->pasv || data->type=='N' || data->type=='L'){
705: ctrl->substate = NEED_SELECT;
706: break;
707: }
708: if (!ctrl->sent) {
709: char type[2];
710: *type = data->type;
711: *(type+1) = '\0';
712: status = SendCommand(request, ctrl, "TYPE", type);
713: if (status == HT_WOULD_BLOCK)
714: return HT_WOULD_BLOCK;
715: else if (status == HT_ERROR)
716: ctrl->substate = SUB_ERROR;
717: ctrl->sent = YES;
718: } else {
719: status = HTSocketRead(request, cnet);
720: if (status == HT_WOULD_BLOCK)
721: return HT_WOULD_BLOCK;
722: else if (status == HT_LOADED) {
723: if (ctrl->repcode/100 == 2)
724: ctrl->substate = NEED_SELECT;
725: else
726: ctrl->substate = SUB_ERROR;
727: } else
728: ctrl->substate = SUB_ERROR;
729: ctrl->sent = NO;
730: }
731: break;
732:
733: case NEED_SELECT:
734: if (FTPMode & FTP_DATA_PASV && !data->pasv)
735: ctrl->substate = NEED_PASV;
736: else if (ListenSocket(cnet, dnet, data))
737: ctrl->substate = NEED_PORT;
738: else
739: ctrl->substate = SUB_ERROR;
740: break;
741:
742: case NEED_PASV:
743: if (!ctrl->sent) {
744: status = SendCommand(request, ctrl, "PASV", NULL);
745: if (status == HT_WOULD_BLOCK)
746: return HT_WOULD_BLOCK;
747: else if (status == HT_ERROR)
748: ctrl->substate = SUB_ERROR;
749: ctrl->sent = YES;
750: } else {
751: status = HTSocketRead(request, cnet);
752: if (status == HT_WOULD_BLOCK)
753: return HT_WOULD_BLOCK;
754: else if (status == HT_LOADED) {
755: if (ctrl->repcode == 227) {
756: /*
757: ** If succes, we have to scan for the returned number.
758: ** As the format for the response isn't standard,
759: ** the best thing to do is to scan for the first digit
760: ** after the status code, see RFC1123
761: */
762: char *host = ctrl->reply;
763: int h0, h1, h2, h3, p0=0, p1=0;
764: while (*host && !isdigit(*host++));
765: if (!*host || sscanf(--host, "%d,%d,%d,%d,%d,%d",
766: &h0,&h1,&h2,&h3,&p0,&p1) < 6) {
767: if (PROT_TRACE)
768: fprintf(TDEST, "FTP......... PASV No addr\n");
769: ctrl->substate = SUB_ERROR;
770: break;
771: } else {
772: int port = (p0<<8)+p1;
773: sprintf(data->host, "ftp://%d.%d.%d.%d:%d/",
774: h0, h1, h2, h3, port);
775: data->pasv = YES;
776: ctrl->substate = SUB_SUCCESS;
777: }
778: } else {
779: ctrl->substate = ListenSocket(cnet, dnet, data) ?
780: NEED_PORT : SUB_ERROR;
781: }
782: } else
783: ctrl->substate = SUB_ERROR;
784: ctrl->sent = NO;
785: }
786: break;
1.22 frystyk 787:
1.60 frystyk 788: case NEED_PORT:
789: if (!ctrl->sent) {
790: status = SendCommand(request, ctrl, "PORT", data->host);
791: if (status == HT_WOULD_BLOCK)
792: return HT_WOULD_BLOCK;
793: else if (status == HT_ERROR)
794: ctrl->substate = SUB_ERROR;
795: ctrl->sent = YES;
796: } else {
797: status = HTSocketRead(request, cnet);
798: if (status == HT_WOULD_BLOCK)
799: return HT_WOULD_BLOCK;
800: else if (status == HT_LOADED) {
801: data->pasv = NO;
802: ctrl->substate = (ctrl->repcode/100 == 2) ?
803: SUB_SUCCESS : SUB_ERROR;
804: } else
805: ctrl->substate = SUB_ERROR;
806: ctrl->sent = NO;
807: }
808: break;
1.1 timbl 809:
1.60 frystyk 810: case SUB_ERROR:
811: if (PROT_TRACE)
812: fprintf(TDEST, "FTP......... Can't setup data connection\n");
813: ctrl->substate = 0;
814: return HT_ERROR;
815: break;
1.1 timbl 816:
1.60 frystyk 817: case SUB_SUCCESS:
818: if (PROT_TRACE)
819: fprintf(TDEST, "FTP......... Data connection negotiated\n");
820: ctrl->substate = 0;
821: return HT_OK;
822: break;
1.23 frystyk 823: }
1.58 frystyk 824: }
1.23 frystyk 825: }
1.1 timbl 826:
827:
1.60 frystyk 828: /* HTFTPServerInfo
829: ** ---------------
830: ** This function finds out what server we are talking to.
831: ** Maybe we can upgrade from NLST to LIST.
832: ** Returns HT_OK, HT_ERROR, or HT_WOULD_BLOCK
833: ** Thanks to James.W.Matthews@Dartmouth.EDU (James W. Matthews) for making
834: ** his code available.
1.1 timbl 835: */
1.60 frystyk 836: PRIVATE int HTFTPServerInfo (HTRequest *request, HTNet *cnet,
837: ftp_ctrl *ctrl, ftp_data *data)
1.23 frystyk 838: {
1.60 frystyk 839: int status;
840: typedef enum _state {
841: SUB_ERROR = -2,
842: SUB_SUCCESS = -1,
843: NEED_SYST = 0,
844: CHECK_SYST,
845: NEED_PWD,
846: CHECK_PWD
847: } state;
848:
849: /* Jump into a second level state machine */
850: while (1) {
851: switch ((state) ctrl->substate) {
852: case NEED_SYST:
853: if (!ctrl->sent) {
1.63 frystyk 854: if (ctrl->server != FTP_UNSURE) {
1.60 frystyk 855: FTPListType(data, ctrl->server);
856: return HT_OK;
857: }
858: status = SendCommand(request, ctrl, "SYST", NULL);
859: if (status == HT_WOULD_BLOCK)
860: return HT_WOULD_BLOCK;
861: else if (status == HT_ERROR)
862: ctrl->substate = SUB_ERROR;
863: ctrl->sent = YES;
864: } else {
865: status = HTSocketRead(request, cnet);
866: if (status == HT_WOULD_BLOCK)
867: return HT_WOULD_BLOCK;
868: else if (status == HT_LOADED) {
869: ctrl->substate=ctrl->repcode==215 ? CHECK_SYST : NEED_PWD;
870: } else
871: ctrl->substate = SUB_ERROR;
872: ctrl->sent = NO;
1.23 frystyk 873: }
874: break;
875:
1.60 frystyk 876: case CHECK_SYST:
1.23 frystyk 877: {
1.60 frystyk 878: char *reply = ctrl->reply;
879: if (!*reply) {
1.43 frystyk 880: if (PROT_TRACE)
1.48 frystyk 881: fprintf(TDEST, "FTP......... No server info?\n");
1.60 frystyk 882: ctrl->substate = NEED_PWD;
1.23 frystyk 883: break;
884: }
1.60 frystyk 885: if (strncmp(reply, "UNIX Type: L8MAC-OSMachTen", 28) == 0) {
886: ctrl->server = FTP_MACHTEN;
887: } else if (strstr(reply, "UNIX") != NULL) {
888: ctrl->server = FTP_UNIX;
889: } else if (strncmp(reply, "VMS", 3) == 0) {
890: ctrl->server = FTP_VMS;
891: } else if ((strncmp(reply, "VM/CMS", 6) == 0) ||
892: (strncmp(reply, "VM", 2) == 0)) {
893: ctrl->server = FTP_CMS;
894: } else if (strncmp(reply, "DCTS", 4) == 0) {
895: ctrl->server = FTP_DCTS;
896: } else if (strstr(reply, "MAC-OS TCP/ConnectII") != NULL) {
1.23 frystyk 897: /* Check old versions of TCP/C using / in pathnames */
1.63 frystyk 898: ctrl->server = FTP_TCPC + FTP_UNSURE;
1.60 frystyk 899: } else if (strncmp(reply, "MACOS Peter's Server", 20) == 0) {
900: ctrl->server = FTP_PETER_LEWIS;
901: } else if (strncmp(reply, "Windows_NT", 10) == 0) {
902: ctrl->server = FTP_WINNT;
1.23 frystyk 903: }
904:
905: /* If we are unsure, try PWD to get more information */
1.60 frystyk 906: if (ctrl->server & FTP_UNSURE)
907: ctrl->substate = NEED_PWD;
1.23 frystyk 908: else
1.60 frystyk 909: ctrl->substate = SUB_SUCCESS;
1.1 timbl 910: }
1.23 frystyk 911: break;
912:
913: case NEED_PWD:
1.60 frystyk 914: if (!ctrl->sent) {
915: status = SendCommand(request, ctrl, "PWD", NULL);
916: if (status == HT_WOULD_BLOCK)
917: return HT_WOULD_BLOCK;
918: else if (status == HT_ERROR)
919: ctrl->substate = SUB_ERROR;
920: ctrl->sent = YES;
1.23 frystyk 921: } else {
1.60 frystyk 922: status = HTSocketRead(request, cnet);
923: if (status == HT_WOULD_BLOCK)
924: return HT_WOULD_BLOCK;
925: else if (status == HT_LOADED) {
926: ctrl->substate = (ctrl->repcode/100 == 2) ?
927: CHECK_PWD : SUB_ERROR;
928: } else
929: ctrl->substate = SUB_ERROR;
930: ctrl->sent = NO;
1.23 frystyk 931: }
932: break;
933:
1.60 frystyk 934: case CHECK_PWD:
935: {
936: char *start = strchr(ctrl->reply, '"');
937: char *end;
938: if (!start || (end = strchr(++start, '"')) == NULL) {
1.43 frystyk 939: if (PROT_TRACE)
1.60 frystyk 940: fprintf(TDEST, "FTP......... No current directory?\n");
941: ctrl->server = FTP_GENERIC;
1.23 frystyk 942: } else {
1.60 frystyk 943: *end = '\0';
1.63 frystyk 944: if (ctrl->server & FTP_TCPC) {
1.60 frystyk 945: ctrl->server = *start == '/' ? FTP_NCSA : FTP_TCPC;
946: } else if (*start == '/') {
947: /* path names starting with / imply Unix, right? */
948: ctrl->server = FTP_UNIX;
949: } else if (*(end-1) == ']') {
950: /* path names ending with ] imply VMS, right? */
951: ctrl->server = FTP_VMS;
952: } else
953: ctrl->server = FTP_GENERIC;
1.23 frystyk 954: }
1.60 frystyk 955: ctrl->substate = SUB_SUCCESS;
1.1 timbl 956: }
1.23 frystyk 957: break;
1.1 timbl 958:
1.60 frystyk 959: case SUB_ERROR:
960: if (PROT_TRACE)
961: fprintf(TDEST, "FTP......... Can't get server information\n");
962: ctrl->substate = 0;
1.63 frystyk 963: ctrl->server = FTP_GENERIC;
1.60 frystyk 964: return HT_ERROR;
1.23 frystyk 965: break;
1.22 frystyk 966:
1.60 frystyk 967: case SUB_SUCCESS:
1.63 frystyk 968: if (PROT_TRACE)
969: fprintf(TDEST, "FTP Server.. Guessed type %d\n", ctrl->server);
1.60 frystyk 970: HTDNS_setServerVersion(cnet->dns, ctrl->server);
971: FTPListType(data, ctrl->server);
972: ctrl->substate = 0;
973: return HT_OK;
1.23 frystyk 974: break;
1.22 frystyk 975: }
1.23 frystyk 976: }
977: }
978:
1.60 frystyk 979: /* HTFTPGetData
980: ** ------------
981: ** This function asks for the file or a directory. First we try in one go,
1.23 frystyk 982: ** but if that doesn't work, then we use CWD for each segment and then
983: ** try to retrieve it. If that also fails, then we try if it is a
1.60 frystyk 984: ** directory.
985: ** Returns HT_OK, HT_LOADED, HT_ERROR, or HT_WOULD_BLOCK
1.23 frystyk 986: */
1.63 frystyk 987: PRIVATE int HTFTPGetData (HTRequest *request, HTNet *cnet, SOCKFD sockfd,
1.60 frystyk 988: ftp_ctrl *ctrl, ftp_data *data)
1.23 frystyk 989: {
990: int status;
1.60 frystyk 991: char *segment = NULL;
992: HTNet *dnet = ctrl->dnet;
993: typedef enum _state {
994: SUB_ERROR = -2,
995: SUB_SUCCESS = -1,
996: NEED_SELECT = 0,
997: NEED_CONNECT,
998: NEED_ACCEPT,
999: NEED_ACTION,
1000: NEED_CWD,
1001: NEED_SEGMENT,
1002: NEED_STREAM,
1.63 frystyk 1003: NEED_BODY
1.60 frystyk 1004: } state;
1005:
1006: /* Jump into a second level state machine */
1007: while (1) {
1008: switch ((state) ctrl->substate) {
1009: case NEED_SELECT:
1010: ctrl->substate = data->pasv ? NEED_CONNECT : NEED_ACTION;
1011: break;
1012:
1013: case NEED_CONNECT:
1014: status = HTDoConnect(dnet, data->host, FTP_DATA);
1015: if (status == HT_WOULD_BLOCK)
1016: return HT_WOULD_BLOCK;
1017: else if (status == HT_OK) {
1018: if (PROT_TRACE)
1019: fprintf(TDEST, "FTP Data.... Active data socket %d\n",
1020: dnet->sockfd);
1021: ctrl->substate = NEED_ACTION;
1022: } else { /* Swap to PORT on the fly */
1023: NETCLOSE(dnet->sockfd);
1024: dnet->sockfd = INVSOC;
1025: if (PROT_TRACE)
1026: fprintf(TDEST, "FTP......... Swap to PORT on the fly\n");
1027: ctrl->substate = 0;
1028: FREE(segment);
1029: return HT_OK;
1030: }
1.23 frystyk 1031: break;
1032:
1.60 frystyk 1033: case NEED_ACCEPT:
1034: {
1035: SOCKFD newfd = INVSOC;
1036: status = HTDoAccept(dnet, &newfd);
1037: if (status == HT_WOULD_BLOCK)
1038: return HT_WOULD_BLOCK;
1039: else if (status == HT_OK) {
1040: if (PROT_TRACE)
1041: fprintf(TDEST, "FTP Data.... Passive data socket %d\n",
1042: dnet->sockfd);
1043: NETCLOSE(dnet->sockfd);
1044: dnet->sockfd = newfd;
1.43 frystyk 1045: if (PROT_TRACE)
1.60 frystyk 1046: fprintf(TDEST, "FTP Data.... New data socket %d\n",
1047: dnet->sockfd);
1048: ctrl->substate = NEED_STREAM;
1049: } else
1050: ctrl->substate = SUB_ERROR;
1.23 frystyk 1051: }
1.60 frystyk 1052: break;
1.33 frystyk 1053:
1.60 frystyk 1054: case NEED_ACTION:
1055: if (!ctrl->sent) {
1056: char *cmd = (data->type=='L') ? "LIST" :
1057: (data->type=='N') ? "NLST" : "RETR";
1058: StrAllocCopy(segment, data->offset);
1059: HTUnEscape(segment);
1060: HTCleanTelnetString(segment);
1061: status = SendCommand(request, ctrl, cmd, segment);
1.63 frystyk 1062: FREE(segment);
1.60 frystyk 1063: if (status == HT_WOULD_BLOCK)
1064: return HT_WOULD_BLOCK;
1065: else if (status == HT_ERROR)
1066: ctrl->substate = SUB_ERROR;
1067: ctrl->sent = YES;
1068: } else {
1069: status = HTSocketRead(request, cnet);
1070: if (status == HT_WOULD_BLOCK)
1071: return HT_WOULD_BLOCK;
1072: else if (status == HT_LOADED) {
1073: int code = ctrl->repcode;
1074: if (code==125 || code==150 || code==225)
1075: ctrl->substate=data->pasv ? NEED_STREAM : NEED_ACCEPT;
1076: else if (code/100==5 && !ctrl->cwd)
1077: ctrl->substate = NEED_SEGMENT;
1078: else
1079: ctrl->substate = SUB_ERROR;
1080: } else
1081: ctrl->substate = SUB_ERROR;
1082: ctrl->sent = NO;
1.23 frystyk 1083: }
1084: break;
1.60 frystyk 1085:
1086: case NEED_SEGMENT:
1.23 frystyk 1087: {
1.60 frystyk 1088: char *ptr;
1089: if (data->offset == data->file) {
1.63 frystyk 1090: if (ctrl->server == FTP_VMS) { /* Change to root */
1091: segment = (char *) malloc(strlen(ctrl->uid)+3);
1092: sprintf(segment, "[%s]", ctrl->uid);
1093: } else
1094: StrAllocCopy(segment, "/");
1.60 frystyk 1095: data->offset++;
1096: ctrl->substate = NEED_CWD;
1097: } else {
1098: if ((ptr = strchr(data->offset, '/'))) {
1099: *ptr='\0';
1100: StrAllocCopy(segment, data->offset);
1101: *ptr='/';
1102: data->offset = ++ptr;
1103: HTUnEscape(segment);
1104: HTCleanTelnetString(segment);
1105: ctrl->substate = NEED_CWD;
1.33 frystyk 1106: } else
1.60 frystyk 1107: ctrl->substate = NEED_ACTION;
1.23 frystyk 1108: }
1109: }
1110: break;
1111:
1.60 frystyk 1112: case NEED_CWD:
1113: if (!ctrl->sent) {
1114: status = SendCommand(request, ctrl, "CWD", segment);
1.63 frystyk 1115: FREE(segment);
1.60 frystyk 1116: if (status == HT_WOULD_BLOCK)
1117: return HT_WOULD_BLOCK;
1118: else if (status == HT_ERROR)
1119: ctrl->substate = SUB_ERROR;
1120: ctrl->cwd = YES;
1121: ctrl->sent = YES;
1122: } else {
1123: status = HTSocketRead(request, cnet);
1124: if (status == HT_WOULD_BLOCK)
1125: return HT_WOULD_BLOCK;
1126: else if (status == HT_LOADED) {
1127: if (ctrl->repcode/100 == 2)
1128: ctrl->substate = NEED_SEGMENT;
1129: else
1130: ctrl->substate = SUB_ERROR;
1131: } else
1132: ctrl->substate = SUB_ERROR;
1133: ctrl->sent = NO;
1.33 frystyk 1134: }
1.60 frystyk 1135: break;
1.33 frystyk 1136:
1.60 frystyk 1137: case NEED_STREAM:
1138: {
1139: dnet->isoc = HTInputSocket_new(dnet->sockfd);
1.63 frystyk 1140: if (FTP_DIR(data)) {
1141: dnet->target =
1142: (!HTImProxy && request->output_format==WWW_SOURCE) ?
1143: request->output_stream :
1144: HTFTPDir_new(request, ctrl->server,data->type);
1145: } else {
1146: dnet->target =
1147: HTStreamStack(HTAnchor_format(request->anchor),
1148: request->output_format,
1149: request->output_stream,
1150: request,YES);
1151: }
1152: sockfd = dnet->sockfd; /* Ensure that we try data first */
1.60 frystyk 1153: ctrl->substate = NEED_BODY;
1154: }
1.23 frystyk 1155: break;
1156:
1.60 frystyk 1157: case NEED_BODY:
1.63 frystyk 1158: if (sockfd == dnet->sockfd) {
1159: status = HTSocketRead(request, dnet);
1160: if (status == HT_WOULD_BLOCK)
1161: return HT_WOULD_BLOCK;
1162: else if (status == HT_LOADED) {
1163: if (data->ready)
1164: ctrl->substate = SUB_SUCCESS;
1165: else
1166: sockfd = cnet->sockfd;
1167: data->ready = YES;
1168: } else
1169: ctrl->substate = SUB_ERROR;
1170: } else {
1171: status = HTSocketRead(request, cnet);
1172: if (status == HT_WOULD_BLOCK)
1173: return HT_WOULD_BLOCK;
1174: else if (status == HT_LOADED) {
1175: if (ctrl->repcode/100 == 2) {
1176: if (data->ready)
1177: ctrl->substate = SUB_SUCCESS;
1178: else
1179: sockfd = dnet->sockfd;
1180: data->ready = YES;
1181: } else
1182: ctrl->substate = SUB_ERROR;
1183: } else
1.60 frystyk 1184: ctrl->substate = SUB_ERROR;
1.63 frystyk 1185: }
1.23 frystyk 1186: break;
1.22 frystyk 1187:
1.60 frystyk 1188: case SUB_ERROR:
1189: if (PROT_TRACE)
1.63 frystyk 1190: fprintf(TDEST, "FTP......... Can't retrieve object\n");
1.60 frystyk 1191: ctrl->substate = 0;
1192: FREE(segment);
1193: return HT_ERROR;
1.23 frystyk 1194: break;
1195:
1.60 frystyk 1196: case SUB_SUCCESS:
1197: if (PROT_TRACE)
1.63 frystyk 1198: fprintf(TDEST, "FTP......... Object is loaded\n");
1.60 frystyk 1199: ctrl->substate = 0;
1200: FREE(segment);
1201: return HT_LOADED;
1.23 frystyk 1202: break;
1.1 timbl 1203: }
1204: }
1.23 frystyk 1205: }
1.1 timbl 1206:
1.23 frystyk 1207: /* ------------------------------------------------------------------------- */
1208:
1209: /* Retrieve File from Server as an atomic action.
1210: ** -----------------------------------------------
1.58 frystyk 1211: ** Given a hypertext address, this routine loads a document.
1.23 frystyk 1212: **
1213: ** On entry,
1.58 frystyk 1214: ** request This is the request structure
1215: ** returns HT_ERROR Error has occured in call back
1216: ** HT_OK Call back was OK
1217: */
1.65 ! frystyk 1218: PUBLIC int HTLoadFTP (SOCKET soc, HTRequest * request, SockOps ops)
1.58 frystyk 1219: {
1220: int status = HT_ERROR;
1.60 frystyk 1221: HTNet *cnet = request->net;
1.58 frystyk 1222: ftp_ctrl *ctrl;
1.60 frystyk 1223: ftp_data *data;
1.58 frystyk 1224: char *url = HTAnchor_physical(request->anchor);
1.23 frystyk 1225:
1.58 frystyk 1226: /*
1.60 frystyk 1227: ** Initiate a new FTP ctrl and data structure and bind to request structure
1.58 frystyk 1228: ** This is actually state FTP_BEGIN, but it can't be in the state
1229: ** machine as we need the structure first.
1230: */
1231: if (ops == FD_NONE) {
1232: if (PROT_TRACE) fprintf(TDEST, "FTP......... Looking for `%s\'\n",url);
1.60 frystyk 1233: if ((ctrl = (ftp_ctrl *) calloc(1, sizeof(ftp_ctrl))) == NULL ||
1234: (data = (ftp_data *) calloc(1, sizeof(ftp_data))) == NULL)
1.58 frystyk 1235: outofmem(__FILE__, "HTLoadFTP");
1.60 frystyk 1236: ctrl->cmd = HTChunkCreate(128);
1.58 frystyk 1237: ctrl->state = FTP_BEGIN;
1.60 frystyk 1238: ctrl->server = FTP_UNSURE;
1239: HTNet_dup(cnet, &ctrl->dnet);
1240: cnet->context = ctrl; /* Context for control connection */
1241: ctrl->dnet->context = data; /* Context for data connection */
1242: } else if (ops == FD_CLOSE) { /* Interrupted */
1243: if(HTRequest_isPostWeb(request)&&!HTRequest_isMainDestination(request))
1244: FTPCleanup(request, HT_IGNORE);
1245: else
1246: FTPCleanup(request, HT_INTERRUPTED);
1247: return HT_OK;
1248: } else {
1249: ctrl = (ftp_ctrl *) cnet->context; /* Get existing copy */
1250: data = (ftp_data *) ctrl->dnet->context;
1251: }
1.58 frystyk 1252:
1253: /* Now jump into the machine. We know the state from the previous run */
1254: while (1) {
1255: switch (ctrl->state) {
1.60 frystyk 1256: case FTP_BEGIN:
1257: HTFTPParseURL(url, ctrl, data);
1258: if (data->type != 'N') {
1259: HTBind_getBindings(request->anchor);
1260: if (HTAnchor_encoding(request->anchor) != HTAtom_for("7bit"))
1261: data->type = 'I';
1262: }
1263: ctrl->state = FTP_NEED_CCON;
1264: break;
1265:
1266: case FTP_NEED_CCON:
1267: status = HTDoConnect(cnet, url, FTP_PORT);
1268: if (status == HT_OK) {
1269: char *s_class = HTDNS_serverClass(cnet->dns);
1270: if (s_class && strcasecomp(s_class, "ftp")) {
1271: HTErrorAdd(request, ERR_FATAL, NO, HTERR_CLASS, NULL, 0,
1272: "HTLoadFTP");
1273: ctrl->state = FTP_ERROR;
1274: break;
1275: }
1276: HTDNS_setServerClass(cnet->dns, "ftp");
1.63 frystyk 1277: if (HTDNS_socket(cnet->dns) != INVSOC) {
1278: ctrl->server = HTDNS_serverVersion(cnet->dns);
1279: if (PROT_TRACE)
1280: fprintf(TDEST, "FTP Server.. We know from cache that this is a type %d server\n",
1281: ctrl->server);
1.60 frystyk 1282: ctrl->reset = 1;
1.63 frystyk 1283: } else
1.60 frystyk 1284: HTDNS_setSocket(cnet->dns, cnet->sockfd);
1285: if (PROT_TRACE)
1286: fprintf(TDEST, "FTP Ctrl.... Connected, socket %d\n",
1287: cnet->sockfd);
1.23 frystyk 1288:
1.60 frystyk 1289: /* Set up stream TO network */
1290: request->input_stream = HTWriter_new(cnet, YES);
1.23 frystyk 1291:
1.60 frystyk 1292: /*
1293: ** Set up concurrent read/write if this request isn't the
1294: ** source for a PUT or POST. As source we don't start reading
1295: ** before all destinations are ready. If destination then
1296: ** register the input stream and get ready for read
1297: */
1298: if (HTRequest_isPostWeb(request)) {
1299: HTEvent_Register(cnet->sockfd, request, (SockOps) FD_READ,
1300: HTLoadFTP, cnet->priority);
1301: HTRequest_linkDestination(request);
1302: }
1303:
1304: /* Set up stream FROM network and corresponding read buffer */
1305: cnet->isoc = HTInputSocket_new(cnet->sockfd);
1306: cnet->target = FTPStatus_new(request, ctrl);
1307: ctrl->state = FTP_NEED_LOGIN;
1308: } else if (status == HT_WOULD_BLOCK || status == HT_PERSISTENT)
1309: return HT_OK;
1310: else
1311: ctrl->state = FTP_ERROR; /* Error or interrupt */
1312: break;
1313:
1314: case FTP_NEED_LOGIN:
1315: status = HTFTPLogin(request, cnet, ctrl);
1316: if (status == HT_WOULD_BLOCK) return HT_OK;
1317: ctrl->state = (status == HT_OK) ? FTP_NEED_DCON : FTP_ERROR;
1318: break;
1319:
1320: case FTP_NEED_DCON:
1321: status = HTFTPDataConnection(request, cnet, ctrl, data);
1322: if (status == HT_WOULD_BLOCK) return HT_OK;
1323: if (status == HT_OK)
1324: ctrl->state = (data->type=='N') ?
1325: FTP_NEED_SERVER : FTP_NEED_DATA;
1326: else
1327: ctrl->state = FTP_ERROR;
1328: break;
1329:
1330: case FTP_NEED_DATA:
1.63 frystyk 1331: status = HTFTPGetData(request, cnet, soc, ctrl, data);
1.60 frystyk 1332: if (status == HT_WOULD_BLOCK) return HT_OK;
1333: if (status == HT_LOADED)
1334: ctrl->state = FTP_SUCCESS;
1335: else if (status == HT_OK)
1336: ctrl->state = FTP_NEED_DCON;
1337: else if (!FTP_DIR(data)) {
1338: FTPListType(data, ctrl->server);
1339: ctrl->state = FTP_NEED_SERVER; /* Try a dir instead? */
1340: } else
1341: ctrl->state = FTP_ERROR;
1342: break;
1.23 frystyk 1343:
1.60 frystyk 1344: case FTP_NEED_SERVER:
1345: status = HTFTPServerInfo(request, cnet, ctrl, data);
1346: if (status == HT_WOULD_BLOCK) return HT_OK;
1347: ctrl->state = FTP_NEED_DATA;
1348: break;
1349:
1350: case FTP_SUCCESS:
1351: if (HTRequest_isPostWeb(request)) {
1352: BOOL main = HTRequest_isMainDestination(request);
1353: if (HTRequest_isDestination(request)) {
1354: HTLink *link =
1355: HTAnchor_findLink((HTAnchor *) request->source->anchor,
1356: (HTAnchor *) request->anchor);
1357: HTAnchor_setLinkResult(link, HT_LINK_OK);
1.23 frystyk 1358: }
1.60 frystyk 1359: HTRequest_removeDestination(request);
1360: FTPCleanup(request, main ? HT_LOADED : HT_IGNORE);
1361: } else
1362: FTPCleanup(request, HT_LOADED);
1363: return HT_OK;
1364: break;
1365:
1366: case FTP_ERROR:
1367: /* Clean up the other connections or just this one */
1368: if (HTRequest_isPostWeb(request)) {
1369: BOOL main = HTRequest_isMainDestination(request);
1370: HTRequest_killPostWeb(request);
1371: if (HTRequest_isDestination(request)) {
1372: HTLink *link =
1373: HTAnchor_findLink((HTAnchor *) request->source->anchor,
1374: (HTAnchor *) request->anchor);
1375: HTAnchor_setLinkResult(link, HT_LINK_ERROR);
1.30 luotonen 1376: }
1.60 frystyk 1377: HTRequest_removeDestination(request);
1378: FTPCleanup(request, main ? HT_ERROR : HT_IGNORE);
1379: } else
1380: FTPCleanup(request, HT_ERROR);
1381: return HT_OK;
1382: break;
1.23 frystyk 1383: }
1.60 frystyk 1384: } /* End of while(1) */
1.23 frystyk 1385: }
1.22 frystyk 1386:
Webmaster