"Fossies" - the Fresh Open Source Software archive 
Member "cftp-0.12/ftp.c" of archive cftp-0.12.tar.gz:
/*
$NiH: ftp.c,v 1.74 2002/09/17 14:58:18 dillo Exp $
ftp.c -- ftp protocol functions
Copyright (C) 1996-2002 Dieter Baron
This file is part of cftp, a fullscreen ftp client
The author can be contacted at <dillo@giga.or.at>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_BASENAME
# ifdef HAVE_LIBGEN_H
# include <libgen.h>
# endif
#else
char *basename(char *);
#endif
#include "directory.h"
#include "display.h"
#include "sockets.h"
#include "ftp.h"
#include "readdir.h"
#include "options.h"
#include "util.h"
#include "bindings.h"
#include "status.h"
#include "signals.h"
char *ftp_lcwd;
char *ftp_pcwd;
char ftp_curmode; /* current transfer mode (' ' for unknown) */
char *ftp_last_resp;
static char *_ftp_host, *_ftp_port, *_ftp_user, *_ftp_pass;
/* host, port, user, passwd (for reconnect) */
static int _ftp_anon; /* whether we're using anonymous ftp */
struct ftp_hist *ftp_history; /* command/response history */
struct ftp_hist *ftp_hist_last; /* tail of history */
int ftp_hist_cursize; /* size of history (for dynamic growth) */
static int _ftp_keptresp;
static int ftp_dosnames; /* server sends directory names in
DOS format */
char **ftp_response;
long ftp_response_size;
FILE *conin, *conout; /* control connection to server */
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
static struct sockaddr_storage _ftp_addr;
#else
static struct sockaddr _ftp_addr;
#endif
struct sockaddr *ftp_addr = (struct sockaddr *)&_ftp_addr;
/* local ip address (for port commands) */
struct _ftp_transfer_stats {
long got, start, size;
int secs, stall_sec;
int ncur, ccur;
long *cur;
};
static int ftp_abort(FILE *fin);
static FILE *ftp_accept(int fd, char *mode);
static int ftp_gethostaddr(int fd);
static int ftp_mode(char m);
static int ftp_port(void);
static int ftp_put(char *fmt, ...);
static int ftp_resp(void);
static void ftp_unresp(int resp);
static int _ftp_ascii2host(char *buf, char *buf2, int n, int *trail_cr);
static int _ftp_host2ascii(char *buf, char *buf2, int n, int *trail_cr);
static void _ftp_transfer_stats_init(struct _ftp_transfer_stats *tr,
long start, long size, int ncur);
static void _ftp_transfer_stats_cleanup(struct _ftp_transfer_stats *tr);
static void _ftp_update_transfer(struct _ftp_transfer_stats *tr, long got,
int secs);
void
ftp_init(void)
{
ftp_lcwd = ftp_pcwd = NULL;
ftp_curmode = ' ';
_ftp_anon = 0;
ftp_last_resp = NULL;
_ftp_host = _ftp_user = _ftp_port = _ftp_pass = NULL;
ftp_history = NULL;
_ftp_keptresp = -1;
ftp_dosnames = -1;
ftp_response = NULL;
ftp_response_size = 0;
conin = conout = NULL;
}
int
rftp_open(char *host, char *port, char *user, char *pass)
{
int fd;
if (host) {
free(_ftp_host);
_ftp_host = strdup(host);
free(_ftp_port);
if (port && strcmp(port, "ftp") != 0)
_ftp_port = strdup(port);
else
_ftp_port = NULL;
}
if ((fd=sopen(_ftp_host, _ftp_port ? _ftp_port : "ftp", AF_UNSPEC)) == -1)
return -1;
if (ftp_gethostaddr(fd) == -1) {
close(fd);
return -1;
}
conin = fdopen(fd, "r");
conout = fdopen(fd, "w");
if (!conin || !conout) {
close(fd);
return -1;
}
if (ftp_resp() != 220) {
close(fd);
return -1;
}
ftp_remember_user(user, pass);
return 0;
}
int
rftp_login(char *user, char *pass)
{
int resp;
char *b;
int free_pass;
status.host = mkhoststr(0, 0);
ftp_put("user %s", _ftp_user);
resp = ftp_resp();
if (resp == 331) {
free_pass = 0;
if (_ftp_pass)
pass = _ftp_pass;
else {
if (_ftp_anon) {
free_pass = 1;
pass = get_anon_passwd();
}
else {
b = (char *)malloc(strlen(status.host)+14);
sprintf(b, "Password (%s): ", status.host);
if (disp_active) {
pass = read_string(b, 0);
free_pass = 1;
}
else
pass = getpass(b);
free(b);
}
}
ftp_put("pass %s", pass);
resp = ftp_resp();
if (free_pass)
free(pass);
}
if (resp != 230)
return -1;
free(status.remote.path);
status.remote.path = NULL;
status_do(bs_none);
return 0;
}
int
rftp_site(char *cmd)
{
ftp_put("%s", cmd);
return ftp_resp();
}
int
ftp_reconnect(void)
{
char *pass;
pass = _ftp_pass;
if (_ftp_host == NULL || _ftp_port == NULL || _ftp_user == NULL)
return -1;
ftp_close();
disp_status(DISP_INFO, "connecting. . .");
if (ftp_open(NULL, NULL, NULL, NULL) == -1) {
/* Error printed in ftp_open or sopen. */
return -1;
}
if (ftp_login(NULL, NULL) == -1) {
/* ftp response is error message */
return -1;
}
status.remote.path = strdup(ftp_lcwd);
status_do(bs_remote);
ftp_pcwd = NULL;
ftp_curmode = ' ';
return 0;
}
int
rftp_close(void)
{
int err = 0;
if (conin == NULL)
return 0;
ftp_put("quit");
if (ftp_resp() != 221)
err = 1;
if (conin)
fclose(conin);
if (conout)
fclose(conout);
conin = conout = NULL;
return err;
}
directory *
rftp_list(char *path)
{
directory *dir;
int fd;
FILE *f;
if (ftp_mode('a') == -1 || ftp_cwd(path) == -1)
return NULL;
if ((fd=ftp_port()) == -1)
return NULL;
ftp_put("list");
if (ftp_resp() != 150) {
close(fd);
dir = (directory *)malloc(sizeof(directory));
dir->line = (direntry *)malloc(sizeof(direntry));
dir->path = strdup(path);
dir->len = 0;
dir->cur = dir->top = 0;
dir->size = sizeof(struct direntry);
dir->line->line = strdup("");
dir->line->type = 'x';
dir->line->name = strdup("");
dir->line->link = NULL;
return dir;
}
if ((f=ftp_accept(fd, "r")) == NULL)
return NULL;
dir = read_dir(f);
if (dir)
dir->path = strdup(path);
fclose(f);
ftp_resp();
return dir;
}
directory *
ftp_cd(char *wd, int force)
{
directory *dir;
char *nwd;
nwd = canonical(wd, NULL);
dir = get_dir(nwd, force);
if (dir != NULL) {
free(ftp_lcwd);
ftp_lcwd = nwd;
free(status.remote.path);
status.remote.path = strdup(ftp_lcwd);
status_do(bs_remote);
}
else
free(nwd);
return dir;
}
void *
rftp_retr(char *file, int mode, long *startp, long *sizep)
{
int fd;
char *dir, *name, *can, *p;
FILE *fin;
can = canonical(file, NULL);
dir = xdirname(can);
name = basename(can);
if (ftp_mode(mode) == -1 || ftp_cwd(dir) == -1)
return NULL;
if ((fd=ftp_port()) == -1)
return NULL;
if (startp && *startp > 0) {
ftp_put("rest %ld", *startp);
if (ftp_resp() != 350)
*startp = 0;
}
ftp_put("retr %s", name);
if (ftp_resp() != 150) {
close(fd);
return NULL;
}
if (sizep != NULL) {
/* XXX: check how other servers format 150s */
if (strcmp(ftp_last_resp+strlen(ftp_last_resp)-8, " bytes).") == 0) {
if ((p=strrchr(ftp_last_resp, '(')) != NULL)
*sizep = strtol(p+1, NULL, 10);
}
else
*sizep = -1;
}
if ((fin=ftp_accept(fd, "r")) == NULL) {
close(fd);
return NULL;
}
return fin;
}
void *
rftp_stor(char *file, int mode)
{
int fd, resp;
char *dir, *name, *can;
FILE *fin;
can = canonical(file, NULL);
dir = xdirname(can);
name = basename(can);
if (ftp_mode(mode) == -1 || ftp_cwd(dir) == -1)
return NULL;
if ((fd=ftp_port()) == -1)
return NULL;
ftp_put("stor %s", name);
if ((resp=ftp_resp()) != 150 && resp != 125) {
close(fd);
return NULL;
}
if ((fin=ftp_accept(fd, "w")) == NULL) {
close(fd);
return NULL;
}
return fin;
}
int
rftp_fclose(void *f)
{
int err;
err = fclose(f);
if ((ftp_resp() != 226))
return -1;
return err;
}
int
rftp_mkdir(char *path)
{
ftp_put("mkd %s", path);
if (ftp_resp() != 257)
return -1;
return 0;
}
int
rftp_rmdir(char *path)
{
ftp_put("rmd %s", path);
if (ftp_resp() != 250)
return -1;
return 0;
}
int
rftp_deidle(void)
{
ftp_put("noop");
if (ftp_resp() != 200)
return -1;
return 0;
}
char *
rftp_pwd(void)
{
char *s, *e, *dir;
ftp_put("pwd");
if (ftp_resp() != 257)
return NULL;
if ((s=strchr(ftp_last_resp, '"')) == NULL
|| (e=strchr(s+1, '"')) == NULL)
return NULL;
if ((dir=(char *)malloc(e-s+1)) == NULL)
return NULL;
strncpy(dir, s+1, e-s-1);
dir[e-s-1] = '\0';
if (ftp_dosnames == -1) {
if ((isalpha(dir[0]) && dir[1] == ':' && dir[2] == '\\'))
ftp_dosnames = 1;
else if (dir[0] == '\\')
ftp_dosnames = 2;
else
ftp_dosnames = 0;
}
if (ftp_dosnames == 1 || ftp_dosnames == 2) {
if (ftp_dosnames == 1) {
strncpy(dir+1, s+1, e-s-1);
dir[e-s] = '\0';
dir[0] = '/';
}
for (s=dir; *s; s++)
if (*s == '\\')
*s = '/';
}
free(ftp_pcwd);
ftp_pcwd = strdup(dir);
return dir;
}
char *
ftp_gets(FILE *f)
{
char buf[8192], *line;
int l, l2;
if (fgets(buf, 8192, f) == NULL)
return NULL;
line = strdup(buf);
l = strlen(line);
while (line[l-1] != '\n') {
if (fgets(buf, 8192, f) == NULL) {
free(line);
return NULL;
}
l2 = strlen(buf);
if ((line=realloc(line, l+l2+1)) == NULL) {
disp_status(DISP_ERROR, "malloc failure");
return NULL;
}
strcpy(line+l, buf);
l += l2;
}
if (line[l-2] == '\r')
line[l-2] = '\0';
else
line[l-1] = '\0';
return line;
}
static int
ftp_put(char *fmt, ...)
{
char buf[8192];
va_list argp;
if (conout == NULL) {
disp_status(DISP_ERROR, "not connected");
return -1;
}
va_start(argp, fmt);
vsprintf(buf, fmt, argp);
va_end(argp);
if (strncmp(buf, "pass ", 5) == 0 && _ftp_anon == 0)
disp_status(DISP_PROTO, "-> pass ********");
else
disp_status(DISP_PROTO, "-> %s", buf);
fprintf(conout, "%s\r\n", buf);
if (fflush(conout) || ferror(conout)) {
disp_status(DISP_ERROR, "error writing to server: %s",
strerror(errno));
return -1;
}
return 0;
}
static int
ftp_abort(FILE *fin)
{
int resp;
char buf[4096];
fd_set ready;
struct timeval poll;
/* check wether abort is necessary (no data on control connection) */
poll.tv_sec = poll.tv_usec = 0;
FD_ZERO(&ready);
FD_SET(fileno(conin), &ready);
/* XXX: error ignored */
if (select(fileno(conin)+1, &ready, NULL, NULL, &poll) == 1)
return 0;
/* do abort */
disp_status(DISP_PROTO, "-> <attention>");
if (send(fileno(conout), "\377\364\377" /* IAC IP IAC */, 3,
MSG_OOB) != 3) {
disp_status(DISP_ERROR, "cannot send attention: %s",
strerror(errno));
return -1;
}
if (fputc('\362' /* DM */, conout) == EOF) {
disp_status(DISP_ERROR, "cannot send attention: %s",
strerror(errno));
return -1;
}
ftp_put("abor");
/* XXX: what about uploads? */
/* read remaining bytes from data connection */
while (fread(buf, 4096, 1, fin) > 0)
;
/* hanlde server response */
resp = ftp_resp();
if (resp == 226) {
ftp_unresp(226);
sleep(1);
/* check wether 226 is for us */
poll.tv_sec = poll.tv_usec = 0;
FD_ZERO(&ready);
FD_SET(fileno(conin), &ready);
/* XXX: error ignored */
if (select(fileno(conin)+1, &ready, NULL, NULL, &poll) == 1)
ftp_resp();
return 0;
}
else if (resp == 426) {
resp = ftp_resp();
ftp_unresp(426);
disp_status(DISP_STATUS, "426 Transfer aborted.");
return resp == 226;
}
else
return 1;
}
static int
ftp_resp(void)
{
char *line;
int resp;
if (_ftp_keptresp != -1) {
resp = _ftp_keptresp;
_ftp_keptresp = -1;
return resp;
}
if (conin == NULL) {
disp_status(DISP_ERROR, "not connected");
return -1;
}
clearerr(conin);
if ((line=ftp_gets(conin)) == NULL) {
if (ferror(conin))
disp_status(DISP_ERROR, "read error from server: %s",
strerror(errno));
else
disp_status(DISP_ERROR, "connection to server lost");
return -1;
}
/* XXX: DISP_PROTO should be used for first line */
resp = atoi(line);
disp_status(DISP_STATUS, "%s", line);
free(ftp_last_resp);
ftp_last_resp = strdup(line);
while (!(isdigit(line[0]) && isdigit(line[1]) &&
isdigit(line[2]) && line[3] == ' ')) {
disp_status(DISP_HIST, "%s", line);
if ((line=ftp_gets(conin)) == NULL) {
disp_status(DISP_ERROR, "read error from server: %s",
strerror(errno));
return -1;
}
}
disp_status(DISP_HIST, "%s", line);
return resp;
}
static void
ftp_unresp(int resp)
{
_ftp_keptresp = resp;
}
static int
ftp_port(void)
{
int fd, port, val, i, delim;
int len;
unsigned int iaddr;
unsigned char addr[4];
char baddr[16], bport[6], *s, *e;
char *saddr, *sport;
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
struct sockaddr_storage sa;
#else
struct sockaddr sa;
#endif
struct sockaddr_in *sin;
if (!opt_pasv) {
sin = (struct sockaddr_in *)ftp_addr;
len = sizeof(sa);
if ((fd=spassive(ftp_addr->sa_family,
(struct sockaddr *)&sa, &len)) == -1)
return -1;
port = htons(((struct sockaddr_in *)&sa)->sin_port);
if (ftp_addr->sa_family == AF_INET) {
iaddr = htonl(sin->sin_addr.s_addr);
ftp_put("port %d,%d,%d,%d,%d,%d",
(iaddr>>24) & 0xff, (iaddr>>16) & 0xff,
(iaddr>>8) & 0xff, iaddr & 0xff,
(port>>8) & 0xff, port & 0xff);
}
else {
ftp_put("eprt |%d|%s|%d|",
/* proto no */ 2,
sockaddr_ntop(ftp_addr), port);
}
if (ftp_resp() != 200) {
close(fd);
return -1;
}
}
else {
if (ftp_addr->sa_family == AF_INET) {
ftp_put("pasv");
if (ftp_resp() != 227)
return -1;
if ((s=strchr(ftp_last_resp, ',')) == NULL)
return -1;
while (isdigit(*(--s)))
;
s++;
for (i=0; i<4; i++) {
val = strtol(s, &e, 10);
if (val < 0 || val > 255 || *e != ',')
return -1;
addr[i] = val;
s = e+1;
}
port = strtol(s, &e, 10);
if (port < 0 || port > 255 || *e != ',')
return -1;
s = e+1;
val = strtol(s, &e, 10);
if (val < 0 || val > 255)
return -1;
port = port*256+val;
sprintf(baddr, "%d.%d.%d.%d",
addr[0], addr[1], addr[2], addr[3]);
sprintf(bport, "%d", port);
saddr = baddr;
sport = bport;
}
else { /* inet 6 passive */
ftp_put("epsv");
if (ftp_resp() != 229)
return -1;
if ((s=strchr(ftp_last_resp, '(')) == NULL)
return -1;
delim = s[1];
if ((e=strchr(s+2, delim)) == NULL
|| (e=strchr(e+1, delim)) == NULL)
return -1;
s = e+1;
if ((e=strchr(s, delim)) == NULL)
return -1;
if (e-s>5)
return -1;
strncpy(bport, s, e-s);
bport[e-s] = '\0';
sport = bport;
saddr = _ftp_host;
}
fd = sopen(saddr, sport, ftp_addr->sa_family);
}
return fd;
}
static FILE *
ftp_accept(int fd, char *mode)
{
int len, ns;
struct sockaddr addr;
if (!opt_pasv) {
len = sizeof(addr);
if ((ns=accept(fd, &addr, &len)) == -1)
return NULL;
close(fd);
fd = ns;
}
fcntl(fd, F_SETFD, 1); /* XXX: error check */
return fdopen(fd, mode);
}
static int
ftp_mode(char m)
{
if (m == ftp_curmode)
return 0;
ftp_put("type %c", toupper(m));
if (ftp_resp() != 200)
return -1;
ftp_curmode = m;
return 0;
}
int
rftp_cwd(char *path)
{
char *s, *e;
int off;
if (ftp_pcwd && strcmp(path, ftp_pcwd) == 0)
return 0;
if (ftp_dosnames == 1 || ftp_dosnames == 2) {
off = (ftp_dosnames == 1) ? 1 : 0;
for (s=path+off; *s; s++)
if (*s == '/')
*s = '\\';
ftp_put("cwd %s", path+off);
for (s=path+off; *s; s++)
if (*s == '\\')
*s = '/';
}
else
ftp_put("cwd %s", path);
if (ftp_resp() != 250)
return -1;
if (ftp_dosnames == -1) {
if ((s=strchr(ftp_last_resp, '"')) != NULL
&& (e=strchr(s+1, '"')) != NULL) {
if (isalpha(s[1]) && s[2] == ':' && s[3] == '\\')
ftp_dosnames = 1;
else if (s[1] == '\\')
ftp_dosnames = 2;
else
ftp_dosnames = 0;
}
else
ftp_dosnames = 0;
}
free(ftp_pcwd);
ftp_pcwd = strdup(path);
return 0;
}
int
ftp_cat(void *fin, void *fout, long start, long size, int upload)
{
char buf[4096], buf2[8192], *p;
int nread, nwritten, err, trail_cr, errno_copy;
enum { ERR_NONE, ERR_FIN, ERR_FOUT } error_cause;
int old_alarm;
long got;
struct itimerval itv;
struct _ftp_transfer_stats trstat;
int do_read;
int (*fn_read)(void *, size_t, void *);
int (*fn_write)(void *, size_t, void *);
int (*fn_eof)(void *);
got = start;
signal(SIGINT, sig_remember);
_ftp_transfer_stats_init(&trstat, start, size, 3);
old_alarm = sig_alarm = sig_intr = 0;
itv.it_value.tv_sec = itv.it_interval.tv_sec = 1;
itv.it_value.tv_usec = itv.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, NULL);
/* XXX: error check */
if (upload) {
/* fout is to server */
if (ftp_xfer_start(fout) < 0)
return -1;
fn_read = rftp_xfer_read;
fn_eof = rftp_xfer_eof;
fn_write = ftp_xfer_write;
rftp_xfer_start(fin);
}
else {
/* fin is from server */
if (ftp_xfer_start(fin) < 0)
return -1;
fn_read = ftp_xfer_read;
fn_eof = ftp_xfer_eof;
fn_write = rftp_xfer_write;
rftp_xfer_start(fout);
}
error_cause = ERR_NONE;
trail_cr = 0;
do_read = 1;
for (;;) {
if (do_read) {
if ((nread=fn_read(buf, 4096, fin)) > 0) {
do_read = 0;
nwritten = 0;
if (ftp_curmode != 'a')
p = buf;
else {
p = buf2;
if (upload)
nread = _ftp_host2ascii(buf, buf2, nread, &trail_cr);
else
nread = _ftp_ascii2host(buf, buf2, nread, &trail_cr);
}
}
else if (nread < 0) {
if (!fn_eof(fin)) {
errno_copy = errno;
error_cause = ERR_FIN;
}
break;
}
}
else {
if ((err=fn_write(p+nwritten, nread-nwritten, fout)) < 0) {
errno_copy = errno;
error_cause = ERR_FOUT;
break;
}
nwritten += err;
got += err;
if (nwritten == nread)
do_read = 1;
}
if (old_alarm != sig_alarm) {
_ftp_update_transfer(&trstat, got, sig_alarm);
old_alarm = sig_alarm;
}
if (sig_intr)
break;
}
signal(SIGINT, sig_end);
itv.it_value.tv_sec = itv.it_interval.tv_sec = 0;
itv.it_value.tv_usec = itv.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &itv, NULL);
_ftp_transfer_stats_cleanup(&trstat);
if (error_cause || sig_intr) {
sig_intr = 0;
ftp_xfer_stop((upload ? fout : fin), 1);
/* this is a hack, we should really use local file methods */
rftp_xfer_stop((upload ? fin : fout), 0);
switch (error_cause) {
case ERR_FIN:
disp_status(DISP_ERROR, "read error: %s", strerror(errno_copy));
break;
case ERR_FOUT:
disp_status(DISP_ERROR, "write error: %s", strerror(errno_copy));
break;
default:
;
}
return -1;
}
ftp_xfer_stop((upload ? fout : fin), 0);
rftp_xfer_stop((upload ? fin : fout), 0);
return 0;
}
static void
_ftp_update_transfer(struct _ftp_transfer_stats *tr, long got, int secs)
{
long base, step;
int nsec, i, stsec, eta;
double rtot, rcur, rbps;
char b[512];
nsec = secs-tr->secs;
if (nsec) {
base = tr->cur[tr->ccur];
step = (got-base) / nsec;
for (i=0; i<nsec-1; i++) {
tr->ccur = (tr->ccur+1) % tr->ncur;
tr->cur[tr->ccur] = base + step*i;
}
tr->ccur = (tr->ccur+1) % tr->ncur;
tr->cur[tr->ccur] = got;
}
if (tr->got != got) {
tr->got = got;
tr->stall_sec = secs;
}
tr->secs = secs;
rtot = (got-tr->start)/(double)(secs*1024);
rcur = ((tr->cur[tr->ccur] - tr->cur[(tr->ccur+1)%tr->ncur])
/ ((double)(secs < tr->ncur-1 ? secs : tr->ncur-1)*1024));
if (got == tr->start)
eta = -1;
else {
rbps = (got-tr->start)/(double)secs;
eta = (tr->size-got) / rbps;
}
stsec = secs - tr->stall_sec;
if (stsec > opt_stall) {
disp_status(DISP_ERROR, "stalled for more than %d seconds, aborting.");
sig_intr = 1;
}
else {
if (stsec > tr->ncur) {
if (stsec >= 60)
sprintf(b, "stalled: %d:%02ds", stsec/60, stsec%60);
else
sprintf(b, "stalled: %ds", stsec);
}
else
sprintf(b, "cur: %.2fkb/s", rcur);
if (eta != -1) {
if (eta > 60*60)
sprintf(b+strlen(b), ", eta: %d:%02d:%02ds",
eta/(60*60), (eta/60)%60, eta%60);
else if (eta > 60)
sprintf(b+strlen(b), ", eta: %d:%02ds",
eta/60, eta%60);
else
sprintf(b+strlen(b), ", eta: %ds", eta);
}
if (tr->size != -1)
disp_status(DISP_STATUS,
"transferred %ld/%ld (tot: %.2fkb/s, %s)",
got, tr->size, rtot, b);
else
disp_status(DISP_STATUS,
"transferred %ld (tot: %.2fkb/s, %s)",
got, rtot, b);
}
}
static void
_ftp_transfer_stats_init(struct _ftp_transfer_stats *tr,
long start, long size, int cur_secs)
{
int i;
/* XXX: check return value */
tr->ncur = cur_secs+1;
tr->cur = malloc(tr->ncur*sizeof(long));
tr->ccur = 0;
for (i=0; i<tr->ncur; i++)
tr->cur[i] = start;
tr->got = tr->start = start;
tr->size = size;
tr->secs = tr->stall_sec = 0;
}
static void
_ftp_transfer_stats_cleanup(struct _ftp_transfer_stats *tr)
{
free(tr->cur);
tr->ncur = 0;
}
static int
ftp_gethostaddr(int fd)
{
int len;
len = sizeof(_ftp_addr);
if (getsockname(fd, ftp_addr, &len) == -1) {
disp_status(DISP_ERROR, "can't get host address: %s",
strerror(errno));
return -1;
}
return 0;
}
void ftp_hist(char *line)
{
struct ftp_hist *p;
if (ftp_hist_size == 0)
return;
if (ftp_history == NULL) {
if ((ftp_history=(struct ftp_hist *)
malloc(sizeof(struct ftp_hist))) == NULL) {
free(line);
return;
}
ftp_hist_last = ftp_history;
ftp_history->next = NULL;
ftp_history->line = line;
ftp_hist_cursize = 1;
return;
}
if ((ftp_hist_last->next=(struct ftp_hist *)
malloc(sizeof(struct ftp_hist))) == NULL) {
free(line);
return;
}
ftp_hist_last = ftp_hist_last->next;
ftp_hist_last->next = NULL;
ftp_hist_last->line = line;
if (++ftp_hist_cursize > ftp_hist_size) {
--ftp_hist_cursize;
p = ftp_history;
ftp_history = ftp_history->next;
free(p->line);
free(p);
}
}
void
ftp_set_hist_size(int size, int *sizep)
{
struct ftp_hist *p;
if (size < 0)
return;
while (ftp_hist_cursize > size) {
--ftp_hist_cursize;
p = ftp_history;
ftp_history = ftp_history->next;
free(p->line);
free(p);
}
ftp_hist_size = size;
}
void
opts_mode(int c, int *cp)
{
switch (c) {
case 'a':
opt_mode = 'a';
break;
case 'i':
case 'b':
opt_mode = 'i';
}
}
char *
ftp_host(void)
{
return _ftp_host;
}
char *
ftp_prt(void)
{
return _ftp_port;
}
char *
ftp_user(void)
{
return _ftp_user;
}
char *
ftp_pass(void)
{
return _ftp_pass;
}
int
ftp_anon(void)
{
return _ftp_anon;
}
static int
_ftp_ascii2host(char *buf, char *buf2, int n, int *trail_cr)
{
char *s, *t;
int cr;
if (n == 0)
return 0;
s = buf;
t = buf2;
cr = *trail_cr;
for (; n; --n,s++) {
if (cr) {
if (*s != '\n')
*t++ = '\r';
cr = 0;
}
if (*s == '\r')
cr = 1;
else
*(t++) = *s;
}
*trail_cr = cr;
return t-buf2;
}
static int
_ftp_host2ascii(char *buf, char *buf2, int n, int *trail_cr)
{
char *s, *t;
int cr;
if (n == 0)
return 0;
s = buf;
t = buf2;
cr = *trail_cr;
for (; n; --n,s++) {
if (*s == '\n' && !cr)
*t++ = '\r';
cr = (*s == '\r');
*t++ = *s;
}
*trail_cr = cr;
return t-buf2;
}
void
ftp_remember_user(char *user, char *pass)
{
if (user) {
free(_ftp_user);
_ftp_user = strdup(user);
free(_ftp_pass);
if (pass)
_ftp_pass = strdup(pass);
else
_ftp_pass = NULL;
if (strcmp(_ftp_user, "ftp") != 0
&& strcmp(_ftp_user, "anonymous") != 0)
_ftp_anon = 0;
else
_ftp_anon = 1;
}
}
void
ftp_remember_host(char *host, char *port)
{
if (host) {
free(_ftp_host);
_ftp_host = strdup(host);
free(_ftp_port);
if (port)
_ftp_port = strdup(port);
else
_ftp_port = NULL;
}
}
/* this method is also used for local files */
int
rftp_xfer_read(void *buf, size_t len, void *file)
{
int n;
FILE *f;
fd_set fdset;
f = (FILE *)file;
for (;;) {
n = fread(buf, 1, len, f);
if (n == 0) {
if (ferror(f) && (errno == EINTR || errno == EAGAIN)) {
clearerr(f);
if (errno == EAGAIN) {
FD_ZERO(&fdset);
FD_SET(fileno(f), &fdset);
if (select(fileno(f)+1, &fdset, NULL, NULL, NULL) == -1
|| !FD_ISSET(fileno(f), &fdset))
return 0;
continue;
}
}
else
return -1;
}
return n;
}
}
int
rftp_xfer_start(void *file)
{
FILE *f;
f = (FILE *)file;
set_file_blocking(fileno(f), 0);
return 0;
}
int
rftp_xfer_stop(void *file, int aborting)
{
if (aborting)
return ftp_abort((FILE *)file);
return 0;
}
/* this method is also used for local files */
int
rftp_xfer_write(void *buf, size_t len, void *file)
{
int n;
FILE *f;
fd_set fdset;
f = (FILE *)file;
for (;;) {
n = fwrite(buf, 1, len, f);
if (n == 0) {
if (ferror(f) && (errno == EINTR || errno == EAGAIN)) {
clearerr(f);
if (errno == EAGAIN) {
FD_ZERO(&fdset);
FD_SET(fileno(f), &fdset);
if (select(fileno(f)+1, NULL, &fdset, NULL, NULL) == -1
|| !FD_ISSET(fileno(f), &fdset))
return 0;
continue;
}
}
else
return -1;
}
return n;
}
}
/* this method is also used for local files */
int
rftp_xfer_eof(void *file)
{
return feof((FILE *)file);
}