#ifdef RCS
static char rcsid[]="$Id: servfunc.c,v 1.1.1.1 2000/11/13 02:42:48 holsta Exp $";
#endif
/******************************************************************************
 *                    Internetting Cooperating Programmers
 * ----------------------------------------------------------------------------
 *
 *  ____    PROJECT
 * |  _ \  __ _ _ __   ___ ___ _ __ 
 * | | | |/ _` | '_ \ / __/ _ \ '__|
 * | |_| | (_| | | | | (_|  __/ |   
 * |____/ \__,_|_| |_|\___\___|_|   the IRC bot
 *
 * All files in this archive are subject to the GNU General Public License.
 *
 * $Source: /cvsroot/dancer/dancer/src/servfunc.c,v $
 * $Revision: 1.1.1.1 $
 * $Date: 2000/11/13 02:42:48 $
 * $Author: holsta $
 * $State: Exp $
 * $Locker:  $
 *
 * ---------------------------------------------------------------------------
 *****************************************************************************/

#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "list.h"
#include "function.h"
#include "servfunc.h"
#include "fplrun.h"

/* --- Global ----------------------------------------------------- */

extern time_t now;

extern char servername[];
extern char servfile[];
extern char *errfrom;
extern itemserv *currentserv;

itemserv *servHead;
static int servid = 1;


/* --- FindServ --------------------------------------------------- */

itemserv *FindServ(char *name, long port)
{
  int id;
  itemserv *s;

  snapshot;
  id = atoi(name);
  for (s = First(servHead); s; s = Next(s)) {
    if ((id && (id == s->s.id)) ||
        (StrEqual(name, s->s.name) && (port == s->s.port))) {
      return s;
    }
  }
  return NULL;
}

/* --- NextServ --------------------------------------------------- */

itemserv *NextServ(void)
{
  itemserv *afterthis;
  itemserv *s;

  snapshot;
  afterthis = currentserv;
  for (s = First(servHead); s; s = Next(s)) {
    if (s->s.flags & SERV_DISABLED)
      continue;
    if (NULL == afterthis)
      return s;
    else if (s == afterthis)
      afterthis = NULL; /* get the next */
  }
  return First(servHead); /* if nothing matched, return the first */
}

int AddServ(struct ServerSub *sub)
{
  int retcode = TRUE;
  itemserv *s;

  snapshot;
  s = FindServ(sub->name, sub->port);
  if (NULL == s) {
    s = NewEntry(itemserv);
    if (s) {
      InsertLast(servHead, s);
      s->s    = *sub;
      s->s.id = servid++;
      return FALSE;
    }
  }
  else if (s->s.flags & SERV_DISABLED &&
           !(sub->flags & SERV_DISABLED) &&
           !(sub->flags & SERV_INCONFIG)) {
    /*
     * The existing one is marked disabled. The new one is not, and it is
     * not from the .config file.
     * .config file entries are not allowed to undo disabled ones.
     */

    /*
     * The added one already exists, so we should return successful
     * but we should free the resources of this new struct since we
     * are reusing an old one.
     */
    s->s.flags &= ~SERV_DISABLED;
    retcode = FALSE; 
  }
  else if (sub->flags & SERV_INCONFIG) {
    s->s.flags |= SERV_INCONFIG; /* this exist in .config we know now */
  }

  /* Free the resources */
  if (sub->name)
    StrFree(sub->name);
  if (sub->passwd)
    StrFree(sub->passwd);
  if (sub->adder)
    StrFree(sub->adder);
  if (sub->message)
    StrFree(sub->message);

  return retcode;
}

int ManAddServ(char *name, long port, char *passwd, char *who, long flags)
{
  struct ServerSub sub;

  snapshot;
  memset(&sub, 0, sizeof(struct ServerSub));
  sub.name    = StrDuplicate(name);
  sub.port    = port ? port : IRCPORT;
  sub.passwd  = passwd ? StrDuplicate(passwd) : NULL;
  sub.adder   = who ? StrDuplicate(who) : NULL;
  sub.addtime = now;
  sub.flags   = flags;
  return AddServ(&sub);
}

void ListServ(char *from, char *line)
{
  char match[MIDBUFFER] = "*";
  itemserv *s;

  snapshot;
  StrScan(line, "%"MIDBUFFERTXT"s", match);
  for (s = First(servHead); s; s = Next(s)) {
    if (Match(s->s.name, match)) {
      Sendf(from, "%d - %s %d%s%s", s->s.id, s->s.name, s->s.port,
            s->s.passwd ? " <password>" : "",
            (s->s.flags & SERV_DISABLED) ? " [DISABLED]" : "");
    }
  }
}

/* --- SaveServ --------------------------------------------------- */

int SaveServ(void)
{
  char tempfile[BIGBUFFER];
  bool ok = TRUE;
  itemserv *s;
  FILE *f;

  snapshot;
  if (NIL == servfile[0])
    return TRUE;

  StrFormatMax(tempfile, sizeof(tempfile), "%s~", servfile);

  f = fopen(tempfile, "w");
  if (f) {
    if (0 > fprintf(f, "# Dancer serverlist version: " VERSIONMSG "\n")) {
      ok = FALSE;
    }
    else {
      for (s = First(servHead); s; s = Next(s)) {
        if (0 > fprintf(f, "%s:%d:%s\n" ":%s\n" "-%s\n" "!%d %d %d %d %d %d\n",
                        s->s.name, s->s.port, s->s.passwd ? s->s.passwd : "",
                        s->s.message ? s->s.message : "",
                        s->s.adder ? s->s.adder : "",
                        s->s.connect, s->s.disconnect, s->s.average,
                        s->s.numconnects, s->s.flags, s->s.addtime)) {
          ok = FALSE;
          break;
        }
      }
    }
    fclose(f);

    if (ok)
      rename(tempfile, servfile);
  }
  else
    return TRUE;

  return FALSE;
}

/* --- LoadServ --------------------------------------------------- */

int LoadServ(void)
{
  char buffer[MAXLINE];
  char name1[BIGBUFFER];
  char name2[BIGBUFFER];
  int number[6];
  struct ServerSub sub;
  FILE *f;

  snapshot;
  f = fopen(servfile, "r");
  if (f) {
    memset(&sub, 0, sizeof(struct ServerSub));
    while (fgets(buffer, sizeof(buffer), f)) {
      switch(buffer[0]) {

      case '#':
      case '\n':
        break;

      case ':':
        if (1 == StrScan(buffer, ":%[^\n]", name1)) {
          sub.message = StrDuplicate(name1);
        }
        break;

      case '-':
        if (1 == StrScan(buffer, "-%[^\n]", name1)) {
          sub.adder = StrDuplicate(name1);
        }
        break;

      case '!':
        if (6 == StrScan(buffer, "!%d %d %d %d %d %d",
                         &number[0], &number[1], &number[2],
                         &number[3], &number[4], &number[5])) {
          sub.connect     = number[0];
          sub.disconnect  = number[1];
          sub.average     = number[2];
          sub.numconnects = number[3];
          sub.flags       = number[4] & ~SERV_INCONFIG;
          sub.addtime     = number[5];
        }
        AddServ(&sub);
        memset(&sub, 0, sizeof(struct ServerSub));
        break;

      default:
        name2[0]  = (char)0;
        number[0] = IRCPORT;
        if (1 <= StrScan(buffer, "%[^:]:%d:%[^\n]",
                         name1, &number[0], name2)) {
          sub.name = StrDuplicate(name1);
          sub.port = number[0];
          if (name2[0])
            sub.passwd = StrDuplicate(name2);
        }
        break;
      }
    }
    fclose(f);
  }
  else
    return TRUE;

  return FALSE;
}

void FreeServ(void *v)
{
  itemserv *s;

  snapshot;
  s = (itemserv *)v;
  if (s) {
    if (s->s.name)
      StrFree(s->s.name);
    if (s->s.passwd)
      StrFree(s->s.passwd);
    if (s->s.message)
      StrFree(s->s.message);
    if (s->s.adder)
      StrFree(s->s.adder);
  }
}

void ServInit(void)
{
  extern itemlist *serverHead;
  char servername[BIGBUFFER], serverpasswd[BIGBUFFER];
  int serverport;
  itemlist *p;

  snapshot;
  servHead = NewList(itemserv);
  if (servHead) {
    /* First, we get the servers from the .serv file */
    LoadServ(); 

    /* Now, we convert the old-style serverlist */
    for (p = First(serverHead); p; p = Next(p)) {
      serverpasswd[0] = (char)0;
      serverport = IRCPORT;
      StrScan((char *)p->pointer, "%[a-zA-Z0-9.-]%*[ :]%d%*[ :]%[^\n]",
              servername, &serverport, serverpasswd);
      ManAddServ(servername, serverport, serverpasswd[0] ? serverpasswd : NULL,
                 NULL, SERV_INCONFIG);
    }
  }
}

void ServCleanup(void)
{
  snapshot;
  SaveServ();
  DeleteList(servHead, FreeServ);
}

int DelServ(char *name, long port)
{
  itemserv *s;

  snapshot;
  s = FindServ(name, port);
  if (s) {
    if (s->s.flags & SERV_INCONFIG) {
      /*
       * This is entered in the .config file and therefore we cannot
       * remove this for real, just mark it disabled
       */
      s->s.flags |= SERV_DISABLED;
    }
    else {
      DeleteEntry(servHead, s, FreeServ); /* remove */
    }
    return FALSE;
  }
  return TRUE;
}

int ManDelServ(char *from, char *line)
{
  char server[SEMIBUFFER];
  long port = IRCPORT;

  snapshot;
  if (StrScan(line, "%"SEMIBUFFERTXT"s %d", server, &port)) {
    if (currentserv &&
        StrEqual(server, currentserv->s.name) &&
        (port == currentserv->s.port)) {
      Send(errfrom, GetText(msg_cant_delete_current_server));
      return TRUE;
    }
    if (DelServ(server, port)) {
      Sendf(errfrom, GetText(msg_could_not_find_server), server, port);
      return TRUE;
    }
    Sendf(from, GetText(msg_server_removed), server, port);
    SaveServ();
  }
  else
    return TRUE;

  return FALSE;
}

void StatServ(char *from, int options, char *server, long port)
{
  itemserv *s;

  snapshot;
  if (NIL == server[0]) {
    s = currentserv;
    if (NULL == s) {
      Sendf(errfrom, GetText(msg_server_not_in_list), servername);
      return;
    }
  }
  else {
    s = FindServ(server, port);
    if (NULL == s) {
      Sendf(errfrom, GetText(msg_could_not_find_server), server, port);
      return;
    }
  }

  /*
   * The switch makes duplicate flags get ignored, only one of them set
   * will make a change
   */
  switch(options) {
  case SERV_ENABLED:
    s->s.flags &= ~SERV_DISABLED;
    break;
  case SERV_DISABLED:
    s->s.flags |= SERV_DISABLED;
    break;
  }

  Sendf(from, "%s port %d %s", s->s.name, s->s.port,
        s->s.passwd ? "<passwd>" : "");
  Sendf(from, "%s last connect %s ago", (currentserv == s) ? "<current>" : "",
        s->s.connect ? TimeAgo(s->s.connect) : "ages");
  Sendf(from, "last disconnect %s ago, average connect time %s",
        s->s.disconnect ? TimeAgo(s->s.disconnect) : "ages",
        SecsToString(s->s.average));
  Sendf(from, "number of connects: %d", s->s.numconnects);
  Sendf(from, "State:%s",
        (s->s.flags & SERV_DISABLED) ? " [DISABLED]" : " normal");
}

void ConnectServ(itemserv *serv)
{
  snapshot;
  currentserv = serv;
  if (serv) {
#ifdef HAVE_LIBFPL
    if (runfpl(RUN_CONNECT, serv->s.name, FPLRUN_PRE))
      return;
#endif
    serv->s.connect = now;
    serv->s.numconnects++;
    SaveServ();
    runfpl(RUN_CONNECT, serv->s.name, FPLRUN_POST);
  }
}

void DisconnectServ(char *fmt, ...)
{
  char buf[BIGBUFFER];
  va_list args;

  snapshot;
  va_start(args, fmt);
  trio_vsnprintf(buf, sizeof(buf), fmt, args);
  va_end(args);

#ifdef HAVE_LIBFPL
  if (runfpl(RUN_DISCONNECT, buf, FPLRUN_PRE))
    return;
#endif

  if (currentserv) {
    if (currentserv->s.message)
      StrFree(currentserv->s.message);
    currentserv->s.message = StrDuplicate(buf);
    currentserv->s.disconnect = now;

    /* calculate the new average connect time */
    currentserv->s.average = ((currentserv->s.average *
                              (currentserv->s.numconnects - 1)) +
                              (currentserv->s.disconnect -
                               currentserv->s.connect)) /
                               currentserv->s.numconnects;
  }

  runfpl(RUN_DISCONNECT, buf, FPLRUN_POST);
}


syntax highlighted by Code2HTML, v. 0.9.1