[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Fixes for that bug I just sent
- To: hagen@itooktheredpill.dyndns.org
- Subject: Fixes for that bug I just sent
- From: Tyler Montbriand <tsm@accesscomm.ca>
- Date: Wed, 21 Jul 2004 20:08:31 -0600
- Envelope-to: hagen@itooktheredpill.dyndns.org
- Organization: TSM Computer Interfacing
- User-agent: KMail/1.5.4
I modified get_msg in ftp.c to append lines instead of replacing the whole
line every time it reads another comment line.
I also did some cleanups in utils.c - there were a few lines of code that
assumed that sizeof(void *)==sizeof(int), which definitely isn't true on my
amd64 system - all pointers are 64-bit, as are long ints. The broken pointer
comparisons were working, suprisingly, because it was butchering all pointers
equally. :)
I'm afraid I cannot give diffs, since the original files are in MSDOS text
format - any diffs I make list the whole file. I have attached the modified
ftp.c and utils.c instead.
/* Declarations for wput.
Copyright (C) 1989-1994, 1996-1999, 2001
This file is part of wput.
The wput 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.1 of the License, or (at your option) any later version.
The wput 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.
You should have received a copy of the GNU General Public
License along with the wput; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* this file contains all the functions that fit nowhere else.
in this case these are esp. string-functions */
#include "utils.h"
#include "socket.h"
//#include "ftp.h"
#include "windows.h"
unsigned char get_filemode(char * filename){
/* TODO: do it like diff and check whether the file contains binary data */
char * txtfiles[] = {"txt", "c", "java", "cpp", "sh", "f", "f90", "f77", "f95", "bas",
"pro", "csh", "ksh", "conf", "htm", "html", "php", "pl", "cgi", "inf", "js",
"asp", "bat", "cfm", "css", "dhtml", "diz", "h", "hpp", "ini", "mak", "nfo",
"shtml", "shtm", "tcl", "pas"};
int i, k;
char * suffix;
int dotpos = strlen(filename);
while(filename[--dotpos] != '.');
suffix = (char *)(filename + (++dotpos));
k = strlen(suffix);
for(i = 0; k > 0 && i < 14; i ++)
if(strncasecmp(suffix, txtfiles[i], k) == 0)
return TYPE_A;
return TYPE_I;
}
void Abort(char * msg){
fprintf(opt.output, "%s", msg);
exit(1);
}
/* linux does not know anymore about itoa and windows
* does not support 64bit ints. so take that! :)
* keep in mind that con_unit sould be <= 10, otherwise
* you get strange output... (me was too lazy to fix it) */
char * int64toa(UINT64 num, char * buf, int con_unit){
UINT64 tmp=num;
if(num == 0) {
buf[0] = '0',
buf[1] = 0;
return buf;
}
while(tmp > 0) {
buf++;
tmp /= con_unit;
}
*buf = 0;
while(num > 0) {
*--buf = '0' + (char) (num % con_unit);
num /= con_unit;
}
return buf;
}
void printout(unsigned char verbose, const char * fmt, ...){
va_list argp;
const char *p;
UINT64 i;
char *s;
//unsigned char counter=0;
char fmtbuf[256];
if(opt.verbose >= verbose) {
//vfprintf(fsession.output, fmt, argp);
va_start(argp, fmt);
for(p = fmt; *p != '\0'; p++)
{
if(*p != '%')
{
putc(*p, opt.output);
continue;
}
// switch_again:
switch(*++p)
{
case 'c':
i = va_arg(argp, int);
putc((int) i, opt.output);
break;
case 'l':
case 'd':
if(*p == 'l')
i = va_arg(argp, UINT64);
else
i = va_arg(argp, int);
s = int64toa(i, fmtbuf, 10);
/*if(counter > 0) {
printf("GNU: %s -> %d,%d\n", s, strlen(s), counter);
while(strlen(s) < counter) {
char * t=s;
char x = '0';
// strcpy um 1 nach hinten
while(*t++) *(t-1) = x, x = *t, *t = *(t-1);
*s = '0';
printf("GNA\n");
}
counter=0;
}*/
fputs(s, opt.output);
break;
case 's':
s = va_arg(argp, char *);
if(s != NULL)
fputs(s, opt.output);
break;
case 'x':
i = va_arg(argp, int);
sprintf(fmtbuf, "%x", (int) i);
//s = itoa(i, fmtbuf, 16);
fputs(fmtbuf, opt.output);
break;
case '*':
i = va_arg(argp, int);
p++;
while(i-- > 0)
putc(*p, opt.output);
break;
/*case '2': //0 and 1 don't make any sense
case '3': //however this is only used by %d
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
counter = *p-- - '0';
* yeah i know this is ugly, but i cannot set *(p+1) to '%' since const char
is read only... and this thing is not worth further expenditure *
goto switch_again; */
case '%':
putc('%', opt.output);
break;
}
}
va_end(argp);
fflush(opt.output);
}
}
char * indexOf(char *str, char *delimiter) {
char *p, *q;
p = str;
while(*p != 0) {
q = delimiter;
while(*q != 0)
if(*p == *q++)
return p;
p++;
}
return NULL;
}
int file_exists (const char * filename)
{
#ifdef HAVE_ACCESS
return access (filename, F_OK) >= 0;
#else
struct stat buf;
return stat(filename, &buf) >= 0;
#endif
}
char * home_dir (void)
{
char *home = getenv ("HOME");
if (!home)
{
#ifndef WIN32
/* If HOME is not defined, try getting it from the password file. */
struct passwd *pwd = getpwuid (getuid ());
if (!pwd || !pwd->pw_dir)
return NULL;
home = pwd->pw_dir;
#else /* WINDOWS */
home = "C:\\";
/* #### Maybe I should grab home_dir from registry, but the best
that I could get from there is user's Start menu. It sucks! */
#endif /* WINDOWS */
}
return home ? cpy(home) : NULL;
}
char * read_line (FILE *fp)
{
int length = 0;
int bufsize = 82;
char *line = (char *) malloc (bufsize);
while (fgets (line + length, bufsize - length, fp))
{
length += strlen (line + length);
if (length == 0)
/* Possible for example when reading from a binary file where
a line begins with \0. */
continue;
if (line[length - 1] == '\n')
break;
/* fgets() guarantees to read the whole line, or to use up the
space we've given it. We can double the buffer
unconditionally. */
bufsize <<= 1;
line = realloc (line, bufsize);
}
if (length == 0 || ferror (fp))
{
free (line);
return NULL;
}
if (length + 1 < bufsize)
/* Relieve the memory from our exponential greediness. We say
`length + 1' because the terminating \0 is not included in
LENGTH. We don't need to zero-terminate the string ourselves,
though, because fgets() does that. */
line = realloc (line, length + 1);
return line;
}
#ifndef WIN32
#ifndef isspace
int isspace(char c) {
switch(c) {
case ' ':
case '\t':
case '\r':
case '\n': return 1;
};
return 0;
}
#endif
#endif
char * printip(unsigned char * ip) {
static char rv[32];
sprintf(rv, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
return rv;
}
int hextoi(char h) {
if(h >= 'A' && h <= 'F')
return h - 'A' + 10;
if(h >= 'a' && h <= 'f')
return h - 'a' + 10;
if(h >= '0' && h <= '9')
return h - '0';
printout(vMORE, "parse-error in escaped character: %c is not a hexadecimal character\n", h);
exit(1);
}
/* transform things like user%40host.com to user@host.com */
long int unescape(char * str, char terminator) {
char * ptr = str;
while(*ptr != terminator) {
if(*ptr == '%') {
ptr += 2;
*ptr = (hextoi(*(ptr-1)) << 4) + hextoi(*(ptr));
}
*(str++) = *(ptr++);
}
*str = *ptr;
return (ptr-str)/2;
}
/* maybe not the most efficient method, but its enough
* for our purposes */
char * base64(char * p, int len) {
int i = 0;
int len2 = 3 * ((len+2) / 3);
char * ret = malloc(len2 * 4 / 3);
int bytepointer = 0;
for(i = 0; i < len2 * 4 / 3; i++) {
if((i+1) * 3 / 4 > len) {
ret[i] = '=';
continue;
}
ret[i] = (htons(*(int *) (p + bytepointer/8)) & (((1 << (10 - bytepointer%8))-1) ^ ((1 << (16 - bytepointer % 8))-1))) >> (10 - bytepointer%8);
bytepointer+=6;
if(ret[i] < 26)
ret[i] += 'A';
else if(ret[i] < 52)
ret[i] += 'a' - 26;
else if(ret[i] < 62)
ret[i] += '0' - 52;
else if(ret[i] == 62)
ret[i] = '+';
else
ret[i] = '/';
}
return ret;
}
/* TODO we could get strange behaviour if a url like
TODO ftp://username@host:port/ is supplied
so the @
*/
int parse_url(_fsession * fsession, char *url) {
char * d, *e, *ptr;
if(strncasecmp(url, "ftp://", 6)==0)
url += 6;
d = indexOf(url, ":");
e = indexOf(url, "@");
if(e != NULL) {
/* warn if there is a second @ */
ptr = indexOf(e+1, "@");
if(ptr != NULL) {
printout(vNORMAL, "Warning: I noticed a second `@' in your URL. Use %%40 (see RFC 2396#2.2;2.4.1)\n");
/* set the @ as username-delimiter unless it comes after the first slash */
if(ptr < indexOf(url, "/"))
e = ptr;
printout(vDEBUG, "e is: %s\n", e);
}
/* if we have an '@', but no ':' or ':' comes after the @ (e.g. a port)
* then we have to deal with a null-password. */
if(d == NULL || e < d) d = e;
/* we need a few chars less if we unescape */
ptr = (char *) unescape(url, *d);
fsession->user = (char *) malloc(d-url+1 - (long int)ptr); /* +1 for null-char */
strncpy(fsession->user, url, d-url-(long int)ptr);
fsession->user[d-url-(long int)ptr] = 0;
/* e is only d when we have set it, thus only when we have no : and
therefore no password */
if(e != d) {
ptr = (char *) unescape(url, *e);
fsession->pass = (char *) malloc(e-d-(long int)ptr);
//printout(vDEBUG, "e-d-1: %d\n", e-d-1);
strncpy(fsession->pass, d+1, e-d-1-(long int)ptr);
fsession->pass[e-d-1] = 0;
}
url = e + 1;
} else {
/* use default username:password for anonymous login */
fsession->user = cpy("anonymous");
fsession->pass = cpy(email_address);
}
//printout(vDEBUG, "step 2.0: %s\n", url);
/* check for some termination-character. if none is found use
* all remaining chars as hostname */
d = indexOf(url, ":/#?");
//if(d) printout(vDEBUG, "step 2.1: %s\n", d);
if(!d)
d = url + strlen(url);
//printout(vDEBUG, "step 2.2: %s\t%d\n", d, d-url);
/* TODO unescape also in remote_dir and remote_filename */
ptr = (char *) malloc(d-url+1);
strncpy(ptr, url, d-url);
ptr[d-url] = 0;
//printout(vDEBUG, "step 2.3: %s\n", ptr);
if(get_ip_addr(ptr, &fsession->ip) == -1) {
if(opt.proxy != PROXY_OFF) {
fsession->hostname = cpy(ptr);
printout(vMORE, "Warning: '%s' could not be resolved. Let the proxy do the dirty work.\n");
} else {
printout(vLESS, "Error: cannot determinete ip addr of %s. Skipping this URL.\n", ptr);
free(ptr);
return -1;
}
} else
fsession->hostname = NULL;
free(ptr);
weiter:
url = d;
//printout(vDEBUG, "step 3.0: %s\n", url);
if(*url == 0) {
/* if we have not even one slash after the hostname, we assume current directory
* to be uploaded to the remote side, and assume also relative CWDs only */
opt.relative_cwds = 1;
fsession->target_dname = NULL;
return 0;
}
if(*url == ':') {
/* we got a port */
/* if we get multiple ports (e.g. ftp://somehost:21:22/), we
* take them all and the last one remains in fsession->port */
d = indexOf(d, "/#?");
//printout(vDEBUG, "step 3.1: %s\n", d);
if(!d)
d = url + strlen(url);
//printout(vDEBUG, "step 3.2: %s\n", d);
ptr = (char *) malloc(d-url);
strncpy(ptr, url+1, d-url);
ptr[d-url-1]=0;
//printout(vDEBUG, "step 3.3: %s\n", ptr);
fsession->port = atoi(ptr);
free(ptr);
if(fsession->port <= 0 || fsession->port >= 65535) fsession->port = 21;
//printout(vDEBUG, "step 3.4: %d\n", fsession.sftpport);
goto weiter;
} else
if(*url == '/') {
/* this is the remote location.
* look out for the last '/' char where we find the remote filename */
d = url + strlen(url);
while(d >= url && *--d != '/') ;
d++;
//printout(vDEBUG, "step 3.6: %s %s %d\n", d, url,d-url);
fsession->target_dname = (char *) malloc(d-url+1); /* +1 for \0 */
strncpy(fsession->target_dname, url, d-url);
fsession->target_dname[d-url] = 0;
//printout(vDEBUG, "step 3.7: %s\n", fsession->target_dname);
if(d != url + strlen(url)) {
fsession->target_fname = (char *) malloc(url + strlen(url) - d + 1);
strncpy(fsession->target_fname, d, url + strlen(url) - d + 1);
}
//printout(vDEBUG, "step 3.8: %s\n", fsession->target_fname);
} else {
printout(vLESS, "wput-url-parser: # and ?-functions unimplemented\n");
}
return 0;
}
/* Engine for legible;
* add thousand separators to numbers printed in strings.
*/
static char *
legible_1 (const char *repr)
{
static char outbuf[48];
int i, i1, mod;
char *outptr;
const char *inptr;
/* Reset the pointers. */
outptr = outbuf;
inptr = repr;
/* Ignore the sign for the purpose of adding thousand
separators. */
if (*inptr == '-')
{
*outptr++ = '-';
++inptr;
}
/* How many digits before the first separator? */
mod = strlen (inptr) % 3;
/* Insert them. */
for (i = 0; i < mod; i++)
*outptr++ = inptr[i];
/* Now insert the rest of them, putting separator before every
third digit. */
for (i1 = i, i = 0; inptr[i1]; i++, i1++)
{
if (i % 3 == 0 && i1 != 0)
*outptr++ = ',';
*outptr++ = inptr[i1];
}
/* Zero-terminate the string. */
*outptr = '\0';
return outbuf;
}
/* Legible -- return a static pointer to the legibly printed long. */
char *
legible (UINT64 l)
{
char inbuf[24];
/* Print the number into the buffer. */
int64toa(l, inbuf, 10);
return legible_1 (inbuf);
}
/* Count the digits in a (long) integer. */
int
numdigit (long number)
{
int cnt = 1;
if (number < 0)
{
number = -number;
++cnt;
}
while ((number /= 10) > 0)
++cnt;
return cnt;
}
#ifndef MEMDBG
/* return a malloced copy of the string */
char * cpy(char * s) {
char * t = (char *) malloc(strlen(s)+1);
strcpy(t, s);
return t;
}
#endif
/* gets the filename */
char * basename(char * p) {
char * t = p + strlen(p);
while(*t != dirsep && t != p) t--;
return (t == p) ? t : t+1;
}
/* makes from /some/path/ and /some/other/path/
* /../other/path/
* requires src and dst to begin and end with a slash */
char * get_relative_path(char * src, char * dst) {
char * tmp = dst;
char * mark_src = NULL;
char * mark_dst = NULL;
int counter = 0;
/* find the point where they differ */
while( *src != 0 && *dst != 0 && *src == *dst) {
if(*src == '/') {
mark_src = src;
mark_dst = dst;
}
src++; dst++;
}
if(!mark_src) Abort("FATAL ERROR while calculating relative path");
/* now count the remaining slashes */
tmp = mark_src;
while(*tmp++ != 0) if(*tmp == '/') counter++;
tmp = malloc(counter * 3 + strlen(mark_dst) + 1);
strcpy(tmp, "/");
while(counter-- > 0)
strcat(tmp, "../");
strcat(tmp, mark_dst + 1);
return tmp;
}
#ifdef WIN32
char * win32_replace_dirsep(char * p) {
char * t = p;
while(*p != 0) {
if(*p == dirsep) *p = '/';
p++;
}
return t;
}
#endif
#if 0
int invoke_script(char * event, char * url, char * file) {
char argv[3] = { event, url, file };
int oldfd[2]; /* stdin, stdout */
int res;
/* create some backup pipes */
if(pipe(oldfd) == -1) printout(vLESS, "Error: Pipe creation failed.\n");
dup2(0, old[0]);
dup2(1, old[1]);
printout(vDEBUG, "Redirecting input/output to socket. Invoking Scriptfile... ");
dup2(s, 0);
close(s);
dup2(0, 1);
res = execv(opt.scriptfile, argv);
/* reconstruct socket, stdin/stdout */
dup2(0, s);
close(0);
close(1);
dup2(old[0], 0);
dup2(old[1], 1);
printout(vDEBUG, "finished.\n");
if(res == -1) printout(vLESS, "Invokation of '%s' failed.\n", opt.scriptfile);
}
#endif
/* Declarations for wput.
Copyright (C) 2003
This file is part of wput.
The wput 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.1 of the License, or (at your option) any later version.
The wput 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.
You should have received a copy of the GNU General Public
License along with the wput; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* This file contains procedures for acting with the FTP-Server */
#include "ftp.h"
#include "utils.h"
#include "progress.h"
#include "windows.h"
#include "socket.h"
#include "_queue.h"
void do_quit(){
send_msg(opt.csock, "QUIT\r\n");
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.s_socket != -1) {
close(opt.s_socket);
opt.s_socket = -1;
}
if(opt.data_socket != -1) {
close(opt.data_socket);
opt.data_socket = -1;
}
if(opt.csock != -1) {
close(opt.csock);
opt.csock = -1;
}
}
/* TODO make do_abrt do ABRT only and do not cancel the control connection */
void do_abrt(){
printout(vMORE, "==> ABRT ... ");
send_msg(opt.csock, "ABRT\r\n");
get_msg(opt.csock, opt.rbuf, BUFLEN);
/*if(opt.csock != -1) {
close(opt.csock);
opt.csock = -1;
}*/
if(opt.data_socket != -1) {
close(opt.data_socket);
opt.data_socket = -1;
}
/* is there anything else that needs to be
resetted if we are going to reconnect? */
//opt.loggedin = 0;
printout(vMORE, "done.\n");
}
int do_login(_fsession * fsession){
get_msg(opt.csock, opt.rbuf, BUFLEN);
fill_cmd("USER", fsession->user);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
/* rfc states: 331 need password
* 332 need account TODO (do we need support for this?)
* 230 logged in */
if(opt.rbuf[0] == '3') {
/* if we have no password (e.g. ftp://guest@host) the PASS command is
left out. if we have a "" one (e.g. ftp://guest:@host) we'll send it */
if(opt.rbuf[2] == '1') {
if(!fsession->pass) {
printout(vLESS, "Warning: remote server requires a password, but none set. Using an empty one\n");
fsession->pass = cpy("");
}
fill_cmd("PASS", fsession->pass);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') return -1;
} else if(opt.rbuf[2] == '2') {
printout(vLESS, "Error: Server requires account login, which is not supported\n");
return -1;
}
} else if(opt.rbuf[0] != '2')
return -1;
/* we need the OS-information only for parsing the LIST
* so don't request it unless timestamping is activated */
if(opt.timestamping) {
send_msg(opt.csock, "SYST\r\n");
get_msg(opt.csock, opt.rbuf, BUFLEN);
opt.OS = ST_OTHER;
if( opt.rbuf[0] == '2' ) {
if (!strncasecmp (opt.rbuf+4, "VMS", 3))
opt.OS = ST_VMS;
else if (!strncasecmp (opt.rbuf+4, "UNIX", 4))
opt.OS = ST_UNIX;
else if (!strncasecmp (opt.rbuf+4, "WINDOWS_NT", 10))
opt.OS = ST_WINNT;
else if (!strncasecmp (opt.rbuf+4, "MACOS", 5))
opt.OS = ST_MACOS;
else if (!strncasecmp (opt.rbuf+4, "OS/400", 6))
opt.OS = ST_OS400;
}
printout(vDEBUG, "Operating System (enum): %d\n", opt.OS);
}
opt.loggedin = 1;
return 0;
}
int makeskip(_fsession * fsession, char * tmp) {
char * ptr = strtok(0x0, "/");
int len = ((ptr != 0x0) ? ptr-tmp : strlen(fsession->target_dname));
char * skipdname = malloc(len+1);
printout(vDEBUG, "makeskip: %s (%s)\n", tmp, ptr);
strncpy(skipdname, fsession->target_dname, len);
skipdname[len] = 0;
printout(vDEBUG, "len: %d -> path: %s (ptr: %s) => skipd: %s\n", ptr-tmp, tmp, ptr, skipdname);
/* tmp is a malloced buffer. free it */
free(tmp);
opt.skipdlist = skiplist_add_entry(opt.skipdlist, fsession->ip, fsession->port, cpy(fsession->user), cpy(fsession->pass), skipdname);
return -1;
}
int try_do_cwd(_fsession * fsession){
char * tmpbuf;
char * ptr;
if(!opt.relative_cwds) {
printout(vMORE, "==> CWD /");
send_msg(opt.csock, "CWD /\r\n");
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') { /* cannot even change dir to root dir */
printout(vMORE, " failed.\n");
return -1;
}
}
if(opt.current_directory && opt.relative_cwds)
tmpbuf = get_relative_path(opt.current_directory, fsession->target_dname);
else {
tmpbuf = malloc(strlen(fsession->target_dname)+1);
strcpy(tmpbuf, fsession->target_dname);
}
ptr = strtok(tmpbuf, "/");
while(ptr != 0x0){
fill_cmd("CWD", ptr);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2'){
if(opt.force) {
fill_cmd("MKD", ptr);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') return makeskip(fsession, tmpbuf);
fill_cmd("CWD", ptr);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') return makeskip(fsession, tmpbuf);
} else return makeskip(fsession, tmpbuf);
}
ptr = strtok(0x0, "/");
}
free(tmpbuf);
return 0;
}
int do_cwd(_fsession * fsession){
printout(vMORE, "==> CWD %s", fsession->target_dname);
fill_cmd("CWD", fsession->target_dname);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') {
printout(vMORE, " failed.\n");
return -1;
}
printout(vMORE, " done.\n");
return 0;
}
int do_size(_fsession * fsession){
/* in certain situations we don't need to SIZE */
if(opt.resume_table.large_small == RESUME_TABLE_UPLOAD &&
opt.resume_table.large_large == RESUME_TABLE_UPLOAD &&
opt.resume_table.small_large == RESUME_TABLE_UPLOAD) {
printout(vDEBUG, "Skipping SIZE\n");
fsession->target_fsize = 0;
return 0;
}
fill_cmd("SIZE", fsession->target_fname);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] == '5') {
printout(vMORE, "Cannot determine remote filesize. Starting at 0 Bytes\n");
return -1;
}
if(opt.rbuf[0] == '2')
fsession->target_fsize = strtol((char *)opt.rbuf + 4, NULL, 0);
printout(vDEBUG, "Remote filesize: %d\n", fsession->target_fsize);
return 0;
}
int create_data_socket(_fsession * fsession){
int res = 0;
printout(vDEBUG, "Portmode: %d\n", opt.portmode);
if(!opt.portmode){
res = do_passive(fsession);
if(res == -1){
/* revert back to port mode if passive mode fails,
* and adjust our settings, so that we don't need
* to try passive again */
res = do_port(fsession);
if(res == -1) return res;
opt.portmode = 1;
}
}
else{
res = do_port(fsession);
if(res == -1){ /* s.a. */
res = do_passive(fsession);
if(res == -1) return res;
opt.portmode = 0;
}
}
return res;
}
/* retrieve a LIST of the current directory */
int get_list(_fsession * fsession) {
int res;
int size = 1;
char * list;
/* look it up in the list, we already have */
fsession->directory = find_directory(fsession);
if(fsession->directory != NULL) return 0;
/* otherwise, retrieve it */
res = create_data_socket(fsession);
if(res < 0) {
printout(vLESS, "Error: Cannot initiate data-connection\n");
return res;
}
res = do_list(fsession);
if(res < 0) return res;
list = malloc(1);
list[0] = 0;
/* until the socket is done, add everything to the list-buffer */
while( (res = recv(opt.data_socket, opt.rbuf, BUFLEN-1, 0)) > 0) {
opt.rbuf[res] = 0;
size += res;
list = realloc(list, size);
strcat(list, opt.rbuf);
}
if(opt.data_socket) {
close(opt.data_socket);
opt.data_socket = -1;
}
/* receive the last message about list-completion. we expect no errors... */
get_msg(opt.csock, opt.rbuf, BUFLEN);
printout(vDEBUG, "Directory-Listing:\n%s\n-----\n", list);
fsession->directory = ftp_parse_ls (list, opt.OS);
free(list);
/* add it to the list of known directories */
opt.directorylist = add_directory(opt.directorylist, fsession->directory);
return 0;
}
int do_send(_fsession * fsession){
#define DBUFSIZE 1024
int fd;
FILE * pipe = NULL;
char databuf[DBUFSIZE];
int readbytes = 0;
int res = 0;
UINT64 transfered_size = 0;
int transfered_last = 0;
int oflags = O_RDONLY;
unsigned char backupbarstyle = opt.barstyle;
struct wput_timer * timers[2];
char * d = 0x0;
char * p = 0x0;
int convertbytes = 0;
char convertbuf[DBUFSIZE];
int crcount = 0;
printout(vDEBUG, "local_fsize: %d\ntarget_fsize: %d\n",
(int) fsession->local_fsize,
(int) fsession->target_fsize);
/* handle the resume_table. urgs ugly. TODO better idea? */
if(fsession->local_fsize > fsession->target_fsize){
if(opt.resume_table.large_small == RESUME_TABLE_SKIP) {
printout(vLESS, "Remote file size is larger than local size. Skipping.\n");
fsession->done = 1;
return 0;
} else if(opt.resume_table.large_small == RESUME_TABLE_UPLOAD) {
fsession->target_fsize = 0;
printout(vMORE, "Remote file size is smaller than local size. Restarting at 0\n");
} /* input-pipe --> ignore */
} else if(fsession->local_fsize == fsession->target_fsize && fsession->local_fname){
if(opt.resume_table.large_large == RESUME_TABLE_SKIP) {
printout(vNORMAL, "Remote file size is equal to local size. Skipping.\n");
fsession->done = 1;
return 0;
} else if(opt.resume_table.large_large == RESUME_TABLE_UPLOAD) {
fsession->target_fsize = 0;
printout(vMORE, "Remote file size is equal to local size. Restarting at 0\n");
}
} else if(fsession->local_fsize < fsession->target_fsize) {
if(opt.resume_table.small_large == RESUME_TABLE_SKIP) {
printout(vNORMAL, "Remote file size is smaller than local size. Skipping.\n");
fsession->done = 1;
return 0;
} else if(opt.resume_table.small_large == RESUME_TABLE_UPLOAD) {
fsession->target_fsize = 0;
printout(vMORE, "Remote file size is smaller than local size. Restarting at 0.\n");
}
}
if(opt.timestamping) {
res = get_list(fsession);
if(res < 0)
printout(vNORMAL, "Could not retrieve directory-list. Disabling timestamping for this file.\n");
else {
struct fileinfo * finfo = fileinfo_find_file(fsession->directory, fsession->target_fname);
if(finfo != NULL) {/* seems as though the file exist remotely */
fsession->target_ftime = finfo->tstamp - opt.time_offset * 60 * 60 - opt.time_deviation;
printout(vDEBUG, "timestamping: local: %d seconds; remote %d seconds; diff: %d\n",
fsession->local_ftime, fsession->target_ftime,
fsession->local_ftime - fsession->target_ftime);
if(fsession->target_ftime > fsession->local_ftime) {
printout(vLESS, "-- Skipping file: %s (remote is newer)\n", fsession->local_fname);
fsession->done = 1;
return 0;
}
}
}
}
/* TODO getlist, compare file dates */
if(fsession->binary == -1)
fsession->binary = get_filemode(fsession->target_fname);
res = do_type(fsession->binary);
if(res == -1) {
printout(vMORE, "Unable to set transfer mode. Assuming binary\n");
if(fsession->binary == TYPE_UNDEFINED) fsession->binary = TYPE_I;
}
/* don't know why */
#if defined(O_BINARY)
if(fsession->binary == TYPE_I) oflags = oflags | O_BINARY;
#endif
/* read if selected from the input-pipe */
if(fsession->local_fname) {
if((fd=open(fsession->local_fname, oflags)) == -1) {
printout(vLESS, "Error: Cannot open local source file to read\n");
return -1;
}
} else if(opt.input_pipe) {
/* TODO hostname if ip is unset */
char * cmd = (char *) malloc(
strlen(opt.input_pipe)
+ strlen(fsession->user)
+ (fsession->ip ? 15 : strlen(fsession->hostname)) + 5 /*port*/
+ strlen(fsession->target_dname)
+ strlen(fsession->target_fname)
+ 18);
sprintf(cmd, "%s ftp \"%s\" \"%s\" %d \"%s\" \"%s\"",
opt.input_pipe, fsession->user,
(fsession->ip ? printip((char *) &fsession->ip) : fsession->hostname),
fsession->port, fsession->target_dname, fsession->target_fname);
pipe = popen(cmd, "r");
free(cmd);
if(pipe == NULL) {
printout(vLESS, "Error opening pipe: %s\n", strerror(errno));
return -1;
}
fd = fileno(pipe);
/* TODO make the progressbar show, that we don't know the filesize/eta */
fsession->local_fsize = 1024 * 1024 * 1024;
opt.barstyle = 0;
}
res = create_data_socket(fsession);
if(res < 0) {
printout(vLESS, "Error: Cannot initiate data-connection\n");
return res;
}
/* resuming does not work for ascii-mode */
if(fsession->binary == TYPE_A) fsession->target_fsize = 0;
res = do_stor(fsession);
if(res < 0) {
return res;
}
/* we now have to accept the socket (if listening) and close the listening server */
if(opt.portmode) {
if(opt.proxy == PROXY_SOCKS && opt.proxy_bind) {
char t[10];
recv(opt.s_socket, t, 10, 0);
if(t[1] != 0) {
printout(vLESS, "Error: Proxy encountered an error while accepting. Error-Code: %d\n", t[1]);
close(fd);
return -1;
}
opt.data_socket = opt.s_socket;
printout(vDEBUG, "Proxy received an incoming connection on %s:%d.\n", printip(t+4), *(unsigned short int *) (t+8));
} else {
opt.data_socket = create_data_sockfd(opt.s_socket);
if(opt.s_socket != -1)
close(opt.s_socket);
}
opt.s_socket = -1;
}
bar_create(fsession);
// set start time
timers[0] = wtimer_alloc();
timers[1] = wtimer_alloc();
wtimer_reset(timers[0]);
wtimer_reset(timers[1]);
/* prepare resuming */
if(fsession->target_fsize > 0) {
/* TODO fseek in win (don't know about other OS) does not know
TODO what to do about files >4GB (__int64), so we need ideas
TODO here ;) */
fseek(fdopen(fd,"r"),
#ifdef WIN32
(long)
#endif
fsession->target_fsize, SEEK_SET);
transfered_size = fsession->target_fsize;
}
memset(databuf, 0, DBUFSIZE);
while( (readbytes = read(fd, (char *)databuf, DBUFSIZE)) != 0 ){
if( readbytes == -1 ){
printout(vLESS, "Error reading local file\n");
free(timers[0]);
free(timers[1]);
return -1;
}
/* TODO is it really that easy */
if(opt.speed_limit > 0 ) {
double elapsed_time = wtimer_elapsed(timers[0]);
while(elapsed_time > 0 && 1000 * WINCONV(transfered_size - fsession->target_fsize) / elapsed_time > opt.speed_limit) {
usleep(1000 * 300); /* sleep 0.3 seconds */
elapsed_time = wtimer_elapsed(timers[0]);
}
}
if(fsession->binary == TYPE_A){
d = databuf;
p = convertbuf;
crcount = 0;
/* TODO ascii mode means more than just CRLF (7-bit), i suppose this
* TODO is enough, but maybe someone has time to play around... */
while( d < databuf + readbytes){
while ((p < convertbuf + DBUFSIZE) && (d < databuf + readbytes)){
if (*d == '\n' && *(d-1) != '\r'){
*p = '\r'; p++;
crcount++;
}
*p = *d;
d++;
p++;
}
/* send data converted so far */
convertbytes = p - convertbuf;
res = send(opt.data_socket, convertbuf, convertbytes, 0);
if (res != convertbytes){
printout(vLESS, "Error: Error encountered during uploading data\n");
do_abrt();
free(timers[0]);
free(timers[1]);
opt.transfered_bytes += transfered_size - fsession->target_fsize;
return -1;
}
/* reset convert buffer to proceed */
p = convertbuf;
}
transfered_size += crcount + readbytes;
transfered_last += crcount + readbytes;
}
else {
transfered_size += readbytes;
transfered_last += readbytes;
res = send(opt.data_socket, databuf, readbytes, 0);
if(res != readbytes){
printout(vLESS, "Error encountered during uploading data\n");
do_abrt();
free(timers[0]);
free(timers[1]);
opt.transfered_bytes += transfered_size - fsession->target_fsize;
return -1;
}
}
if(opt.verbose >= vNORMAL) {
bar_update(fsession, transfered_size, transfered_last, timers[1]);
transfered_last = 0;
}
}
if(!fsession->local_fname && opt.input_pipe)
pclose(pipe);
else if(fd != -1)
close(fd);
#ifdef WIN32
shutdown(opt.data_socket,SD_BOTH);
#endif
if(opt.data_socket != -1) {
close(opt.data_socket);
opt.data_socket = -1;
}
printout(vNORMAL, "\n");
get_msg(opt.csock, opt.rbuf, BUFLEN);
printout(vNORMAL, "%s (%s) - `%s' [%l]\n\n",
time_str(),
fsession->target_fname,
calculate_transfer_rate(
wtimer_elapsed(timers[0]),
transfered_size - fsession->target_fsize,
0),
(fsession->local_fname ? fsession->local_fsize : transfered_size));
free(timers[0]);
free(timers[1]);
opt.transfered_bytes += transfered_size - fsession->target_fsize;
opt.files_transfered++;
if(opt.rbuf[0] != '2') return -1;
if(transfered_size == fsession->local_fsize || fsession->binary == TYPE_A || !fsession->local_fname) fsession->done = 1;
if( fsession->local_fname
&& (transfered_size == fsession->local_fsize || fsession->binary == TYPE_A)
&& opt.unlink) {
printout(vMORE, "Removing source file `%s'\n", fsession->local_fname);
unlink(fsession->local_fname);
}
opt.barstyle = backupbarstyle;
return 0;
}
int do_passive(_fsession * fsession){
char * resp, * ptr;
int ntok = 0;
int sport = 0, n1;
printout(vMORE, "==> PASV ");
fill_cmd("PASV", NULL);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') {
printout(vMORE, "failed.\n");
return -1;
}
printout(vMORE, "done.\n");
resp = (char *)opt.rbuf;
ptr = strtok(resp, ",");
while ( ((ptr = strtok(0x0, ",")) != 0x0) && ntok < 3)
ntok ++;
sport = atoi(ptr);
if( (ptr = strtok(0x0, ",")) != 0x0){ /* one more token */
n1 = atoi(ptr);
sport = ((sport << 8) & 0xff00) +n1;
}
printout(vDEBUG, "Remote server data port: %d\n", sport);
if(opt.proxy == PROXY_OFF)
opt.data_socket = create_new_reply_sockfd(fsession->ip, sport);
else
opt.data_socket = proxy_connect(fsession->ip, (unsigned short) sport, fsession->hostname);
if(opt.data_socket == -1) {
printout(vMORE, "connection failed.\n");
return -1;
}
return 0;
}
int do_list(_fsession * fsession) {
do_type(TYPE_A);
printout(vNORMAL, "==> LIST ... ");
fill_cmd("LIST", NULL);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
/* maybe we could live with a 200-series reply, too? */
if(opt.rbuf[0] != '1') {
printout(vNORMAL, "failed.\n");
return -1;
}
printout(vNORMAL, "done.\n");
return 0;
}
int do_type(int type) {
/* there are other types such as L 36 or E, but i think noone needs them */
static char * types[] = {"A", "I"};
if(opt.current_type == type) return 0;
printout(vMORE, "==> TYPE %s ... ", types[type]);
fill_cmd("TYPE", types[type]);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] == '2') {
printout(vMORE, "done\n");
opt.current_type = type;
return 0;
}
printout(vMORE, "failed.\n");
return -1;
}
int do_stor(_fsession * fsession){
/* try to resume if required */
if( fsession->binary !=
fsession->target_fsize != 0 &&
fsession->target_fsize < fsession->local_fsize &&
opt.resume_table.large_small == RESUME_TABLE_RESUME){
if(fsession->binary == TYPE_A)
printout(vNORMAL, "Warning: The file is about to be resumed in ascii mode.\n"
"Resuming for Ascii-Mode transfers is known not to work.\n");
printout(vMORE, "==> REST %d ... ", fsession->target_fsize);
fill_cmd("REST", int64toa(fsession->target_fsize, (char *)opt.tmpbuf, 10));
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '3') {
printout(vMORE, "failed.\nServer seems not to support resuming. Restarting at 0\n");
fsession->target_fsize = 0;
}
printout(vMORE, "done.\n");
}
printout(vMORE, "==> STOR %s ", fsession->target_fname);
fill_cmd("STOR", fsession->target_fname);
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
/* we want to distinguish the errors, because some might be
* recoverable, others not. */
/* rfc states:
125 Data connection already open; transfer starting.
150 File status okay; about to open data connection.
226 Closing data connection.
425 Can't open data connection.
426 Connection closed; transfer aborted.
450 Requested file action not taken.
File unavailable (e.g., file busy).
451 Requested action aborted: local error in processing.
452 Requested action not taken.
Insufficient storage space in system.
* 1 is ok, 5 is cancel, 2 and 4 is retry */
if(opt.rbuf[0] == '2' || opt.rbuf[0] == '4') {
printout(vMORE, "failed (recoverable).\n");
return -1; /* retry */
} else if(opt.rbuf[0] != '1') {
printout(vMORE, "failed.\n");
return -2; /* cancel */
}
printout(vMORE, "done.\n");
return 0;
}
char * get_port_fmt(int ip, unsigned int port) {
unsigned char b[6];
static char buf[6 * 4];
* (int *) b = ip;
*(unsigned int *)(b+4) = port;
sprintf(buf, "%d,%d,%d,%d,%d,%d", b[0], b[1], b[2], b[3], b[4], b[5]);
return buf;
}
int do_port(_fsession * fsession){
int sport = 0;
opt.s_socket = -1;
if(opt.proxy == PROXY_SOCKS && opt.proxy_bind) {
printout(vMORE, "Trying to listen on proxy server... ");
opt.s_socket = proxy_listen((unsigned short *) &sport);
if(opt.s_socket == -1) {
printout(vMORE, "failed. Falling back to listen locally\n"
"Warning: Unless FXP is enabled remotely, your control-connection "
"should be from the same IP-address, as your PORT bind-request. "
"So you should consider PASV-mode or reconnect without a proxy.\n"
);
} else {
printout(vMORE, "done.\n");
}
} else if(opt.proxy == PROXY_HTTP)
printout(vNORMAL, "Warning: Using port-mode. Unable to use the http-proxy for this connection\n");
if(opt.s_socket == -1)
if(initialize_server_master(&opt.s_socket, &sport) < 0)
return -1;
printout(vMORE, "==> PORT ... ");
if(opt.proxy == PROXY_SOCKS && opt.proxy_bind)
fill_cmd("PORT", get_port_fmt(opt.proxy_ip, sport));
else
fill_cmd("PORT", get_port_fmt(opt.local_ip, sport));
send_msg(opt.csock, opt.sbuf);
get_msg(opt.csock, opt.rbuf, BUFLEN);
if(opt.rbuf[0] != '2') {
printout(vMORE, "failed.\n");
return -1;
}
printout(vMORE, "done.\n");
return 0;
}
int do_connect(_fsession * fsession) {
int res = 0;
/* if we have a previous connection, close it before
* creating a new one */
if(opt.csock != -1) {
printout(vMORE, "Closing connection\n");
do_quit();
}
printout(vNORMAL, "Connecting to %s:%d... ", printip((char *) &fsession->ip), fsession->port);
if(opt.proxy != PROXY_OFF)
opt.csock = proxy_connect(fsession->ip, fsession->port, fsession->hostname);
else
opt.csock = create_new_reply_sockfd(fsession->ip, fsession->port);
if(opt.csock == -1) {
printout(vNORMAL, " failed!\n");
return -1;
}
printout(vNORMAL, " connected!\n");
/* We always need to log in and CWD on a new connection */
opt.loggedin= 0;
opt.needcwd = 1;
opt.current_type = TYPE_UNDEFINED; /* don't know the transfer_mode yet */
opt.conn_ip = fsession->ip;
opt.conn_port = fsession->port;
if(opt.proxy != PROXY_OFF) {
if(opt.conn_hostname) {
free(opt.conn_hostname);
opt.conn_hostname = NULL;
}
if(fsession->hostname)
opt.conn_hostname = cpy(fsession->hostname);
}
printout(vDEBUG, "determing local ip_addr\n");
res = get_local_ip(opt.csock, (char *) &opt.local_ip);
if(res == -1) Abort("Cannot determine local IP address");
printout(vDEBUG, "Local IP: %s\n", printip((char *) &opt.local_ip));
return 0;
}
int send_msg(int csock, char * msg){
/* i don't need to filter PASS if it is just
* to be displayed in debug-mode. */
//if(strncmp(msg, "PASS", 4) != 0)
printout(vDEBUG, "---->%s", msg);
return send(csock, msg, strlen(msg), 0);
}
int get_msg(int csock, char * msg, int buflen)
{
static char buf[BUFLEN];
int res = 0;
msg[0]='\0';
do
{
memset(buf,0,BUFLEN);
res = recv(csock, buf, BUFLEN-1, 0);
printout(vDEBUG, "%s", buf);
if((strlen(msg)+res)<buflen)
strcat(msg,buf);
/* multiline messages are required by rfc to have a hiphen
* after the command-code (e.g. 250-).
* this counts for all but the last one, so look out for it. */
/* Apparently this does NOT count for all lines, either that or some
*FTP servers are noncompliant. */
} while((buf[3] == '-')||(!isdigit(msg[0])));
return res;
}
int fill_cmd(char * cmd, char * value){
int len = strlen(cmd)
+ ((value) ? strlen(value) + 1 : 0) /* value + space */
+ 2 /* \r\n */
+ 1; /* \0 */
/* for performance-reasons we only allocate a new buffer
* if the old one is not big enough anymore */
if(opt.sbuflen < len || !opt.sbuf) {
if(opt.sbuf) free(opt.sbuf);
opt.sbuf = (char *) malloc(len);
opt.sbuflen = len;
}
memset(opt.sbuf, 0, opt.sbuflen);
/* i don't trust sprintf */
strcpy(opt.sbuf, cmd);
if(value) {
int pos = strlen(cmd);
opt.sbuf[pos] = ' ';
strcpy(opt.sbuf+pos+1, value);
}
{
int pos = strlen(opt.sbuf);
strncpy(opt.sbuf+pos, "\r\n\0", 3);
}
return 0;
}