CTRACE(tfp, "HTFTP: Rejecting path due to illegal escaped slash.\n");
CTRACE((tfp, "HTFTP: Rejecting path due to illegal escaped slash.\n"));
PUBLIC int HTFTPLoad ARGS4(	CONST char *,		name,	HTParentAnchor *,	anchor,	HTFormat,		format_out,	HTStream *,		sink){    BOOL isDirectory = NO;    HTAtom * encoding = NULL;    int status;    int retry;			/* How many times tried? */    HTFormat format;    /* set use_list to NOT since we don't know what kind of server     * this is yet.  And set the type to GENERIC     */    use_list = FALSE;    server_type = GENERIC_SERVER;    for (retry = 0; retry < 2; retry++) { /* For timed out/broken connections */	status = get_connection(name, anchor);	if (status < 0)	    return status;	if (!ftp_local_passive) {	    status = get_listen_socket();	    if (status < 0) {		NETCLOSE (control->socket);		control->socket = -1;		close_master_socket ();		/* HT_INTERRUPTED would fall through, if we could interrupt		   somehow in the middle of it, which we currently can't. */		return status;	    }#ifdef REPEAT_PORT	    /* Inform the server of the port number we will listen on	    */	    status = response(port_command);	    if (status == HT_INTERRUPTED) {		CTRACE (tfp, "HTFTP: Interrupted in response (port_command)\n");		_HTProgress (CONNECTION_INTERRUPTED);		NETCLOSE (control->socket);		control->socket = -1;		close_master_socket ();		return HT_INTERRUPTED;	    }	    if (status != 2) {		/* Could have timed out */		if (status < 0)		    continue;		/* try again - net error*/		return -status;		/* bad reply */	    }	    CTRACE(tfp, "HTFTP: Port defined.\n");#endif /* REPEAT_PORT */	} else {		/* Tell the server to be passive */	    char command[LINE_LENGTH+1];	    char *p;	    int reply, h0, h1, h2, h3, p0, p1;	/* Parts of reply */	    data_soc = status;	    status = send_cmd_1("PASV");	    if (status != 2) {		if (status < 0)		    continue;		/* retry or Bad return */		return -status;		/* bad reply */	    }	    for (p = response_text; *p && *p != ','; p++)		; /* null body */	    while (--p > response_text && '0' <= *p && *p <= '9')		; /* null body */	   status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",		   &h0, &h1, &h2, &h3, &p0, &p1);	   if (status < 4) {	       fprintf(tfp, "HTFTP: PASV reply has no inet address!\n");	       return -99;	   }	   passive_port = (p0<<8) + p1;	   CTRACE(tfp, "HTFTP: Server is listening on port %d\n",			passive_port);/*	Open connection for data:*/	    sprintf(command,		    "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port);	    status = HTDoConnect(name, "FTP", passive_port, &data_soc);	    if (status < 0) {		(void) HTInetStatus(gettext("connect for data"));		NETCLOSE(data_soc);		return status;			/* Bad return */	    }	    CTRACE(tfp, "FTP data connected, socket %d\n", data_soc);	}	status = 0;	break;	/* No more retries */    } /* for retries */    if (status < 0)	return status;		/* Failed with this code *//*	Ask for the file:*/    {	char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);	char *fname = filename; /** Save for subsequent free() **/	BOOL binary;	char *type = NULL;	char *cp;	if (server_type == CMS_SERVER) {	    /** If the unescaped path has a %2f, reject it as illegal. - FM **/	    if (((cp = strstr(filename, "%2")) != NULL) &&		TOUPPER(cp[2]) == 'F') {		FREE(fname);		init_help_message_cache();  /* to free memory */		NETCLOSE(control->socket);		control->socket = -1;		CTRACE(tfp, "HTFTP: Rejecting path due to illegal escaped slash.\n");		return -1;	    }	}	if (!*filename) {	    StrAllocCopy(filename, "/");	    type = "D";	} else if ((type = strrchr(filename, ';')) != NULL) {	    /*	    **	Check and trim the type= parameter. - FM	    */	    if (!strncasecomp((type+1), "type=", 5)) {		switch(TOUPPER(*(type+6))) {		case 'D':		    *type = '\0';		    type = "D";		    break;		case 'A':		    *type = '\0';		    type = "A";		    break;		case 'I':		    *type = '\0';		    type = "I";		    break;		default:		    type = "";		    break;		}		if (!*filename) {		    *filename = '/';		    *(filename+1) = '\0';		}	    }	    if (*type != '\0') {		CTRACE(tfp, "HTFTP: type=%s\n", type);	    }	}	HTUnEscape(filename);	CTRACE(tfp, "HTFTP: UnEscaped %s\n", filename);	if (filename[1] == '~') {	    /*	    ** Check if translation of HOME as tilde is supported,	    ** and adjust filename if so. - FM	    */	    char *cp2 = NULL;	    char *fn = NULL;	    if ((cp2 = strchr((filename+1), '/')) != NULL) {		*cp2 = '\0';	    }	    status = send_cmd_1("PWD");	    if (status == 2 && response_text[5] == '/') {		status = send_cwd(filename+1);		if (status == 2) {		    StrAllocCopy(fn, (filename+1));		    if (cp2) {			*cp2 = '/';			if (fn[strlen(fn)-1] != '/') {			    StrAllocCat(fn, cp2);			} else {			    StrAllocCat(fn, (cp2+1));			}			cp2 = NULL;		    }		    FREE(fname);		    fname = filename = fn;		}	    }	    if (cp2) {		*cp2 = '/';	    }	}	if (strlen(filename) > 3) {	    char *cp2;	    if (((cp2=strrchr(filename, '.')) != NULL &&		 0 == strncasecomp(cp2, ".me", 3)) &&		(cp2[3] == '\0' || cp2[3] == ';')) {		/*		**  Don't treat this as application/x-Troff-me		**  if it's a Unix server but has the string		**  "read.me", or if it's not a Unix server. - FM		*/		if ((server_type != UNIX_SERVER) ||		    (cp2 > (filename + 3) &&		     0 == strncasecomp((cp2 - 4), "read.me", 7))) {		    *cp2 = '\0';		    format = HTFileFormat(filename, &encoding, NULL);		    *cp2 = '.';		} else {		    format = HTFileFormat(filename, &encoding, NULL);		}	    } else {		format = HTFileFormat(filename, &encoding, NULL);	    }	} else {	    format = HTFileFormat(filename, &encoding, NULL);	}	format = HTCharsetFormat(format, anchor, -1);	binary = (encoding != HTAtom_for("8bit") &&		  encoding != HTAtom_for("7bit"));	if (!binary &&	    /*	    **	Force binary if we're in source, download or dump	    **	mode and this is not a VM/CMS server, so we don't	    **	get CRLF instead of LF (or CR) for newlines in text	    **	files.	Can't do this for VM/CMS or we'll get	    **	raw EBCDIC.  - FM	    */	    (format_out == WWW_SOURCE ||	     format_out == HTAtom_for("www/download") ||	     format_out == HTAtom_for("www/dump")) &&	    (server_type != CMS_SERVER))	    binary = TRUE;	if (!binary && type && *type == 'I') {	    /*	    **	Force binary if we had ;type=I - FM	    */	    binary = TRUE;	} else if (binary && type && *type == 'A') {	    /*	    **	Force ASCII if we had ;type=A - FM	    */	    binary = FALSE;	}	if (binary != control->binary) {	    /*	    **	Act on our setting if not already set. - FM	    */	    char * mode = binary ? "I" : "A";	    status = send_cmd_2("TYPE", mode);	    if (status != 2) {		init_help_message_cache();  /* to free memory */		return ((status < 0) ? status : -status);	    }	    control->binary = binary;	}	switch (server_type) {	/*	**  Handle what for Lynx are special case servers, e.g.,	**  for which we respect RFC 1738, or which have known	**  conflicts in suffix mappings. - FM	*/	case VMS_SERVER:	  {	    char *cp1, *cp2;	    BOOL included_device = FALSE;	    /** Accept only Unix-style filename **/	    if (strchr(filename, ':') != NULL ||		strchr(filename, '[') != NULL) {		FREE(fname);		init_help_message_cache();  /* to free memory */		NETCLOSE(control->socket);		control->socket = -1;		CTRACE(tfp, "HTFTP: Rejecting path due to non-Unix-style syntax.\n");		return -1;	    }	    /** Handle any unescaped "/%2F" path **/	    if (!strncmp(filename, "//", 2)) {		int i;		included_device = TRUE;		for (i = 0; filename[(i+1)]; i++)		    filename[i] = filename[(i+1)];		filename[i] = '\0';		CTRACE(tfp, "HTFTP: Trimmed '%s'\n", filename);		cp = HTMake_VMS_name("", filename);		CTRACE(tfp, "HTFTP: VMSized '%s'\n", cp);		if ((cp1=strrchr(cp, ']')) != NULL) {		    cp1++;		    for (i = 0; cp1[i]; i++)			filename[i] = cp1[i];		    filename[i] = '\0';		    CTRACE(tfp, "HTFTP: Filename '%s'\n", filename);		    *cp1 = '\0';		    status = send_cwd(cp);		    if (status != 2) {			char *dotslash = 0;			if ((cp1=strchr(cp, '[')) != NULL) {			    *cp1++ = '\0';			    status = send_cwd(cp);			    if (status != 2) {				FREE(fname);				init_help_message_cache(); /* to free memory */				NETCLOSE(control->socket);				control->socket = -1;				return ((status < 0) ? status : -status);			    }			    HTSprintf0(&dotslash, "[.%s", cp1);			    status = send_cwd(dotslash);			    FREE(dotslash);			    if (status != 2) {				FREE(fname);				init_help_message_cache(); /* to free memory */				NETCLOSE(control->socket);				control->socket = -1;				return ((status < 0) ? status : -status);			    }			} else {			    FREE(fname);			    init_help_message_cache();	/* to free memory */			    NETCLOSE(control->socket);			    control->socket = -1;			    return ((status < 0) ? status : -status);			}		    }		} else if ((cp1=strchr(cp, ':')) != NULL &&			   strchr(cp, '[') == NULL &&			   strchr(cp, ']') == NULL) {		    cp1++;		    if (*cp1 != '\0') {			for (i = 0; cp1[i]; i++)			    filename[i] = cp1[i];			filename[i] = '\0';			CTRACE(tfp, "HTFTP: Filename '%s'\n", filename);			*cp1 = '\0';			strcat(cp, "[");			strcat(cp, filename);			strcat(cp, "]");			status = send_cwd(cp);			if (status != 2) {			    *cp1 = '\0';			    strcat(cp, "[000000]");			    status = send_cwd(cp);			    if (status != 2) {				*cp1 = '\0';				status = send_cwd(cp);				if (status != 2) {				    FREE(fname);				    init_help_message_cache();				    NETCLOSE(control->socket);				    control->socket = -1;				    return ((status < 0) ? status : -status);				}			    }			} else {			    strcpy(cp, "000000");			    filename = cp;			}		    }		} else if (0==strcmp(cp, (filename+1))) {		    status = send_cwd(cp);		    if (status != 2) {			strcat(cp, ":");			status = send_cwd(cp);			if (status != 2) {			    FREE(fname);			    init_help_message_cache();	/* to free memory */			    NETCLOSE(control->socket);			    control->socket = -1;			    return ((status < 0) ? status : -status);			}		    }		    strcpy(cp, "000000");		    filename = cp;		}	    }	    /** Trim trailing slash if filename is not the top directory **/	    if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')		filename[strlen(filename)-1] = '\0';#ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */	    if (!included_device) {		/** Get the current default VMS device:[directory] **/		status = send_cmd_1("PWD");		if (status != 2) {		    FREE(fname);		    init_help_message_cache();	/* to free memory */		    NETCLOSE(control->socket);		    control->socket = -1;		    return ((status < 0) ? status : -status);		}		/** Go to the VMS account's top directory **/		if ((cp=strchr(response_text, '[')) != NULL &&		    (cp1=strrchr(response_text, ']')) != NULL) {		    char *tmp = 0;		    unsigned len = 4;		    StrAllocCopy(tmp, cp);		    if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1) {			len += (cp2 - cp);		    } else {			len += (cp1 - cp);		    }		    tmp[len] = 0;		    StrAllocCat(tmp, "]");		    status = send_cwd(tmp);		    FREE(tmp);		    if (status != 2) {			FREE(fname);			init_help_message_cache();  /* to free memory */			NETCLOSE(control->socket);			control->socket = -1;			return ((status < 0) ? status : -status);		    }		}	    }#endif /* MAINTAIN_CONNECTION */	    /** If we want the VMS account's top directory, list it now **/	    if (!(strcmp(filename, "/~")) ||		(included_device && 0==strcmp(filename, "000000")) ||		(strlen(filename) == 1 && *filename == '/')) {		isDirectory = YES;		status = send_cmd_1("LIST");		FREE(fname);		if (status != 1) {		    /* Action not started */		    init_help_message_cache();	/* to free memory */		    NETCLOSE(control->socket);		    control->socket = -1;		    return ((status < 0) ? status : -status);		}		/** Big goto! **/		goto listen;	    }	    /** Otherwise, go to appropriate directory and doctor filename **/	    if (!strncmp(filename, "/~", 2))		filename += 2;	    CTRACE(tfp, "check '%s' to translate x/y/ to [.x.y]\n", filename);	    if (!included_device &&		(cp = strchr(filename, '/')) != NULL &&		(cp1 = strrchr(cp, '/')) != NULL &&		(cp1 - cp) > 1) {		char *tmp = 0;		HTSprintf0(&tmp, "[.%.*s]", cp1-cp-1, cp+1);		CTRACE(tfp, "change path '%s'\n", tmp);		while ((cp2 = strrchr(tmp, '/')) != NULL)		    *cp2 = '.';		CTRACE(tfp, "...to  path '%s'\n", tmp);		status = send_cwd(tmp);		FREE(tmp);		if (status != 2) {		    FREE(fname);		    init_help_message_cache();	/* to free memory */		    NETCLOSE(control->socket);		    control->socket = -1;		    return ((status < 0) ? status : -status);		}		filename = cp1+1;	    } else {		if (!included_device) {		    filename += 1;		}	    }	    break;	  }	case CMS_SERVER:	  {	    /*	    **	If we want the CMS account's top directory, or a base	    **	SFS or anonymous directory path (i.e., without a slash),	    **	list it now. FM	    */	    if ((strlen(filename) == 1 && *filename == '/') ||		((0 == strncasecomp((filename+1), "vmsysu:", 7)) &&		 (cp = strchr((filename+1), '.')) != NULL &&		 strchr(cp, '/') == NULL) ||		(0 == strncasecomp(filename+1, "anonymou.", 9) &&		 strchr(filename+1, '/') == NULL)) {		if (filename[1] != '\0') {		    status = send_cwd(filename+1);		    if (status != 2) {			/* Action not started */			init_help_message_cache();  /* to free memory */			NETCLOSE(control->socket);			control->socket = -1;			return ((status < 0) ? status : -status);		    }		}		isDirectory = YES;		if (use_list)		    status = send_cmd_1("LIST");		else		    status = send_cmd_1("NLST");		FREE(fname);		if (status != 1) {		    /* Action not started */		    init_help_message_cache();	/* to free memory */		    NETCLOSE(control->socket);		    control->socket = -1;		    return ((status < 0) ? status : -status);		}		/** Big goto! **/		goto listen;	    }	    filename++;	    /** Otherwise, go to appropriate directory and adjust filename **/	    while ((cp = strchr(filename, '/')) != NULL) {		*cp++ = '\0';		status = send_cwd(filename);		if (status == 2) {		    if (*cp == '\0') {			isDirectory = YES;			if (use_list)			    status = send_cmd_1("LIST");			else			    status = send_cmd_1("NLST");			FREE(fname);			if (status != 1) {			    /** Action not started **/			    init_help_message_cache();	/* to free memory */			    NETCLOSE(control->socket);			    control->socket = -1;			    return ((status < 0) ? status : -status);			}			/** Clear any messages from the login directory **/			init_help_message_cache();			/** Big goto! **/			goto listen;		    }		    filename = cp;		}	    }	    break;	  }	default:	    /** Shift for any unescaped "/%2F" path **/	    if (!strncmp(filename, "//", 2))		filename++;	    break;	}	/*	**  Act on a file or listing request, or try to figure out	**  which we're dealing with if we don't know yet. - FM	*/	if (!(type) || (type && *type != 'D')) {	    status = send_cmd_2("RETR", filename);	} else {	    status = 5;		/* Failed status set as flag. - FM */	}	if (status != 1) {	/* Failed : try to CWD to it */	    /** Clear any login messages if this isn't the login directory **/	    if (strcmp(filename, "/"))		init_help_message_cache();	    status = send_cwd(filename);	    if (status == 2) {	/* Succeeded : let's NAME LIST it */		isDirectory = YES;		if (use_list)		    status = send_cmd_1("LIST");		else		    status = send_cmd_1("NLST");	    }	}	FREE(fname);	if (status != 1) {	    init_help_message_cache();	/* to free memory */	    NETCLOSE(control->socket);	    control->socket = -1;	    if (status < 0)		return status;	    else		return -status;	}    }listen:    if (!ftp_local_passive) {	/* Wait for the connection */	struct sockaddr_in soc_address;	int	soc_addrlen=sizeof(soc_address);#ifdef SOCKS	if (socks_flag)	    status = Raccept(master_socket,			     (struct sockaddr *)&soc_address,			     (void *)&soc_addrlen);	else#endif /* SOCKS */	status = accept(master_socket,			(struct sockaddr *)&soc_address,			(void *)&soc_addrlen);	if (status < 0) {	    init_help_message_cache();	/* to free memory */	    return HTInetStatus("accept");	}	CTRACE(tfp, "TCP: Accepted new socket %d\n", status);	data_soc = status;    } /* !ftp_local_passive */    if (isDirectory) {	status = read_directory (anchor, name, format_out, sink);	NETCLOSE(data_soc);	NETCLOSE(control->socket);	control->socket = -1;	init_help_message_cache();  /* to free memory */	return status;      /* returns HT_LOADED or error */    } else {	int rv;	int len;	char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);	/** Clear any login messages **/	init_help_message_cache();	/** Fake a Content-Encoding for compressed files. - FM **/	HTUnEscape(FileName);	if (!IsUnityEnc(encoding)) {	    /*	     *	We already know from the call to HTFileFormat above that	     *	this is a compressed file, no need to look at the filename	     *	again. - kw	     */	    StrAllocCopy(anchor->content_type, format->name);	    StrAllocCopy(anchor->content_encoding, HTAtom_name(encoding));	    format = HTAtom_for("www/compressed");	} else if ((len = strlen(FileName)) > 2) {	    if ((FileName[len - 1] == 'Z') &&		(FileName[len - 2] == '.' ||		 FileName[len - 2] == '-' ||		 FileName[len - 2] == '_')) {		FileName[len - 2] = '\0';		format = HTFileFormat(FileName, &encoding, NULL);		format = HTCharsetFormat(format, anchor, -1);		StrAllocCopy(anchor->content_type, format->name);		StrAllocCopy(anchor->content_encoding, "x-compress");		format = HTAtom_for("www/compressed");	    } else if ((len > 3) &&		       !strcasecomp((char *)&FileName[len - 2], "gz")) {		if (FileName[len - 3] == '.' ||		    FileName[len - 3] == '-' ||		    FileName[len - 3] == '_') {		    FileName[len - 3] = '\0';		    format = HTFileFormat(FileName, &encoding, NULL);		    format = HTCharsetFormat(format, anchor, -1);		    StrAllocCopy(anchor->content_type, format->name);		    StrAllocCopy(anchor->content_encoding, "x-gzip");		    format = HTAtom_for("www/compressed");		}	    }	}	FREE(FileName);	_HTProgress (gettext("Receiving FTP file."));	rv = HTParseSocket(format, format_out, anchor, data_soc, sink);	if (rv == HT_INTERRUPTED)	     _HTProgress(TRANSFER_INTERRUPTED);	HTInitInput(control->socket);	/* Reset buffering to control connection DD 921208 */	status = NETCLOSE(data_soc);	CTRACE(tfp, "HTFTP: Closing data socket %d\n", data_soc);	if (status < 0 && rv != HT_INTERRUPTED && rv != -1) {	    (void) HTInetStatus("close");	/* Comment only */	    data_soc = -1;			/* invalidate it */	} else {	    data_soc = -1;			/* invalidate it */	    status = response(NIL);		/* Pick up final reply */	    if (status != 2 && rv != HT_INTERRUPTED && rv != -1) {		init_help_message_cache();  /* to free memory */		return HTLoadError(sink, 500, response_text);	    }	}	NETCLOSE(control->socket);	control->socket = -1;	init_help_message_cache();  /* to free memory */	return HT_LOADED;    }} /* open_file_read */