/* sftp-handler.c: 
 *
 ****************************************************************
 * Copyright (C) 2002 Scott Parish
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/str.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/vu-network/url-socket.h"

#include "ftp-utils/clients/sftp-protocol.h"

#include "ftp-utils/with-ftp/with-ftp.h"
#include "ftp-utils/with-ftp/sftp-handler.h"
#include "ftp-utils/with-ftp/utils.h"



/* Remote starting directory.
 * 
 * sftp://site/this
 *            ^^^^^
 */
static t_uchar * remote_home;

/* rename_from_path
 * 
 * Between RNFR and RNTO, this holds the path name from RNFR.
 */
static t_uchar * rename_from_path;

/* FTP fds 
 * 
 * These are the control connection to the remote FTP server.
 *
 */
static int in_fd;
static int out_fd;



ftp_handler_t
sftp_init_handler (t_uchar *** new_env, t_uchar * site_spec)
{
  int errn;
  t_uchar * site;
  t_uchar * site_spec_end;
  int code;
  long len;
  t_uchar *message;

  site_spec_end = str_chr_index (site_spec, '/');

  if (!site_spec_end)
    {
      remote_home = str_save (0, "/");
      site = str_save (0, site_spec);
    }
  else
    {
      remote_home = str_save (0, site_spec_end);
      site = str_save_n (0, site_spec, site_spec_end - site_spec);
    }


  if (0 > sftp_client_connect_and_login (&in_fd, &out_fd, 0, &errn, &code, &message, &len, site))
    {
      safe_printfmt (2, "unable to establish connection to %s\n", site);
      if (message)
        safe_printfmt (2, "%.*s\n", (int)len, message);
      with_ftp_exit (1);
    }


  if (0 > sftp_client_cwd (0, &errn, &code, &message, &len, in_fd, out_fd, remote_home))
    {
      safe_printfmt (2, "unable to cd to remote directory: %s\n", remote_home);
      if (message)
        safe_printfmt (2, "%.*s\n", (int)len, message);
      with_ftp_exit (1);
    }

  *(t_uchar **)ar_push ((void **)new_env, 0, sizeof (t_uchar *)) = str_save (0, "WFTP_SITE_ADDR=");
  return sftp_handle_command;
}




void
sftp_handle_command (int connect_in, int connect_out, t_uchar * command, int command_len)
{
  int errn, code, cmd_status;
  long len;
  t_uchar *message;

  message = 0;
  errn = 0;

  command_len = trim_crnl (command, command_len);

  /* commands not requiring a data port */

  if (!str_cmp_prefix ("cd ", command))
    {
      /* "cd <path>" */
      if (0 > (cmd_status = sftp_client_cwd (0, &errn, &code, &message, &len,
					     in_fd, out_fd, command + 3)))
	if (errn)
	  {
	  sftp_protocol_error:
	    message = message ? message :
				  (t_uchar *)"  (no output from server)";
	    safe_printfmt (2, "with-ftp: SFTP protocol error\n%s\n", message);
	    with_ftp_exit (1);
	  }
    }
  else if (!str_cmp_prefix ("cdup", command))
    {
      /* "cdup" */
      if (0 > (cmd_status = sftp_client_cdup (0, &errn, &code, &message,
					      in_fd, out_fd)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "cdup ok");
    }
  else if (!str_cmp_prefix ("delete ", command))
    {
      /* "delete <path>" */
      if (0 > (cmd_status = sftp_client_unlink (0, &errn, &code, &message,
					        in_fd, out_fd, command + 7)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "delete ok");
    }
  else if (!str_cmp_prefix ("home", command))
    {
      /* "home" */
      if (0 > (cmd_status = sftp_client_cwd (0, &errn, &code, &message, &len,
					     in_fd, out_fd, remote_home)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "home ok");
    }
  else if (!str_cmp_prefix ("rmdir ", command))
    {
      /* "rmdir <path>" */
      if (0 > (cmd_status = sftp_client_rmd (0, &errn, &code, &message,
					     in_fd, out_fd, command + 6)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "rmdir ok");
    }
  else if (!str_cmp_prefix ("mkdir ", command))
    {
      /* "mkdir <path>" */
      if (0 > (cmd_status = sftp_client_mkd (0, &errn, &code, &message,
					     in_fd, out_fd, command + 6)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "mkdir ok");
    }
  else if (!str_cmp_prefix ("noop", command))
    {
      /* "noop" */
      cmd_status = 0;
      message = str_save (0, "noop ok");
    }
  else if (!str_cmp_prefix ("pwd", command))
    {
      /* "pwd" */
      if (0 > (cmd_status = sftp_client_pwd (0, &errn, &code, &message,
					     in_fd, out_fd)))
	if (errn)
	  goto sftp_protocol_error;

	safe_printfmt (connect_out, "OK %lu\n%s\n",
		       (unsigned long)(1 + str_length (message)), message);
	safe_flush (connect_out);
	lim_free (0, message);
	return;
    }
  else if (!str_cmp_prefix ("rnfr ", command))
    {
      if (rename_from_path)
	lim_free (0, rename_from_path);

      rename_from_path = str_save (0, command + 5);
      message = str_save (0, "rnfr ok");
      cmd_status=0;
    }
  else if (!str_cmp_prefix ("rnto ", command))
    {
      if (!rename_from_path)
	goto bad_command;
      
      if (0 > (cmd_status = sftp_client_rename (0, &errn, &code, &message,
					        in_fd, out_fd, rename_from_path,
						command + 5)))
	{
	  if (errn)
	    goto sftp_protocol_error;
	}
      else
	message = str_save (0, "rename ok");
    }  

  /* commands requiring a data port */

  else
    {
      t_uchar * host;
      t_uchar * comma;
      t_uchar * port;
      t_uchar * args;
      t_ulong h;
      t_uint p;
      int data_fd;

      /* <cmd> host,port args...
       * or
       * <cmd> pasv args...
       */

      host = str_chr_index (command, ' ');
      if (!host)
	goto bad_command;

      ++host;

      if (!str_cmp_prefix ("pasv ", host))
	{
	  /* passive mode */
	  /* block postconditions: args points at the list of args, */
	  /*                       data_fd ready */
	  safe_printfmt (connect_out, "%s\n", my_data_url);
	  safe_flush (connect_out);
	  data_fd = safe_open (my_data_url, O_RDWR, 0);
	  args = host + 5;	/* "pasv " */
	}
      else
	{
	  /* passive mode */
	  /* block postconditions: args points at the list of args, */
	  /*                       data_fd ready */

	  comma = str_chr_index (host, ',');
	  if (!comma)
	    goto bad_command;

	  if (cvt_decimal_to_ulong (&errn, &h, host, comma - host))
	    goto bad_command;

	  port = comma + 1;

	  args = str_chr_index (port, ' ');
	  if (!args)
	    goto bad_command;

	  if (cvt_decimal_to_uint (&errn, &p, port, args - port))
	    goto bad_command;

	  args += 1;

	  data_fd = url_inet_client_addr (&errn, h, p);
	  if (data_fd < 0)
	    {
	      message = str_save (0, errno_to_string (errn));
	      cmd_status = -1;
	      goto cmd_done;
	    }
	}

      if (!str_cmp_prefix ("get ", command))
	{
	  message = str_save (0, "file opened for get");
	  safe_printfmt (connect_out, "OK %lu\n %s\n",
			 (unsigned long)(2 + str_length (message)), message);
	  safe_flush (connect_out);

	  if (0 > sftp_client_get (0, &errn, &code, &message, in_fd, out_fd,
				   data_fd, args))
	    {
	      int ign;
	    bad_data_cmd:
	      vu_close (&ign, data_fd);
	      cmd_status = -1;
	      goto cmd_done;
	    }

	  if (0 > vu_close (&errn, data_fd))
	    goto bad_data_cmd;

	  message = str_save (0, "get command ok");
	  cmd_status = 0;
	}
      else if (!str_cmp_prefix ("put ", command))
	{
	  if (0 > sftp_client_put (0, &errn, &code, &message, in_fd, out_fd,
				   data_fd, connect_out, args))
	    goto bad_data_cmd;

	  if (0 > vu_close (&errn, data_fd))
	    goto bad_data_cmd;

	  message = str_save (0, "put command ok");
	  cmd_status = 0;
	}
      else if (!str_cmp_prefix ("nlist ", command) ||
	       !str_cmp_prefix ("list ", command))
	{
	  int longfmt = 0;

	  if (command[0] == 'l')
	    longfmt = 1;

	  message = str_save (0, "dir opened for nlist");
	  safe_printfmt (connect_out, "OK %lu\n %s\n",
			 (unsigned long)(2 + str_length (message)), message);
	  safe_flush (connect_out);

	  if (0 > sftp_client_list (0, &errn, &code, &message, in_fd, out_fd,
				    data_fd, args, longfmt))
	    goto bad_data_cmd;

	  if (0 > vu_close (&errn, data_fd))
	    goto bad_data_cmd;
	  message = str_save (0, "nlist command ok");
	  cmd_status = 0;
	}
      else
	{
	bad_command:
	  safe_printfmt (2, "with-ftp: bad command recieved (%s)\n", command);
	  with_ftp_panic ("with-ftp protocol error");
	}
    }
      
 cmd_done:
  safe_printfmt (connect_out, "%s %lu\n %s\n", ((cmd_status < 0) ? "ERROR" : "OK"), (unsigned long)(2 + str_length (message)), message);
  safe_flush (connect_out);
  if (message)
    {
      lim_free (0, message);
      message = 0;
    }

}


/* tag: Tom Lord Sat Jan 18 15:38:30 2003 (sftp-handler.c)
 */
