/* reply.c - reading FTP reply messages
 *
 ****************************************************************
 * 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/bugs/panic.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/vu/vu-utils.h"
#include "ftp-utils/clients/ftp-reply.h"



/****************************************************************
 * From rfc 959
 * 
 * An FTP reply consists of a three digit number (transmitted as
 * three alphanumeric characters) followed by some text.  The number
 * is intended for use by automata to determine what state to enter
 * next; the text is intended for the human user.  It is intended
 * that the three digits contain enough encoded information that the
 * user-process (the User-PI) will not need to examine the text and
 * may either discard it or pass it on to the user, as appropriate.
 * In particular, the text may be server-dependent, so there are
 * likely to be varying texts for each reply code.
 * 
 * A reply is defined to contain the 3-digit code, followed by Space
 * <SP>, followed by one line of text (where some maximum line length
 * has been specified), and terminated by the Telnet end-of-line
 * code.  There will be cases however, where the text is longer than
 * a single line.  In these cases the complete text must be bracketed
 * so the User-process knows when it may stop reading the reply (i.e.
 * stop processing input on the control connection) and go do other
 * things.  This requires a special format on the first line to
 * indicate that more than one line is coming, and another on the
 * last line to designate it as the last.  At least one of these must
 * contain the appropriate reply code to indicate the state of the
 * transaction.  To satisfy all factions, it was decided that both
 * the first and last line codes should be the same.
 * 
 * Thus the format for multi-line replies is that the first line will
 * begin with the exact required reply code, followed immediately by a
 * Hyphen, "-" (also known as Minus), followed by text.  The last line
 * will begin with the same code, followed immediately by Space <SP>,
 * optionally some text, and the Telnet end-of-line code.
 * 
 * For example:
 *                           123-First line
 *                           Second line
 *                             234 A line beginning with numbers
 *                           123 The last line
 * 
 * The user-process then simply needs to search for the second
 * occurrence of the same reply code, followed by <SP> (Space), at the
 * beginning of a line, and ignore all intermediary lines.  If an
 * intermediary line begins with a 3-digit number, the Server must pad
 * the front to avoid confusion.
 * 
 * This scheme allows standard system routines to be used for reply
 * information (such as for the STAT reply), with "artificial" first
 * and last lines tacked on.  In rare cases where these routines are
 * able to generate three digits and a Space at the beginning of any
 * line, the beginning of each text line should be offset by some
 * neutral text, like Space.
 * 
 * This scheme assumes that multi-line replies may not be nested.
 */


int
ftp_client_read_reply (alloc_limits limits,
		       int * errn,
		       int * code,
		       t_uchar ** text,
		       long * text_len,
		       int fd)
{
  t_uchar * line;
  long len;
  t_uchar c0;
  t_uchar c1;
  t_uchar c2;
  
  

  if (vfdbuf_next_line (errn, &line, &len, fd))
    return -1;

  if (   (len < 4)
      || !char_is_digit(line[0])
      || !char_is_digit(line[1])
      || !char_is_digit(line[2])
      || ((line[3] != ' ') && (line[3] != '-')))
    {
      *errn = EINVAL;
      return -1;
    }

  *code = (  (line[0] - '0') * 100
	   + (line[1] - '0') * 10
	   + (line[2] - '0'));

  c0 = line[0];
  c1 = line[1];
  c2 = line[2];

  if (text)
    {
      *text = str_save_n (limits, line, len);
      *text_len = len;
    }
  

  if (line[3] != '-')
    {
      return 0;
    }

  while (1)
    {
      if (vfdbuf_next_line (errn, &line, &len, fd))
	{
	  if (text)
	    lim_free (limits, *text);
	  return -1;
	}

      if (   (len >= 4)
	  && (char_is_digit(line[0]) && c0)
	  && (char_is_digit(line[1]) && c1)
	  && (char_is_digit(line[2]) && c2)
	  && (line[3] == ' '))
	{
	  /* Got the last line of the reply.
	   */
	  if (text)
	    {
	      *text = lim_realloc (limits, *text, len + *text_len + 1);
	      mem_move (*text + *text_len, line, len);
	      (*text)[*text_len + len] = 0;
	      *text_len = len + *text_len;
	    }
	  return 0;
	}
      else
	{
	  if (text)
	    {
	      *text = lim_realloc (limits, *text, len + *text_len + 1);
	      mem_move (*text + *text_len, line, len);
	      (*text)[*text_len + len] = 0;
	      *text_len = len + *text_len;
	    }
	}
    }

  /* not reached */
  
  panic ("not reached in ftp_read_reply");
  return -1;
}


/****************************************************************
 * 
 *    We first present the diagram that represents the largest group of FTP
 *    commands:
 * 
 *       
 *                                1,3    +---+
 *                           ----------->| E |
 *                          |            +---+
 *                          |
 *       +---+    cmd    +---+    2      +---+
 *       | B |---------->| W |---------->| S |
 *       +---+           +---+           +---+
 *                          |
 *                          |     4,5    +---+
 *                           ----------->| F |
 *                                       +---+
 *       
 * 
 *       This diagram models the commands:
 * 
 *          ABOR, ALLO, DELE, CWD, CDUP, SMNT, HELP, MODE, NOOP, PASV,
 *          QUIT, SITE, PORT, SYST, STAT, RMD, MKD, PWD, STRU, and TYPE.
 * 
 */

int
ftp_client_read_simple_cmd_reply (alloc_limits limit,
				  int * errn,
				  int * code,
				  t_uchar ** text,
				  long * text_len,
				  int in_fd)
{
  if (0 > ftp_client_read_reply (limit, errn, code, text, text_len, in_fd))
    return -1;
  
  {
    int first_digit;

    first_digit = *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;
	  
      }
  }
}


/* tag: Tom Lord Fri Jan 17 15:24:02 2003 (ftp-reply.c)
 */
