801 lines
18 KiB
C
801 lines
18 KiB
C
/* $Id: ftp.c,v 1.42 2010/12/15 10:50:24 htrb Exp $ */
|
|
#include <stdio.h>
|
|
#ifndef __MINGW32_VERSION
|
|
#include <pwd.h>
|
|
#endif /* __MINGW32_VERSION */
|
|
#include <Str.h>
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
#include <time.h>
|
|
|
|
#include "fm.h"
|
|
#include "html.h"
|
|
#include "myctype.h"
|
|
|
|
#ifdef DEBUG
|
|
#include <malloc.h>
|
|
#endif /* DEBUG */
|
|
|
|
#ifndef __MINGW32_VERSION
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#else
|
|
#include <winsock.h>
|
|
#endif /* __MINGW32_VERSION */
|
|
|
|
#ifndef HAVE_SOCKLEN_T
|
|
typedef int socklen_t;
|
|
#endif
|
|
|
|
typedef struct _FTP {
|
|
char *host;
|
|
int port;
|
|
char *user;
|
|
char *pass;
|
|
InputStream rf;
|
|
FILE *wf;
|
|
FILE *data;
|
|
} *FTP;
|
|
|
|
static struct _FTP current_ftp = {
|
|
NULL, 0, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
static JMP_BUF AbortLoading;
|
|
|
|
static MySignalHandler
|
|
KeyAbort(SIGNAL_ARG)
|
|
{
|
|
LONGJMP(AbortLoading, 1);
|
|
SIGNAL_RETURN;
|
|
}
|
|
|
|
static Str
|
|
ftp_command(FTP ftp, char *cmd, char *arg, int *status)
|
|
{
|
|
Str tmp;
|
|
|
|
if (!ftp->host)
|
|
return NULL;
|
|
if (cmd) {
|
|
if (arg)
|
|
tmp = Sprintf("%s %s\r\n", cmd, arg);
|
|
else
|
|
tmp = Sprintf("%s\r\n", cmd);
|
|
fwrite(tmp->ptr, sizeof(char), tmp->length, ftp->wf);
|
|
fflush(ftp->wf);
|
|
}
|
|
if (!status)
|
|
return NULL;
|
|
*status = -1; /* error */
|
|
tmp = StrISgets(ftp->rf);
|
|
if (IS_DIGIT(tmp->ptr[0]) && IS_DIGIT(tmp->ptr[1]) &&
|
|
IS_DIGIT(tmp->ptr[2]) && tmp->ptr[3] == ' ')
|
|
sscanf(tmp->ptr, "%d", status);
|
|
|
|
if (tmp->ptr[3] != '-')
|
|
return tmp;
|
|
/* 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 = StrISgets(ftp->rf);
|
|
if (IS_DIGIT(tmp->ptr[0]) && IS_DIGIT(tmp->ptr[1]) &&
|
|
IS_DIGIT(tmp->ptr[2]) && tmp->ptr[3] == ' ') {
|
|
sscanf(tmp->ptr, "%d", status);
|
|
break;
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
static void
|
|
ftp_close(FTP ftp)
|
|
{
|
|
if (!ftp->host)
|
|
return;
|
|
if (ftp->rf) {
|
|
IStype(ftp->rf) &= ~IST_UNCLOSE;
|
|
ISclose(ftp->rf);
|
|
ftp->rf = NULL;
|
|
}
|
|
if (ftp->wf) {
|
|
fclose(ftp->wf);
|
|
ftp->wf = NULL;
|
|
}
|
|
if (ftp->data) {
|
|
fclose(ftp->data);
|
|
ftp->data = NULL;
|
|
}
|
|
ftp->host = NULL;
|
|
return;
|
|
}
|
|
|
|
static int
|
|
ftp_login(FTP ftp)
|
|
{
|
|
int sock, status;
|
|
|
|
sock = openSocket(ftp->host, "ftp", 21);
|
|
if (sock < 0)
|
|
goto open_err;
|
|
if (ftppass_hostnamegen && !strcmp(ftp->user, "anonymous")) {
|
|
size_t n = strlen(ftp->pass);
|
|
|
|
if (n > 0 && ftp->pass[n - 1] == '@') {
|
|
#ifdef INET6
|
|
struct sockaddr_storage sockname;
|
|
#else
|
|
struct sockaddr_in sockname;
|
|
#endif
|
|
socklen_t socknamelen = sizeof(sockname);
|
|
|
|
if (!getsockname(sock, (struct sockaddr *)&sockname, &socknamelen)) {
|
|
struct hostent *sockent;
|
|
Str tmp = Strnew_charp(ftp->pass);
|
|
#ifdef INET6
|
|
char hostbuf[NI_MAXHOST];
|
|
|
|
if (getnameinfo((struct sockaddr *)&sockname, socknamelen,
|
|
hostbuf, sizeof hostbuf, NULL, 0, NI_NAMEREQD)
|
|
== 0)
|
|
Strcat_charp(tmp, hostbuf);
|
|
else if (getnameinfo((struct sockaddr *)&sockname, socknamelen,
|
|
hostbuf, sizeof hostbuf, NULL, 0, NI_NUMERICHOST)
|
|
== 0)
|
|
Strcat_m_charp(tmp, "[", hostbuf, "]", NULL);
|
|
else
|
|
Strcat_charp(tmp, "unknown");
|
|
#else
|
|
|
|
if ((sockent = gethostbyaddr((char *)&sockname.sin_addr,
|
|
sizeof(sockname.sin_addr),
|
|
sockname.sin_family)))
|
|
Strcat_charp(tmp, sockent->h_name);
|
|
else
|
|
Strcat_m_charp(tmp, "[", inet_ntoa(sockname.sin_addr),
|
|
"]", NULL);
|
|
#endif
|
|
ftp->pass = tmp->ptr;
|
|
}
|
|
}
|
|
}
|
|
ftp->rf = newInputStream(sock);
|
|
ftp->wf = fdopen(dup(sock), "wb");
|
|
if (!ftp->rf || !ftp->wf)
|
|
goto open_err;
|
|
IStype(ftp->rf) |= IST_UNCLOSE;
|
|
ftp_command(ftp, NULL, NULL, &status);
|
|
if (status != 220)
|
|
goto open_err;
|
|
if (fmInitialized) {
|
|
message(Sprintf("Sending FTP username (%s) to remote server.",
|
|
ftp->user)->ptr, 0, 0);
|
|
refresh();
|
|
}
|
|
ftp_command(ftp, "USER", ftp->user, &status);
|
|
/*
|
|
* Some ftp daemons(e.g. publicfile) return code 230 for user command.
|
|
*/
|
|
if (status == 230)
|
|
goto succeed;
|
|
if (status != 331)
|
|
goto open_err;
|
|
if (fmInitialized) {
|
|
message("Sending FTP password to remote server.", 0, 0);
|
|
refresh();
|
|
}
|
|
ftp_command(ftp, "PASS", ftp->pass, &status);
|
|
if (status != 230)
|
|
goto open_err;
|
|
succeed:
|
|
return TRUE;
|
|
open_err:
|
|
ftp_close(ftp);
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
ftp_pasv(FTP ftp)
|
|
{
|
|
int status;
|
|
int n1, n2, n3, n4, p1, p2;
|
|
int data;
|
|
char *p;
|
|
Str tmp;
|
|
int family;
|
|
#ifdef INET6
|
|
struct sockaddr_storage sockaddr;
|
|
int port;
|
|
socklen_t sockaddrlen;
|
|
unsigned char d1, d2, d3, d4;
|
|
char abuf[INET6_ADDRSTRLEN];
|
|
#endif
|
|
|
|
#ifdef INET6
|
|
sockaddrlen = sizeof(sockaddr);
|
|
if (getpeername(fileno(ftp->wf),
|
|
(struct sockaddr *)&sockaddr, &sockaddrlen) < 0)
|
|
return -1;
|
|
#ifdef HAVE_OLD_SS_FAMILY
|
|
family = sockaddr.__ss_family;
|
|
#else
|
|
family = sockaddr.ss_family;
|
|
#endif
|
|
#else
|
|
family = AF_INET;
|
|
#endif
|
|
switch (family) {
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
tmp = ftp_command(ftp, "EPSV", NULL, &status);
|
|
if (status != 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;
|
|
data = openSocket(abuf, "", port);
|
|
break;
|
|
#endif
|
|
case AF_INET:
|
|
tmp = ftp_command(ftp, "PASV", NULL, &status);
|
|
if (status != 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 = openSocket(tmp->ptr, "", p1 * 256 + p2);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
if (data < 0)
|
|
return -1;
|
|
ftp->data = fdopen(data, "rb");
|
|
return 0;
|
|
}
|
|
|
|
static time_t
|
|
ftp_modtime(FTP ftp, char *path)
|
|
{
|
|
int status;
|
|
Str tmp;
|
|
char *p;
|
|
struct tm tm;
|
|
time_t t, lt, gt;
|
|
|
|
tmp = ftp_command(ftp, "MDTM", path, &status);
|
|
if (status != 213)
|
|
return -1;
|
|
for (p = tmp->ptr + 4; *p && *p == ' '; p++) ;
|
|
memset(&tm, 0, sizeof(struct tm));
|
|
if (sscanf(p, "%04d%02d%02d%02d%02d%02d",
|
|
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
|
&tm.tm_hour, &tm.tm_min, &tm.tm_sec) < 6)
|
|
return -1;
|
|
tm.tm_year -= 1900;
|
|
tm.tm_mon--;
|
|
t = mktime(&tm);
|
|
lt = mktime(localtime(&t));
|
|
gt = mktime(gmtime(&t));
|
|
return t + (lt - gt);
|
|
}
|
|
|
|
static int
|
|
ftp_quit(FTP ftp)
|
|
{
|
|
/*
|
|
* int status;
|
|
* ftp_command(ftp, "QUIT", NULL, &status);
|
|
* ftp_close(ftp);
|
|
* if (status != 221)
|
|
* return -1;
|
|
*/
|
|
ftp_command(ftp, "QUIT", NULL, NULL);
|
|
ftp_close(ftp);
|
|
return 0;
|
|
}
|
|
|
|
static int ex_ftpdir_name_size_date(char *, char **, char **, char **,
|
|
char **);
|
|
|
|
#define SERVER_NONE 0
|
|
#define UNIXLIKE_SERVER 1
|
|
|
|
#define FTPDIR_NONE 0
|
|
#define FTPDIR_DIR 1
|
|
#define FTPDIR_LINK 2
|
|
#define FTPDIR_FILE 3
|
|
|
|
static void
|
|
closeFTPdata(FILE * f)
|
|
{
|
|
int status;
|
|
if (f) {
|
|
fclose(f);
|
|
if (f == current_ftp.data)
|
|
current_ftp.data = NULL;
|
|
}
|
|
ftp_command(¤t_ftp, NULL, NULL, &status);
|
|
/* status == 226 */
|
|
}
|
|
|
|
void
|
|
closeFTP(void)
|
|
{
|
|
ftp_close(¤t_ftp);
|
|
}
|
|
|
|
InputStream
|
|
openFTPStream(ParsedURL *pu, URLFile *uf)
|
|
{
|
|
Str tmp;
|
|
int status;
|
|
char *user = NULL;
|
|
char *pass = NULL;
|
|
Str uname = NULL;
|
|
Str pwd = NULL;
|
|
int add_auth_cookie_flag = FALSE;
|
|
char *realpathname = NULL;
|
|
|
|
if (!pu->host)
|
|
return NULL;
|
|
|
|
if (pu->user == NULL && pu->pass == NULL) {
|
|
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
|
|
user = "anonymous";
|
|
|
|
if (current_ftp.host) {
|
|
if (!strcmp(current_ftp.host, pu->host) &&
|
|
current_ftp.port == pu->port && !strcmp(current_ftp.user, user)) {
|
|
ftp_command(¤t_ftp, "NOOP", NULL, &status);
|
|
if (status != 200)
|
|
ftp_close(¤t_ftp);
|
|
else
|
|
goto ftp_read;
|
|
}
|
|
else
|
|
ftp_quit(¤t_ftp);
|
|
}
|
|
|
|
if (pass)
|
|
/* do nothing */ ;
|
|
else if (pu->pass)
|
|
pass = pu->pass;
|
|
else if (pu->user) {
|
|
pwd = NULL;
|
|
find_auth_user_passwd(pu, NULL, &uname, &pwd, 0);
|
|
if (pwd == NULL) {
|
|
if (fmInitialized) {
|
|
term_raw();
|
|
pwd = Strnew_charp(inputLine("Password: ", NULL, IN_PASSWORD));
|
|
pwd = Str_conv_to_system(pwd);
|
|
term_cbreak();
|
|
}
|
|
else {
|
|
#ifndef __MINGW32_VERSION
|
|
pwd = Strnew_charp((char *)getpass("Password: "));
|
|
#else
|
|
term_raw();
|
|
pwd = Strnew_charp(inputLine("Password: ", NULL, IN_PASSWORD));
|
|
pwd = Str_conv_to_system(pwd);
|
|
term_cbreak();
|
|
#endif /* __MINGW32_VERSION */
|
|
}
|
|
add_auth_cookie_flag = TRUE;
|
|
}
|
|
pass = pwd->ptr;
|
|
}
|
|
else if (ftppasswd != NULL && *ftppasswd != '\0')
|
|
pass = ftppasswd;
|
|
else {
|
|
#ifndef __MINGW32_VERSION
|
|
struct passwd *mypw = getpwuid(getuid());
|
|
tmp = Strnew_charp(mypw ? mypw->pw_name : "anonymous");
|
|
#else
|
|
tmp = Strnew_charp("anonymous");
|
|
#endif /* __MINGW32_VERSION */
|
|
Strcat_char(tmp, '@');
|
|
pass = tmp->ptr;
|
|
}
|
|
|
|
if (!current_ftp.host) {
|
|
current_ftp.host = allocStr(pu->host, -1);
|
|
current_ftp.port = pu->port;
|
|
current_ftp.user = allocStr(user, -1);
|
|
current_ftp.pass = allocStr(pass, -1);
|
|
if (!ftp_login(¤t_ftp))
|
|
return NULL;
|
|
}
|
|
if (add_auth_cookie_flag)
|
|
add_auth_user_passwd(pu, NULL, uname, pwd, 0);
|
|
|
|
ftp_read:
|
|
ftp_command(¤t_ftp, "TYPE", "I", &status);
|
|
if (ftp_pasv(¤t_ftp) < 0) {
|
|
ftp_quit(¤t_ftp);
|
|
return NULL;
|
|
}
|
|
if (pu->file == NULL || *pu->file == '\0' ||
|
|
pu->file[strlen(pu->file) - 1] == '/')
|
|
goto ftp_dir;
|
|
|
|
realpathname = file_unquote(pu->file);
|
|
if (*realpathname == '/' && *(realpathname + 1) == '~')
|
|
realpathname++;
|
|
/* Get file */
|
|
uf->modtime = ftp_modtime(¤t_ftp, realpathname);
|
|
ftp_command(¤t_ftp, "RETR", realpathname, &status);
|
|
if (status == 125 || status == 150)
|
|
return newFileStream(current_ftp.data, (void (*)())closeFTPdata);
|
|
|
|
ftp_dir:
|
|
pu->scheme = SCM_FTPDIR;
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef USE_M17N
|
|
Str
|
|
loadFTPDir(ParsedURL *pu, wc_ces * charset)
|
|
#else
|
|
Str
|
|
loadFTPDir0(ParsedURL *pu)
|
|
#endif
|
|
{
|
|
Str FTPDIRtmp;
|
|
Str tmp;
|
|
int status;
|
|
volatile int sv_type;
|
|
char *realpathname, *fn, *q;
|
|
char **flist;
|
|
int i, nfile, nfile_max;
|
|
MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
|
|
#ifdef USE_M17N
|
|
wc_ces doc_charset = DocumentCharset;
|
|
|
|
*charset = WC_CES_US_ASCII;
|
|
#endif
|
|
if (current_ftp.data == NULL)
|
|
return NULL;
|
|
tmp = ftp_command(¤t_ftp, "SYST", NULL, &status);
|
|
if (strstr(tmp->ptr, "UNIX") != NULL || !strncmp(tmp->ptr + 4, "Windows_NT", 10)) /* :-) */
|
|
sv_type = UNIXLIKE_SERVER;
|
|
else
|
|
sv_type = SERVER_NONE;
|
|
if (pu->file == NULL || *pu->file == '\0') {
|
|
if (sv_type == UNIXLIKE_SERVER)
|
|
ftp_command(¤t_ftp, "LIST", NULL, &status);
|
|
else
|
|
ftp_command(¤t_ftp, "NLST", NULL, &status);
|
|
pu->file = "/";
|
|
}
|
|
else {
|
|
realpathname = file_unquote(pu->file);
|
|
if (*realpathname == '/' && *(realpathname + 1) == '~')
|
|
realpathname++;
|
|
if (sv_type == UNIXLIKE_SERVER) {
|
|
ftp_command(¤t_ftp, "CWD", realpathname, &status);
|
|
if (status == 250)
|
|
ftp_command(¤t_ftp, "LIST", NULL, &status);
|
|
}
|
|
else
|
|
ftp_command(¤t_ftp, "NLST", realpathname, &status);
|
|
}
|
|
if (status != 125 && status != 150) {
|
|
fclose(current_ftp.data);
|
|
current_ftp.data = NULL;
|
|
return NULL;
|
|
}
|
|
tmp = parsedURL2Str(pu);
|
|
if (Strlastchar(tmp) != '/')
|
|
Strcat_char(tmp, '/');
|
|
fn = html_quote(tmp->ptr);
|
|
tmp =
|
|
convertLine(NULL, Strnew_charp(file_unquote(tmp->ptr)), RAW_MODE,
|
|
charset, doc_charset);
|
|
q = html_quote(tmp->ptr);
|
|
FTPDIRtmp = Strnew_m_charp("<html>\n<head>\n<base href=\"", fn,
|
|
"\">\n<title>", q,
|
|
"</title>\n</head>\n<body>\n<h1>Index of ", q,
|
|
"</h1>\n", NULL);
|
|
|
|
if (SETJMP(AbortLoading) != 0) {
|
|
if (sv_type == UNIXLIKE_SERVER)
|
|
Strcat_charp(FTPDIRtmp, "</a></pre>\n");
|
|
else
|
|
Strcat_charp(FTPDIRtmp, "</a></ul>\n");
|
|
Strcat_charp(FTPDIRtmp, "<p>Transfer Interrupted!\n");
|
|
goto ftp_end;
|
|
}
|
|
TRAP_ON;
|
|
|
|
if (sv_type == UNIXLIKE_SERVER)
|
|
Strcat_charp(FTPDIRtmp, "<pre>\n");
|
|
else
|
|
Strcat_charp(FTPDIRtmp, "<ul>\n<li>");
|
|
Strcat_charp(FTPDIRtmp, "<a href=\"..\">[Upper Directory]</a>\n");
|
|
|
|
nfile_max = 100;
|
|
flist = New_N(char *, nfile_max);
|
|
nfile = 0;
|
|
if (sv_type == UNIXLIKE_SERVER) {
|
|
char *name, *link, *date, *size, *type_str;
|
|
int ftype, max_len, len, j;
|
|
|
|
max_len = 20;
|
|
while (tmp = Strfgets(current_ftp.data), tmp->length > 0) {
|
|
Strchop(tmp);
|
|
if ((ftype =
|
|
ex_ftpdir_name_size_date(tmp->ptr, &name, &link, &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;
|
|
flist[nfile++] = Sprintf("%s%s\n%s %5s%s", name, type_str, date,
|
|
size, link)->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 = strchr(fn, '\n');
|
|
if (*(date - 1) == '/') {
|
|
ftype = FTPDIR_DIR;
|
|
*date = '\0';
|
|
}
|
|
else if (*(date - 1) == '@') {
|
|
ftype = FTPDIR_LINK;
|
|
*(date - 1) = '\0';
|
|
}
|
|
else {
|
|
ftype = FTPDIR_FILE;
|
|
*(date - 1) = '\0';
|
|
}
|
|
date++;
|
|
tmp = convertLine(NULL, Strnew_charp(fn), RAW_MODE, charset,
|
|
doc_charset);
|
|
if (ftype == FTPDIR_LINK)
|
|
Strcat_char(tmp, '@');
|
|
Strcat_m_charp(FTPDIRtmp, "<a href=\"", html_quote(file_quote(fn)),
|
|
"\">", html_quote(tmp->ptr), "</a>", NULL);
|
|
for (i = get_Str_strwidth(tmp); i <= max_len; i++) {
|
|
if ((max_len % 2 + i) % 2)
|
|
Strcat_char(FTPDIRtmp, '.');
|
|
else
|
|
Strcat_char(FTPDIRtmp, ' ');
|
|
}
|
|
tmp = convertLine(NULL, Strnew_charp(date), RAW_MODE, charset,
|
|
doc_charset);
|
|
Strcat_m_charp(FTPDIRtmp, html_quote(tmp->ptr), "\n", NULL);
|
|
}
|
|
Strcat_charp(FTPDIRtmp, "</pre>\n");
|
|
}
|
|
else {
|
|
while (tmp = Strfgets(current_ftp.data), tmp->length > 0) {
|
|
Strchop(tmp);
|
|
flist[nfile++] = mybasename(tmp->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];
|
|
tmp = convertLine(NULL, Strnew_charp(fn), RAW_MODE, charset,
|
|
doc_charset);
|
|
Strcat_m_charp(FTPDIRtmp, "<li><a href=\"",
|
|
html_quote(file_quote(fn)), "\">",
|
|
html_quote(tmp->ptr), "</a>\n", NULL);
|
|
}
|
|
Strcat_charp(FTPDIRtmp, "</ul>\n");
|
|
}
|
|
|
|
ftp_end:
|
|
Strcat_charp(FTPDIRtmp, "</body>\n</html>\n");
|
|
TRAP_OFF;
|
|
closeFTPdata(current_ftp.data);
|
|
return FTPDIRtmp;
|
|
}
|
|
|
|
void
|
|
disconnectFTP(void)
|
|
{
|
|
ftp_quit(¤t_ftp);
|
|
}
|
|
|
|
#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;\
|
|
}
|
|
#define EX_COUNT_DIGIT(cp) {\
|
|
size = 0;\
|
|
while (*cp && IS_DIGIT(*cp))\
|
|
size = size * 10 + *(cp++) - '0';\
|
|
if (*cp == '\0')\
|
|
goto done;\
|
|
}
|
|
|
|
static Str size_int2str(clen_t);
|
|
|
|
static int
|
|
ex_ftpdir_name_size_date(char *line, char **name, char **link, char **date,
|
|
char **sizep)
|
|
{
|
|
int ftype = FTPDIR_NONE;
|
|
char *cp = line, *p;
|
|
clen_t size;
|
|
|
|
if (strlen(cp) < 11)
|
|
goto done;
|
|
/* skip permission */
|
|
cp += 10;
|
|
if (!IS_SPACE(*cp))
|
|
goto done;
|
|
cp++;
|
|
|
|
/* skip link count */
|
|
EX_SKIP_SPACE(cp);
|
|
EX_COUNT_DIGIT(cp);
|
|
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);
|
|
p = cp;
|
|
EX_COUNT_DIGIT(cp);
|
|
if (*cp == ',') { /* device file ? */
|
|
cp++;
|
|
EX_SKIP_SPACE(cp);
|
|
EX_SKIP_NONE_SPACE(cp);
|
|
*sizep = allocStr(p, cp - p);
|
|
}
|
|
else {
|
|
*sizep = size_int2str(size)->ptr;
|
|
}
|
|
cp++;
|
|
|
|
/* extract date */
|
|
/* loose check for i18n server */
|
|
p = cp;
|
|
EX_SKIP_SPACE(cp);
|
|
EX_SKIP_NONE_SPACE(cp); /* month ? */
|
|
EX_SKIP_SPACE(cp);
|
|
EX_SKIP_NONE_SPACE(cp); /* day ? */
|
|
EX_SKIP_SPACE(cp);
|
|
EX_SKIP_NONE_SPACE(cp); /* year or time ? */
|
|
*date = allocStr(p, cp - p);
|
|
cp++;
|
|
|
|
/* extract file name */
|
|
EX_SKIP_SPACE(cp);
|
|
switch (line[0]) {
|
|
case 'l':
|
|
ftype = FTPDIR_LINK;
|
|
if ((p = strstr(cp, " -> ")) == NULL)
|
|
goto done;
|
|
*name = allocStr(cp, p - cp);
|
|
*link = allocStr(p, -1);
|
|
*sizep = "";
|
|
break;
|
|
case 'd':
|
|
ftype = FTPDIR_DIR;
|
|
*name = allocStr(cp, -1);
|
|
*link = "";
|
|
*sizep = "";
|
|
break;
|
|
default:
|
|
ftype = FTPDIR_FILE;
|
|
*name = allocStr(cp, -1);
|
|
*link = "";
|
|
break;
|
|
}
|
|
|
|
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);
|
|
}
|