/*****************************************************************************\
* Copyright (c) 2002 Pelle Johansson.                                         *
* All rights reserved.                                                        *
*                                                                             *
* This file is part of the moftpd package. Use and distribution of            *
* this software is governed by the terms in the file LICENCE, which           *
* should have come with this package.                                         *
\*****************************************************************************/

/* $moftpd: com_file.c 1251 2005-03-06 22:24:29Z morth $ */

#include "system.h"

#include "commands.h"

#include "connection.h"
#include "utf8fs/memory.h"

int command_rnfr(connection_t *conn, const char *arg, int expected)
{
  struct stat st;
  
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if(check_access(arg, acRename) || lstat(arg, &st))
  {
    switch(errno)
    {
    case ENOENT:
      reply(conn, "550 File does not exist.");
      return 0;
    default:
      reply(conn, "553 %s.", strerror(errno));
      return 0;
    }
  }
  
  if(conn->rnfr)
    pfree(conn->rnfr, conn);
  conn->rnfr = pstring(arg, conn);
  if(!conn->rnfr)
  {
    reply(conn, "553 %s.", strerror(errno));
    return 0;
  }
  
  reply(conn, "350 RNTO expected.");
  conn->expect = "RNTO";
  return 0;
}

int command_rnto(connection_t *conn, const char *arg, int expected)
{
  struct stat st;
  
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  if(!expected)
  {
    reply(conn, "503 RNTO not expected here.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if(check_access(arg, acRename))
  {
    reply(conn, "553 %s.", strerror(errno));
    return 0;
  }
  
  if(!lstat(arg, &st))
  {
    reply(conn, "553 File exists.");
    return 0;
  }
  
  if(rename_file(conn->rnfr, arg))
  {
    pfree(conn->rnfr, conn);
    conn->rnfr = NULL;
    reply(conn, "553 %s.", strerror(errno));
    return 0;
  }
  pfree(conn->rnfr, conn);
  conn->rnfr = NULL;
  reply(conn, "250 Rename ok.");
  return 0;
}

int command_dele(connection_t *conn, const char *arg, int expected)
{
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if(delete_file(arg))
  {
    switch(errno)
    {
    case ENOENT:
      reply(conn, "550 File does not exist.");
      return 0;
    default:
      reply(conn, "553 %s.", strerror(errno));
      return 0;
    }
  }
  
  reply(conn, "250 File deleted.");
  return 0;
}

int command_rmd(connection_t *conn, const char *arg, int expected)
{
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if(delete_dir(arg))
  {
    switch(errno)
    {
    case ENOENT:
      reply(conn, "550 File does not exist.");
      return 0;
    default:
      reply(conn, "553 %s.", strerror(errno));
      return 0;
    }
  }
  
  reply(conn, "250 Directory deleted.");
  return 0;
}

int command_mkd(connection_t *conn, const char *arg, int expected)
{
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if(create_dir(arg))
  {
    reply(conn, "553 %s.", strerror(errno));
    return 0;
  }
  
  reply(conn, "250 Directory created.");
  return 0;
}

int command_size(connection_t *conn, const char *arg, int expected)
{
  struct stat st;
  long long res;
  
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if (!check_access (arg, acReadFile) && !stat (arg, &st))
  {
    if(S_ISDIR(st.st_mode))
    {
      reply(conn, "553 Is a directory.");
      return 0;
    }
  }
  else
  {
    reply(conn, "550 %s.", strerror(errno));
    return 0;
  }
  
  res = st.st_size;
  if (conn->type == ttText)
  {
    int fd = open_file_reading (arg, 0);
    int l;
    char buf[1024], *bp;
    
    if (fd < 0)
    {
      reply (conn, "550 %s.", strerror (errno));
      return 0;
    }
    
    while ((l = read (fd, buf, 1024)) > 0)
    {
      bp = buf;
      while (l > 0 && (bp = memchr (bp, '\n', l)))
      {
	res++;
	l -= ++bp - buf;
      }
    }
    
    close (fd);
  }
  reply(conn, "213 %lld", res);
  return 0;
}

int command_mdtm(connection_t *conn, const char *arg, int expected)
{
  struct stat st;
  
  if (!strlen (arg))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  arg = chroot_path (arg, conn->cwd, conn->user->home);
  if (!check_access (arg, acReadFile) && !stat (arg, &st))
  {
    struct tm *tm = gmtime (&st.st_mtime);
    
    reply (conn, "213 %04d%02d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1,
	  tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
  }
  else
    reply (conn, "550 %s.", strerror (errno));
  return 0;
}

int sitecommand_chmod (connection_t *conn, const char *arg, int expected)
{
  const char *path = strchr (arg, ' ');
  char *ap;
  unsigned int mode;
  
  if (!strlen (arg))
  {
    reply (conn, "501 mode missing.");
    return 0;
  }
  
  mode = strtoul (arg, &ap, 8);
  if (ap != path)
  {
    reply (conn, "500 Invalid mode.");
    return 0;
  }
  
  if (!path || !strlen (++path))
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  path = chroot_path (path, conn->cwd, conn->user->home);
  if (!check_access (path, acCreateFile))
  {
    if (chmod (path, mode))
      reply (conn, "550 %s.", strerror (errno));
    else
      reply (conn, "250 Mode changed successfully.");
  }
  else
    reply (conn, "550 Access denied.");
  return 0;
}

int command_mfmt (connection_t *conn, const char *arg, int expected)
{
  const char *path = strchr (arg, ' ');
  char *timestr;
  struct timeval t[2];
  
  if (!arg[0])
  {
    reply (conn, "501 time missing.");
    return 0;
  }
  
  if (!path || strlen (path) < 2)
  {
    reply (conn, "501 Path missing.");
    return 0;
  }
  
  timestr = talloc (path - arg + 1);
  if (!timestr)
  {
    reply (conn, "500 %s.", strerror (errno));
    return 0;
  }
  strncpy (timestr, arg, path - arg);
  timestr[path++ - arg] = 0;
  t[1].tv_sec = t[0].tv_sec = get_timeval (timestr);
  if (t[0].tv_sec == -1)
  {
    reply (conn, "500 Invalid time-val.");
    return 0;
  }
  t[1].tv_usec = t[0].tv_usec = 0;
  
  path = chroot_path (path, conn->cwd, conn->user->home);
  if (!check_access (path, acCreateFile))
  {
    if (utimes (path, t))
      reply (conn, "550 %s.", strerror (errno));
    else
    {
      time_t ti = t[0].tv_sec;
      struct tm *tm = gmtime (&ti);
      
      reply (conn, "213 Modify=%04d%02d%02d%02d%02d%02d; %s", tm->tm_year + 1900, tm->tm_mon + 1,
	    tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, print_path (path));
    }
  }
  else
    reply (conn, "550 Access denied.");
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1