/*****************************************************************************\
* 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_misc.c 1251 2005-03-06 22:24:29Z morth $ */

#include "system.h"

#include "commands.h"

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

extern command_t siteCommands[];

/* Miscellaneous commands */

int command_abor(connection_t *conn, const char *arg, int expected)
{
  switch (close_data_connection (conn))
  {
  case -1:
    return 1;
  case 0:
    reply (conn, "226 Abort successful.");
    return 0;
  default:
    reply (conn, "426 Data connection closed.");
    reply (conn, "226 Abort successful.");
    return 0;
  }
}

int command_site (connection_t *conn, const char *arg, int expected)
{
  const char *narg, *cname;
  command_t *cmd;
  
  narg = strchr (arg, ' ');
  if (narg)
  {
    cname = talloc (narg - arg + 1);
    if (!cname)
    {
      reply (conn, "500 %s", strerror (errno));
      return 0;
    }
    strncpy ((char*)cname, arg, narg++ - arg);
  }
  else
  {
    cname = arg;
    narg = "";
  }
  
  if (!strlen (cname))
  {
    reply (conn, "500 Need a command name.");
    return 0;
  }
  
  for (cmd = siteCommands; cmd->name; cmd++)
  {
    if (!strcasecmp(cmd->name, cname))
    {
      if (conn->working && !cmd->whileWorking)
	reply (conn, "500 Please wait until the server is ready.");
      else if (!conn->authed && !cmd->unauthed)
	reply(conn, "503 Please login.");
      else if (cmd->handler (conn, narg, expected))
	return 1;
      break;
    }
  }
  if (!cmd->name)
    reply(conn, "500 No such command SITE %s.", cname);
  
  return 0;
}

int command_noop(connection_t *conn, const char *arg, int expected)
{
  reply(conn, "200 No action taken.");
  return 0;
}

int command_host (connection_t *conn, const char *arg, int expected)
{
  struct sockaddr_storage lAddr, rAddr;
  server_t *serv;
  int l;
  
  if (conn->inLookUp || conn->authed
#ifdef USE_TLS
	|| conn->tlsControl
#endif
      )
  {
    reply (conn, "503 Too late to switch host, please connect again.");
    return 0;
  }
  
  l = sizeof(lAddr);
  getsockname(conn->sock, (struct sockaddr*)&lAddr, &l);
  l = sizeof(rAddr);
  getpeername(conn->sock, (struct sockaddr*)&rAddr, &l);
  
  if (!arg[0])
  {
    reply (conn, "200-You can connect to these hosts (* = current):");
    serv = NULL;
    while ((serv = find_server ((struct sockaddr*)&lAddr, (struct sockaddr*)&rAddr, serv)))
    {
      if (serv == conn->server)
	reply (conn, " %s*", serv->name);
      else
	reply (conn, " %s", serv->name);
    }
    reply (conn, "200 End of hosts.");
    return 0;
  }
  
  serv = find_named_server (arg, 0, (struct sockaddr*)&rAddr, NULL);
  if (serv)
  {
    int i, port = -1;
    
    if (serv == conn->server)
    {
      reply (conn, "220 Already at %s.", serv->name);
      return 0;
    }
    
    switch(lAddr.ss_family)
    {
    case AF_INET:
      port = ntohs (((struct sockaddr_in*)&lAddr)->sin_port);
      break;
    case AF_INET6:
      port = ntohs (((struct sockaddr_in6*)&lAddr)->sin6_port);
      break;
    }
    
    for(i = 0; i < serv->numPorts; i++)
      if(serv->ports[i] == port)
	break;
    if (i < serv->numPorts)
    {
      char rhost[NI_MAXHOST];
      
      if (getnameinfo ((struct sockaddr*)&rAddr, l, rhost, sizeof (rhost), NULL,
		0, NI_NUMERICHOST))
	strcpy (rhost, "unknown");
      
      if (accounter (conn->accSock, "CONNECT %s %s\n", rhost, serv->name))
      {
	syslog (LOG_ERR, "%d: Error in acocunter: %m", conn->id);
	reply (conn, "500 Error: %s.", strerror (errno));
      }
      else
      {
	conn->oldserver = conn->server;
	conn->server = pattach (serv, conn);
	syslog (LOG_INFO, "%d: Switching to %s.", conn->id, conn->server->name);
	conn->accWait = acWelcome;
      }
      return 0;
    }
  }
  reply (conn, "500 No such host.");
  return 0;
}

int sitecommand_admin (connection_t *conn, const char *arg, int expected)
{
  char buf[4097];
  int i;
  
  buf[0] = i = 0;
  if (!strcasecmp (arg, "LIST"))
  {
    if (conn->user->adminPrivs & admList)
    {
      if (accounter (conn->accSock, "LIST\n"))
	reply (conn, "500 %s.", strerror (errno));
      else
      {
	reply (conn, "200-Connections (id: pid ip server user account email action):");
	conn->accWait = acList;
      }
      return 0;
    }
  }
  else if (!strncasecmp (arg, "MSG ", 4))
  {
    if (conn->user->adminPrivs & admMsg)
    {
      if (sscanf (arg + 4, "%d %[^\n]", &i, buf) < 2)
	reply (conn, "501 Not enough arguments.");
      else if (accounter (conn->accSock, "MSG %d %s\n", i, buf))
	reply (conn, "500 %s.", strerror (errno));
      else
	conn->accWait = acMsg;
      return 0;
    }
  }
  else if (!strncasecmp (arg, "MSGALL ", 7))
  {
    if (conn->user->adminPrivs & admMsg)
    {
      if (sscanf (arg + 7, "%[^\n]", buf) < 1)
	reply (conn, "501 Not enough arguments.");
      else if (accounter (conn->accSock, "MSGALL %s\n", buf))
	reply (conn, "500 %s.", strerror (errno));
      else
	conn->accWait = acMsg;
      return 0;
    }
  }
  else if (!strncasecmp (arg, "ABORT ", 6))
  {
    if (conn->user->adminPrivs & admAbort)
    {
      if (sscanf (arg + 6, "%d %[^\n]", &i, buf) < 1)
	reply (conn, "501 Not enough arguments.");
      else if (accounter (conn->accSock, "ABORT %d %s\n", i, buf))
	reply (conn, "500 %s.", strerror (errno));
      else
	conn->accWait = acAbort;
      return 0;
    }
  }
  else if (!strncasecmp (arg, "DISCONNECT ", 11))
  {
    if (conn->user->adminPrivs & admDisconnect)
    {
      if (sscanf (arg + 11, "%d %[^\n]", &i, buf) < 1)
	reply (conn, "501 Not enough arguments.");
      else if (accounter (conn->accSock, "DISCONNECT %d %s\n", i, buf))
	reply (conn, "500 %s.", strerror (errno));
      else
	conn->accWait = acDisconnect;
      return 0;
    }
  }
  else if (!strncasecmp (arg, "DISCABORT ", 10))
  {
    if ((conn->user->adminPrivs & (admDisconnect | admAbort)) == (admDisconnect | admAbort))
    {
      if (sscanf (arg + 10, "%d %[^\n]", &i, buf) < 1)
	reply (conn, "501 Not enough arguments.");
      else if (accounter (conn->accSock, "DISCABORT %d %s\n", i, buf))
	reply (conn, "500 %s.", strerror (errno));
      else
	conn->accWait = acDisconnect;
      return 0;
    }
  }
  else if (!strcasecmp (arg, "RELOAD"))
  {
    if (conn->user->adminPrivs & admReload)
    {
      if (accounter (conn->accSock, "RELOAD\n"))
	reply (conn, "500 %s.", strerror (errno));
      else
	reply (conn, "200 Reloading configuration file.");
      return 0;
    }
  }
  else
  {
    reply (conn, "500 Unknown admin command %s.", arg);
    return 0;
  }
  reply (conn, "500 Access denied.");
  syslog (LOG_NOTICE, "%d: No access to SITE ADMIN %s", conn->id, arg);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1