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

#ifndef _SYSTEM_H
#include "system.h"
#endif

#include "utf8fs/memory.h"

const char *sockPath = VARDIR "/run/" PACKAGE_NAME ".acct";
int debug = 0;

void usage (void)
{
  fprintf (stderr, "Usage: %s [-f <socket>] list\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] msg <id> <message>\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] msgall <message>\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] abort <id> [<message>]\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] disconnect <id> [<message>]\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] discabort <id> [<message>]\n", getprogname ());
  fprintf (stderr, "       %s [-f <socket>] reload\n", getprogname ());
  exit(1);
}

int parse_options (int argc, char ***argv)
{
  int ch;
  
  if (getenv("MOFTP_SOCKET"))
  {
    sockPath = tstring (getenv ("MOFTP_SOCKET"));
    if(!sockPath)
    {
      perror ("tstring");
      return -1;
    }
  }
  
  while((ch = getopt(argc, *argv, "f:")) != -1)
  {
    switch(ch)
    {
    case 'f':
      sockPath = tstring (optarg);
      if(!sockPath)
      {
	perror ("tstring");
	return -1;
      }
      break;
    default:
      usage ();
    }
  }
  
  *argv += optind;
  return argc - optind;
}

const char *combine_args (char *argv[], int nargs)
{
  int l = -1, i;
  char *res, *rp;
  
  for (i = 0; i < nargs; i++)
    l += strlen (argv[i]) + 1;
  
  if (l <= 0)
    return "";
  
  rp = res = talloc (l);
  if (!res)
  {
    perror ("talloc");
    return NULL;
  }
  
  for (i = 0; i < nargs; i++)
  {
#ifdef HAVE_STPCPY
    rp = stpcpy (rp, argv[i]);
#else
    strcpy (rp, argv[i]);
    rp += strlen (rp);
#endif
    *rp++ = ' ';
  }
  *--rp = 0;
  
  return res;
}

int accounter (int sock, const char *format, ...)
{
  va_list ap;
  char *str, *sp;
  int res;
#ifdef HAVE_VASPRINTF
  int doFree;
#endif
  
  va_start (ap, format);
#ifdef HAVE_VASPRINTF
  vasprintf (&str, format, ap);
  doFree = 1;
#else
  str = talloc (4097);
  if (str)
    vsnprintf (str, 4096, format, ap);
#endif
  va_end (ap);
  
  if (!str)
  {
    str = tstring (format);
#ifdef HAVE_VASPRINTF
    doFree = 0;
#endif
  }
  
  while ((sp = strchr (str, '\n')) && *(sp + 1))
    *sp = '?';
  
  res = write (sock, str, strlen (str));
  
#ifdef HAVE_VASPRINTF
  if (doFree)
    free (str);
#endif
  
  if (res < 0)
    return -1;
  return 0;
}

int ok_message (int sock, const char *type, int id, const char *message)
{
  int l;
  char buf[4096], *bp;
  
  if (id != -1)
    l = accounter (sock, "%s %d %s\n", type, id, message);
  else
    l = accounter (sock, "%s %s", type, message);
  if (l)
  {
    perror ("accounter");
    return 1;
  }
  l = read (sock, buf, sizeof (buf) - 1);
  if (l < 0)
  {
    perror ("read");
    return 1;
  }
  buf[l] = 0;
  bp = strchr (buf, '\n');
  if (bp)
    *bp = 0;
  if (!strcmp (buf, "OK"))
    return 0;
  if (!strcmp (buf, "INVALID"))
    fprintf (stderr, "Invalid connection id.\n");
  else
    fprintf (stderr, "Unknown error.\n");
  return 1;
}

int main (int argc, char *argv[])
{
  int sock;
  struct sockaddr_un addr;
  int bl, l, i;
  char buf[4096], *bp, *nbp;
  const char *msg;
  
  setprogname (argv[0]);
  
  argc = parse_options (argc, &argv);
  if (argc < 0)
    return 1;
  if (!argc)
    usage ();
  
  sock = socket (PF_UNIX, SOCK_STREAM, 0);
  if (sock < 0)
  {
    perror ("socket");
    return 1;
  }
  
  addr.sun_family = AF_UNIX;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
  addr.sun_len = sizeof (addr);
#endif
  strncpy (addr.sun_path, sockPath, sizeof (addr.sun_path) - 1);
  addr.sun_path[sizeof (addr.sun_path) - 1] = 0;
  
  if (connect (sock, (struct sockaddr*)&addr, sizeof (addr)))
  {
    perror ("connect");
    return 1;
  }
  
  if (!strcasecmp (argv[0], "LIST"))
  {
    if (argc != 1)
      usage ();
    if (accounter (sock, "LIST\n"))
    {
      perror ("accounter");
      return 1;
    }
    bl = l = 0;
    while (bl > 0 || (l = read (sock, buf + bl, sizeof (buf) - bl)) > 0)
    {
      if (l > 0)
	bl += l;
      nbp = memchr (buf, '\n', bl);
      if (nbp)
	*nbp++ = 0;
      if (!strcmp (buf, "END"))
	break;
      printf ("%s\n", buf);
      if (!nbp)
	break;
      bl -= nbp - buf;
      memmove (buf, nbp, bl);
      l = 0;
    }
  }
  else if (!strcasecmp (argv[0], "MSG"))
  {
    if (argc < 3 || !*argv[1])
      usage ();
    i = strtol (argv[1], &bp, 0);
    if (*bp)
      usage ();
    msg = combine_args (argv + 2, argc - 2);
    if (!msg)
      return 1;
    if (!*msg)
    {
      fprintf (stderr, "No message given.\n");
      return 1;
    }
    if (ok_message (sock, "MSG", i, msg))
      return 1;
  }
  else if (!strcasecmp (argv[0], "MSGALL"))
  {
    if (argc < 2)
      usage ();
    msg = combine_args (argv + 1, argc - 1);
    if (!msg)
      return 1;
    if (!*msg)
    {
      fprintf (stderr, "No message given.\n");
      return 1;
    }
    if (ok_message (sock, "MSGALL", -1, msg))
      return 1;
  }
  else if (!strcasecmp (argv[0], "ABORT"))
  {
    if (argc < 2 || !*argv[1])
      usage ();
    i = strtol (argv[1], &bp, 0);
    if (*bp)
      usage ();
    msg = combine_args (argv + 2, argc - 2);
    if (!msg)
      return 1;
    if (ok_message (sock, "ABORT", i, msg))
      return 1;
  }
  else if (!strcasecmp (argv[0], "DISCONNECT"))
  {
    if (argc < 2 || !*argv[1])
      usage ();
    i = strtol (argv[1], &bp, 0);
    if (*bp)
      usage ();
    msg = combine_args (argv + 2, argc - 2);
    if (!msg)
      return 1;
    if (ok_message (sock, "DISCONNECT", i, msg))
      return 1;
  }
  else if (!strcasecmp (argv[0], "DISCABORT"))
  {
    if (argc < 2 || !*argv[1])
      usage ();
    i = strtol (argv[1], &bp, 0);
    if (*bp)
      usage ();
    msg = combine_args (argv + 2, argc - 2);
    if (!msg)
      return 1;
    if (ok_message (sock, "DISCABORT", i, msg))
      return 1;
  }
  else if (!strcasecmp (argv[0], "RELOAD"))
  {
    if (argc != 1)
      usage ();
    if (accounter (sock, "RELOAD\n"))
    {
      perror ("accounter");
      return 1;
    }
    printf ("Configuration reloading.\n");
  }
  else
    usage ();
  
  close (sock);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1