/*****************************************************************************\
* Copyright (c) 2002-2004 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_info.c 1245 2004-12-09 12:01:59Z morth $ */

#include "system.h"

#include "commands.h"

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

extern const command_t commands[], siteCommands[];
extern const int numCommands, numSiteCommands;
extern const char *localeDir;
extern const char *localeSuffix;

/* Informational commands */

int command_syst(connection_t *conn, const char *arg, int expected)
{
  reply(conn, "215 UNIX system type.");
  return 0;
}

int command_stat(connection_t *conn, const char *arg, int expected)
{
  if(conn->working)
  {
    if(conn->data)
      reply(conn, "213 Sent %llu of %llu bytes.",
	    conn->dataOffset - conn->restart, conn->dataLen - conn->restart);
    else if(conn->sending)
      reply(conn, "213 Sent %llu bytes.", conn->dataOffset);
    else
      reply(conn, "213 Received %llu bytes.", conn->dataOffset);
  }
  else
  {
    if(strlen(arg))
    {
      char *list = make_file_list(arg, conn->cwd, conn->user->home, 1, " ");
      if(!list)
      {
	if(errno == ENOENT)
	{
	  reply(conn, "500 No such file or directory.");
	  return 0;
	}
	reply(conn, "450 %s.", strerror(errno));
	return 0;
      }
      reply (conn, "212-Listing of \"%s\":", arg);
      send_telnet (conn, list, 0);
      reply (conn, "212 End of listing.");
    }
    else
    {
      reply (conn, "211-Connection status:");
      reply (conn, " Transfer type is %s.", conn->type == ttImage ? 
	    conn->subtype? "Local 8" : "Image" :
	    conn->subtype? "Telnet Formatted Text" : "Non-Print Text");
      reply (conn, " Transfer mode is Stream.");
      reply (conn, " File structure is File.");
      reply (conn, " Data connection is %s.", conn->dataSock >= 0 ?
	    !conn->passive || conn->passiveAccepted ? "open" :
	    "waiting for connect" : "closed");
      reply (conn, "211 End of status.");
    }
  }
  return 0;
}

int command_help(connection_t *conn, const char *arg, int expected)
{
  /* gcc says spbuf is variable-sized if I just const int. */
#define cwidth 6
  const int columns = 80 / cwidth;
  const int rows = (numCommands + columns - 1) / columns;
  const command_t *comm[columns];
  int i, j;
  char line[81], *lend;
  
  if (!strncasecmp (arg, "SITE", 4) && (!arg[4] || arg[4] == ' '))
  {
    if (!arg[4])
      return sitecommand_help (conn, "", 0);
    return sitecommand_help (conn, arg + 5, 0);
  }
  
  if (arg[0])
  {
    for (comm[0] = commands; comm[0]->name; comm[0]++)
    {
      if (!strcasecmp (arg, comm[0]->name))
      {
	if (comm[0]->help)
	  reply (conn, "214 %s%s", comm[0]->name, comm[0]->help);
	else
	  reply (conn, "214 %s: No help is available.", comm[0]->name);
	return 0;
      }
    }
    reply (conn, "500 No such command %s.", arg);
    return 0;
  }
  
  reply(conn, "214-These commands are recognised:");
  for(i = 0; i < columns; i++)
  {
    if (rows * i < numCommands)
      comm[i] = &commands[rows * i];
    else
      comm[i] = &commands[numCommands];
  }
  while(comm[0] != &commands[rows])
  {
    strcpy (line, " ");
    lend = line + 1;
    for(i = 0; i < columns; i++)
    {
      if(comm[i]->name)
      {
	strcpy (lend, comm[i]->name);
	j = strlen (lend);
	lend += j;
	for (; j < cwidth; j++)
	  *lend++ = ' ';
	comm[i]++;
      }
      else
	break;
    }
    *lend = 0;
    reply (conn, "%s", line);
  }
  
  reply(conn, "214 End of help.");
  return 0;
#undef cwidth
}

int sitecommand_help(connection_t *conn, const char *arg, int expected)
{
  /* gcc says spbuf is variable-sized if I just const int. */
#define cwidth 7
  const int columns = 80 / cwidth;
  const int rows = (numSiteCommands + columns - 1) / columns;
  const command_t *comm[columns];
  int i, j;
  char line[81], *lend;
  
  if (arg[0])
  {
    for (comm[0] = siteCommands; comm[0]->name; comm[0]++)
    {
      if (!strcasecmp (arg, comm[0]->name))
      {
	if (comm[0]->help)
	  reply (conn, "214 SITE %s%s", comm[0]->name, comm[0]->help);
	else
	  reply (conn, "214 SITE %s: No help is available.", comm[0]->name);
	return 0;
      }
    }
    reply (conn, "500 No such command SITE %s.", arg);
    return 0;
  }
  
  reply (conn, "214-These SITE commands are recognised:");
  for (i = 0; i < columns; i++)
  {
    if (rows * i < numSiteCommands)
      comm[i] = &siteCommands[rows * i];
    else
      comm[i] = &siteCommands[numSiteCommands];
  }
  while (comm[0] != &siteCommands[rows])
  {
    strcpy (line, " ");
    lend = line + 1;
    for (i = 0; i < columns; i++)
    {
      if (comm[i]->name)
      {
	strcpy (lend, comm[i]->name);
	j = strlen (lend);
	lend += j;
	for (; j < cwidth; j++)
	  *lend++ = ' ';
	comm[i]++;
      }
      else
	break;
    }
    *lend = 0;
    reply (conn, "%s", line);
  }
  
  reply (conn, "214 End of help.");
  return 0;
#undef cwidth
}

int command_feat (connection_t *conn, const char *arg, int expected)
{
  DIR *locDir;
  struct dirent *dp;
  char *ltxt, *suff, *lp, *np;
  int i;
  
  reply (conn, "211-These features are supported.");
  //reply (conn, " ADAT");
  reply (conn, " AUTH"
#ifdef USE_TLS
	" TLS"
#endif
	 );
  reply (conn, " CCC");
  //reply (conn, " CONF");
  //reply (conn, " ENC");
  reply (conn, " EPRT");
  reply (conn, " EPSV");
  reply (conn, " HOST");
  if ((locDir = opendir (localeDir)))
  {
    ltxt = talloc (4);
    if (ltxt)
    {
      strcpy (ltxt, "EN");
      lp = ltxt + 2;
      if (!conn->currLang)
	*lp++ = '*';
      i = 0;
      while ((dp = readdir (locDir)))
      {
	if (dp->d_name[0] == '.')
	  continue;
	suff = strstr (dp->d_name, localeSuffix);
	if (!suff || strlen (suff) != strlen (localeSuffix))
	  continue;
	ltxt = trealloc (ltxt, lp - ltxt + suff - dp->d_name + 3);
	if (!ltxt)
	  break;
	lp = ltxt + strlen (ltxt);
	*lp++ = ';';
	for (np = dp->d_name; np != suff; np++)
	  *lp++ = toupper (*np);
	if (conn->currLang && !strncmp (dp->d_name, conn->currLang, suff - dp->d_name))
	{
	  *lp++ = '*';
	  i = 1;
	}
      }
      if (ltxt && !i && conn->currLang)
      {
	ltxt = trealloc (ltxt, lp - ltxt + strlen (conn->currLang) + 3);
	if (ltxt)
	{
	  *lp++ = ';';
	  for (np = conn->currLang; *np; np++)
	    *lp++ = toupper (*np);
	  *lp++ = '*';
	}
      }
      if (ltxt)
      {
	*lp = 0;
	reply (conn, " LANG %s", ltxt);
      }
    }
    closedir (locDir);
  }
  else
    reply (conn, " LANG EN*");
  reply (conn, " LPRT");
  reply (conn, " LPSV");
  reply (conn, " MDTM");
  reply (conn, " MFMT");
  //reply (conn, " MIC");
  reply (conn, " MLST TYPE%s;UNIQUE%s;MODIFY%s;PERM%s;SIZE%s;",
	conn->mlstTags & tfType? "*" : "", conn->mlstTags & tfUnique? "*" : "",
	conn->mlstTags & tfModify? "*" : "", conn->mlstTags & tfPerm? "*" : "",
	conn->mlstTags & tfSize? "*" : "");
  reply (conn, " PBSZ");
  reply (conn, " PROT");
  reply (conn, " REST STREAM");
  reply (conn, " SIZE");
  reply (conn, " TVFS");
  reply (conn, " UTF8");
  reply (conn, "211 End of features list.");
  return 0;
}

int sitecommand_ftpd (connection_t *conn, const char *arg, int expected)
{
  reply (conn, "200-%s, Copyright ® 2002-2004 Pelle Johansson", PACKAGE_STRING);
  reply (conn, "200-%s comes with ABSOLUTELY NO WARRANTY", PACKAGE_NAME);
  reply (conn, "200-This is free software, and you are welcome to redistribute ");
  reply (conn, "200-it under certain conditions.");
  reply (conn, "200 See the GNU GPL available at http://www.gnu.org/ for details.");
  
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1