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