[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Fixes for that bug I just sent



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;
}