881 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			881 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* $Id: ftp.c,v 1.15 2002/11/18 18:26:13 ukai Exp $ */
 | 
						|
#include <stdio.h>
 | 
						|
#include <pwd.h>
 | 
						|
#include <Str.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <setjmp.h>
 | 
						|
 | 
						|
#include "fm.h"
 | 
						|
#include "html.h"
 | 
						|
#include "myctype.h"
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
#include <malloc.h>
 | 
						|
#endif				/* DEBUG */
 | 
						|
 | 
						|
#include <sys/socket.h>
 | 
						|
#if defined(FTPPASS_HOSTNAMEGEN) || defined(INET6)
 | 
						|
#include <netinet/in.h>
 | 
						|
#include <netdb.h>
 | 
						|
#include <arpa/inet.h>
 | 
						|
#endif
 | 
						|
 | 
						|
typedef struct _FTP {
 | 
						|
    FILE *rcontrol;
 | 
						|
    FILE *wcontrol;
 | 
						|
    FILE *data;
 | 
						|
} *FTP;
 | 
						|
 | 
						|
#define FtpError(status) ((status)<0)
 | 
						|
#define FTPDATA(ftp) ((ftp)->data)
 | 
						|
 | 
						|
typedef int STATUS;
 | 
						|
 | 
						|
static FTP current_ftp;
 | 
						|
 | 
						|
static Str
 | 
						|
read_response1(FTP ftp)
 | 
						|
{
 | 
						|
    char c;
 | 
						|
    Str buf = Strnew();
 | 
						|
    while (1) {
 | 
						|
	c = getc(ftp->rcontrol);
 | 
						|
	if (c == '\r') {
 | 
						|
	    c = getc(ftp->rcontrol);
 | 
						|
	    if (c == '\n') {
 | 
						|
		Strcat_charp(buf, "\r\n");
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		Strcat_char(buf, '\r');
 | 
						|
		Strcat_char(buf, c);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else if (c == '\n') {
 | 
						|
	    Strcat_charp(buf, "\r\n");
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	else if (feof(ftp->rcontrol))
 | 
						|
	    break;
 | 
						|
	else
 | 
						|
	    Strcat_char(buf, c);
 | 
						|
    }
 | 
						|
    return buf;
 | 
						|
}
 | 
						|
 | 
						|
Str
 | 
						|
read_response(FTP ftp)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    tmp = read_response1(ftp);
 | 
						|
    if (feof(ftp->rcontrol)) {
 | 
						|
	return tmp;
 | 
						|
    }
 | 
						|
    if (tmp->ptr[3] == '-') {
 | 
						|
	/* RFC959 4.2 FTP REPLIES */
 | 
						|
	/* multi-line response start */
 | 
						|
	/* 
 | 
						|
	 * Thus the format for multi-line replies is that the
 | 
						|
	 * first line will begin with the exact required reply
 | 
						|
	 * code, followed immediately by a Hyphen, "-" (also known 
 | 
						|
	 * as Minus), followed by text.  The last line will begin
 | 
						|
	 * with the same code, followed immediately by Space <SP>, 
 | 
						|
	 * optionally some text, and the Telnet end-of-line code. */
 | 
						|
	while (1) {
 | 
						|
	    tmp = read_response1(ftp);
 | 
						|
	    if (feof(ftp->rcontrol)) {
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    if (IS_DIGIT(tmp->ptr[0])
 | 
						|
		&& IS_DIGIT(tmp->ptr[1])
 | 
						|
		&& IS_DIGIT(tmp->ptr[2])
 | 
						|
		&& tmp->ptr[3] == ' ') {
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return tmp;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpLogin(FTP * ftp_return, char *host, char *user, char *pass)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
    FTP ftp = New(struct _FTP);
 | 
						|
    int fd;
 | 
						|
    *ftp_return = current_ftp = ftp;
 | 
						|
    fd = openSocket(host, "ftp", 21);
 | 
						|
    if (fd < 0)
 | 
						|
	return -1;
 | 
						|
#ifdef FTPPASS_HOSTNAMEGEN
 | 
						|
    if (ftppass_hostnamegen && !strcmp(user, "anonymous")) {
 | 
						|
	size_t n = strlen(pass);
 | 
						|
 | 
						|
	if (n > 0 && pass[n - 1] == '@') {
 | 
						|
	    struct sockaddr_in sockname;
 | 
						|
	    int socknamelen = sizeof(sockname);
 | 
						|
 | 
						|
	    if (!getsockname(fd, (struct sockaddr *)&sockname, &socknamelen)) {
 | 
						|
		struct hostent *sockent;
 | 
						|
		Str tmp2 = Strnew_charp(pass);
 | 
						|
 | 
						|
		if ((sockent = gethostbyaddr((char *)&sockname.sin_addr,
 | 
						|
					     sizeof(sockname.sin_addr),
 | 
						|
					     sockname.sin_family)))
 | 
						|
		    Strcat_charp(tmp2, sockent->h_name);
 | 
						|
		else
 | 
						|
		    Strcat_m_charp(tmp2, "[", inet_ntoa(sockname.sin_addr),
 | 
						|
				   "]", NULL);
 | 
						|
 | 
						|
		pass = tmp2->ptr;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    ftp->rcontrol = fdopen(fd, "rb");
 | 
						|
    ftp->wcontrol = fdopen(dup(fd), "wb");
 | 
						|
    ftp->data = NULL;
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (atoi(tmp->ptr) != 220)
 | 
						|
	return -1;
 | 
						|
    if (fmInitialized) {
 | 
						|
	message(Sprintf("Sending FTP username (%s) to remote server.", user)->
 | 
						|
		ptr, 0, 0);
 | 
						|
	refresh();
 | 
						|
    }
 | 
						|
    tmp = Sprintf("USER %s\r\n", user);
 | 
						|
    fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    /*
 | 
						|
     * Some ftp daemons(e.g. publicfile) return code 230 for user command.
 | 
						|
     */
 | 
						|
    if (atoi(tmp->ptr) == 230)
 | 
						|
	goto succeed;
 | 
						|
    if (atoi(tmp->ptr) != 331)
 | 
						|
	return -1;
 | 
						|
    if (fmInitialized) {
 | 
						|
	message(Sprintf("Sending FTP password to remote server.")->ptr, 0, 0);
 | 
						|
	refresh();
 | 
						|
    }
 | 
						|
    tmp = Sprintf("PASS %s\r\n", pass);
 | 
						|
    fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (atoi(tmp->ptr) != 230)
 | 
						|
	return -1;
 | 
						|
  succeed:
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpBinary(FTP ftp)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
    fwrite("TYPE I\r\n", 8, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (atoi(tmp->ptr) != 200)
 | 
						|
	return -1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
ftp_pasv(FTP ftp)
 | 
						|
{
 | 
						|
    int n1, n2, n3, n4, p1, p2;
 | 
						|
    int data_s;
 | 
						|
    char *p;
 | 
						|
    Str tmp;
 | 
						|
    int family;
 | 
						|
#ifdef INET6
 | 
						|
    struct sockaddr_storage sockaddr;
 | 
						|
    int sockaddrlen, port;
 | 
						|
    unsigned char d1, d2, d3, d4;
 | 
						|
    char abuf[INET6_ADDRSTRLEN];
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef INET6
 | 
						|
    sockaddrlen = sizeof(sockaddr);
 | 
						|
    if (getpeername(fileno(ftp->wcontrol),
 | 
						|
		    (struct sockaddr *)&sockaddr, &sockaddrlen) < 0)
 | 
						|
	return -1;
 | 
						|
    family = sockaddr.ss_family;
 | 
						|
#else
 | 
						|
    family = AF_INET;
 | 
						|
#endif
 | 
						|
    switch (family) {
 | 
						|
#ifdef INET6
 | 
						|
    case AF_INET6:
 | 
						|
	fwrite("EPSV\r\n", 6, sizeof(char), ftp->wcontrol);
 | 
						|
	fflush(ftp->wcontrol);
 | 
						|
	tmp = read_response(ftp);
 | 
						|
	if (atoi(tmp->ptr) != 229)
 | 
						|
	    return -1;
 | 
						|
	for (p = tmp->ptr + 4; *p && *p != '('; p++) ;
 | 
						|
	if (*p == '\0')
 | 
						|
	    return -1;
 | 
						|
	if (sscanf(++p, "%c%c%c%d%c", &d1, &d2, &d3, &port, &d4) != 5
 | 
						|
	    || d1 != d2 || d1 != d3 || d1 != d4)
 | 
						|
	    return -1;
 | 
						|
	if (getnameinfo((struct sockaddr *)&sockaddr, sockaddrlen,
 | 
						|
			abuf, sizeof(abuf), NULL, 0, NI_NUMERICHOST) != 0)
 | 
						|
	    return -1;
 | 
						|
	tmp = Sprintf("%s", abuf);
 | 
						|
	data_s = openSocket(tmp->ptr, "", port);
 | 
						|
	break;
 | 
						|
#endif
 | 
						|
    case AF_INET:
 | 
						|
	fwrite("PASV\r\n", 6, sizeof(char), ftp->wcontrol);
 | 
						|
	fflush(ftp->wcontrol);
 | 
						|
	tmp = read_response(ftp);
 | 
						|
	if (atoi(tmp->ptr) != 227)
 | 
						|
	    return -1;
 | 
						|
	for (p = tmp->ptr + 4; *p && !IS_DIGIT(*p); p++) ;
 | 
						|
	if (*p == '\0')
 | 
						|
	    return -1;
 | 
						|
	sscanf(p, "%d,%d,%d,%d,%d,%d", &n1, &n2, &n3, &n4, &p1, &p2);
 | 
						|
	tmp = Sprintf("%d.%d.%d.%d", n1, n2, n3, n4);
 | 
						|
	data_s = openSocket(tmp->ptr, "", p1 * 256 + p2);
 | 
						|
	break;
 | 
						|
    default:
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    if (data_s < 0)
 | 
						|
	return -1;
 | 
						|
    ftp->data = fdopen(data_s, "rb");
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ftp_fclose(FTP ftp)
 | 
						|
{
 | 
						|
    int control_closed = 0;
 | 
						|
 | 
						|
    if (ftp->rcontrol != NULL) {
 | 
						|
	fclose(ftp->rcontrol);
 | 
						|
	ftp->rcontrol = NULL;
 | 
						|
	control_closed = 1;
 | 
						|
    }
 | 
						|
    if (ftp->wcontrol != NULL) {
 | 
						|
	fclose(ftp->wcontrol);
 | 
						|
	ftp->wcontrol = NULL;
 | 
						|
	control_closed = 1;
 | 
						|
    }
 | 
						|
    if (control_closed && ftp->data != NULL) {
 | 
						|
	fclose(ftp->data);
 | 
						|
	ftp->data = NULL;
 | 
						|
    }
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpCwd(FTP ftp, char *path)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    tmp = Sprintf("CWD %s\r\n", path);
 | 
						|
    fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (tmp->ptr[0] == '5') {
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpOpenReadBody(FTP ftp, char *path)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    tmp = Sprintf("RETR %s\r\n", path);
 | 
						|
    fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (tmp->ptr[0] == '5') {
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpOpenRead(FTP ftp, char *path)
 | 
						|
{
 | 
						|
    if (ftp_pasv(ftp) < 0)
 | 
						|
	return -1;
 | 
						|
    if (FtpOpenReadBody(ftp, path) < 0) {
 | 
						|
	fclose(ftp->data);
 | 
						|
	ftp->data = NULL;
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
Ftpfclose(FILE * f)
 | 
						|
{
 | 
						|
    fclose(f);
 | 
						|
    if (f == current_ftp->data)
 | 
						|
	current_ftp->data = NULL;
 | 
						|
    ftp_fclose(current_ftp);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpDataBody(FTP ftp, char *cmd, char *arg, char *mode)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    tmp = Sprintf(cmd, arg);
 | 
						|
    Strcat_charp(tmp, "\r\n");
 | 
						|
    fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (tmp->ptr[0] == '5') {
 | 
						|
	fclose(ftp->data);
 | 
						|
	ftp->data = NULL;
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpData(FTP ftp, char *cmd, char *arg, char *mode)
 | 
						|
{
 | 
						|
    if (ftp_pasv(ftp) < 0)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    return FtpDataBody(ftp, cmd, arg, mode);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpClose(FTP ftp)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    fclose(ftp->data);
 | 
						|
    ftp->data = NULL;
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (atoi(tmp->ptr) != 226)
 | 
						|
	return -1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
FtpBye(FTP ftp)
 | 
						|
{
 | 
						|
    Str tmp;
 | 
						|
    int ret_val;
 | 
						|
 | 
						|
    fwrite("QUIT\r\n", 6, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (atoi(tmp->ptr) != 221)
 | 
						|
	ret_val = -1;
 | 
						|
    else
 | 
						|
	ret_val = 0;
 | 
						|
    ftp_fclose(ftp);
 | 
						|
    return ret_val;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int ex_ftpdir_name_size_date(char *, char **, char **, char **);
 | 
						|
static int ftp_system(FTP);
 | 
						|
 | 
						|
#define	SERVER_NONE	0
 | 
						|
#define	UNIXLIKE_SERVER	1
 | 
						|
 | 
						|
#define	FTPDIR_NONE	0
 | 
						|
#define	FTPDIR_DIR	1
 | 
						|
#define	FTPDIR_LINK	2
 | 
						|
#define	FTPDIR_FILE	3
 | 
						|
 | 
						|
FILE *
 | 
						|
openFTP(ParsedURL *pu)
 | 
						|
{
 | 
						|
    Str tmp2 = Strnew();
 | 
						|
    Str tmp3 = Strnew();
 | 
						|
    STATUS s;
 | 
						|
    char *user = NULL;
 | 
						|
    char *pass = NULL;
 | 
						|
    Str pwd = NULL;
 | 
						|
    int add_auth_cookie_flag;
 | 
						|
    char *realpathname = NULL;
 | 
						|
#ifdef JP_CHARSET
 | 
						|
    char code = '\0', ic;
 | 
						|
    Str pathStr;
 | 
						|
#endif
 | 
						|
 | 
						|
    add_auth_cookie_flag = 0;
 | 
						|
    if (pu->user == NULL && pu->pass == NULL) {
 | 
						|
	Str uname, pwd;
 | 
						|
	if (find_auth_user_passwd(pu, NULL, &uname, &pwd, 0)) {
 | 
						|
	    if (uname)
 | 
						|
		user = uname->ptr;
 | 
						|
	    if (pwd)
 | 
						|
		pass = pwd->ptr;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    if (user)
 | 
						|
	/* do nothing */ ;
 | 
						|
    else if (pu->user)
 | 
						|
	user = pu->user;
 | 
						|
    else {
 | 
						|
	Strcat_charp(tmp3, "anonymous");
 | 
						|
	user = tmp3->ptr;
 | 
						|
    }
 | 
						|
    if (pass)
 | 
						|
	/* do nothing */ ;
 | 
						|
    else if (pu->pass)
 | 
						|
	pass = pu->pass;
 | 
						|
    else if (pu->user) {
 | 
						|
	pwd = find_auth_cookie(pu->host, pu->port, pu->file, pu->user);
 | 
						|
	if (pwd == NULL) {
 | 
						|
	    if (fmInitialized) {
 | 
						|
		term_raw();
 | 
						|
		pwd = Strnew_charp(inputLine("Password: ", NULL, IN_PASSWORD));
 | 
						|
		pwd = Str_conv_to_system(pwd);
 | 
						|
		term_cbreak();
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		pwd = Strnew_charp((char *)getpass("Password: "));
 | 
						|
	    }
 | 
						|
	    add_auth_cookie_flag = 1;
 | 
						|
	}
 | 
						|
	pass = pwd->ptr;
 | 
						|
    }
 | 
						|
    else if (ftppasswd != NULL && *ftppasswd != '\0')
 | 
						|
	pass = ftppasswd;
 | 
						|
    else {
 | 
						|
	struct passwd *mypw = getpwuid(getuid());
 | 
						|
	if (mypw == NULL)
 | 
						|
	    Strcat_charp(tmp2, "anonymous");
 | 
						|
	else
 | 
						|
	    Strcat_charp(tmp2, mypw->pw_name);
 | 
						|
	Strcat_char(tmp2, '@');
 | 
						|
	pass = tmp2->ptr;
 | 
						|
    }
 | 
						|
    s = FtpLogin(¤t_ftp, pu->host, user, pass);
 | 
						|
    if (FtpError(s))
 | 
						|
	return NULL;
 | 
						|
    if (add_auth_cookie_flag)
 | 
						|
	add_auth_cookie(pu->host, pu->port, pu->file, pu->user, pwd);
 | 
						|
    if (pu->file == NULL || *pu->file == '\0')
 | 
						|
	goto ftp_dir;
 | 
						|
    else
 | 
						|
	realpathname = file_unquote(pu->file);
 | 
						|
 | 
						|
    if (pu->file[strlen(pu->file) - 1] == '/')
 | 
						|
	goto ftp_dir;
 | 
						|
 | 
						|
    /* Get file */
 | 
						|
    FtpBinary(current_ftp);
 | 
						|
    if (ftp_pasv(current_ftp) < 0) {
 | 
						|
	FtpBye(current_ftp);
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
    s = FtpOpenReadBody(current_ftp, realpathname);
 | 
						|
    if (!FtpError(s)) {
 | 
						|
#ifdef JP_CHARSET
 | 
						|
	pathStr = Strnew_charp(realpathname);
 | 
						|
	if ((ic = checkShiftCode(pathStr, code)) != '\0') {
 | 
						|
	    pathStr = conv_str(pathStr, (code = ic), InnerCode);
 | 
						|
	    realpathname = pathStr->ptr;
 | 
						|
	}
 | 
						|
#endif				/* JP_CHARSET */
 | 
						|
	pu->file = realpathname;
 | 
						|
	return FTPDATA(current_ftp);
 | 
						|
    }
 | 
						|
    goto ftp_dir1;
 | 
						|
 | 
						|
    /* Get directory */
 | 
						|
  ftp_dir:
 | 
						|
    if (ftp_pasv(current_ftp) < 0) {
 | 
						|
	FtpBye(current_ftp);
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
  ftp_dir1:
 | 
						|
    pu->scheme = SCM_FTPDIR;
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
Str
 | 
						|
readFTPDir(ParsedURL *pu)
 | 
						|
{
 | 
						|
    Str FTPDIRtmp = Strnew();
 | 
						|
    Str host;
 | 
						|
    Str curdir;
 | 
						|
    char *fn;
 | 
						|
    char *qdir;
 | 
						|
    char **flist;
 | 
						|
    int i, nfile, nfile_max = 100;
 | 
						|
    int sv_type;
 | 
						|
    STATUS s;
 | 
						|
    char *realpathname = NULL;
 | 
						|
    Str tmp2 = Strnew();
 | 
						|
 | 
						|
    if (current_ftp->data == NULL)
 | 
						|
	return FTPDIRtmp;
 | 
						|
    sv_type = ftp_system(current_ftp);
 | 
						|
    if (pu->file == NULL || *pu->file == '\0') {
 | 
						|
	if (sv_type == UNIXLIKE_SERVER) {
 | 
						|
	    s = FtpDataBody(current_ftp, "LIST", NULL, "r");
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    s = FtpDataBody(current_ftp, "NLST", NULL, "r");
 | 
						|
	}
 | 
						|
	curdir = Strnew_charp("/");
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	realpathname = file_unquote(pu->file);
 | 
						|
	if (sv_type == UNIXLIKE_SERVER) {
 | 
						|
	    s = FtpCwd(current_ftp, realpathname);
 | 
						|
	    if (!FtpError(s)) {
 | 
						|
		s = FtpDataBody(current_ftp, "LIST", NULL, "r");
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    s = FtpDataBody(current_ftp, "NLST %s", realpathname, "r");
 | 
						|
	}
 | 
						|
	if (realpathname[0] == '/')
 | 
						|
	    curdir = Strnew_charp(realpathname);
 | 
						|
	else
 | 
						|
	    curdir = Sprintf("/%s", realpathname);
 | 
						|
	if (Strlastchar(curdir) != '/')
 | 
						|
	    Strcat_char(curdir, '/');
 | 
						|
    }
 | 
						|
    if (FtpError(s)) {
 | 
						|
	FtpBye(current_ftp);
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
    host = Strnew_charp("ftp://");
 | 
						|
    if (pu->user) {
 | 
						|
	Strcat_m_charp(host, pu->user, "@", NULL);
 | 
						|
    }
 | 
						|
    Strcat_charp(host, pu->host);
 | 
						|
    if (Strlastchar(host) == '/')
 | 
						|
	Strshrink(host, 1);
 | 
						|
    qdir = html_quote(curdir->ptr);
 | 
						|
    FTPDIRtmp =
 | 
						|
	Sprintf
 | 
						|
	("<html><head><title>%s%s</title></head><body><h1>Index of %s%s</h1>\n",
 | 
						|
	 host->ptr, qdir, host->ptr, qdir);
 | 
						|
    curdir = Strnew_charp(file_quote(curdir->ptr));
 | 
						|
    qdir = curdir->ptr;
 | 
						|
    tmp2 = Strdup(curdir);
 | 
						|
    if (Strcmp_charp(curdir, "/") != 0) {
 | 
						|
	Strshrink(tmp2, 1);
 | 
						|
	while (Strlastchar(tmp2) != '/' && tmp2->length > 0)
 | 
						|
	    Strshrink(tmp2, 1);
 | 
						|
    }
 | 
						|
    if (sv_type == UNIXLIKE_SERVER) {
 | 
						|
	Strcat_charp(FTPDIRtmp, "<pre><a href=\"");
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	Strcat_charp(FTPDIRtmp, "<ul><li><a href=\"");
 | 
						|
    }
 | 
						|
    Strcat_m_charp(FTPDIRtmp, host->ptr,
 | 
						|
		   html_quote(tmp2->ptr), "\">[Upper Directory]</a>\n", NULL);
 | 
						|
 | 
						|
    flist = New_N(char *, nfile_max);
 | 
						|
    nfile = 0;
 | 
						|
    if (sv_type == UNIXLIKE_SERVER) {
 | 
						|
	char *name, *date, *size, *type_str;
 | 
						|
	int ftype, max_len, len, j;
 | 
						|
	Str line_tmp;
 | 
						|
 | 
						|
	max_len = 0;
 | 
						|
	while (tmp2 = Strfgets(FTPDATA(current_ftp)), tmp2->length > 0) {
 | 
						|
	    Strchop(tmp2);
 | 
						|
	    if ((ftype =
 | 
						|
		 ex_ftpdir_name_size_date(tmp2->ptr, &name, &date, &size))
 | 
						|
		== FTPDIR_NONE) {
 | 
						|
		continue;
 | 
						|
	    }
 | 
						|
	    if (!strcmp(".", name) || !strcmp("..", name)) {
 | 
						|
		continue;
 | 
						|
	    }
 | 
						|
	    len = strlen(name);
 | 
						|
	    if (!len)
 | 
						|
		continue;
 | 
						|
	    if (ftype == FTPDIR_DIR) {
 | 
						|
		len++;
 | 
						|
		type_str = "/";
 | 
						|
	    }
 | 
						|
	    else if (ftype == FTPDIR_LINK) {
 | 
						|
		len++;
 | 
						|
		type_str = "@";
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		type_str = "";
 | 
						|
	    }
 | 
						|
	    if (max_len < len)
 | 
						|
		max_len = len;
 | 
						|
	    line_tmp =
 | 
						|
		Sprintf("%s%s %-12.12s %6.6s", name, type_str, date, size);
 | 
						|
	    flist[nfile++] = line_tmp->ptr;
 | 
						|
	    if (nfile == nfile_max) {
 | 
						|
		nfile_max *= 2;
 | 
						|
		flist = New_Reuse(char *, flist, nfile_max);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	qsort(flist, nfile, sizeof(char *), strCmp);
 | 
						|
	for (j = 0; j < nfile; j++) {
 | 
						|
	    fn = flist[j];
 | 
						|
	    date = fn + strlen(fn) - 20;
 | 
						|
	    if (*(date - 1) == '/') {
 | 
						|
		ftype = FTPDIR_DIR;
 | 
						|
		*(date - 1) = '\0';
 | 
						|
	    }
 | 
						|
	    else if (*(date - 1) == '@') {
 | 
						|
		ftype = FTPDIR_LINK;
 | 
						|
		*(date - 1) = '\0';
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		ftype = FTPDIR_FILE;
 | 
						|
		*date = '\0';
 | 
						|
	    }
 | 
						|
	    date++;
 | 
						|
	    len = strlen(fn);
 | 
						|
	    Strcat_m_charp(FTPDIRtmp, "<a href=\"",
 | 
						|
			   host->ptr,
 | 
						|
			   qdir,
 | 
						|
			   html_quote(file_quote(fn)),
 | 
						|
			   "\">", html_quote(fn), NULL);
 | 
						|
	    if (ftype == FTPDIR_DIR) {
 | 
						|
		Strcat_charp(FTPDIRtmp, "/");
 | 
						|
		len++;
 | 
						|
	    }
 | 
						|
	    else if (ftype == FTPDIR_LINK) {
 | 
						|
		Strcat_charp(FTPDIRtmp, "@");
 | 
						|
		len++;
 | 
						|
	    }
 | 
						|
	    Strcat_charp(FTPDIRtmp, "</a>");
 | 
						|
	    for (i = len; i <= max_len; i++) {
 | 
						|
		if ((max_len % 2 + i) % 2) {
 | 
						|
		    Strcat_charp(FTPDIRtmp, ".");
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    Strcat_charp(FTPDIRtmp, " ");
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    Strcat_m_charp(FTPDIRtmp, date, "\n", NULL);
 | 
						|
	}
 | 
						|
	Strcat_charp(FTPDIRtmp, "</pre></body></html>\n");
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	while (tmp2 = Strfgets(FTPDATA(current_ftp)), tmp2->length > 0) {
 | 
						|
	    Strchop(tmp2);
 | 
						|
	    flist[nfile++] = mybasename(tmp2->ptr);
 | 
						|
	    if (nfile == nfile_max) {
 | 
						|
		nfile_max *= 2;
 | 
						|
		flist = New_Reuse(char *, flist, nfile_max);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	qsort(flist, nfile, sizeof(char *), strCmp);
 | 
						|
	for (i = 0; i < nfile; i++) {
 | 
						|
	    fn = flist[i];
 | 
						|
	    Strcat_m_charp(FTPDIRtmp, "<li><a href=\"",
 | 
						|
			   host->ptr, qdir,
 | 
						|
			   html_quote(file_quote(fn)),
 | 
						|
			   "\">", html_quote(fn), "</a>\n", NULL);
 | 
						|
	}
 | 
						|
	Strcat_charp(FTPDIRtmp, "</ul></body></html>\n");
 | 
						|
    }
 | 
						|
 | 
						|
    FtpClose(current_ftp);
 | 
						|
    FtpBye(current_ftp);
 | 
						|
    return FTPDIRtmp;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ftp_system(FTP ftp)
 | 
						|
{
 | 
						|
    int sv_type = SERVER_NONE;
 | 
						|
    Str tmp;
 | 
						|
 | 
						|
    fwrite("SYST\r\n", 6, sizeof(char), ftp->wcontrol);
 | 
						|
    fflush(ftp->wcontrol);
 | 
						|
    tmp = read_response(ftp);
 | 
						|
    if (strstr(tmp->ptr, "UNIX") != NULL || !strncmp(tmp->ptr + 4, "Windows_NT", 10)) {	/* :-) */
 | 
						|
	sv_type = UNIXLIKE_SERVER;
 | 
						|
    }
 | 
						|
 | 
						|
    return (sv_type);
 | 
						|
}
 | 
						|
 | 
						|
#define XD_CTOD(c) {\
 | 
						|
  if (c >= '0' && c <= '9') {\
 | 
						|
    c -= (unsigned char)'0';\
 | 
						|
  } else if (c >= 'a' && c <= 'f') {\
 | 
						|
    c = c - (unsigned char)'a' + (unsigned char)10;\
 | 
						|
  } else if (c >= 'A' && c <= 'F') {\
 | 
						|
    c = c - (unsigned char)'A' + (unsigned char)10;\
 | 
						|
  } else {\
 | 
						|
    goto skip;\
 | 
						|
  }\
 | 
						|
}
 | 
						|
 | 
						|
#define EX_SKIP_SPACE(cp) {\
 | 
						|
  while (IS_SPACE(*cp) && *cp != '\0') cp++;\
 | 
						|
  if (*cp == '\0') {\
 | 
						|
    goto done;\
 | 
						|
  }\
 | 
						|
}
 | 
						|
#define EX_SKIP_NONE_SPACE(cp) {\
 | 
						|
  while (!IS_SPACE(*cp) && *cp != '\0') cp++;\
 | 
						|
  if (*cp == '\0') {\
 | 
						|
    goto done;\
 | 
						|
  }\
 | 
						|
}
 | 
						|
 | 
						|
static Str size_int2str(clen_t);
 | 
						|
 | 
						|
static int
 | 
						|
ex_ftpdir_name_size_date(char *line, char **name, char **date, char **sizep)
 | 
						|
{
 | 
						|
    int ftype = FTPDIR_NONE;
 | 
						|
    char *cp, *endp;
 | 
						|
    Str date_str, name_str, size_str;
 | 
						|
    clen_t size;
 | 
						|
 | 
						|
    if (strlen(line) < 11) {
 | 
						|
	goto done;
 | 
						|
    }
 | 
						|
    /* skip permission */
 | 
						|
    if (!IS_SPACE(line[10])) {
 | 
						|
	goto done;
 | 
						|
    }
 | 
						|
    cp = line + 11;
 | 
						|
 | 
						|
    /* skip link count */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	while (IS_DIGIT(*cp) && *cp != '\0')
 | 
						|
	cp++;
 | 
						|
    if (!IS_SPACE(*cp) || *cp == '\0') {
 | 
						|
	goto done;
 | 
						|
    }
 | 
						|
    cp++;
 | 
						|
 | 
						|
    /* skip owner string */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	EX_SKIP_NONE_SPACE(cp)
 | 
						|
	cp++;
 | 
						|
 | 
						|
    /* skip group string */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	EX_SKIP_NONE_SPACE(cp)
 | 
						|
	cp++;
 | 
						|
 | 
						|
    /* extract size */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	size = 0;
 | 
						|
    while (*cp && IS_DIGIT(*cp)) {
 | 
						|
	size = size * 10 + *(cp++) - '0';
 | 
						|
    }
 | 
						|
    if (*cp == '\0') {
 | 
						|
	goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    /* extract date */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	if (IS_ALPHA(cp[0]) && IS_ALPHA(cp[1]) && IS_ALPHA(cp[2])
 | 
						|
	    && IS_SPACE(cp[3])
 | 
						|
	    && (IS_SPACE(cp[4]) || IS_DIGIT(cp[4])) && IS_DIGIT(cp[5])
 | 
						|
	    && IS_SPACE(cp[6])
 | 
						|
	    && (IS_SPACE(cp[7]) || IS_DIGIT(cp[7])) && IS_DIGIT(cp[8])
 | 
						|
	    && (cp[9] == ':' || IS_DIGIT(cp[9]))
 | 
						|
	    && IS_DIGIT(cp[10]) && (IS_DIGIT(cp[11]) || IS_SPACE(cp[11]))
 | 
						|
	    && IS_SPACE(cp[12])) {
 | 
						|
	cp[12] = '\0';
 | 
						|
	date_str = Strnew_charp(cp);
 | 
						|
	cp += 13;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    /* extract file name */
 | 
						|
    EX_SKIP_SPACE(cp)
 | 
						|
	if (line[0] == 'l') {
 | 
						|
	if ((endp = strstr(cp, " -> ")) == NULL) {
 | 
						|
	    goto done;
 | 
						|
	}
 | 
						|
	*endp = '\0';
 | 
						|
	size_str = Strnew_charp("-");
 | 
						|
	ftype = FTPDIR_LINK;
 | 
						|
    }
 | 
						|
    else if (line[0] == 'd') {
 | 
						|
	size_str = Strnew_charp("-");
 | 
						|
	ftype = FTPDIR_DIR;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	size_str = size_int2str(size);
 | 
						|
	ftype = FTPDIR_FILE;
 | 
						|
    }
 | 
						|
    name_str = Strnew_charp(cp);
 | 
						|
    *date = date_str->ptr;
 | 
						|
    *name = name_str->ptr;
 | 
						|
    *sizep = size_str->ptr;
 | 
						|
 | 
						|
  done:
 | 
						|
    return (ftype);
 | 
						|
}
 | 
						|
 | 
						|
static Str
 | 
						|
size_int2str(clen_t size)
 | 
						|
{
 | 
						|
    Str size_str;
 | 
						|
    int unit;
 | 
						|
    double dtmp;
 | 
						|
    char *size_format, *unit_str;
 | 
						|
 | 
						|
    dtmp = (double)size;
 | 
						|
    for (unit = 0; unit < 3; unit++) {
 | 
						|
	if (dtmp < 1024) {
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	dtmp /= 1024;
 | 
						|
    }
 | 
						|
    if (!unit || dtmp > 100) {
 | 
						|
	size_format = "%.0f%s";
 | 
						|
    }
 | 
						|
    else if (dtmp > 10) {
 | 
						|
	size_format = "%.1f%s";
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	size_format = "%.2f%s";
 | 
						|
    }
 | 
						|
    switch (unit) {
 | 
						|
    case 3:
 | 
						|
	unit_str = "G";
 | 
						|
	break;
 | 
						|
    case 2:
 | 
						|
	unit_str = "M";
 | 
						|
	break;
 | 
						|
    case 1:
 | 
						|
	unit_str = "K";
 | 
						|
	break;
 | 
						|
    default:
 | 
						|
	unit_str = "";
 | 
						|
	break;
 | 
						|
    }
 | 
						|
    size_str = Sprintf(size_format, dtmp, unit_str);
 | 
						|
 | 
						|
    return (size_str);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
closeFTP(FILE * f)
 | 
						|
{
 | 
						|
    if (f) {
 | 
						|
	fclose(f);
 | 
						|
	if (f == current_ftp->data)
 | 
						|
	    current_ftp->data = NULL;
 | 
						|
    }
 | 
						|
    FtpBye(current_ftp);
 | 
						|
}
 |