/*
 * IMC2 - an inter-mud communications protocol
 *
 * router.c: a simple router/hub for IMC2
 *
 * Copyright (C) 1996,1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.nz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* This is the code for a standalone hub for IMC2. You do NOT need to
 * link this if you're running a normal mud using IMC!
 */

#include <stdlib.h> 
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#include "imc.h"

#define USE_ICED

#ifdef USE_ICED
#include "iced.h"
#endif

/* commands:
 *
 * password connect name
 * password disconnect name
 * password reboot
 * password sockets
 * password list
 * password (command as in imc_command)
 *
 */

char *password;
int logging;

#ifndef CONFIG
#define CONFIG "hub/"
#endif

#ifndef EMAIL
#define EMAIL "moodyg@erols.com"
#endif

#define onearg(x,y) imc_getarg(x,y,IMC_DATA_LENGTH)

int rebooting;
int reloading;

void icec_shutdown(void) /* dummy catch for non muds */
{
}
void *imc_malloc(int size)
{
  return malloc(size);
}

void IMC_free(void *block, int size)
{
  if(block)
  	free(block);
}

char *imc_strdup(const char *orig)
{
  return strdup(orig);
}

void IMC_strfree(char *str)
{
  if(str)
  	free(str);
}

int imc_readconfighook(const char *type, const char *value)
{
  if (!strcasecmp(type, "password"))
  {
    if (password)
    {
      imc_strfree(password);
    }
    password=imc_strdup(value);
    return 1;
  }
  else if (!strcasecmp(type, "logging"))
  {
    logging=atoi(value);
    return 1;
  }

  return 0;
}

void imc_saveconfighook(FILE *fp)
{
  if (password)
    fprintf(fp, "Password %s\n", password);
  fprintf(fp, "Logging %d\n", logging);
}

void ev_reboot(void *data)
{
  rebooting=1;
}

void ev_reload(void *data)
{
  reloading=1;
}

const char *docommand(const char *name, const char *arg)
{
  char buf[IMC_DATA_LENGTH];
  const char *orig;
  int r;

  orig=arg;
  arg=onearg(arg,buf);

  if (!password || strcmp(buf, password))
  {
    imc_logstring("bad password on command from %s: %s", name, orig);
    return NULL;
  }

  imc_logstring("command from %s: %s", name, arg);

  orig=arg;
  arg=onearg(arg,buf);

  if (!strcasecmp(buf, "reboot"))
  {
    imc_add_event(10, ev_reboot, NULL, 1);
    if (logging)
      imc_send_chat(NULL, 2, "Rebooting in 10 seconds", "*");
    return "'k";
  }

  if (!strcasecmp(buf, "reload"))
  {
    imc_add_event(10, ev_reload, NULL, 1);
    if (logging)
      imc_send_chat(NULL, 2, "Reloading config in 10 seconds", "*");
    return "'k";
  }
  
  if (!strcasecmp(buf, "logging"))
  {
    logging=!logging;
    return logging ? "Logging now enabled" : "Logging now disabled";
  }

  if (!strcasecmp(buf, "connect"))
  {
    arg=onearg(arg,buf);
    if (imc_connect_to(buf))
      return "'k";
    else
      return imc_error();
  }

  if (!strcasecmp(buf, "disconnect"))
  {
    arg=onearg(arg,buf);
    if (imc_disconnect(buf))
      return "'k";
    else
      return imc_error();
  }

  if (!strcasecmp(buf, "sockets"))
    return imc_sockets();

  if (!strcasecmp(buf, "list"))
    return imc_list(2);

  if (!strcasecmp(buf, "config"))
    return imc_list(5);

#ifdef USE_ICED
  if (!strcasecmp(buf, "iced"))
  {
    char chan[IMC_DATA_LENGTH], cmd[IMC_DATA_LENGTH];
    const char *argument;

    argument=imc_getarg(buf, cmd, IMC_DATA_LENGTH);
    argument=imc_getarg(argument, chan, IMC_DATA_LENGTH);

    iced_recv_command(name, chan, cmd, argument, 1);
    return "Command executed.";
  }
#endif
  
  if ((r=imc_command(orig))>0)
    return "'k";

  if (!r)
    return "Unknown command";
  else
    return imc_error();
}

char *getstats(void)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char buf1[IMC_DATA_LENGTH];
  int found=0;
  imc_connect *c;
  imc_reminfo *i;

  sprintf(buf, "Connections to %s (using %s):\n\r", imc_name, IMC_VERSIONID);

  for (c=imc_connect_list; c; c=c->next)
  {
    if (c->state==IMC_CONNECTED)
    {
      found++;
      i=imc_find_reminfo(c->info->name, 0);
      if (i && i->ping)
	sprintf(buf1, " [%4dms] %s@%s\n\r",
		i->ping,
		c->info->name,
		c->info->host);
      else
	sprintf(buf1, " [????ms] %s@%s\n\r",
		c->info->name,
		c->info->host);
      strcat(buf, buf1);
    }
  }

  sprintf(buf1, "%d direct connections total.\n\r", found);
  strcat(buf, buf1);

  imc_shrinksbuf(buf);
  return buf;
}

void imc_debug(const imc_connect *c, int out, const char *string)
{
#if 1
  char *dir;

  dir=out ? "<" : ">";

  printf("%s %s %s\n", imc_getconnectname(c), dir, string);
#endif
}

void imc_log(const char *string)
{
  char buf[IMC_DATA_LENGTH];
  char *msg;
  int log=0;

  strcpy(buf, ctime(&imc_now));
  buf[strlen(buf)-1]=' ';
  fprintf(stderr, "%s%s\n", buf, string);

  if (!strncmp(string, "connect to", 10))
    log=1;
  else
  {
    msg=strchr(string, ':');
    if (msg)
    {
      msg++;
      if (!strncmp(string, "connect to", 10) ||
          !strcmp(msg, " closing link") ||
          !strncmp(msg, " password failure", 17) ||
          !strncmp(msg, " connected ", 11))
       log=1;
    }
  }
 
  if (log && logging)
  {
    sprintf(buf, "%s", string);
    imc_send_chat(NULL, 2, buf, "*"); /* log to rinfo */
  }
}

void imc_recv_who(const imc_char_data *from, const char *type)
{
  char arg[IMC_DATA_LENGTH];
  char sendarg[6000];

  type=onearg(type, arg);

  if (!strcasecmp(arg, "who"))
    imc_send_whoreply(from->name, getstats(), -1);
  else if (!strcasecmp(arg, "info"))
  {
	/* use info from config file - shogar*/
	sprintf(sendarg,"\n\r%s\n\rInfoHost:%s\n\rAdminstrated by %s\n\remail:%s \n\rimcmail:%s\n\r",
		imc_siteinfo.details,
		imc_siteinfo.host,
		imc_siteinfo.name,
		imc_siteinfo.email,
		imc_siteinfo.imail);
        imc_send_whoreply(from->name,sendarg,-1);
/*
    imc_send_whoreply(from->name,
		      "This is a stand-alone IMC router prototype based on Oliver Jowett's router.c.\n\r"
		      "It is administered by " EMAIL "\n\r"
                      "For information on IMC: http://moodyg.erols.com/imc/index.html\n\r", -1);
*/
  }
  else if (!strcasecmp(arg, "direct"))
    imc_send_whoreply(from->name, imc_list(0), -1);
  else if (!strcasecmp(arg, "list"))
    imc_send_whoreply(from->name, imc_list(3), -1);
  else if (!strcasecmp(arg, "config"))
    imc_send_whoreply(from->name, imc_list(4), -1);
  else if (!strcasecmp(arg, "istats"))
    imc_send_whoreply(from->name, imc_getstats(), -1);
  else if (!strcasecmp(arg, "help") || !strcasecmp(arg, "services") ||
	   !strcasecmp(arg, "help"))
    imc_send_whoreply(from->name,
		      "Available rquery types:\n\r"
		      "help       - this list\n\r"
		      "who        - active muds on IMC\n\r"
		      "info       - router information\n\r"
		      "list       - list known muds on IMC\n\r"
		      "direct     - list directly connected muds\n\r"
		      "config     - show local configuration\n\r"
		      "istats     - IMC statistics\n\r", -1);
  else
    imc_send_whoreply(from->name,
		      "Sorry, no information of that type is available", -1);
}

void imc_recv_tell(const imc_char_data *from, const char *to, const char *text,
                   int isreply)
{
  const char *response;

  if (strcmp(to, "*"))
    return;

  response=docommand(from->name, text);
  if (response)
    imc_send_tell(NULL, from->name, response, 1);
}

void imc_recv_chat(const imc_char_data *from, int channel, const char *text)
{
}

void imc_recv_emote(const imc_char_data *from, int channel, const char *text)
{
}

void imc_recv_whois(const imc_char_data *from, const char *to)
{
}

void imc_recv_whoisreply(const char *to, const char *text)
{
}

void imc_recv_whoreply(const char *to, const char *text, int sequence)
{
}

void imc_recv_beep(const imc_char_data *from, const char *to)
{
}

void imc_traceroute(int ping, const char *pathto, const char *pathfrom)
{
}

char *imc_mail_arrived(const char *from, const char *to, const char *date,
		       const char *subject, const char *text)
{
  char *buf=imc_getsbuf(200);
  sprintf(buf, "%s is a router only, and does not accept mail.", imc_name);
  imc_shrinksbuf(buf);
  return buf;
}
static void graceful_exit(int sig)
{
        imc_logstring("Killed from shell");
        imc_shutdown();
	exit(0);
}

int main(void)
{
  signal(SIGPIPE, SIG_IGN);
  signal(SIGTERM,graceful_exit );

  imc_is_router=1;

  imc_startup(CONFIG);
  if ((imc_active < IA_UP) || (imc_lock_file<0))
  {
    imc_logstring("router: giving up");
    exit(1);
  }

#ifdef USE_ICED
  iced_init();
#endif
  
  while (!rebooting)
  {
    imc_idle(imc_get_max_timeout(), 0);
    if (reloading)
    {
      imc_shutdown();
      imc_startup(CONFIG);
      reloading=0;
    }
  }
    
  imc_shutdown();

  exit(0);
}
