📄 htftp.c
字号:
/* File Transfer Protocol (FTP) Client** for a WorldWideWeb browser** ===================================**** 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 (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@info.cern.ch>** 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>** 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.**** 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.*//*BUGS: @@@ Limit connection cache size! Error reporting to user. 400 & 500 errors are ack'ed by user with windows. Use configuration file for user names** Note for portability this version does not use select() and** so does not watch the control and data channels at the** same time.*/#include <HTUtils.h>#include <HTAlert.h>#include <HTFTP.h> /* Implemented here */#include <HTTCP.h>#include <HTTP.h>#include <HTFont.h>#define REPEAT_PORT /* Give the port number for each file */#define REPEAT_LISTEN /* Close each listen socket and open a new one *//* define POLL_PORTS If allocation does not work, poll ourselves.*/#define LISTEN_BACKLOG 2 /* Number of pending connect requests (TCP)*/#define FIRST_TCP_PORT 1024 /* Region to try for a listening port */#define LAST_TCP_PORT 5999#define LINE_LENGTH 256#include <HTParse.h>#include <HTAnchor.h>#include <HTFile.h> /* For HTFileFormat() */#include <HTBTree.h>#include <HTChunk.h>#ifndef IPPORT_FTP#define IPPORT_FTP 21#endif /* !IPORT_FTP */#include <LYUtils.h>#include <LYGlobalDefs.h>#include <LYStrings.h>#include <LYLeaks.h>typedef struct _connection { struct _connection * next; /* Link on list */ unsigned long addr; /* IP address */ int socket; /* Socket number for communication */ BOOL binary; /* Binary mode? */} connection;/* Hypertext object building machinery*/#include <HTML.h>#define PUTC(c) (*targetClass.put_character) (target, c)#define PUTS(s) (*targetClass.put_string) (target, s)#define START(e) (*targetClass.start_element) (target, e, 0, 0, -1, 0)#define END(e) (*targetClass.end_element) (target, e, 0)#define FREE_TARGET (*targetClass._free) (target)#define ABORT_TARGET (*targetClass._free) (target)struct _HTStructured { CONST HTStructuredClass * isa; /* ... */};/* Global Variables** ---------------------*/PUBLIC int HTfileSortMethod = FILE_BY_NAME;#ifndef DISABLE_FTP /*This disables everything to end-of-file */PRIVATE char ThisYear[8];PRIVATE char LastYear[8];PRIVATE int TheDate;PRIVATE BOOLEAN HaveYears = FALSE;/* Module-Wide Variables** ---------------------*/PRIVATE connection * connections = NULL;/* Linked list of connections */PRIVATE char response_text[LINE_LENGTH+1];/* Last response from ftp host */PRIVATE connection * control = NULL; /* Current connection */PRIVATE int data_soc = -1; /* Socket for data transfer =invalid */PRIVATE char *user_entered_password = NULL;PRIVATE char *last_username_and_host = NULL;/* * ProFTPD 1.2.5rc1 is known to have a broken implementation of RETR. If asked * to retrieve a directory, it gets confused and fails subsequent commands such * as CWD and LIST. Since this is an unusual bug, we should remove this ifdef * at some point - TD 2004/1/1. */#define BROKEN_PROFTPD 1PRIVATE int ProFTPD_bugs = FALSE;typedef enum { GENERIC_SERVER , MACHTEN_SERVER , UNIX_SERVER , VMS_SERVER , CMS_SERVER , DCTS_SERVER , TCPC_SERVER , PETER_LEWIS_SERVER , NCSA_SERVER , WINDOWS_NT_SERVER , WINDOWS_2K_SERVER , MS_WINDOWS_SERVER , MSDOS_SERVER , APPLESHARE_SERVER , NETPRESENZ_SERVER , DLS_SERVER} eServerType;PRIVATE eServerType server_type = GENERIC_SERVER; /* the type of ftp host */PRIVATE int unsure_type = FALSE; /* sure about the type? */PRIVATE BOOLEAN use_list = FALSE; /* use the LIST command? */PRIVATE int interrupted_in_next_data_char = FALSE;#ifdef POLL_PORTSPRIVATE PortNumber port_number = FIRST_TCP_PORT;#endif /* POLL_PORTS */PRIVATE int master_socket = -1; /* Listening socket = invalid */PRIVATE char port_command[255]; /* Command for setting the port */PRIVATE fd_set open_sockets; /* Mask of active channels */PRIVATE int num_sockets; /* Number of sockets to scan */PRIVATE PortNumber passive_port; /* Port server specified for data */#define NEXT_CHAR HTGetCharacter() /* Use function in HTFormat.c */#define DATA_BUFFER_SIZE 2048PRIVATE char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */PRIVATE char * data_read_pointer;PRIVATE char * data_write_pointer;#define NEXT_DATA_CHAR next_data_char()PRIVATE int close_connection PARAMS(( connection * con));#ifdef LY_FIND_LEAKS/*** This function frees module globals. - FM*/PRIVATE void free_FTPGlobals NOARGS{ FREE(user_entered_password); FREE(last_username_and_host); if (control) { if (control->socket != -1) close_connection(control); FREE(control); }}#endif /* LY_FIND_LEAKS *//* PUBLIC HTVMS_name()** CONVERTS WWW name into a VMS name** ON ENTRY:** nn Node Name (optional)** fn WWW file name**** ON EXIT:** returns vms file specification**** Bug: Returns pointer to static -- non-reentrant*/PUBLIC char * HTVMS_name ARGS2( CONST char *, nn, CONST char *, fn){/* We try converting the filename into Files-11 syntax. That is, we assume** first that the file is, like us, on a VMS node. We try remote** (or local) DECnet access. Files-11, VMS, VAX and DECnet** are trademarks of Digital Equipment Corporation.** The node is assumed to be local if the hostname WITHOUT DOMAIN** matches the local one. @@@*/ static char *vmsname; char * filename = (char*)malloc(strlen(fn)+1); char * nodename = (char*)malloc(strlen(nn)+2+1); /* Copies to hack */ char *second; /* 2nd slash */ char *last; /* last slash */ CONST char * hostname = HTHostName(); if (!filename || !nodename) outofmem(__FILE__, "HTVMSname"); strcpy(filename, fn); strcpy(nodename, ""); /* On same node? Yes if node names match */ if (strncmp(nn, "localhost", 9)) { CONST char *p; CONST char *q; for (p = hostname, q = nn; *p && *p != '.' && *q && *q != '.'; p++, q++){ if (TOUPPER(*p) != TOUPPER(*q)) { char *r; strcpy(nodename, nn); r = strchr(nodename, '.'); /* Mismatch */ if (r) *r = '\0'; /* Chop domain */ strcat(nodename, "::"); /* Try decnet anyway */ break; } } } second = strchr(filename+1, '/'); /* 2nd slash */ last = strrchr(filename, '/'); /* last slash */ if (!second) { /* Only one slash */ HTSprintf0(&vmsname, "%s%s", nodename, filename + 1); } else if (second == last) { /* Exactly two slashes */ *second = '\0'; /* Split filename from disk */ HTSprintf0(&vmsname, "%s%s:%s", nodename, filename+1, second+1); *second = '/'; /* restore */ } else { /* More than two slashes */ char * p; *second = '\0'; /* Split disk from directories */ *last = '\0'; /* Split dir from filename */ HTSprintf0(&vmsname, "%s%s:[%s]%s", nodename, filename+1, second+1, last+1); *second = *last = '/'; /* restore filename */ for (p = strchr(vmsname, '['); *p!=']'; p++) if (*p == '/') *p = '.'; /* Convert dir sep. to dots */ } FREE(nodename); FREE(filename); return vmsname;}/* Procedure: Read a character from the data connection** ----------------------------------------------------*/PRIVATE int next_data_char NOARGS{ int status; if (data_read_pointer >= data_write_pointer) { status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE); if (status == HT_INTERRUPTED) interrupted_in_next_data_char = 1; if (status <= 0) return -1; data_write_pointer = data_buffer + status; data_read_pointer = data_buffer; }#ifdef NOT_ASCII { char c = *data_read_pointer++; return FROMASCII(c); }#else return UCH(*data_read_pointer++);#endif /* NOT_ASCII */}/* Close an individual connection***/PRIVATE int close_connection ARGS1( connection *, con){ connection * scan; int status; CTRACE((tfp, "HTFTP: Closing control socket %d\n", con->socket)); status = NETCLOSE(con->socket); if (TRACE && status != 0) {#ifdef UNIX CTRACE((tfp, "HTFTP:close_connection: %s", LYStrerror(errno)));#else if (con->socket != INVSOC) HTInetStatus("HTFTP:close_connection");#endif } con->socket = -1; if (connections == con) { connections = con->next; return status; } for (scan = connections; scan; scan = scan->next) { if (scan->next == con) { scan->next = con->next; /* Unlink */ if (control == con) control = (connection*)0; return status; } /*if */ } /* for */ return -1; /* very strange -- was not on list. */}PRIVATE char *help_message_buffer = NULL; /* global :( */PRIVATE void init_help_message_cache NOARGS{ FREE(help_message_buffer);}PRIVATE void help_message_cache_add ARGS1( char *, string){ if (help_message_buffer) StrAllocCat(help_message_buffer, string); else StrAllocCopy(help_message_buffer, string); CTRACE((tfp,"Adding message to help cache: %s\n",string));}PRIVATE char *help_message_cache_non_empty NOARGS{ return(help_message_buffer);}PRIVATE char *help_message_cache_contents NOARGS{ return(help_message_buffer);}/* Send One Command** ----------------**** This function checks whether we have a control connection, and sends** one command if given.**** On entry,** control points to the connection which is established.** cmd points to a command, or is zero to just get the response.**** The command should already be terminated with the CRLF pair.**** On exit,** returns: 1 for success,** or negative for communication failure (in which case** the control connection will be closed).*/PRIVATE int write_cmd ARGS1( char *, cmd){ int status; if (!control) { CTRACE((tfp, "HTFTP: No control connection set up!!\n")); return -99; } if (cmd) { CTRACE((tfp, " Tx: %s", cmd));#ifdef NOT_ASCII { char * p; for (p = cmd; *p; p++) { *p = TOASCII(*p); } }#endif /* NOT_ASCII */ status = NETWRITE(control->socket, cmd, (int)strlen(cmd)); if (status < 0) { CTRACE((tfp, "HTFTP: Error %d sending command: closing socket %d\n", status, control->socket)); close_connection(control); return status; } } return 1;}/* Execute Command and get Response** --------------------------------**** See the state machine illustrated in RFC959, p57. This implements** one command/reply sequence. It also interprets lines which are to** be continued, which are marked with a "-" immediately after the** status code.**** Continuation then goes on until a line with a matching reply code** an a space after it.**** On entry,** control points to the connection which is established.** cmd points to a command, or is zero to just get the response.**** The command must already be terminated with the CRLF pair.**** On exit,** returns: The first digit of the reply type,** or negative for communication failure.*/PRIVATE int response ARGS1( char *, cmd){ int result; /* Three-digit decimal code */ int continuation_response = -1; int status; if ((status = write_cmd(cmd)) < 0) return status; do { char *p = response_text; for (;;) { int ich = NEXT_CHAR; if (((*p++ = (char) ich) == LF) || (p == &response_text[LINE_LENGTH])) { char continuation; if (interrupted_in_htgetcharacter) { CTRACE((tfp, "HTFTP: Interrupted in HTGetCharacter, apparently.\n")); NETCLOSE (control->socket); control->socket = -1; return HT_INTERRUPTED; } *p = '\0'; /* Terminate the string */ CTRACE((tfp, " Rx: %s", response_text)); /* Check for login or help messages */ if (!strncmp(response_text,"230-",4) || !strncmp(response_text,"250-",4) || !strncmp(response_text,"220-",4)) help_message_cache_add(response_text+4); sscanf(response_text, "%d%c", &result, &continuation); if (continuation_response == -1) { if (continuation == '-') /* start continuation */ continuation_response = result; } else { /* continuing */ if (continuation_response == result && continuation == ' ') continuation_response = -1; /* ended */ }#ifdef BROKEN_PROFTPD if (result == 220 && LYstrstr(response_text, "ProFTPD 1.2.5")) { ProFTPD_bugs = TRUE; CTRACE((tfp, "This server is broken (RETR)\n")); }#endif break; } /* if end of line */ if (interrupted_in_htgetcharacter) { CTRACE((tfp, "HTFTP: Interrupted in HTGetCharacter, apparently.\n")); NETCLOSE (control->socket);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -