/*
 *  ftnnode.c -- Handle our links
 *
 *  ftnnode.c is a part of binkd project
 *
 *  Copyright (C) 1996-1998  Dima Maloff, 5047/13
 *
 *  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. See COPYING.
 */

/*
 * $Id: ftnnode.c,v 2.12.2.2 2003/07/12 18:14:08 gul Exp $
 *
 * $Log: ftnnode.c,v $
 * Revision 2.12.2.2  2003/07/12 18:14:08  gul
 * Fixed node output on debugcfg
 *
 * Revision 2.12.2.1  2003/06/30 22:49:39  hbrew
 * Allow to override -ip, -sip, -md, -nomd in add_node()
 *
 * Revision 2.12  2003/05/04 08:49:05  gul
 * Fix previous patch
 *
 * Revision 2.11  2003/05/04 08:45:30  gul
 * Lock semaphores more safely for resolve and IP-addr print
 *
 * Revision 2.10  2003/05/01 09:55:01  gul
 * Remove -crypt option, add global -r option (disable crypt).
 *
 * Revision 2.9  2003/04/23 04:35:34  gul
 * Fix semaphores usage
 *
 * Revision 2.8  2003/03/31 19:51:29  gul
 * Fix prev patch
 *
 * Revision 2.7  2003/03/31 19:35:16  gul
 * Clean semaphores usage
 *
 * Revision 2.6  2003/02/28 20:39:08  gul
 * Code cleanup:
 * change "()" to "(void)" in function declarations;
 * change C++-style comments to C-style
 *
 * Revision 2.5  2003/02/22 15:53:45  gul
 * Bugfix with locking array of nodes in multithread version
 *
 * Revision 2.4  2003/02/22 14:30:18  gul
 * Make nNod var static
 *
 * Revision 2.3  2003/02/22 12:12:33  gul
 * Cleanup sources
 *
 * Revision 2.2  2002/05/10 17:46:06  gul
 * passwords file usage bugfix
 *
 * Revision 2.1  2001/02/15 11:03:18  gul
 * Added crypt traffic possibility
 *
 * Revision 2.0  2001/01/10 12:12:38  gul
 * Binkd is under CVS again
 *
 * Revision 1.3  1997/11/03  06:10:39  mff
 * +nodes_init()
 *
 * Revision 1.2  1997/10/23  04:07:55  mff
 * many changes to hide pNod int ftnnode.c
 *
 * Revision 1.1  1996/12/29  09:41:37  mff
 * Initial revision
 */

#include <stdlib.h>
#include <string.h>
#include "assert.h"
#include "ftnnode.h"
#include "ftnq.h"
#include "tools.h"
#include "sem.h"
#include "readcfg.h"

#if defined(HAVE_THREADS) || defined(AMIGA)
static MUTEXSEM NSem;
#endif

static int nNod = 0;
static FTN_NODE *pNod = 0;
static int nNodSorted = 0;

/*
 * Call this before all others functions from this file.
 */
void nodes_init (void)
{
  InitSem (&NSem);
}

void locknodesem (void)
{
  LockSem (&NSem);
}

void releasenodesem (void)
{
  ReleaseSem (&NSem);
}

void nodes_deinit(void)
{
  CleanSem (&NSem);
}

/*
 * Compares too nodes. 0 == don't match
 */
static int node_cmp (FTN_NODE *a, FTN_NODE *b)
{
  return ftnaddress_cmp (&a->fa, &b->fa);
}

/*
 * Sorts pNod array. Must NOT be called if NSem is locked!
 */
static void sort_nodes (void)
{
  qsort (pNod, nNod, sizeof (FTN_NODE), (int (*) (const void *, const void *)) node_cmp);
  nNodSorted = 1;
}

/*
 * Add a new node, or edit old settings for a node
 *
 * 1 -- ok, 0 -- error;
 */
static int add_node_nolock(FTN_ADDR *fa, char *hosts, char *pwd, char obox_flvr,
	      char *obox, char *ibox, int NR_flag, int ND_flag,
	      int MD_flag, int restrictIP)
{
  int cn;

  for (cn = 0; cn < nNod; ++cn)
  {
    if (!ftnaddress_cmp (&pNod[cn].fa, fa))
      break;
  }
  /* Node not found, create new entry */
  if (cn >= nNod)
  {
    cn = nNod;

    pNod = xrealloc (pNod, sizeof (FTN_NODE) * ++nNod);
    memset (pNod + cn, 0, sizeof (FTN_NODE));
    memcpy (&pNod[cn].fa, fa, sizeof (FTN_ADDR));
    strcpy (pNod[cn].pwd, "-");
    pNod[cn].hosts = NULL;
    pNod[cn].obox_flvr = 'f';
    pNod[cn].NR_flag = NR_OFF;
    pNod[cn].ND_flag = ND_OFF;
    pNod[cn].MD_flag = MD_USE_OLD;
    pNod[cn].restrictIP = RIP_OFF;

    /* We've broken the order... */
    nNodSorted = 0;
  }

  if (MD_flag != MD_USE_OLD)
    pNod[cn].MD_flag = MD_flag;
  if (restrictIP != RIP_USE_OLD) 
    pNod[cn].restrictIP = restrictIP;
  if (NR_flag != NR_USE_OLD)
    pNod[cn].NR_flag = NR_flag;
  if (ND_flag != ND_USE_OLD)
    pNod[cn].ND_flag = ND_flag;

  if (hosts && *hosts)
  {
    if (pNod[cn].hosts) free (pNod[cn].hosts);	       
    pNod[cn].hosts = xstrdup (hosts);
  }

  if (pwd && *pwd && strcmp(pwd, "-"))
  {
    strnzcpy (pNod[cn].pwd, pwd, sizeof (pNod[cn].pwd));
  }

  if (obox_flvr != '-')
  {
    pNod[cn].obox_flvr = obox_flvr;
  }

  if (obox)
  {
    if (pNod[cn].obox)
      free (pNod[cn].obox);
    pNod[cn].obox = xstrdup (obox);
  }

  if (ibox)
  {
    if (pNod[cn].ibox)
      free (pNod[cn].ibox);
    pNod[cn].ibox = xstrdup (ibox);
  }

  return 1;
}

int add_node (FTN_ADDR *fa, char *hosts, char *pwd, char obox_flvr,
	      char *obox, char *ibox, int NR_flag, int ND_flag,
	      int MD_flag, int restrictIP)
{
  int res;
  locknodesem();
  res = add_node_nolock(fa, hosts, pwd, obox_flvr, obox, ibox, NR_flag, ND_flag,
                        MD_flag, restrictIP);
  releasenodesem();
  return res;
}

static FTN_NODE *get_defnode_info(FTN_ADDR *fa, FTN_NODE *on)
{
  struct hostent *he;
  FTN_NODE n, *np;
  char host[MAXHOSTNAMELEN + 1];       /* current host/port */
  unsigned short port;
  int i;

  strcpy(n.fa.domain, "defnode");
  n.fa.z=n.fa.net=n.fa.node=n.fa.p=0;
  np = (FTN_NODE *) bsearch (&n, pNod, nNod, sizeof (FTN_NODE),
                 (int (*) (const void *, const void *)) node_cmp);

  if (!np) /* we don't have defnode info */
    return on;

  for (i=1; get_host_and_port(i, host, &port, np->hosts, fa)==1; i++)
  {
    lockresolvsem();
    he=gethostbyname(host);
    releaseresolvsem();
    if (!he) continue;
    sprintf (host+strlen(host), ":%d", port);
    i=0;
    break;
  }
  if (i)
    strcpy(host, "-");
  if (on)
  { /* on contains only passwd */
    on->hosts=xstrdup(host);
    on->NR_flag=np->NR_flag;
    on->ND_flag=np->ND_flag;
    on->MD_flag=np->MD_flag;
    on->ND_flag=np->ND_flag;
    on->restrictIP=np->restrictIP;
    return on;
  }

  if(!add_node_nolock(fa, host, NULL, np->obox_flvr, np->obox, np->ibox, 
       np->NR_flag, np->ND_flag, np->MD_flag, np->restrictIP))
    return NULL;
  sort_nodes ();
  memcpy (&n.fa, fa, sizeof (FTN_ADDR));
  return (FTN_NODE *) bsearch (&n, pNod, nNod, sizeof (FTN_NODE),
                      (int (*) (const void *, const void *)) node_cmp);
}
/*
 * Return up/downlink info by fidoaddress. 0 == node not found
 */
FTN_NODE *get_node_info (FTN_ADDR *fa)
{
  FTN_NODE n, *np;

  if (!nNodSorted)
    sort_nodes ();
  memcpy (&n.fa, fa, sizeof (FTN_ADDR));
  np = (FTN_NODE *) bsearch (&n, pNod, nNod, sizeof (FTN_NODE),
			   (int (*) (const void *, const void *)) node_cmp);
  if ((!np || !np->hosts) && havedefnode)
    np=get_defnode_info(fa, np);
  else if (np && !np->hosts)
    /* node exists only in passwords and defnode is not defined */
    np->hosts = xstrdup("-");
  return np;
}

/*
 * Find up/downlink info by fidoaddress and write info into node var.
 * Return pointer to node structure or NULL if node not found.
 */
FTN_NODE *get_node (FTN_ADDR *fa, FTN_NODE *node)
{
  FTN_NODE *n;

  locknodesem();
  if ((n = get_node_info(fa)) == NULL)
  {
    releasenodesem();
    return NULL;
  }
  memcpy(node, n, sizeof(*node));
  releasenodesem();
  return node;
}

/*
 * Iterates through nodes while func() == 0.
 */
int foreach_node (int (*func) (FTN_NODE *, void *), void *arg)
{
  int i, rc = 0;

  locknodesem();
  for (i = 0; i < nNod; ++i)
  {
    if (!pNod[i].hosts)
      rc = func (get_node_info(&(pNod[i].fa)), arg);
    else
      rc = func (pNod + i, arg);
    if (rc != 0)
      break;
  }
  releasenodesem();
  return rc;
}

static int print_node_info_1 (FTN_NODE *fn, void *arg)
{
  char szfa[FTN_ADDR_SZ + 1];

  ftnaddress_to_str (szfa, &fn->fa);
  fprintf ((FILE *) arg, "%-20.20s %s %s %c %s %s%s%s%s%s%s%s\n",
	   szfa, fn->hosts ? fn->hosts : "-", fn->pwd,
	   fn->obox_flvr, fn->obox ? fn->obox : "-",
	   fn->ibox ? fn->ibox : "-",
	   (fn->NR_flag == NR_ON) ? " -nr" : "",
	   (fn->ND_flag == ND_ON) ? " -nd" : "",
	   (fn->ND_flag == MD_ON) ? " -md" : "",
	   (fn->ND_flag == MD_OFF) ? " -nomd" : "",
	   (fn->restrictIP == RIP_ON) ? " -ip" : "",
	   (fn->restrictIP == RIP_SIP) ? " -sip" : "");
  return 0;
}

void print_node_info (FILE *out)
{
  foreach_node (print_node_info_1, out);
}

/*
 * Create a poll for an address (in "z:n/n.p" format) (0 -- bad)
 */
int poll_node (char *s)
{
  FTN_ADDR target;

  if (!parse_ftnaddress (s, &target))
  {
    Log (1, "`%s' cannot be parsed as a Fido-style address\n", s);
    return 0;
  }
  else
  {
    char buf[FTN_ADDR_SZ + 1];

    exp_ftnaddress (&target);
    ftnaddress_to_str (buf, &target);
    Log (4, "creating a poll for %s (`%c' flavour)", buf, POLL_NODE_FLAVOUR);
    locknodesem();
    if (!get_node_info (&target))
      if (!add_node_nolock (&target, "*", 0, '-', 0, 0, 0, 0, 0, 0))
	Log (1, "%s: add_node() failed", buf);
    releasenodesem();
    return create_poll (&target, POLL_NODE_FLAVOUR);
  }
}
