/* tag: Tom Lord Tue Dec  4 14:41:25 2001 (protocol.c)
 */
/* protocol.c -
 *
 ****************************************************************
 * Copyright (C) 2001 Tom Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/os/errno.h"
#include "hackerlab/char/str.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/vu/vfdbuf.h"
#include "hackerlab/vu-network/url-socket.h"
#include "ftp-utils/clients/ftp-reply.h"
#include "ftp-utils/clients/ftp-command.h"
#include "ftp-utils/clients/ftp-protocol.h"


int
ftp_client_connect_and_login (int * in_fd,
			      int * out_fd,
			      alloc_limits limits,
			      int * errn,
			      int * code,
			      t_uchar ** text,
			      long * text_len,
			      t_uchar * host)
{
  t_uchar * user;
  t_uchar * passwd;
  t_uchar * hostname;
  int status;


  hostname = str_chr_index (host, '@');
  if (!hostname)
    {
      hostname = str_save (0, host);
      user = str_save (0, "anonymous");
      passwd = str_save (0, "ftp-utils");
    }
  else
    {
      user = host;
      passwd = str_chr_index_n (user, hostname - user, ':');

      if (passwd)
	{
	  ++passwd;
	  user = str_save_n (0, user, (passwd - 1) - user);
	  passwd = str_save_n (0, passwd, hostname - passwd);
	  hostname = str_save (0, hostname + 1);
	}
      else
	{
	  user = str_save_n (0, user, hostname - user);
	  passwd = str_save (0, "ftp-utils");
	  hostname = str_save (0, hostname + 1);
	}
    }

  /* hostname, user, passwd allocated strings.
   */
  *in_fd = url_inet_client (errn, hostname, 21);

  if (*in_fd < 0)
    {
      status = -1;
    }
  else
    {
      int e;

      *out_fd = vu_dup (errn, *in_fd);

      if (*out_fd < 0)
	{
	  vu_close (&e, *in_fd);
	  status = -1;
	}
      else
	{
	  if (   (0 > vfdbuf_buffer_fd (errn, *in_fd, 0, O_RDONLY, 0))
	      || (0 > vfdbuf_buffer_fd (errn, *out_fd, 0, O_WRONLY, 0))
	      || (0 > ftp_client_greet (limits, errn, code, text, text_len, *in_fd, *out_fd))
	      || (0 > ftp_client_login (limits, errn, code, text, text_len, *in_fd, *out_fd, user, passwd)))
	    {
	      vu_close (&e, *in_fd);
	      vu_close (&e, *out_fd);
	      status = -1;
	    }
	  else
	    {
	      status = 0;
	    }
	}
    }

  lim_free (0, hostname);
  lim_free (0, user);
  lim_free (0, passwd);

  return status;
}




int
ftp_client_greet (alloc_limits limits,
		  int * errn,
		  int * code,
		  t_uchar ** text,
		  long * len,
		  int in_fd,
		  int out_fd)
{
  int delayed;

  delayed = 0;

 retry:

  if (0 > ftp_client_read_reply (limits, errn, code, text, len, in_fd))
    return -1;

  if (*code == 120)
    {
      if (delayed)
	{
	  *errn = EINVAL;
	  return -1;
	}
      else
	{
	  delayed = 1;
	  goto retry;
	}
    }

  if (*code == 220)
    return 0;

  if (*code == 421)
    {
      *errn = 0;
      return -1;
    }

  *errn = EINVAL;
  return -1;
}


/****************************************************************
 * 
 * ftp_login
 *             USER <SP> <username> <CRLF>
 *             PASS <SP> <password> <CRLF>
 * 
 * not implemented
 *             ACCT <SP> <account-information> <CRLF>
 * 
 */


int
ftp_client_login (alloc_limits limits,
		  int * errn,
		  int * code,
		  t_uchar ** text,
		  long * text_len,
		  int in_fd,
		  int out_fd,
		  t_uchar * name,
		  t_uchar * passwd)
{
  int user_repl_code;
  t_uchar * user_repl_text;
  long user_repl_len;
  int pass_repl_code;
  t_uchar * pass_repl_text;
  long pass_repl_len;

  if (0 > ftp_client_printfmt (errn, out_fd, "USER %s\r\n", name))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &user_repl_code, &user_repl_text, &user_repl_len, in_fd))
    return -1;

  *code = user_repl_code;

  if (!text)
    lim_free (limits, user_repl_text);
  else
    {
      *text = user_repl_text;
      *text_len = user_repl_len;
    }
  
  
  {
    int first_digit;

    first_digit = user_repl_code / 100;

    switch (first_digit)
      {
      default:
      case 1:
	*errn = EINVAL;
	return -1;

      case 2:
	return 0;

      case 3:
	break;

      case 4:
      case 5:
	*errn = 0;
	return -1;
	  
      }
  }


  if (0 > ftp_client_printfmt (errn, out_fd, "PASS %s\r\n", passwd))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &pass_repl_code, &pass_repl_text, &pass_repl_len, in_fd))
    return -1;  

  *code = pass_repl_code;

  if (!text)
    lim_free (limits, pass_repl_text);
  else
    {
      *text = lim_realloc (limits, *text, user_repl_len + pass_repl_len + 1);
      mem_move (*text + user_repl_len, pass_repl_text, pass_repl_len);
      (*text)[user_repl_len + pass_repl_len] = 0;
      *text_len += pass_repl_len;
      lim_free (limits, pass_repl_text);
    }

  {
    int first_digit;

    first_digit = pass_repl_code / 100;

    switch (first_digit)
      {
      default:
      case 1:
	*errn = EINVAL;
	return -1;

      case 2:
	return 0;

      case 3:
	*errn = ENOSYS;
	return -1;

      case 4:
      case 5:
	*errn = 0;
	return -1;
	  
      }
  }
}


/****************************************************************
 * ftp_cwd
 *             CWD  <SP> <pathname> <CRLF>
 */

int
ftp_client_cwd (alloc_limits limits,
		int * errn,
		int * code,
		t_uchar ** text,
		long * text_len,
		int in_fd,
		int out_fd,
		t_uchar * path)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "CWD %s\r\n", path))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * ftp_cdup
 *             CDUP <CRLF>
 */

int
ftp_client_cdup (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "CDUP\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * not implemented
 * 
 *             SMNT <SP> <pathname> <CRLF>
 */


/****************************************************************
 * ftp_quit
 *             QUIT <CRLF>
 * 
 */
int
ftp_client_quit (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "QUIT\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


/****************************************************************
 * not implemented
 *             REIN <CRLF>
 * 
 */

/****************************************************************
 * ftp_port
 * 
 *             PORT <SP> <host-port> <CRLF>
 * 
 */
int
ftp_client_port (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd,
		 t_ulong host,
		 t_uint16 port)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "PORT %d,%d,%d,%d,%d,%d\r\n",
			       (int)((host >> 24) & 0xff),
			       (int)((host >> 16) & 0xff),
			       (int)((host >> 8) & 0xff),
			       (int)(host & 0xff),
			       (port >> 8) & 0xff,
			       port & 0xff))
			
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * ftp_pasv
 *
 *             PASV <CRLF>
 */
int
ftp_client_pasv (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_ulong * host,
		 t_uint16 * port,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd)
{
  t_uchar * t;
  long tl;
  int status;

  if (0 > ftp_client_printfmt (errn, out_fd, "PASV\r\n"))
    return -1;

  t = 0;
  tl = 0;

  if (!text)
    {
      text = &t;
      text_len = &tl;
    }

  if (0 > ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd))
    return -1;


  status = 0;

  if (*code != 227)
    {
      switch (*code / 100)
	{
	default:
	case 1:
	case 2:
	case 3:
	bogus_reply:
	  *errn = EINVAL;
	  status = -1;
	  break;

	case 4:
	case 5:
	  *errn = 0;
	  status = -1;
	  break;
	}
    }
  else
    {
      int er;
      t_uchar * start_byte;
      t_uchar * end_byte;
      t_uint h1;
      t_uint h2;
      t_uint h3;
      t_uint h4;
      t_uint p1;
      t_uint p2;
      t_ulong h;
      t_uint16 p;

      start_byte = str_chr_rindex (*text, '(');
      if (!start_byte)
	goto bogus_reply;

      ++start_byte;

      end_byte = str_chr_index (start_byte, ',');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &h1, start_byte, end_byte - start_byte);

      start_byte = end_byte + 1;
      end_byte = str_chr_index (start_byte, ',');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &h2, start_byte, end_byte - start_byte);

      start_byte = end_byte + 1;
      end_byte = str_chr_index (start_byte, ',');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &h3, start_byte, end_byte - start_byte);

      start_byte = end_byte + 1;
      end_byte = str_chr_index (start_byte, ',');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &h4, start_byte, end_byte - start_byte);

      start_byte = end_byte + 1;
      end_byte = str_chr_index (start_byte, ',');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &p1, start_byte, end_byte - start_byte);

      start_byte = end_byte + 1;
      end_byte = str_chr_index (start_byte, ')');
      if (!end_byte)
	goto bogus_reply;
      cvt_decimal_to_uint (&er, &p2, start_byte, end_byte - start_byte);

      h = (h1 << 24) | (h2 << 16) | (h3 << 8) | h4;
      p = (p1 << 8) | p2;

      if (host)
	*host = htonl (h);

      if (port)
	*port = htons (p);
    }

  if (t)
    lim_free (limits, t);

  return 0;
}

/****************************************************************
 * ftp_type_image
 * ftp_type_ascii
 * 
 *             TYPE <SP> <type-code> <CRLF>
 * 
 */
int
ftp_client_type_image (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "TYPE I\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


int
ftp_client_type_ascii (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "TYPE A\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


/****************************************************************
 * not implemented
 * 
 *             STRU <SP> <structure-code> <CRLF>
 * 
 */


/****************************************************************
 * ftp_stream_mode
 * 
 *             MODE <SP> <mode-code> <CRLF>
 * 
 */
int
ftp_client_stream_mode (alloc_limits limits,
			int * errn,
			int * code,
			t_uchar ** text,
			long * text_len,
			int in_fd,
			int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "MODE S\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * ftp_client_begin_retr
 * ftp_client_end_retr
 * 
 *             RETR <SP> <pathname> <CRLF>
 * 
 */
int
ftp_client_begin_retr (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd,
		       t_uchar * path)
{
  int c;

  if (0 > ftp_client_printfmt (errn, out_fd, "RETR %s\r\n", path))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
    return -1;  

  if (code)
    *code = c;

  switch (c / 100)
    {
    case 1:
      /* Not doing anything special for markers yet.
       *
       * 125 Data connection already open; transfer starting.
       * 150 File status okay; about to open data connection.
       * 110 Restart marker reply.
       *     In this case, the text is exact and not left to the
       *     particular implementation; it must read:
       *          MARK yyyy = mmmm
       *     Where yyyy is User-process data stream marker, and mmmm
       *     server's equivalent marker (note the spaces between markers
       *     and "=").
       */
      return 0;

    default:
    case 2:
    case 4:
    case 5:
      /*
       * 226 Closing data connection.
       *     Requested file action successful (for example, file
       *     transfer or file abort).
       * 250 Requested file action okay, completed.
       * 
       * 421 Service not available, closing control connection.
       *     This may be a reply to any command if the service knows it
       *     must shut down.
       * 425 Can't open data connection.
       * 426 Connection closed; transfer aborted.
       * 451 Requested action aborted: local error in processing.
       * 450 Requested file action not taken.
       *     File unavailable (e.g., file busy).
       *
       * 550 Requested action not taken.
       *     File unavailable (e.g., file not found, no access).
       * 500 Syntax error, command unrecognized.
       *     This may include errors such as command line too long.
       * 501 Syntax error in parameters or arguments.
       * 530 Not logged in.
       */
      *errn = 0;
      return -1;

    case 3:
      *errn = EINVAL;
      return -1;
    }
}


int
ftp_client_end_retr (alloc_limits limits,
		     int * errn,
		     int * code,
		     t_uchar ** text,
		     long * text_len,
		     int in_fd,
		     int out_fd)
{
  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * ftp_client_begin_stor
 * ftp_client_end_stor
 * 
 *             STOR <SP> <pathname> <CRLF>
 * 
 */
int
ftp_client_begin_stor (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd,
		       t_uchar * path)
{
  int c;

  if (0 > ftp_client_printfmt (errn, out_fd, "STOR %s\r\n", path))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
    return -1;  

  if (code)
    *code = c;

  switch (c / 100)
    {
    case 1:
      /* Not doing anything special for markers yet.
       */
      return 0;

    default:
    case 2:
    case 4:
    case 5:
      *errn = 0;
      return -1;

    case 3:
      *errn = EINVAL;
      return -1;
    }
}


int
ftp_client_end_stor (alloc_limits limits,
		     int * errn,
		     int * code,
		     t_uchar ** text,
		     long * text_len,
		     int in_fd,
		     int out_fd)
{
  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * not implemented
 * 
 *             STOU <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             APPE <SP> <pathname> <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             ALLO <SP> <decimal-integer>
 *                 [<SP> R <SP> <decimal-integer>] <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             REST <SP> <marker> <CRLF>
 * 
 */


/****************************************************************
 * ftp_rename
 * 
 *             RNFR <SP> <pathname> <CRLF>
 *             RNTO <SP> <pathname> <CRLF>
 * 
 */
int
ftp_client_rename (alloc_limits limits,
		   int * errn,
		   int * code,
		   t_uchar ** text,
		   long * text_len,
		   int in_fd,
		   int out_fd,
		   t_uchar * from,
		   t_uchar * to)
{
  int rnfr_code;
  t_uchar * rnfr_text;
  long rnfr_len;
  int rnto_code;
  t_uchar * rnto_text;
  long rnto_len;

  if (0 > ftp_client_printfmt (errn, out_fd, "RNFR %s\r\n", from))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &rnfr_code, &rnfr_text, &rnfr_len, in_fd))
    return -1;

  *code = rnfr_code;

  if (!text)
    lim_free (limits, rnfr_text);
  else
    {
      *text = rnfr_text;
      *text_len = rnfr_len;
    }

  {
    int first_digit;

    first_digit = rnfr_code / 100;

    switch (first_digit)
      {
      default:
      case 1:
      case 2:
	*errn = EINVAL;
	return -1;

      case 3:
	break;

      case 4:
      case 5:
	*errn = 0;
	return -1;
	  
      }
  }

  if (0 > ftp_client_printfmt (errn, out_fd, "RNTO %s\r\n", to))
    return -1;

  if (0 > ftp_client_read_reply (limits, errn, &rnto_code, &rnto_text, &rnto_len, in_fd))
    return -1;

  *code = rnto_code;

  if (!text)
    lim_free (limits, rnto_text);
  else
    {
      *text = lim_realloc (limits, *text, rnfr_len + rnto_len + 1);
      mem_move (*text + rnfr_len, rnto_text, rnto_len);
      (*text)[rnfr_len + rnfr_len] = 0;
      *text_len += rnto_len;
      lim_free (limits, rnto_text);
    }

  {
    int first_digit;

    first_digit = rnto_code / 100;

    switch (first_digit)
      {
      default:
      case 1:
      case 3:
	*errn = EINVAL;
	return -1;

      case 2:
	return 0;

      case 4:
      case 5:
	*errn = 0;
	return -1;
	  
      }
  }
}

int
ftp_client_dele (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd,
		 t_uchar * path)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "DELE %s\r\n", path))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


/****************************************************************
 * ftp_rmd
 * 
 *             RMD  <SP> <pathname> <CRLF>
 * 
 */
int
ftp_client_rmd (alloc_limits limits,
		int * errn,
		int * code,
		t_uchar ** text,
		long * text_len,
		int in_fd,
		int out_fd,
		t_uchar * path)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "RMD %s\r\n", path))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


/****************************************************************
 * ftp_mkd
 *             MKD  <SP> <pathname> <CRLF>
 * 
 */
int
ftp_client_mkd (alloc_limits limits,
		int * errn,
		int * code,
		t_uchar ** text,
		long * text_len,
		int in_fd,
		int out_fd,
		t_uchar * path)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "MKD %s\r\n", path))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}


/****************************************************************
 * ftp_client_pwd
 * 
 *             PWD  <CRLF>
 * 
 */
int
ftp_client_pwd (alloc_limits limits,
		int * errn,
		int * code,
		t_uchar ** path,
		long * path_len,
		t_uchar ** text,
		long * text_len,
		int in_fd,
		int out_fd)
{
  t_uchar * t;
  long tl;
  int status;

  if (0 > ftp_client_printfmt (errn, out_fd, "PWD\r\n"))
    return -1;

  t = 0;
  tl = 0;

  if (!text)
    {
      text = &t;
      text_len = &tl;
    }

  if (0 > ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd))
    return -1;


  status = 0;

  if (*code != 257)
    {
      switch (*code / 100)
	{
	default:
	case 1:
	case 2:
	case 3:
	bogus_reply:
	  *errn = EINVAL;
	  status = -1;
	  break;

	case 4:
	case 5:
	  *errn = 0;
	  status = -1;
	  break;
	}
    }
  else
    {
      t_uchar * path_start;
      t_uchar * path_end;
      long doubled_quotes;

      path_start = str_chr_index (*text, '"');

      if (!path_start)
	goto bogus_reply;

      path_start += 1;
      path_end = path_start;
      doubled_quotes = 0;

      while (1)
	{
	  if (path_end == (*text + *text_len))
	    goto bogus_reply;
	  else if (*path_end == '"')
	    {
	      if (((path_end + 1) < (*text + *text_len)) && (*(path_end + 1) == '"'))
		{
		  ++doubled_quotes;
		  path_end += 2;
		}
	      else
		break;
	    }
	  else
	    ++path_end;

	}

      if (path)
	{
	  t_uchar * p;
	  *path = lim_malloc (limits, 1 + (path_end - path_start) - doubled_quotes);
	  p = *path;

	  while (path_start < path_end)
	    {
	      *p = *path_start;
	      ++p;
	      if (*path_start == '"')
		++path_start;
	      ++path_start;
	    }
	  *p = 0;

	  *path_len = p - *path;
	}

      status = 0;
    }

  if (t)
    lim_free (limits, t);

  return status;
}


/****************************************************************
 * ftp_client_begin_list
 * ftp_client_end_list
 * 
 *             LIST [<SP> <pathname>] <CRLF>
 * 
 * with wu-ftp:
 * 
 *             LIST <SP> -a [<SP> <pathname>] <CRLF>
 * 
 */
int
ftp_client_begin_list (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd,
                       int is_wu_ftp,
		       t_uchar * path)
{
  int c;

  if (path && path[0])
    {
      if (0 > ftp_client_printfmt (errn, out_fd, "LIST%s %s\r\n", (is_wu_ftp ? " -a" : ""), path))
	return -1;
    }
  else
    {
      if (0 > ftp_client_printfmt (errn, out_fd, "LIST%s\n", (is_wu_ftp ? " -a" : "")))
	return -1;
    }

  if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
    return -1;  

  if (code)
    *code = c;

  switch (c / 100)
    {
    case 1:
      return 0;

    default:
    case 2:
    case 4:
    case 5:
      *errn = 0;
      return -1;

    case 3:
      *errn = EINVAL;
      return -1;
    }
}


int
ftp_client_end_list (alloc_limits limits,
		     int * errn,
		     int * code,
		     t_uchar ** text,
		     long * text_len,
		     int in_fd,
		     int out_fd)
{
  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}




/****************************************************************
 * ftp_client_begin_nlst
 * ftp_client_end_nlst
 * 
 *             NLST [<SP> <pathname>] <CRLF>
 * 
 * 
 * with wu-ftp:
 * 
 *             NLST <SP> -a [<SP> <pathname>] <CRLF>
 * 
 */

int
ftp_client_begin_nlst (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int in_fd,
		       int out_fd,
                       int is_wu_ftp,
		       t_uchar * path)
{
  int c;

  if (path && path[0])
    {
      if (0 > ftp_client_printfmt (errn, out_fd, "NLST%s %s\r\n", (is_wu_ftp ? " -a" : ""), path))
	return -1;
    }
  else
    {
      if (0 > ftp_client_printfmt (errn, out_fd, "NLST%s\r\n", (is_wu_ftp ? " -a" : "")))
	return -1;
    }

  if (0 > ftp_client_read_reply (limits, errn, &c, text, text_len, in_fd))
    return -1;  

  if (code)
    *code = c;

  switch (c / 100)
    {
    case 1:
      return 0;

    default:
    case 2:
    case 4:
    case 5:
      *errn = 0;
      return -1;

    case 3:
      *errn = EINVAL;
      return -1;
    }
}


int
ftp_client_end_nlst (alloc_limits limits,
		     int * errn,
		     int * code,
		     t_uchar ** text,
		     long * text_len,
		     int in_fd,
		     int out_fd)
{
  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}



/****************************************************************
 * not implemented
 * 
 *             SITE <SP> <string> <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             SYST <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             STAT [<SP> <pathname>] <CRLF>
 * 
 */


/****************************************************************
 * not implemented
 * 
 *             HELP [<SP> <string>] <CRLF>
 * 
 */


/****************************************************************
 * ftp_noop
 * 
 *             NOOP <CRLF>
 */
int
ftp_client_noop (alloc_limits limits,
		 int * errn,
		 int * code,
		 t_uchar ** text,
		 long * text_len,
		 int in_fd,
		 int out_fd)
{
  if (0 > ftp_client_printfmt (errn, out_fd, "NOOP\r\n"))
    return -1;

  return ftp_client_read_simple_cmd_reply (limits, errn, code, text, text_len, in_fd);
}
