/**********************************************************************
 * ti library's initialization routine
 *
 * $Author: brlewis $
 * $Source: /afs/net.mit.edu/project/net_dev/brlewis/src/lib/RCS/ti_open.c,v $
 * $Header: /afs/net.mit.edu/project/net_dev/brlewis/src/lib/RCS/ti_open.c,v 0.1 92/03/12 15:27:45 brlewis Exp $
 *
 * Copyright 1991 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 **********************************************************************/

#include <mit-copyright.h>

#ifndef lint
static char rcsid_ti_open_c[] = "$Header: /afs/net.mit.edu/project/net_dev/brlewis/src/lib/RCS/ti_open.c,v 0.1 92/03/12 15:27:45 brlewis Exp $";
#endif /* lint */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <ctype.h>
#include <ti/memory.h>
#include <ti/ti.h>

/**********************************************************************
 * ti_open(LPSTR hostname, LPSTR portname, TI_H FAR *lphti)
 *	caller sets hostname, portname (NULL means use default)
 *
 * - initializes everything for ti library
 * - connects to server
 * - fills in TI structure
 * - returns error code
 **********************************************************************/

/* jms 7/24/92 -- windows-izing begun */

long
ti_open(hostname, portname, lphti)
     LPSTR hostname, portname;
     TI_H FAR *lphti;
{
  long code;

  TI_H temphti = NULL;
  TI_LP templpti = NULL;

#ifdef WINDOWS /* hurl! bleah! yecch! (blame Novell) */
	       /* this stuff is just nasty. we can't do anything about it. */
  GLOBALHANDLE    hhnd = NULL;
  GLOBALHANDLE    shnd = NULL;
#endif
  struct hostent *hp = NULL;
  struct servent *sp = NULL;

#ifdef WINDOWS /* hurl! hurl! */
  int h_errno;
#else
  extern int h_errno;
#endif

  int i, portnum;

  /* NULL means use default */
  if (!hostname) hostname = TI_DEF_HOST;
  if (!portname) portname = TI_DEF_PORT;

  /* initialize if this is first connection */
  if (!ti_nconnections)
    {
      init_ti_err_tbl();
      bzero(ti_connections, TI_MAX_OPEN * sizeof(TI_H));
#if !defined(WINDOWS) && !defined(MEWEL)
      sigblock(sigmask(SIGPIPE)); /* XXX what about Mac, DOS? */
#else
        /* (the DOS program should know when the socket is broken.
	    What to do?) */
#endif
    }

  /* resolve hostname */
#ifdef WINDOWS
  hhnd = gethostbyname(hostname);
  if (hhnd == NULL)
    {
      h_errno = GetHErrno();
#else
  hp = gethostbyname(hostname);
  if (hp == NULL)
    {
#endif
      /* set error context and return error code */
      /* no need to cleanup. (though we really should use CLEANUP_...) */
      strcpy(ti_context, hostname);
      switch(h_errno)
	{
	case HOST_NOT_FOUND:
	  return(TI_ERR_HOST_NOT_FOUND);
	case TRY_AGAIN:
	  return(TI_ERR_TRY_AGAIN);
	case NO_RECOVERY:
	  return(TI_ERR_NO_RECOVERY);
	case NO_ADDRESS:
	  return(TI_ERR_NO_ADDRESS);
	default:
	  return(TI_ERR_HOST);
	}
    }

  /* resolve portname */
  if (isdigit(*portname))
    portnum = htons(atoi(portname));/***/
  else
    {
#ifdef WINDOWS
      shnd = getservbyname(portname, "tcp");
      if (shnd == NULL)
#else
      sp = getservbyname(portname, "tcp");
      if (sp == NULL)
#endif
	{
	  strcpy(ti_context, portname);
	  CLEANUP_AND_RETURN(TI_ERR_UNKNOWN_PORT)
	}
#ifdef WINDOWS
      /* lock the handle */
      sp = (struct servent FAR *)GlobalLock(shnd);
#endif
      portnum = sp->s_port;
      portname = sp->s_name;
    }

#ifdef WINDOWS
  /* ok, now lock the handle. */
  hp = (struct hostent FAR *)GlobalLock(hhnd);
#endif

  /* find TI already connected to that host and port, if one exists */
  for(i=0; i<TI_MAX_OPEN; i++)
    {
      temphti = ti_connections[i];
      if (!temphti) /* no more connections. drat. */
	break;
      templpti = (TI_LP)TI_DEREFERENCE(temphti);
      if (!templpti->links) goto LOOPCONTINUE;
      if (hp->h_addrtype == templpti->sname.sin_family &&
	  portnum == templpti->sname.sin_port &&
	  !bcmp(hp->h_addr, templpti->sname.sin_addr, hp->h_length))
	{
	  TI_REMOVE_DEREFERENCE(temphti);
	  *lphti = temphti;
	  goto SUCCESS;
	}
LOOPCONTINUE:
      TI_REMOVE_DEREFERENCE(temphti);
    }

  /* find first free TI */
  for(i=0; i<TI_MAX_OPEN; i++)
    if (!ti_connections[i]) break;
  if (i>= TI_MAX_OPEN)
    {
      *ti_context='\0';
      CLEANUP_AND_RETURN(TI_ERR_TOOMANY);
    }
  *lphti = ti_connections[i] = New(TI);
  templpti = (TI_LP)TI_DEREFERENCE(*lphti);

  /* build server socket name */
  bzero(&(templpti->sname), sizeof(templpti->sname));
  templpti->sname.sin_family = AF_INET;
  bcopy(hp->h_addr, &templpti->sname.sin_addr, hp->h_length);
  templpti->sname.sin_port = portnum;

  /* fill in strings */
  TI_MEM(ti_host(templpti) = ti_dupstring(hp->h_name));
  TI_MEM(ti_port(templpti) = ti_dupstring(portname));

  /* make a connection to the specified server on the socket */
  if (code = ti_connect(*lphti)) goto CLEANUP;
  ti_nconnections++;

 SUCCESS:
  /* increment reference count to this connection */
  templpti->links++;
  CLEANUP_AND_RETURN(0L)

CLEANUP:
#ifdef WINDOWS
  if (sp)
    GlobalUnlock(shnd);
  if (hp)
    GlobalUnlock(hhnd);
  if (shnd)
    GlobalFree(shnd);
  if (hhnd)
    GlobalFree(hhnd);
#endif
  TI_REMOVE_DEREFERENCE(*lphti, templpti);
  return (code);
}
