view lib/rfc959.c @ 48:e5f6054590b5

2002-11-5 Brian Masney <masneyb@gftp.org> * lib/*.c src/gtk/*.c - removed function declarations for the static functions from the top of the file. I had to rearrange the order of a bunch of functions to avoid compiler warnings * lib/gftp.h - include sys/sysmacros.h. If major() and minor() isn't defined, give a compiler warning and define our own * lib/local.c (local_get_next_file) - if this file is a device, store the major/minor number in the file size * src/gtk/misc-gtk.c (add_file_listbox) - if this file is a device, use the major() and minor() macros to display the major and minor number
author masneyb
date Wed, 06 Nov 2002 02:20:25 +0000
parents 4bcfaf6307b5
children c01d91c10f6c
line wrap: on
line source

/*****************************************************************************/
/*  rfc959.c - General purpose routines for the FTP protocol (RFC 959)       */
/*  Copyright (C) 1998-2002 Brian Masney <masneyb@gftp.org>                  */
/*                                                                           */
/*  This program is free software; you can redistribute it and/or modify     */
/*  it under the terms of the GNU General Public License as published by     */
/*  the Free Software Foundation; either version 2 of the License, or        */
/*  (at your option) any later version.                                      */
/*                                                                           */
/*  This program is distributed in the hope that it will be useful,          */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/*  GNU General Public License for more details.                             */
/*                                                                           */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA      */
/*****************************************************************************/

#include "gftp.h"
static const char cvsid[] = "$Id$";

static int
rfc959_read_response (gftp_request * request)
{
  char tempstr[255], code[4];

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  *code = '\0';
  if (request->last_ftp_response)
    {
      g_free (request->last_ftp_response);
      request->last_ftp_response = NULL;
    }

  do
    {
      if (!gftp_fgets (request, tempstr, sizeof (tempstr), request->sockfd))
	break;
      tempstr[strlen (tempstr) - 1] = '\0';
      if (tempstr[strlen (tempstr) - 1] == '\r')
	tempstr[strlen (tempstr) - 1] = '\0';
      if (isdigit ((int) *tempstr) && isdigit ((int) *(tempstr + 1))
	  && isdigit ((int) *(tempstr + 2)))
	{
	  strncpy (code, tempstr, 3);
	  code[3] = ' ';
	}
      request->logging_function (gftp_logging_recv, request->user_data,
				 "%s\n", tempstr);
    }
  while (strncmp (code, tempstr, 4) != 0);

  if (ferror (request->sockfd)) 
    {
      request->logging_function (gftp_logging_send, request->user_data,
                                 "Error reading from socket: %s\n",
                                 g_strerror (errno));
      gftp_disconnect (request);
      return (-1);
    }

  request->last_ftp_response = g_malloc (strlen (tempstr) + 1);
  strcpy (request->last_ftp_response, tempstr);

  if (request->last_ftp_response[0] == '4' &&
      request->last_ftp_response[1] == '2')
    gftp_disconnect (request);

  return (*request->last_ftp_response);
}


static int
rfc959_send_command (gftp_request * request, const char *command)
{
  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (command != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if (strncmp (command, "PASS", 4) == 0)
    {
      request->logging_function (gftp_logging_send, request->user_data, 
                                 "PASS xxxx\n");
    }
  else if (strncmp (command, "ACCT", 4) == 0)
    {
      request->logging_function (gftp_logging_send, request->user_data, 
                                 "ACCT xxxx\n");
    }
  else
    {
      request->logging_function (gftp_logging_send, request->user_data, "%s",
                                 command);
    }

  if (gftp_fwrite (request, command, strlen (command), 
                   request->sockfd_write) < 0)
    return (-1);

  return (rfc959_read_response (request));
}


static char *
parse_ftp_proxy_string (gftp_request * request)
{
  char *startpos, *endpos, *oldstr, *newstr, *newval, *tempport;

  g_return_val_if_fail (request != NULL, NULL);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, NULL);

  newstr = g_malloc (1);
  *newstr = '\0';
  startpos = endpos = request->proxy_config;
  while (*endpos != '\0')
    {
      tempport = NULL;
      if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'p')
	{
	  switch (tolower ((int) *(endpos + 2)))
	    {
	    case 'u':
	      newval = request->proxy_username;
	      break;
	    case 'p':
	      newval = request->proxy_password;
	      break;
	    case 'h':
	      newval = request->proxy_hostname;
	      break;
	    case 'o':
	      tempport = g_strdup_printf ("%d", request->proxy_port);
	      newval = tempport;
	      break;
	    case 'a':
	      newval = request->proxy_account;
	      break;
	    default:
	      endpos++;
	      continue;
	    }
	}
      else if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'h')
	{
	  switch (tolower ((int) *(endpos + 2)))
	    {
	    case 'u':
	      newval = request->username;
	      break;
	    case 'p':
	      newval = request->password;
	      break;
	    case 'h':
	      newval = request->hostname;
	      break;
	    case 'o':
	      tempport = g_strdup_printf ("%d", request->port);
	      newval = tempport;
	      break;
	    case 'a':
	      newval = request->account;
	      break;
	    default:
	      endpos++;
	      continue;
	    }
	}
      else if (*endpos == '%' && tolower ((int) *(endpos + 1)) == 'n')
	{
	  *endpos = '\0';
	  oldstr = newstr;
	  newstr = g_strconcat (oldstr, startpos, "\r\n", NULL);
	  g_free (oldstr);
	  endpos += 2;
	  startpos = endpos;
	  continue;
	}
      else
	{
	  endpos++;
	  continue;
	}

      *endpos = '\0';
      oldstr = newstr;
      if (!newval)
	newstr = g_strconcat (oldstr, startpos, NULL);
      else
	newstr = g_strconcat (oldstr, startpos, newval, NULL);
      if (tempport)
	{
	  g_free (tempport);
	  tempport = NULL;
	}
      g_free (oldstr);
      endpos += 3;
      startpos = endpos;
    }
  return (newstr);
}


static int
rfc959_chdir (gftp_request * request, const char *directory)
{
  char ret, *tempstr, *dir;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (directory != NULL, -2);

  if (strcmp (directory, "..") == 0)
    ret = rfc959_send_command (request, "CDUP\r\n");
  else
    {
      tempstr = g_strconcat ("CWD ", directory, "\r\n", NULL);
      ret = rfc959_send_command (request, tempstr);
      g_free (tempstr);
    }

  if (ret != '2')
    return (-2);

  if (directory != request->directory)
    {
      if (request->directory)
        {
          g_free (request->directory);
          request->directory = NULL;
        }

      if (rfc959_send_command (request, "PWD\r\n") != '2')
        return (-2);

      tempstr = strchr (request->last_ftp_response, '"');
      if (tempstr != NULL)
        dir = tempstr + 1;
      else
        return (-2);

      tempstr = strchr (dir, '"');
      if (tempstr != NULL)
        *tempstr = '\0';
      else
        {
          gftp_disconnect (request);
          return (-2);
        }

      request->directory = g_malloc (strlen (dir) + 1);
      strcpy (request->directory, dir);
    }

  return (0);
}


static int
rfc959_connect (gftp_request * request)
{
  char tempchar, *startpos, *endpos, *tempstr, *dir;
  int sock, ret, resp;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->hostname != NULL, -2);

  if (request->username == NULL || *request->username == '\0')
    {
      gftp_set_username (request, "anonymous");
      gftp_set_password (request, emailaddr);
    }
  else if (strcasecmp (request->username, "anonymous") == 0)
    gftp_set_password (request, emailaddr);
    
  if (request->sockfd != NULL)
    return (0);

  if ((sock = gftp_connect_server (request, "ftp")) < 0)
    return (-1);

  if ((request->sockfd = fdopen (sock, "rb+")) == NULL)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fdopen() socket: %s\n"),
                                 g_strerror (errno));
      close (sock);
      return (-2);
    }

  if ((request->sockfd_write = fdopen (dup (sock), "wb+")) == NULL)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fdopen() socket: %s\n"),
                                 g_strerror (errno));
      gftp_disconnect (request);
      return (-2);
    }

  /* Get the banner */
  if (rfc959_read_response (request) != '2')
    {
      gftp_disconnect (request);
      return (-2);
    }

  /* Login the proxy server if available */
  if (request->use_proxy)
    {
      resp = '3';
      startpos = endpos = tempstr = parse_ftp_proxy_string (request);
      while ((resp == '3' || resp == '2') && *startpos != '\0')
	{
	  if (*endpos == '\n' || *endpos == '\0')
	    {
	      tempchar = *(endpos + 1);
	      if (*endpos != '\0')
		*(endpos + 1) = '\0';
	      if ((resp = rfc959_send_command (request, startpos)) < 0)
                return (-2);
	      if (*endpos != '\0')
		*(endpos + 1) = tempchar;
	      else
		break;
	      startpos = endpos + 1;
	    }
	  endpos++;
	}
      g_free (tempstr);
    }
  else
    {
      tempstr = g_strconcat ("USER ", request->username, "\r\n", NULL);
      resp = rfc959_send_command (request, tempstr);
      g_free (tempstr);
      if (resp < 0)
        return (-2);
      if (resp == '3')
	{
	  tempstr = g_strconcat ("PASS ", request->password, "\r\n", NULL);
	  resp = rfc959_send_command (request, tempstr);
	  g_free (tempstr);
          if (resp < 0)
            return (-2);
        }
      if (resp == '3' && request->account)
	{
	  tempstr = g_strconcat ("ACCT ", request->account, "\r\n", NULL);
	  resp = rfc959_send_command (request, tempstr);
	  g_free (tempstr);
          if (resp < 0)
            return (-2);
	}
    }

  if (resp != '2')
    {
      gftp_disconnect (request);
      return (-2);
    }

  if (request->data_type == GFTP_TYPE_BINARY)
    tempstr = "TYPE I\r\n";
  else
    tempstr = "TYPE A\r\n";

  if (rfc959_send_command (request, tempstr) < 0)
    return (-2);

  ret = -1;
  if (request->directory != NULL && *request->directory != '\0')
    {
      ret = rfc959_chdir (request, request->directory);
      if (request->sockfd == NULL)
        return (-2);
    }

  if (ret != 0)
    {
      if (rfc959_send_command (request, "PWD\r\n") != '2' || 
          request->sockfd == NULL)
        {
          gftp_disconnect (request);
  	  return (-2);
        }

      if ((tempstr = strchr (request->last_ftp_response, '"')) == NULL)
        {
          gftp_disconnect (request);
	  return (-2);
        }
      dir = tempstr + 1;

      if ((tempstr = strchr (dir, '"')) == NULL)
	{
          gftp_disconnect (request);
          return (0);
        }
      if (tempstr != NULL)
	*tempstr = '\0';

      request->directory = g_malloc (strlen (dir) + 1);
      strcpy (request->directory, dir);
    }

  if (request->sockfd == NULL)
    return (-2);

  return (0);
}


static void
rfc959_disconnect (gftp_request * request)
{
  g_return_if_fail (request != NULL);
  g_return_if_fail (request->protonum == GFTP_FTP_NUM);

  if (request->sockfd != NULL)
    {
      request->logging_function (gftp_logging_misc, request->user_data,
				 _("Disconnecting from site %s\n"),
				 request->hostname);
      fclose (request->sockfd);
      fclose (request->sockfd_write);
      request->sockfd = request->sockfd_write = NULL;
      if (request->datafd)
	{
	  fclose (request->datafd);
	  request->datafd = NULL;
	}
    }
}


static int
rfc959_data_connection_new (gftp_request * request)
{
  char *pos, *pos1, resp, *command;
  struct sockaddr_in data_addr;
  size_t data_addr_len;
  unsigned int temp[6];
  unsigned char ad[6];
  int i, sock;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if ((sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
      request->logging_function (gftp_logging_error, request->user_data,
				 _("Failed to create a socket: %s\n"),
				 g_strerror (errno));
      gftp_disconnect (request);
      return (-1);
    }

  data_addr_len = sizeof (data_addr);
  memset (&data_addr, 0, data_addr_len);
  data_addr.sin_family = AF_INET;

  if (request->transfer_type == gftp_transfer_passive)
    {
      if ((resp = rfc959_send_command (request, "PASV\r\n")) != '2')
	{
          if (request->sockfd == NULL)
            return (-2);

	  request->transfer_type = gftp_transfer_active;
	  return (rfc959_data_connection_new (request));
	}
      pos = request->last_ftp_response + 4;
      while (!isdigit ((int) *pos) && *pos != '\0')
        pos++;
      if (*pos == '\0')
        {
          gftp_disconnect (request);
          close (sock);
          return (-2);
        }
      if (sscanf (pos, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
                  &temp[3], &temp[4], &temp[5]) != 6)
        {
          gftp_disconnect (request);
          close (sock);
          return (-2);
        }
      for (i = 0; i < 6; i++)
        ad[i] = (unsigned char) (temp[i] & 0xff);

      memcpy (&data_addr.sin_addr, &ad[0], 4);
      memcpy (&data_addr.sin_port, &ad[4], 2);
      if (connect (sock, (struct sockaddr *) &data_addr, data_addr_len) == -1)
        {
          request->logging_function (gftp_logging_error, request->user_data,
                                    _("Cannot create a data connection: %s\n"),
                                    g_strerror (errno));
          gftp_disconnect (request);
          close (sock);
          return (-1);
	}
    }
  else
    {
      getsockname (fileno (request->sockfd), (struct sockaddr *) &data_addr,
		   &data_addr_len);
      data_addr.sin_port = 0;
      if (bind (sock, (struct sockaddr *) &data_addr, data_addr_len) == -1)
	{
	  request->logging_function (gftp_logging_error, request->user_data,
				     _("Cannot bind a port: %s\n"),
				     g_strerror (errno));
          gftp_disconnect (request);
	  close (sock);
	  return (-1);
	}

      getsockname (sock, (struct sockaddr *) &data_addr, &data_addr_len);
      if (listen (sock, 1) == -1)
	{
	  request->logging_function (gftp_logging_error, request->user_data,
				     _("Cannot listen on port %d: %s\n"),
				     ntohs (data_addr.sin_port),
				     g_strerror (errno));
          gftp_disconnect (request);
	  close (sock);
	  return (-1);
	}
      pos = (char *) &data_addr.sin_addr;
      pos1 = (char *) &data_addr.sin_port;
      command = g_strdup_printf ("PORT %u,%u,%u,%u,%u,%u\r\n",
				 pos[0] & 0xff, pos[1] & 0xff, pos[2] & 0xff,
				 pos[3] & 0xff, pos1[0] & 0xff,
				 pos1[1] & 0xff);
      resp = rfc959_send_command (request, command);
      g_free (command);
      if (resp != '2')
	{
          gftp_disconnect (request);
	  close (sock);
	  return (-2);
	}
    }

  if ((request->datafd = fdopen (sock, "rb+")) == NULL)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fdopen() socket: %s\n"),
                                 g_strerror (errno));
      gftp_disconnect (request);
      return (-2);
    }

  return (0);
}


static int
rfc959_accept_active_connection (gftp_request * request)
{
  struct sockaddr_in cli_addr;
  size_t cli_addr_len;
  int infd;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->datafd != NULL, -2);
  g_return_val_if_fail (request->transfer_type == gftp_transfer_active, -2);

  cli_addr_len = sizeof (cli_addr);
  if ((infd = accept (fileno (request->datafd), (struct sockaddr *) &cli_addr,
       &cli_addr_len)) == -1)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                _("Cannot accept connection from server: %s\n"),
                                g_strerror (errno));
      gftp_disconnect (request);
      return (-1);
    }

  fclose (request->datafd);

  if ((request->datafd = fdopen (infd, "rb+")) == NULL)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fdopen() socket: %s\n"),
                                 g_strerror (errno));
      gftp_disconnect (request);
      return (-2);
    }
  return (0);
}


static long
rfc959_get_file (gftp_request * request, const char *filename, FILE * fd,
                 off_t startsize)
{
  char *command, *tempstr, resp;
  int ret, flags;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (filename != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if (fd != NULL)
    request->datafd = fd;

  if (request->datafd == NULL && 
      (ret = rfc959_data_connection_new (request)) < 0)
    return (ret);

  flags = fcntl (fileno (request->datafd), F_GETFL, 0);
  if (fcntl (fileno (request->datafd), F_SETFL, flags | O_NONBLOCK) < 0)
    {
      fclose (request->datafd);
      request->datafd = NULL;
      return (-1);
    }

  if (startsize > 0)
    {
#if defined (_LARGEFILE_SOURCE)
      command = g_strdup_printf ("REST %lld\r\n", startsize); 
#else
      command = g_strdup_printf ("REST %ld\r\n", startsize); 
#endif
      resp = rfc959_send_command (request, command);
      g_free (command);

      if (resp != '3')
        {
          fclose (request->datafd);
          request->datafd = NULL;
	  return (-2);
        }
    }

  tempstr = g_strconcat ("RETR ", filename, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);

  if (ret != '1')
    return (-2);

  if (request->transfer_type == gftp_transfer_active &&
      (ret = rfc959_accept_active_connection (request)) < 0)
    return (ret);

  if ((tempstr = strrchr (request->last_ftp_response, '(')) == NULL)
    {
      tempstr = request->last_ftp_response + 4;
      while (!isdigit ((int) *tempstr) && *tempstr != '\0')
	tempstr++;
    }
  else
    tempstr++;

  return (strtol (tempstr, NULL, 10) + startsize);
}


static int
rfc959_put_file (gftp_request * request, const char *filename, FILE * fd,
                 off_t startsize, off_t totalsize)
{
  char *command, *tempstr, resp;
  int ret, flags;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (filename != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if (fd != NULL)
    fd = request->datafd;

  if (request->datafd == NULL && 
      (ret = rfc959_data_connection_new (request)) < 0)
    return (ret);

  flags = fcntl (fileno (request->datafd), F_GETFL, 0);
  if (fcntl (fileno (request->datafd), F_SETFL, flags | O_NONBLOCK) < 0)
    {
      fclose (request->datafd);
      request->datafd = NULL;
      return (-1);
    }

  if (startsize > 0)
    {
#if defined (_LARGEFILE_SOURCE)
      command = g_strdup_printf ("REST %lld\r\n", startsize); 
#else
      command = g_strdup_printf ("REST %ld\r\n", startsize); 
#endif
      resp = rfc959_send_command (request, command);
      g_free (command);
      if (resp != '3')
        {
          fclose (request->datafd);
          request->datafd = NULL;
	  return (-2);
        }
    }

  tempstr = g_strconcat ("STOR ", filename, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  if (ret != '1')
    return (-2);

  if (request->transfer_type == gftp_transfer_active && 
      (ret = rfc959_accept_active_connection (request)) < 0)
    return (ret);

  return (0);
}

static long 
rfc959_transfer_file (gftp_request *fromreq, const char *fromfile, 
                      off_t fromsize, gftp_request *toreq, 
                      const char *tofile, off_t tosize)
{
  char *tempstr, *pos, *endpos;

  g_return_val_if_fail (fromreq != NULL, -2);
  g_return_val_if_fail (fromfile != NULL, -2);
  g_return_val_if_fail (toreq != NULL, -2);
  g_return_val_if_fail (tofile != NULL, -2);
  g_return_val_if_fail (fromreq->sockfd != NULL, -2);
  g_return_val_if_fail (toreq->sockfd != NULL, -2);

  fromreq->transfer_type = gftp_transfer_passive;
  toreq->transfer_type = gftp_transfer_active;

  if (rfc959_send_command (fromreq, "PASV\r\n") != '2' ||
      fromreq->sockfd == NULL)
    return (-2);

  pos = fromreq->last_ftp_response + 4;
  while (!isdigit ((int) *pos) && *pos != '\0') 
    pos++;
  if (*pos == '\0') 
    return (-2);

  endpos = pos;
  while (*endpos != ')' && *endpos != '\0') 
    endpos++;
  if (*endpos == ')') 
    *endpos = '\0';

  tempstr = g_strconcat ("PORT ", pos, "\r\n", NULL);
  if (rfc959_send_command (toreq, tempstr) != '2')
     {
       g_free (tempstr);
       return (-2);
     }
  g_free (tempstr);

  tempstr = g_strconcat ("RETR ", fromfile, "\r\n", NULL);
  if (gftp_fwrite (fromreq, tempstr, strlen (tempstr), 
                   fromreq->sockfd_write) < 0)
     {
       g_free (tempstr);
       return (-2);
     }
  g_free (tempstr);

  tempstr = g_strconcat ("STOR ", tofile, "\r\n", NULL);
  if (gftp_fwrite (toreq, tempstr, strlen (tempstr), toreq->sockfd_write) < 0)
     {
       g_free (tempstr);
       return (-2);
     }
  g_free (tempstr);

  if (rfc959_read_response (fromreq) < 0)
    return (-2);
  if (rfc959_read_response (toreq) < 0)
    return (-2);

  return (0);
}


static int
rfc959_end_transfer (gftp_request * request)
{
  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if (request->datafd)
    {
      fclose (request->datafd);
      request->datafd = NULL;
    }
  return (rfc959_read_response (request) == '2' ? 0 : -2);
}


static int
rfc959_abort_transfer (gftp_request * request)
{
  int ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if (request->datafd)
    {
      fclose (request->datafd);
      request->datafd = NULL;
    }

  /* We need to read two lines of output. The first one is acknowleging
     the transfer and the second line acknowleges the ABOR command */
  rfc959_send_command (request, "ABOR\r\n");

  if (request->sockfd != NULL)
    {
      ret = rfc959_read_response (request);

      if (ret != '2')
        gftp_disconnect (request);
    }
  
  return (0);
}


static int
rfc959_list_files (gftp_request * request)
{
  char *tempstr, parms[3];
  int ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  if ((ret = rfc959_data_connection_new (request)) < 0)
    return (ret);

  *parms = '\0';
  strcat (parms, show_hidden_files ? "a" : "");
  strcat (parms, resolve_symlinks ? "L" : "");
  tempstr = g_strconcat ("LIST", *parms != '\0' ? " -" : "", parms, "\r\n", 
                         NULL); 

  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);

  if (ret != '1')
    return (-2);

  ret = 0;
  if (request->transfer_type == gftp_transfer_active)
    ret = rfc959_accept_active_connection (request);

  return (ret);
}


int
rfc959_get_next_file (gftp_request * request, gftp_file * fle, FILE * fd)
{
  char tempstr[255];
  size_t len;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (fle != NULL, -2);
  g_return_val_if_fail (fd != NULL, -2);

  if (request->last_dir_entry)
    {
      g_free (request->last_dir_entry);
      request->last_dir_entry = NULL;
    }
  do
    {
      if (!gftp_fgets (request, tempstr, sizeof (tempstr), fd))
	{
          gftp_file_destroy (fle);
          if (ferror (fd))
            gftp_disconnect (request);
	  return (-2);
	} 
      tempstr[sizeof (tempstr) - 1] = '\0';

      if (gftp_parse_ls (tempstr, fle) != 0)
	{
	  if (strncmp (tempstr, "total", strlen("total")) != 0 &&
	      strncmp (tempstr, _("total"), strlen(_("total"))) != 0)
	    request->logging_function (gftp_logging_misc, request->user_data,
				       _("Warning: Cannot parse listing %s\n"),
				       tempstr);
	  gftp_file_destroy (fle);
	  continue;
	}
      else
	break;
    }
  while (1);

  len = strlen (tempstr);
  if (!request->cached)
    {
      request->last_dir_entry = g_malloc (len + 1);
      strcpy (request->last_dir_entry, tempstr);
      request->last_dir_entry_len = len;
    }
  return (len);
}


static int
rfc959_set_data_type (gftp_request * request, int data_type)
{
  char *tempstr;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);

  if (request->sockfd != NULL && request->data_type != data_type)
    {
      if (data_type == GFTP_TYPE_BINARY)
	tempstr = "TYPE I\r\n";
      else
	tempstr = "TYPE A\r\n";

      if (rfc959_send_command (request, tempstr) != '2')
	return (-2);
    }
  request->data_type = data_type;
  return (0);
}


static off_t
rfc959_get_file_size (gftp_request * request, const char *filename)
{
  char *tempstr;
  int ret;

  g_return_val_if_fail (request != NULL, 0);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (filename != NULL, 0);
  g_return_val_if_fail (request->sockfd != NULL, 0);

  tempstr = g_strconcat ("SIZE ", filename, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  if (ret < 0)
    return (-2);

  if (*request->last_ftp_response != '2')
    return (0);
  return (strtol (request->last_ftp_response + 4, NULL, 10));
}


static int
rfc959_rmdir (gftp_request * request, const char *directory)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (directory != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_strconcat ("RMD ", directory, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (ret == '2' ? 0 : -2);
}


static int
rfc959_rmfile (gftp_request * request, const char *file)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (file != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_strconcat ("DELE ", file, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (ret == '2' ? 0 : -2);
}


static int
rfc959_mkdir (gftp_request * request, const char *directory)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (directory != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_strconcat ("MKD ", directory, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (ret == '2' ? 0 : -2);
}


static int
rfc959_rename (gftp_request * request, const char *oldname,
	       const char *newname)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (oldname != NULL, -2);
  g_return_val_if_fail (newname != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_strconcat ("RNFR ", oldname, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  if (ret != '3')
    return (-2);

  tempstr = g_strconcat ("RNTO ", newname, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (ret == '2' ? 0 : -2);
}


static int
rfc959_chmod (gftp_request * request, const char *file, int mode)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (file != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_malloc (strlen (file) + (mode / 10) + 16);
  sprintf (tempstr, "SITE CHMOD %d %s\r\n", mode, file);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (ret == '2' ? 0 : -2);
}


static int
rfc959_site (gftp_request * request, const char *command)
{
  char *tempstr, ret;

  g_return_val_if_fail (request != NULL, -2);
  g_return_val_if_fail (request->protonum == GFTP_FTP_NUM, -2);
  g_return_val_if_fail (command != NULL, -2);
  g_return_val_if_fail (request->sockfd != NULL, -2);

  tempstr = g_strconcat ("SITE ", command, "\r\n", NULL);
  ret = rfc959_send_command (request, tempstr);
  g_free (tempstr);
  return (request->sockfd != NULL ? ret : -2);
}


void
rfc959_init (gftp_request * request)
{
  g_return_if_fail (request != NULL);

  request->protonum = GFTP_FTP_NUM;
  request->init = rfc959_init;
  request->destroy = NULL; 
  request->connect = rfc959_connect;
  request->disconnect = rfc959_disconnect;
  request->get_file = rfc959_get_file;
  request->put_file = rfc959_put_file;
  request->transfer_file = rfc959_transfer_file;
  request->get_next_file_chunk = NULL;
  request->put_next_file_chunk = NULL;
  request->end_transfer = rfc959_end_transfer;
  request->abort_transfer = rfc959_abort_transfer;
  request->list_files = rfc959_list_files;
  request->get_next_file = rfc959_get_next_file;
  request->set_data_type = rfc959_set_data_type;
  request->get_file_size = rfc959_get_file_size;
  request->chdir = rfc959_chdir;
  request->rmdir = rfc959_rmdir;
  request->rmfile = rfc959_rmfile;
  request->mkdir = rfc959_mkdir;
  request->rename = rfc959_rename;
  request->chmod = rfc959_chmod;
  request->set_file_time = NULL;
  request->site = rfc959_site;
  request->parse_url = NULL;
  request->url_prefix = "ftp";
  request->protocol_name = "FTP";
  request->need_hostport = 1;
  request->need_userpass = 1;
  request->use_cache = 1;
  request->use_threads = 1;
  request->always_connected = 0;
  gftp_set_config_options (request);
}