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