849 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			849 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* $Id: ftp.c,v 1.8 2001/11/29 10:22:58 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     read_response(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, control_closed;
 | |
| 
 | |
|     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;
 | |
|     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 ret_val;
 | |
| }
 | |
| 
 | |
| 
 | |
| Str FTPDIRtmp;
 | |
| 
 | |
| 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();
 | |
|     Str host;
 | |
|     STATUS s;
 | |
|     Str curdir;
 | |
|     char *user;
 | |
|     char *pass;
 | |
|     char *fn;
 | |
|     char *qdir;
 | |
|     char **flist;
 | |
|     int i, nfile, nfile_max = 100;
 | |
|     Str pwd = NULL;
 | |
|     int add_auth_cookie_flag;
 | |
|     char *realpathname = NULL;
 | |
| #ifdef JP_CHARSET
 | |
|     char code = '\0', ic;
 | |
|     Str pathStr;
 | |
| #endif
 | |
|     int sv_type;
 | |
| 
 | |
|     add_auth_cookie_flag = 0;
 | |
|     if (pu->user)
 | |
| 	user = pu->user;
 | |
|     else {
 | |
| 	Strcat_charp(tmp3, "anonymous");
 | |
| 	user = tmp3->ptr;
 | |
|     }
 | |
|     if (pu->pass)
 | |
| 	pass = pu->pass;
 | |
|     else if (pu->user) {
 | |
| 	pwd = find_auth_cookie(pu->host, 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->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;
 | |
|     FTPDIRtmp = Strnew();
 | |
|     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 {
 | |
| 	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 NULL;
 | |
| }
 | |
| 
 | |
| 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(unsigned long);
 | |
| 
 | |
| 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;
 | |
|     unsigned long 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(unsigned long 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);
 | |
| }
 |