/* IglooFTP - Graphical and User Friendly FTP Client.
 * Copyright (c) 1998-1999 Jean-Marc Jacquet. 
 * All rights reserved.
 * 
 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE
 *
 * IglooFTP Original Packages, information and support,  
 * can be obtained at :
 *                              http://www.littleigloo.org
 * 
 *
 */


#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <sys/types.h>
#include <sys/time.h>
#include <resolv.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>

#include <gtk/gtk.h>
#include "debug.h"
#include "FTP.h"

char *host_type_name[] =
{"AUTO-DETECT", "UNIX", "SUNOS", "WINDOWS_NT", "VMS", 0};


/* DELME vvvvvvvvvvvvvv  */
char want_timeout = FALSE;

#ifndef STAND_ALONE
extern char want_abort;
extern nop (void);
extern void host_message (char *this_message);
extern void local_message (char *this_message);
extern void info_message (char *this_message);
extern void error_message (char *this_message);
extern void show_progress_list (void);
extern void update_file_transfert_status_idle (void);
#define FTP_error_msg(x) error_message(x)
#define FTP_host_msg(x)  host_message(x)
#define FTP_local_msg(x) local_message(x)
#define FTP_info_msg(x) info_message(x)
#define  FTP_progress_list show_progress_list
#define  FTP_progress_download update_file_transfert_status_idle
#define FTP_progress_upload  FTP_progress_download
extern int recv_buf_len;
extern int send_buf_len;

#else

#define FTP_error_msg(x) printf(x)
#define FTP_host_msg(x) printf("%s\n",x)
#define FTP_local_msg(x) printf("%s",x)
#define FTP_info_msg(x) printf(x)
#define  FTP_progress_list(x)  FTP_show_progress_list(x)
#define recv_buf_len 8192
#define recv_buf_len 8192

static void
FTP_show_progress_list (void)
{
}

#define FTP_progress_upload  FTP_progress_download
static void
FTP_show_progress_download (void)
{
}

#endif






#define RETURN_ON_CONNEXION_LOST();  if (FTPctrl->connexion_lost) return 0;
#define FTP_close_socket(socket);  if (socket > 0) close (socket);


static int FTP_error (int error_code, char *msg, char *msg_operand, int socket);
static int FTP_get_msg_code (char *msg);

static int timeout_socket (FTP * FTPctrl, int socket, char READ_OR_WRITE);
static char FTP_send_command (FTP * FTPctrl, char *command);
static char FTP_get_reply (FTP * FTPctrl);
static char FTP_exec_command (FTP * FTPctrl, char *command);

static void FTP_close_session (FTP * FTPctrl);

static char FTP_set_passive (FTP * FTPctrl, struct sockaddr_in *sock_in);
static int FTP_open_data_connection (FTP * FTPctrl);

static char *alloc_xfer_buf (size_t * xfer_buf_len);
static char FTP_download (FTP * FTPctrl, int data_socket, FILE * local);
static char FTP_upload (FTP * FTPctrl, int data_socket, FILE * local);

static char FTP_send_telnet_interrupt (int this_socket);


/* FTP COMMAND FUNCTIONS  */
#include "FTPcommands.h"







/* ERROR Functions */

/* STATIC :
 * FTP_error
 * FTP_get_msg_code
 */

static int
FTP_error (int error_code, char *msg, char *msg_operand, int socket)
{
  char error_msg[255];
  sprintf (error_msg, "%s%s %s", msg, msg_operand, errno ? strerror (errno) : "");
  FTP_error_msg (error_msg);
  FTP_close_socket (socket);
  return error_code;
}

static int
FTP_get_msg_code (char *msg)
{
  char msg_id[4];
  strncpy (msg_id, msg, 3);
  msg_id[3] = 0;
  return atoi (msg_id);
}








/* BASIC functions */

/* STATIC:
 * timeout_socket 
 * FTP_send_command
 * FTP_get_reply
 * FTP_exec_command
 */


static int
timeout_socket (FTP * FTPctrl, int socket, char READ_OR_WRITE)
{
  fd_set fd;
  fd_set *rfd = NULL;
  fd_set *wfd = NULL;
  struct timeval tv;
  int retval = 0;
  int timeout_delay = FTPctrl->timeout * 10;

  RETURN_ON_CONNEXION_LOST ();

  if (READ_OR_WRITE == SOCKET_READ)
    rfd = &fd;
  else
    wfd = &fd;

  FD_ZERO (&fd);

  while (timeout_delay-- > 0)
    {
      FD_SET (socket, &fd);
      tv.tv_sec = 0L;
      tv.tv_usec = 100000L;

      if ((retval = select (socket + 1, rfd, wfd, NULL, &tv)) > 0)
	return retval;
      else if (retval == -1)
	continue;

      while (gtk_events_pending ())
	gtk_main_iteration ();
    }

  FTPctrl->connexion_lost = TRUE;
  FTP_error_msg ((READ_OR_WRITE == SOCKET_READ) ? "TIMEOUT (socket read)." : "TIMEOUT (socket write).");
  return 0;
}



static char
FTP_send_command (FTP * FTPctrl, char *command)
{
  char send_buf[255];

  RETURN_ON_CONNEXION_LOST ();


  FTPctrl->command_busy = TRUE;
  if (!timeout_socket (FTPctrl, FTPctrl->socket, SOCKET_WRITE))
    {
      FTPctrl->command_busy = FALSE;
      return FALSE;
    }

  sprintf (send_buf, "%s", command);
  if (!strstr (send_buf, "\r\n"))
    strcat (send_buf, "\r\n");

  FTP_local_msg (command);
  if (send (FTPctrl->socket, &send_buf, strlen (send_buf), 0) < 1)
    {
      FTPctrl->command_busy = FALSE;
      return FTP_error (FALSE, "Unable to send : ", send_buf, 0);
    }

  if (!timeout_socket (FTPctrl, FTPctrl->socket, SOCKET_WRITE))
    {
      FTPctrl->command_busy = FALSE;
      return FALSE;
    }


  FTPctrl->command_busy = FALSE;
  return TRUE;
}







static char
FTP_get_reply (FTP * FTPctrl)
{
#define REPLY_BUF 1024
  char recv_buf[REPLY_BUF];
  int EOL;
  char *EOL_ptr = NULL;

  RETURN_ON_CONNEXION_LOST ();

  FTPctrl->command_busy = TRUE;




  memset (&recv_buf, 0, sizeof (recv_buf));
  while ((EOL_ptr = strchr (recv_buf, '\n')) == NULL)
    {
      if (!timeout_socket (FTPctrl, FTPctrl->socket, SOCKET_READ))
	return (FTPctrl->command_busy = FALSE);
      if (recv (FTPctrl->socket, &recv_buf, REPLY_BUF - 1, MSG_PEEK) == -1)
	return (FTPctrl->command_busy = !(FTPctrl->connexion_lost = TRUE));
    }

  if (*(EOL_ptr - 1) == '\r')
    EOL_ptr--;
  EOL = (char *) EOL_ptr - (char *) recv_buf;


  if (recv_buf[3] != '-')
    {
      recv (FTPctrl->socket, &recv_buf, (int) EOL + 2, 0);
      recv_buf[EOL] = 0;
      strncpy (FTPctrl->reply, recv_buf, sizeof (FTPctrl->reply));
      FTP_host_msg (FTPctrl->reply);
    }
  else
    {
      const int msg_code = FTP_get_msg_code (recv_buf);
      recv (FTPctrl->socket, &recv_buf, (int) EOL + 2, 0);
      recv_buf[EOL] = 0;
      strncpy (FTPctrl->reply, recv_buf, sizeof (FTPctrl->reply));
      FTP_host_msg (FTPctrl->reply);
      do
	{
	  memset (&recv_buf, 0, sizeof (recv_buf));
	  while ((EOL_ptr = strchr (recv_buf, '\n')) == NULL)
	    {
	      if (!timeout_socket (FTPctrl, FTPctrl->socket, SOCKET_READ))
		return (FTPctrl->command_busy = FALSE);
	      if (recv (FTPctrl->socket, &recv_buf, REPLY_BUF - 1, MSG_PEEK) == -1)
		return (FTPctrl->command_busy = !(FTPctrl->connexion_lost = TRUE));
	    }

	  if (*(EOL_ptr - 1) == '\r')
	    EOL_ptr--;
	  EOL = (char *) EOL_ptr - (char *) recv_buf;

	  recv (FTPctrl->socket, &recv_buf, (int) EOL + 2, 0);
	  recv_buf[EOL] = 0;
	  strncpy (FTPctrl->reply, recv_buf, sizeof (FTPctrl->reply));
	  FTP_host_msg (FTPctrl->reply);
	}
      while ((msg_code != FTP_get_msg_code (recv_buf)) || (recv_buf[3] == '-') || (recv_buf[3] != ' '));
    }

  FTPctrl->command_busy = FALSE;

  return TRUE;
}



static char
FTP_exec_command (FTP * FTPctrl, char *command)
{

  RETURN_ON_CONNEXION_LOST ();

  strcpy (FTPctrl->reply, "5xx");

  if (!FTP_send_command (FTPctrl, command))
    return FTP_error (FALSE, "Unable to send command to host.", "", 0);

  RETURN_ON_CONNEXION_LOST ();

  if (!FTP_get_reply (FTPctrl))
    return FTP_error (FALSE, "Unable to get host  reply.", "", 0);

  if (FTP_get_msg_code (FTPctrl->reply) == 421)
    {
      FTPctrl->connexion_lost = TRUE;
      return FTP_error (FALSE, "Control connection closed by host.", "", FTPctrl->socket);
    }

  return TRUE;
}






/* RESOVLE/CONNECT functions */

/* PUBLIC :
 * FTP_resolve
 * FTP_connect
 * FTP_SOCKSV5_connect
 * STATIC:
 * FTP_close_session
 */

static void
FTP_close_session (FTP * FTPctrl)
{
  FTP_close_socket (FTPctrl->socket);
  FTPctrl->is_connected = FALSE;
}


/* RESOLVE */

char
FTP_resolve (FTP * FTPctrl)
{
  struct hostent *host_info;
  struct servent *serv_info;
  struct sockaddr_in sock_in;
  char msg_buf[256];
  char resolv = FALSE;
  memset (&sock_in, 0, sizeof (sock_in));


  if (!FTPctrl->port)
    {
      if ((serv_info = getservbyname ("ftp", "tcp")))
	sock_in.sin_port = serv_info->s_port;
      else
	FTPctrl->port = 21;
    }
  else
    sock_in.sin_port = htons (FTPctrl->port);


  /* sethostent(RES_INIT); */
  if ((sock_in.sin_addr.s_addr = inet_addr (FTPctrl->hostname)) == -1)
    {
      sprintf (msg_buf, "Resolving %s ...\n", FTPctrl->hostname);
      FTP_info_msg (msg_buf);

      errno = 0;
      if ((host_info = gethostbyname (FTPctrl->hostname)) == NULL)
	{
	  herror ("herror:");
	  return FTP_error (FALSE, "Unable to resolve ", FTPctrl->hostname, 0);
	}
      memcpy ((char *) &sock_in.sin_addr, host_info->h_addr, host_info->h_length);
      resolv = TRUE;
    }

  sprintf (FTPctrl->connect_address, "%s", inet_ntoa (sock_in.sin_addr));
  FTPctrl->port = ntohs (sock_in.sin_port);

  if (resolv)
    {
      sprintf (msg_buf, "Resolved %s as (%s) \n", FTPctrl->hostname, FTPctrl->connect_address);
      FTP_info_msg (msg_buf);
    }

  sock_in.sin_family = AF_INET;

  memcpy (&FTPctrl->sock_in, &sock_in, sizeof (sock_in));

  return TRUE;
}


/* CONNECT */

char
FTP_connect (FTP * FTPctrl)
{
  char msg_buf[256];
  int optval = 1;

  if (!(FTPctrl->socket = socket (PF_INET, SOCK_STREAM, 0)))
    return FTP_error (FALSE, "Unable to create socket ", "", 0);

  if ((setsockopt (FTPctrl->socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval))))
    return FTP_error (FALSE, "Unable to setup socket ", "", FTPctrl->socket);

  if ((setsockopt (FTPctrl->socket, SOL_SOCKET, SO_OOBINLINE, &optval, sizeof (optval))))
    FTP_error (FALSE, "Unable to setup socket (out-of-band data)", "", 0);

  sprintf (msg_buf, "Connecting to %s (%s) on Port %i. Try #%i ...\n",
	   FTPctrl->hostname, FTPctrl->connect_address, FTPctrl->port, FTPctrl->retry_count);
  FTP_info_msg (msg_buf);

  if (connect (FTPctrl->socket, (struct sockaddr *) &FTPctrl->sock_in, sizeof (FTPctrl->sock_in)) == -1)
    return FTP_error (FALSE, "Unable to connect ", "", FTPctrl->socket);


  if (FTPctrl->proxy != FIREWALL_TYPE_SOCKSV5)
    {
      sprintf (msg_buf, "Connected to %s server. Waiting for welcome message ...", FTPctrl->proxy ? "Firewall" : "FTP");
      FTP_info_msg (msg_buf);
      if (!FTP_get_reply (FTPctrl))
	return FTP_error (FALSE, "Unable to get host  reply. Session aborded.", "", FTPctrl->socket);
      FTPctrl->is_connected = TRUE;
    }

  return TRUE;
}


char
FTP_SOCKSV5_connect (FTP * FTPctrl, char *username, char *userpass)
{
  unsigned char msg_buf[128];
  char info_msg[128];
  char HAVE_FIREWALL_USERNAME = strlen (username) ? 1 : 0;
  int address_len = 0;
  const char *socks_error_msg[9] =
  {"Unknow error code",
   "General SOCKS server failure",
   "Connection not allowed by ruleset",
   "Network unreachable",
   "Host unreachable",
   "Connection refused",
   "TTL expired",
   "Command not supported",
   "Address type not supported"};



  FTP_info_msg ("Connected to Firewall.");
  FTP_info_msg (HAVE_FIREWALL_USERNAME ? "Requesting authentication method ... " : "Requesting no authentication ...");


  sprintf (msg_buf, "%c%c%c%c", 5, 1 + HAVE_FIREWALL_USERNAME, 0, 2);

  if (send (FTPctrl->socket, &msg_buf, 3 + HAVE_FIREWALL_USERNAME, 0) == -1)
    return FTP_error (FALSE, "Unable to send request. Session aborded.", "", FTPctrl->socket);

  if (recv (FTPctrl->socket, &msg_buf, 2, 0) == -1)
    return FTP_error (FALSE, "Got no response from Firewall. Session aborded.", "", FTPctrl->socket);


  switch (msg_buf[1])
    {
    case 0:
      sprintf (info_msg, "SOCKSv%c server does not require authentication.", msg_buf[0] + '0');
      FTP_info_msg (info_msg);
      break;

    case 2:
      {
	int username_len = strlen (username);
	int userpass_len = strlen (userpass);

	sprintf (info_msg, "SOCKSv%c server requires USERNAME/PASSWORD authentication.", msg_buf[0] + '0');
	FTP_info_msg (info_msg);

	sprintf (msg_buf, "%c%c%s%c%s", 1, username_len, username, userpass_len, userpass);
	if (send (FTPctrl->socket, &msg_buf, username_len + userpass_len + 3, 0) == -1)
	  return FTP_error (FALSE, "Unable to authenticate. Session aborded.", "", FTPctrl->socket);

	if (recv (FTPctrl->socket, &msg_buf, 2, 0) == -1)
	  return FTP_error (FALSE, "Got no response from Firewall. Session aborded.", "", FTPctrl->socket);

	if (msg_buf[1])
	  return FTP_error (FALSE, "Authentication failed. Session aborded.", "", FTPctrl->socket);

	FTP_info_msg ("Authentication succeeded.");
      }
      break;

    case 0xFF:
    default:
      sprintf (info_msg, "SOCKSv%c server requires UNSUPPORTED authentication method.", msg_buf[0] + '0');
      FTP_info_msg (info_msg);
      return FTP_error (FALSE, "Can't logging on Firewall. Session aborded.", "", FTPctrl->socket);
    }

  sprintf (info_msg, "Requesting to connect to %s on Port %i ...", FTPctrl->hostname, FTPctrl->port);
  FTP_info_msg (info_msg);
  {
    struct sockaddr_in data_sock_in;
    char *ptr = (char *) &data_sock_in.sin_port;
    data_sock_in.sin_port = htons (FTPctrl->port);
    sprintf (msg_buf, "%c%c%c%c%c%s%c%c", 5, 1, 0, 3, strlen (FTPctrl->hostname), FTPctrl->hostname, UC (ptr[0]), UC (ptr[1]));
  }

  if (send (FTPctrl->socket, &msg_buf, strlen (FTPctrl->hostname) + 7, 0) == -1)
    return FTP_error (FALSE, "Unable to send request. Session aborded.", "", FTPctrl->socket);;

  if (recv (FTPctrl->socket, &msg_buf, 5, 0) == -1)
    return FTP_error (FALSE, "Got no response from Firewall. Session aborded.", "", FTPctrl->socket);


  switch (msg_buf[3])
    {
    case 1:
      address_len = 4;
      break;
    case 3:
      address_len = msg_buf[4];
      break;
    case 4:
      address_len = 16;
      break;
    default:
      return FTP_error (FALSE, "Can't get Firewall response address type. Session aborded.", "", FTPctrl->socket);
    }

  address_len = address_len - 1 + 2;

  if (recv (FTPctrl->socket, (char *) &msg_buf + 5, (int) address_len, 0) == -1)
    return FTP_error (FALSE, "Got no response from Firewall. Session aborded.", "", FTPctrl->socket);


  if (msg_buf[1])
    {
      int error_code = msg_buf[1];
      if (error_code > 8)
	error_code = 0;
      sprintf (info_msg, "SOCKSv%c reports error : %s", msg_buf[0] + '0', socks_error_msg[error_code]);
      host_message (info_msg);
      return FTP_error (FALSE, "Unable to connect to FTP host. Session aborded.", "", FTPctrl->socket);
    }

  sprintf (info_msg, "SOCKSv%c reports succeeded request.", msg_buf[0] + '0');
  FTP_info_msg (info_msg);

  FTP_info_msg ("Connected to FTP server. Waiting for welcome message ...");

  if (!FTP_get_reply (FTPctrl))
    return FTP_error (FALSE, "Unable to get host  reply. Session aborded.", "", FTPctrl->socket);


  return (FTPctrl->is_connected = TRUE);
}







/* DATA CONNEXION functions */

/* STATIC :
 * FTP_set_passive
 * FTP_open_data_connection
 */

static char
FTP_set_passive (FTP * FTPctrl, struct sockaddr_in *sock_in)
{
  char *ptr;
  int i[6];
  register int f;
  unsigned char n[6];

  if (!FTP_exec_command (FTPctrl, "PASV"))
    return FTP_error (FALSE, "Unable to set PASV mode...", "", 0);

  ptr = (FTPctrl->reply) + 3;
  while (!isdigit (*(++ptr)));
  sscanf (ptr, "%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]);
  for (f = 0; f < 6; f++)
    n[f] = (unsigned char) (i[f] & 0xff);

  memcpy (&sock_in->sin_addr, &n[0], (size_t) 4);
  memcpy (&sock_in->sin_port, &n[4], (size_t) 2);

  return TRUE;
}


static int
FTP_open_data_connection (FTP * FTPctrl)
{
  int data_socket;
  struct sockaddr_in data_sock_in;
  int length = (int) sizeof (struct sockaddr_in);
  char *a, *p;
  char command_buf[256];
  char *msg_buf = command_buf;

  if (!(data_socket = socket (PF_INET, SOCK_STREAM, 0)))
    return FTP_error (FALSE, "Unable to create socket ", "", 0);


  if (FTPctrl->dmode == PASV_MODE)
    {
      if (!FTP_set_passive (FTPctrl, &data_sock_in))
	return FTP_error (0, "Unable to open data connection.", "", data_socket);

      data_sock_in.sin_family = AF_INET;
      if (connect (data_socket, (struct sockaddr *) &data_sock_in, sizeof (data_sock_in)) == -1)
	return FTP_error (0, "Unable to connect to data socket", "", data_socket);
    }

  else
    {
      data_sock_in.sin_family = AF_INET;
      inet_aton (FTPctrl->connect_address, &data_sock_in.sin_addr);
      data_sock_in.sin_port = 0;

      if (bind (data_socket, &data_sock_in, sizeof (data_sock_in)) == -1)
	return FTP_error (0, "Unable to bind data socket ", "", data_socket);

      if (getsockname (data_socket, &data_sock_in, &length) == -1)
	return FTP_error (0, "Error getting socket name", "", data_socket);

      if (listen (data_socket, 1) == -1)
	return FTP_error (0, "Listen failed", "", data_socket);

      a = (char *) &data_sock_in.sin_addr;
      p = (char *) &data_sock_in.sin_port;

      sprintf (command_buf, "PORT %d,%d,%d,%d,%d,%d",
	  UC (a[0]), UC (a[1]), UC (a[2]), UC (a[3]), UC (p[0]), UC (p[1]));
      if (!FTP_exec_command (FTPctrl, command_buf))
	return FTP_error (0, "PORT command failed.", "", data_socket);
    }


  sprintf (msg_buf, "Data Connection opened (%s:%i)\n", inet_ntoa (data_sock_in.sin_addr), ntohs (data_sock_in.sin_port));
  FTP_info_msg (msg_buf);

  return data_socket;
}









/* XFER functions */

/* STATIC :
 * alloc_xfer_buf
 * FTP_download
 * FTP_upload
 */

static char *
alloc_xfer_buf (size_t * xfer_buf_len)
{
  char *xfer_buf = NULL;

  if ((xfer_buf = (char *) malloc (*xfer_buf_len + 1)) == NULL)
    {
      *xfer_buf_len = *xfer_buf_len >> 1;
      if ((xfer_buf = (char *) malloc (*xfer_buf_len + 1)) == NULL)
	{
	  *xfer_buf_len = 256;
	  if ((xfer_buf = (char *) malloc (*xfer_buf_len + 1)) == NULL)
	    {
	      FTP_error (0, "Can't allocate Xfer buffer. Abording transfert.", "", 0);
	      return NULL;
	    }
	  else
	    FTP_error (0, "Xfer buffer reduced to 256 bytes.", "", 0);
	}
      else
	FTP_error (0, "Xfer buffer reduced to half size.", "", 0);
    }
  return xfer_buf;
}


/* DOWNLOAD */

static char
FTP_download (FTP * FTPctrl, int data_socket, FILE * local)
{
  char *xfer_buf;
  char none_transfert_error = TRUE;
  long xfered_bytes;
  int download_socket;
  struct sockaddr_in serv_sock_in;
  int length = (int) sizeof (struct sockaddr_in);
  time_t timer_elapsed;
  size_t xfer_buf_len = (int) recv_buf_len;

  if ((xfer_buf = alloc_xfer_buf (&xfer_buf_len)) == NULL)
    return 0;

  if (FTPctrl->dmode == PASV_MODE)
    download_socket = data_socket;
  else
    {
      if (!timeout_socket (FTPctrl, data_socket, SOCKET_READ))
	return FTP_error (FALSE, "Error retrieving data_socket. Transfer aborded.", "", data_socket);
      download_socket = accept (data_socket, &serv_sock_in, &length);
    }

  FTPctrl->data_socket = download_socket;
  FTPctrl->xfer_busy = TRUE;
  FTPctrl->xfered_bytes = 0;
  FTPctrl->timer_start = time (NULL);

  none_transfert_error = timeout_socket (FTPctrl, download_socket, SOCKET_READ);
  while (((xfered_bytes = recv (download_socket, xfer_buf, xfer_buf_len, 0)) > 0) && (none_transfert_error))
    {

      if (FTPctrl->tmode == TYPE_ASCII)
	{
	  register long compt = 0;
	  register long corrected_xfered_bytes = 0;
	  while (compt < xfered_bytes)
	    {
	      if ((xfer_buf[compt] != '\r'))
		{
		  fputc (xfer_buf[compt], local);
		  corrected_xfered_bytes++;
		}
	      compt++;
	    }
	  xfered_bytes = corrected_xfered_bytes;
	}
      else
	fwrite (xfer_buf, xfered_bytes, 1, local);


      FTPctrl->xfered_bytes += (int) xfered_bytes;

      FTPctrl->transfert_type != LIST_DOWNLOAD ? FTP_progress_download () : FTP_progress_list ();

      none_transfert_error = timeout_socket (FTPctrl, download_socket, SOCKET_READ);
    }

  fflush (local);
  fclose (local);
  free (xfer_buf);
  close (download_socket);
  close (data_socket);
  FTPctrl->data_socket = 0;
  FTPctrl->xfer_busy = FALSE;


  FTP_get_reply (FTPctrl);

  if (none_transfert_error && FTP_get_msg_code (FTPctrl->reply) == 226)
    {
      char msg_buf[256];
      if ((timer_elapsed = time (NULL) - FTPctrl->timer_start) == 0l)
	timer_elapsed = 1l;
      sprintf (msg_buf, "%ld byte(s) received in %.2f seconds (%3.2fKbytes/sec). ",
	       FTPctrl->xfered_bytes, (float) timer_elapsed, (float) FTPctrl->xfered_bytes / 1024.000 / (float) timer_elapsed);
      FTP_info_msg (msg_buf);
    }
  else
    return FTP_error (FALSE, "Error(s) occured when receiving file.", "", 0);

  return TRUE;
}



/* UPLOAD */

static char
FTP_upload (FTP * FTPctrl, int data_socket, FILE * local)
{
  char *xfer_buf;
  char none_transfert_error = TRUE;
  long xfered_bytes;
  int upload_socket;
  struct sockaddr_in serv_sock_in;
  int length = (int) sizeof (struct sockaddr_in);
  time_t timer_elapsed;
  size_t xfer_buf_len = (int) send_buf_len;
  FILE *out = NULL;


  if ((xfer_buf = alloc_xfer_buf (&xfer_buf_len)) == NULL)
    return FALSE;

  if (FTPctrl->dmode == PASV_MODE)
    upload_socket = data_socket;
  else
    {
      if (!timeout_socket (FTPctrl, data_socket, SOCKET_READ))
	return FTP_error (FALSE, "Error retrieving data_socket. Transfer aborded.", "", data_socket);
      upload_socket = accept (data_socket, &serv_sock_in, &length);
    }

  FTPctrl->data_socket = upload_socket;
  FTPctrl->xfer_busy = TRUE;
  FTPctrl->xfered_bytes = 0;
  FTPctrl->timer_start = time (NULL);

  if (FTPctrl->tmode == TYPE_ASCII)
    out = fdopen (dup (upload_socket), "a");

  while ((xfered_bytes = (size_t) fread (xfer_buf, 1, xfer_buf_len, local)) > 0 && (none_transfert_error))
    {

      if (FTPctrl->tmode == TYPE_ASCII)
	{
	  register long compt = 0;
	  register long corrected_xfered_bytes = 0;
	  while (compt < xfered_bytes)
	    {
	      if ((xfer_buf[compt] == '\n'))
		{
		  fputc ('\r', out);
		  corrected_xfered_bytes++;
		}
	      fputc (xfer_buf[compt++], out);
	      corrected_xfered_bytes++;
	    }
	  xfered_bytes = corrected_xfered_bytes;
	}
      else
	none_transfert_error = (send (upload_socket, xfer_buf, xfered_bytes, 0) != -1);


      if (none_transfert_error)
	{
	  FTPctrl->xfered_bytes += (int) xfered_bytes;
	  none_transfert_error = timeout_socket (FTPctrl, upload_socket, SOCKET_WRITE);
	  FTP_progress_download ();
	  if (want_abort)
	    none_transfert_error = FALSE;
	}
    }

  if (FTPctrl->tmode == TYPE_ASCII)
    {
      fflush (out);
      fclose (out);
    }
  fclose (local);
  free (xfer_buf);
  close (upload_socket);
  close (data_socket);
  FTPctrl->data_socket = 0;
  FTPctrl->xfer_busy = FALSE;


  FTP_get_reply (FTPctrl);

  if (none_transfert_error && FTP_get_msg_code (FTPctrl->reply) == 226)
    {
      char msg_buf[256];
      if ((timer_elapsed = time (NULL) - FTPctrl->timer_start) == 0l)
	timer_elapsed = 1l;
      sprintf (msg_buf, "%ld byte(s) sended in %.2f seconds (%3.2fKbytes/sec). ",
	       FTPctrl->xfered_bytes, (float) timer_elapsed,
	  (float) FTPctrl->xfered_bytes / 1024.000 / (float) timer_elapsed);
      FTP_info_msg (msg_buf);
    }
  else
    return FTP_error (FALSE, "Error(s) occured when sending file.", "", 0);

  return TRUE;
}




/* TELNET */

static char
FTP_send_telnet_interrupt (int this_socket)
{
  char msg = IAC;
  FILE *out = fdopen (dup (this_socket), "a");

  setlinebuf (out);
  fprintf (out, "%c%c", IAC, IP);
  fflush (out);

  if (send (this_socket, &msg, 1, MSG_OOB) == -1)
    {
      fclose (out);
      FTP_error (FALSE, " Failed to send telnet interrupt.", "", 0);
    }

  fprintf (out, "%c", DM);
  fflush (out);
  fclose (out);
  return TRUE;
}

/* EOF */
