/* 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 "wput.h"

#include "utils.h"
#include "wpsocket.h"
#include "_queue.h"
#if 0
#include "ftp.h"
#endif /* 0 */
#include "windows.h"

unsigned char get_filemode(char * filename){
/* TODO USS 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 wpAbort(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;
}

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

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);
          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 '%':
          putc('%', opt.output);
          break;
        }
        }
    
      va_end(argp);
      fflush(opt.output);
  }
}
/* adopted from wget */
int file_exists (const char * filename)
{
#ifdef HAVE_ACCESS
  return access (filename, F_OK) >= 0;
#else
  struct stat buf;
  /* windows is in trouble... */
  if(!filename) return 0;
  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. 
	   * adopted from wget */
      struct passwd *pwd = getpwuid (getuid ());
      if (!pwd || !pwd->pw_dir)
        return NULL;
      home = pwd->pw_dir;
#else
      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
    }
  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 (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 HAVE_ISSPACE
#ifndef WIN32
#ifndef isspace
int isspace(char c) {
  switch(c) {
  case ' ':
  case '\t':
  case '\r':
  case '\n': return 1;
  };
  return 0;
}
#endif
#endif
#endif /* ndef HAVE_ISSPACE */

char * printip(unsigned char * ip) {
    static char rv[16];
    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);
    /* TODO NRV is an error in a hex-char in url really a cause to exit the whole process? */
    exit(1);
}
/* transform things like user%40host.com to user@host.com */
/* overwrites str and returns str. */
char * unescape(char * str) {
	char * ptr = str;
	char * org = str;
	while(*ptr) {
		if(*ptr == '%') {
			ptr += 2;
			*ptr = (hextoi(*(ptr-1)) << 4) + hextoi(*(ptr));
		}
		*(str++) = *(ptr++);
	}
	*str = *ptr;
	return org;
}
/* maybe not the most efficient method, but its enough
 * for our purposes (i spend about 20minutes figuring out the byte-shifting, where
 * i could have taken a sample source from the net, when my great provider
 * would not have forced to to stay offline for a while */
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;
}
/* wait opt.retry_interval seconds */
void retry_wait(_fsession * fsession) {
	if( fsession->retry > 0 || fsession->retry == -1) {
		printout(vLESS, "Waiting %d seconds... ", opt.retry_interval);
		sleep(opt.retry_interval);
	}
	if(fsession->retry > 0) fsession->retry--;
}
/* this function tries to parse a ftp-url that should look like
 * ftp://[user[:password]@]hostname[:port][/[remote_path/][remote_file]] */
int parse_url(_fsession * fsession, char *url) {
	char * d;
	char * host = NULL;
	char * path = NULL;
	
	/* while unescaping, we write to url, but we may not modify it, because it
	 * might be used later on and this leads to undefined behavior. but we also may
	 * not loose the base_pointer for freeing it afterwards, so get a backup */
	url = cpy(url+6);
	d = strchr(url, '/');
	if(d) *d = 0, path = d + 1;
	d = strchr(url, '@');
	if(d) *d = 0, host = d + 1;
	else  host = url;
	
	/* username / password */
	if(d) {
		d = strchr(url, ':');
		if(d) {
			*d = 0;
			fsession->user = cpy(unescape(url));
			fsession->pass = cpy(unescape(d+1));
		} else
			fsession->user = cpy(unescape(url));
	}
	
	/* port */
	d = strchr(host, ':');
	if(d) *d = 0;
	fsession->port = d ? atoi(d + 1) : 21;
	
	/* hostname */
	if( get_ip_addr(host, &fsession->ip) == -1) {
		if(opt.proxy != PROXY_OFF) {
			fsession->hostname = cpy(host);
			printout(vMORE, "Warning: '%s' could not be resolved. Assuming the proxy to do the task.\n", host);
		} else {
			printout(vLESS, "Error: cannot determinete ip addr of %s. Skipping this URL.\n", host);
			free(url);
			return ERR_FAILED;
		}
	} else
		fsession->hostname = NULL;
		
	if(!fsession->pass) {
		password_list * P = password_list_find(opt.pl, host, fsession->user);
		if(P) {
			if(!fsession->user) fsession->user = cpy(P->user);
			fsession->pass = cpy(P->pass);
		} else if(!fsession->user) {
			fsession->user = cpy("anonymous");
			fsession->pass = cpy(opt.email_address);
		}
	}
	
	/* path and filename */
	if(!path) {
		opt.relative_cwds = 1;
		free(url);
		return 0;
	}
	
	d = strrchr(path, '/');
	if(d) 
		*d = 0,
		fsession->target_dname = cpy(path);
	else
		d = path-1;
		
	if(*++d) {
		if(strchr(d, '#') || strchr(d, '?'))
			printout(vNORMAL, "Warning: URL: # or ? functions unimplemented. Assuming they are part of the filename.\n");
		fsession->target_fname = cpy(unescape(d));
	}
	free(url);
	return 0;
}
/* parses getenv("ftp_proxy") by understanding the following formats:
 * for socks: "[user:pass@]host[:port]"
 * for ftp:   "ftp://[user:pass@]host[:port] */
void parse_proxy(char * url) {
	char * p, *host;
	if(!url) return;
	
	if(strncmp(url, "http://", 7) != 0) {
		printout(vMORE, "Warning, no http-header found. Assuming socks-proxy $host:$port for '%s'\n", url);
		opt.proxy = PROXY_SOCKS;
		url = cpy(url);
	} else {
		url = cpy(url + 7);
		opt.proxy = PROXY_HTTP;
	}
	/* username / password */
	p = strchr(url, '@');
	if(p)
		*p++ = 0, host = p;
	else
		host = url;
	
	if(p) {
		p = strchr(url, ':');
		if(p) {
			*p++ = 0;
			set_option("proxy_pass", unescape(p));
		}
		set_option("proxy_user", unescape(url));
	}
	/* host / port */
	p = strchr(host, ':');
	if(!p) {
		opt.proxy_port = (opt.proxy == PROXY_SOCKS) ? 1080 : 3128;
		printout(vMORE, "Warning: no port specified. Assuming default port %d.\n", opt.proxy_port);
	} else {
		set_option("proxy_port", p + 1);
		*p = 0;
	}
	set_option("proxy_host", host);
	free(url);
}
/* the ftp-server takes the four bytes of the ip and the two bytes of the port
 * and puts their decimal values in a comma-seperated string like
 * (192,168,1,1,42,5). this function parses the garbage */
void parse_passive_string(char * msg, unsigned int * ip, unsigned short int * port) {
	char * start = strchr(msg, '(') + 1;
	char * curtok;
	char temp[6];
	int i = 0;
	curtok = strtok(start,   ",");
	do
		temp[i++] = atoi(curtok);
	while( (curtok = strtok(NULL, ",") ));

	*ip   = *(unsigned int   *) temp;
	*port = htons(*(unsigned short *) (temp+4));
}

/* Engine for legible;         from wget
 * add thousand separators to numbers printed in strings.
 */
static char *
legible_1 (const char *repr)
{
  static char outbuf[48]; /* TODO NRV adjust this value to a serious one. who cares? */
  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 (signed 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

#ifndef HAVE_BASENAME

/* gets the filename */
char * basename(char * p) {
    char * t = p + strlen(p);
    while(*t != dirsep && t != p) t--;
    return (t == p) ? t : t+1;
}
#endif /* ndef HAVE_BASENAME */

/* took me ages to write this damn function:
 * transforms things like "../..gaga/welt/.././.fool/../../new" to "../new"
 * (usually i don't expect such input, but who knows. works quite well now) */
void clear_path(char * path) {
	char * src = path;
	char * dst = path;
	char * org = path;
	while(*src) {
		if(src[0] == '/' && src[1] == '.' && ((src[2] == '.' && (src[3] == '/' || src[3] == 0 ))|| src[2] == '/' || src[2] == 0) && dst != org) {
			if(src[2] == '.' && (src[3] == '/' || src[3] == 0)) {
				if((dst-2 == org && !strncmp(dst-2, "..", 2)) || !strncmp(dst-3, "/..", 3)) *dst++ = *src++;
				else {
					while(dst >= org && *--dst != '/') ;
					if(dst < org) dst++;
					src+=3;
				}
			} else if(src[2] == '/' || src[2] == 0)
				src += 2;
		} else {
			if(*src == '/' && dst == org) src++;			
			*dst++ = *src++;
		}
	}
	*dst = 0;
}
		
/* 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 = src;
	char * mark_dst = dst;
	int counter = 1;
	/* find the point where they differ and put the mark after the last
	* common slash */
	while( *src != 0 && *dst != 0 && *src == *dst) {
		if(*src == '/') {
			mark_src = src+1;
			mark_dst = dst+1;
		}
		src++; dst++;
	}
	/* if all of src matches dst, we return the rest of dst */
	if(*src == 0) return cpy(dst+1);
	
	/* now count the remaining slashes */
	tmp = mark_src;
	while(*tmp++ != 0) if(*tmp == '/') counter++;
	tmp = malloc(counter * 3 + strlen(mark_dst) + 1);
	*tmp = 0;
	while(counter-- > 0)
		strcat(tmp, "../");
	strcat(tmp, mark_dst);
	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
