📄 htftp.c
字号:
/* HTFTP.c** FILE TRANSFER PROTOCOL (FTP) CLIENT**** (c) COPYRIGHT MIT 1995.** Please first read the full copyright statement in the file COPYRIGH.** @(#) $Id: HTFTP.c,v 1.102 1999/02/22 22:10:11 frystyk Exp $**** A cache of control connections is kept.**** Note: Port allocation**** It is essential that the port is allocated by the system, rather** than chosen in rotation by us (FTP_POLL_PORTS), or the following** problem occurs.**** It seems that an attempt by the server to connect to a port which has** been used recently by a listen on the same socket, or by another** socket this or another process causes a hangup of (almost exactly)** one minute. Therefore, we have to use a rotating port number.** The problem remains that if the application is run twice in quick** succession, it will hang for what remains of a minute.**** Authors** TBL Tim Berners-lee <timbl@w3.org>** DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>** LM Lou Montulli <montulli@ukanaix.cc.ukans.edu>** FM Foteos Macrides <macrides@sci.wfeb.edu>** HF Henrik Frystyk <frystyk@w3.org>** AL Ari Luotonen <luotonen@www.cern.ch>**** History:** 2 May 91 Written TBL, as a part of the WorldWideWeb project.** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc** 10 Feb 92 Retry if cached connection times out or breaks** 8 Dec 92 Bug fix 921208 TBL after DD** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD** fails on princeton.edu!** 27 Dec 93 (FM) Fixed up so FTP now works with VMS hosts. Path** must be Unix-style and cannot include the device** or top directory.** ?? ??? ?? (LM) Added code to prompt and send passwords for non** anonymous FTP** 25 Mar 94 (LM) Added code to recognize different ftp server types** and code to parse dates and sizes on most hosts.** 27 Mar 93 (FM) Added code for getting dates and sizes on VMS hosts.** 27 Apr 94 (HF) The module is basically rewritten to conform with** rfc 959, 1123 and 1579 and turned into a state ** machine. New semantics of ftp URLs are supported.** 2 May 94 (AL) Fixed possible security hole when the URL contains** a newline, that could cause multiple commands to be** sent to an FTP server.** Sep 95 HFN Rewritten to support streams and persistent conenctions** and multiplexed IO** Mar 1998 NG Neil Griffin - Bug fixes and additions for FTP support on NT.** Notes:** Portions Copyright 1994 Trustees of Dartmouth College** Code for recognizing different FTP servers and** parsing "ls -l" output taken from Macintosh Fetch** program with permission from Jim Matthews,** Dartmouth Software Development Team.*//* Library include files */#include "wwwsys.h"#include "WWWUtil.h"#include "WWWCore.h"#include "WWWStream.h"#include "WWWTrans.h"#include "WWWFile.h"#include "HTReqMan.h"#include "HTNetMan.h"#include "HTFTPDir.h"#include "HTFTP.h" /* Implemented here *//* Macros and other defines */#if 0/* Only use this if ABSOLUTELY necessary! */#define FTP_POLL_PORTS /* If allocation does not work, poll ourselves.*/#endif#ifdef FTP_POLL_PORTS#define FIRST_TCP_PORT 1024 /* Region to try for a listening port */#define LAST_TCP_PORT 5999PRIVATE int DataPort = FIRST_TCP_PORT;#endif#ifndef FTP_PORT#define FTP_PORT 21#define FTP_DATA 20#endif#define WWW_FTP_CLIENT "libwww@" /* If can't get user-info, use this */#define FTP_DIR(me) ((me)->type=='L' || (me)->type=='N')/*** Local context structure used in the HTNet object.*/typedef enum _HTFTPState { FTP_SUCCESS = -2, FTP_ERROR = -1, FTP_BEGIN = 0, FTP_NEED_CCON, /* Control connection */ FTP_NEED_LOGIN, FTP_NEED_DCON, /* Data connection */ FTP_NEED_DATA, FTP_NEED_SERVER /* For directory listings */} HTFTPState;typedef struct _ftp_ctrl { HTChunk * cmd; int repcode; char * reply; char * uid; char * passwd; char * account; HTFTPState state; /* State of the connection */ int substate; /* For hierarchical states */ BOOL sent; /* Read or write command */ BOOL cwd; /* Done cwd */ BOOL reset; /* Expect greeting */ FTPServerType server; /* Type of server */ HTNet * cnet; /* Control connection */ HTNet * dnet; /* Data connection *//* begin _GM_ *//* Note: libwww bug ID: GM3 */ BOOL alreadyLoggedIn;/* end _GM_ */} ftp_ctrl;typedef struct _ftp_data { char host[30]; /* Host to contact for data */ char * file; /* File or dir name */ char * offset; /* offset into file */ BOOL pasv; /* Active or passive */ char type; /* 'A', 'I', 'L'(IST), 'N'(LST) */ int complete; /* Check if both ctrl and data is loaded */ BOOL stream_error;} ftp_data;struct _HTStream { const HTStreamClass * isa; HTStream * target; HTRequest * request; ftp_ctrl * ctrl; HTEOLState state; HTChunk * welcome; BOOL junk; /* For too long lines */ BOOL first_line; char buffer[MAX_FTP_LINE+1]; int buflen; HTHost * host;};struct _HTInputStream { const HTInputStreamClass * isa;};typedef enum _FTPDataCon { FTP_DATA_PASV = 0x1, FTP_DATA_PORT = 0x2} FTPDataCon;PRIVATE FTPDataCon FTPMode = FTP_DATA_PASV;/* Added by Neil Griffin */PRIVATE FTPTransferMode g_FTPTransferMode = FTP_DEFAULT_TRANSFER_MODE;PRIVATE FTPControlMode g_FTPControlMode = FTP_DEFAULT_CONTROL_MODE;/* ------------------------------------------------------------------------- *//* FTP Status Line Stream *//* ------------------------------------------------------------------------- *//* FTPCleanup** ----------** This function closes the connection and frees memory.** Returns YES on OK, else NO*/PRIVATE int FTPCleanup (HTRequest * request, int status){ if (request) { HTNet * cnet = HTRequest_net(request); ftp_ctrl * ctrl = (ftp_ctrl *) HTNet_context(cnet); HTStream * input = HTRequest_inputStream(request); if (status == HT_INTERRUPTED) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_INTERRUPT); if (cbf) (*cbf)(request, HT_PROG_INTERRUPT, HT_MSG_NULL, NULL, NULL, NULL); } else if (status == HT_TIMEOUT) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_TIMEOUT); if (cbf) (*cbf)(request, HT_PROG_TIMEOUT, HT_MSG_NULL, NULL, NULL, NULL); } else if (status == HT_LOADED) { HTAlertCallback * cbf = HTAlert_find(HT_PROG_DONE); if (cbf) (*cbf)(request, HT_PROG_DONE, HT_MSG_NULL, NULL, NULL, NULL); } /* Free control stream with data TO network */ if (!HTRequest_isDestination(request) && input) { if (status == HT_INTERRUPTED) (*input->isa->abort)(input, NULL); else (*input->isa->_free)(input); } /* Remove the request object and our own context structure for http */ if (cnet && ctrl) { HTNet * dnet = ctrl->dnet; ftp_data * data = (ftp_data *) HTNet_context(dnet); HTChunk_delete(ctrl->cmd); HT_FREE(ctrl->reply); HT_FREE(ctrl->uid); HT_FREE(ctrl->passwd); HT_FREE(ctrl->account); HT_FREE(ctrl); if (dnet && data) { HT_FREE(data->file); HT_FREE(data); }#if 0 HTNet_setPersistent(dnet, NO, HT_TP_SINGLE);#endif /* See if we got a content length */ if (status == HT_LOADED) HTAnchor_setLength(HTRequest_anchor(request), HTNet_bytesRead(dnet)); HTNet_delete(dnet, HT_IGNORE); } HTNet_delete(cnet, status); return YES; } return NO;}/* ScanResponse** ------------** Analyzes the response from the FTP server.** Returns HT_LOADED if OK, HT_OK if more, HT_ERROR if error** the control connection.*/PRIVATE int ScanResponse (HTStream * me){ int reply = 0; char cont = '\0'; char *ptr = me->buffer+4; *(me->buffer+me->buflen) = '\0';/* begin _GM_ *//* Note: libwww bug ID: GM3 */ me->ctrl->alreadyLoggedIn = NO;/* end _GM_ */ if (isdigit((int) *(me->buffer))) sscanf(me->buffer, "%d%c", &reply, &cont); if (me->first_line) { HTTRACE(PROT_TRACE, "FTP Rx...... `%s\'\n" _ me->buffer); if (!reply) return HT_ERROR; me->first_line = NO; me->ctrl->repcode = reply; StrAllocCopy(me->ctrl->reply, ptr);/* begin _GM_ *//* Note: libwww bug ID: GM3 */ if ( (reply == 530) && (strcasestr(me->buffer, "already") != NULL) ) { me->ctrl->alreadyLoggedIn = YES; } else { me->ctrl->alreadyLoggedIn = NO; }/* end _GM_ */ } else { HTChunk_puts(me->welcome, ptr); HTChunk_putc(me->welcome, '\n'); } me->buflen = 0; me->state = EOL_BEGIN; if (cont != '-') { me->first_line = YES; return HT_LOADED; } return HT_OK;}/*** Searches for FTP header line until buffer fills up or a CRLF or LF** is found*/PRIVATE int FTPStatus_put_block (HTStream * me, const char * b, int l){ int status; HTHost_setConsumed(me->host, l); while (l-- > 0) { if (me->state == EOL_FCR) { if (*b == LF) { if (!me->junk) { if ((status = ScanResponse(me)) != HT_OK) return status; } else { me->buflen = 0; me->junk = NO; } } } else if (*b == CR) { me->state = EOL_FCR; } else if (*b == LF) { if (!me->junk) { if ((status = ScanResponse(me)) != HT_OK) return status; } else { me->buflen = 0; me->junk = NO; } } else { *(me->buffer+me->buflen++) = *b; if (me->buflen >= MAX_FTP_LINE) { HTTRACE(PROT_TRACE, "FTP Status.. Line too long - chopped\n"); me->junk = YES; if ((status = ScanResponse(me)) != HT_OK) { me->junk = NO; return status; } } } b++; } return HT_OK;}PRIVATE int FTPStatus_put_string (HTStream * me, const char * s){ return FTPStatus_put_block(me, s, (int) strlen(s));}PRIVATE int FTPStatus_put_character (HTStream * me, char c){ return FTPStatus_put_block(me, &c, 1);}PRIVATE int FTPStatus_flush (HTStream * me){ return (*me->target->isa->flush)(me->target);}PRIVATE int FTPStatus_free (HTStream * me){ int status = HT_OK; if (me->target) { if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; } HTChunk_delete(me->welcome); HT_FREE(me); return HT_OK;}PRIVATE int FTPStatus_abort (HTStream * me, HTList * e){ if (me->target) (*me->target->isa->abort)(me->target, e); HTChunk_delete(me->welcome); HT_FREE(me); HTTRACE(PROT_TRACE, "FTPStatus... ABORTING...\n"); return HT_ERROR;}/* FTPStatus Stream** -----------------*/PRIVATE const HTStreamClass FTPStatusClass ={ "FTPStatus", FTPStatus_flush, FTPStatus_free, FTPStatus_abort, FTPStatus_put_character, FTPStatus_put_string, FTPStatus_put_block};PRIVATE HTStream * FTPStatus_new (HTRequest * request, ftp_ctrl * ctrl, HTHost * host){ HTStream * me; if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("FTPStatus_new"); me->isa = &FTPStatusClass; me->request = request; me->first_line = YES; me->welcome = HTChunk_new(256); me->ctrl = ctrl; me->state = EOL_BEGIN; me->host = host; return me;}/* ------------------------------------------------------------------------- *//* FTP Client Functions for managing control and data connections *//* ------------------------------------------------------------------------- */PRIVATE int SendCommand (HTRequest *request, ftp_ctrl *ctrl, char *token, char *pars){ int len = strlen(token) + (pars ? strlen(pars)+1:0) + 2; HTStream * input = HTRequest_inputStream(request); HTChunk_clear(ctrl->cmd); HTChunk_ensure(ctrl->cmd, len); if (pars && *pars) sprintf(HTChunk_data(ctrl->cmd), "%s %s%c%c", token, pars, CR, LF); else sprintf(HTChunk_data(ctrl->cmd), "%s%c%c", token, CR, LF); HTTRACE(PROT_TRACE, "FTP Tx...... %s" _ HTChunk_data(ctrl->cmd));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -