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