/*

$Id: xconnect,v 1.14 2001/03/11 17:39:02 joseph Exp $

*/

#include <stdlib.h>
#include <string.h>
#include "memleak.h"

#include "sys/types.h"

#ifdef SOCKLIB
  #include "inetlib.h"
#else
  #include "sys/byteorder.h"
#endif

#include "arpa/inet.h"
#include "sys/errno.h"
#include "sys/socket.h"

#ifdef MemCheck_MEMCHECK
#include "MemCheck:MemCheck.h"
#endif

#include "defines.h"

#include "errlist.h"
#include "log.h"
#include "msgs.h"
#include "treport.h"
#include "xconnect.h"
#include "stopquit.h"
#include "leakcheck.h"

/* Gets fixed details of service */
static int xconnect_get_fixed( const char *service, int *port )
{
  struct servent *serv = getservbyname( (char *) service, "tcp" );

  if ( !serv )
  {
    xsyslog_logmessage(log_NAME, msgs_lookup1("NoService", service), log_DebugInfo);
    return -1;
  }

  *port = serv->s_port;

  return 0;
}

static xsock xconnect(const char *hostname, int port, int proto,
	status_handle status)
{
  struct sockaddr_in address;
  xsock sock;

  leak_check();

  xsyslog_logmessage(log_NAME, msgs_lookup1("Resolving", hostname),
  	log_DebugMiscInfo);
  status_show_misc(status, msgs_lookup1("Resolving", hostname));

  address.sin_family = AF_INET;
  address.sin_addr.s_addr = inet_addr((char *) hostname);

  leak_check();

  if (address.sin_addr.s_addr == (u_long) -1)
  {
    dns_t *dns;
    struct hostent *host = 0;
    
    if ((host = xsock_gethostbyname(hostname, &dns)) == 0)
    {
      if ( !stopquit_pending )
      {
        char str[32];
        sprintf(str, "%d", errno);
        xsyslog_logmessage(log_NAME,
       	  msgs_lookup2("NoHost", errlist_str(errno), str),
       	  log_SocketError);
      }
      return -1;
    }
    else
    {
      if (host->h_addrtype != AF_INET)
      {
        xsyslog_logmessage(log_NAME,msgs_lookup("NotInet"),log_SocketError);
        dns_dispose(dns);
        return -1;
      }
      address.sin_addr = *(struct in_addr *) host->h_addr;
      dns_dispose(dns);
    }
  }

  leak_check();

  if ((sock = xsock_create(PF_INET, SOCK_STREAM, proto)) == -1)
  {
    char str[32];
    sprintf(str, "%d", errno);
    xsyslog_logmessage(log_NAME,
    	msgs_lookup2("NoSock", errlist_str(errno), str),
    	log_SocketError);
    return -1;
  }
  leak_check();
  
  

  xsyslog_logmessage(log_NAME, msgs_lookup2("Connecting",
  	hostname, inet_ntoa(address.sin_addr)),
  	log_DebugMiscInfo);
  status_show_misc(status, msgs_lookup2("Connecting",
  	hostname, inet_ntoa(address.sin_addr)));

  leak_check();

  address.sin_port = port;

  if (xsock_connect(sock, (struct sockaddr *) &address, sizeof(address)) == -1)
  {
    char str[32];
    sprintf(str, "%d", errno);
    xsyslog_logmessage(log_NAME,
      msgs_lookup2("NoConn", errlist_str(errno), str),
      log_SocketError);
    xsock_close(sock);
    return -1;
  }

  leak_check();

  return sock;
}

xsock xconnect_pop(const char *server, status_handle status)
{
  static int port = -1; /* NB. This must only be used for the official pop3 port! */
  char *x;

  if ( x = strchr(server,':'), x )
  {
    /* server has a : in it... get port number from there */
    char real_server[128];
    int port = htons( atoi( x + 1 ) );
    *real_server = 0;
    if ( x - server >= sizeof real_server )
      strncat( real_server, server, sizeof(real_server) - 1 );
    else
      strncat( real_server, server, x - server );

    return xconnect( real_server, port, 0, status );
  }

  if ( port == -1 )
  {
#ifdef MemCheck_MEMCHECK
    MemCheck_SetReadChecking(0);
#endif
    if ( xconnect_get_fixed( "pop3", &port ) != 0 )
      port = htons( 110 );
#ifdef MemCheck_MEMCHECK
    MemCheck_SetReadChecking(1);
#endif
  }
  return xconnect( server, port, 0, status );
}

xsock xconnect_smtp(const char *server, status_handle status)
{
  static int port = -1; /* NB. This must only be used for the official SMTP port! */
  char *x;

  if ( x = strchr(server,':'), x )
  {
    /* server has a : in it... get port number from there */
    char real_server[128];
    int port = htons( atoi( x + 1 ) );
    *real_server = 0;
    if ( x - server >= sizeof real_server )
      strncat( real_server, server, sizeof(real_server) - 1 );
    else
      strncat( real_server, server, x - server );

    return xconnect( real_server, port, 0, status );
  }

  if ( port == -1 )
  {
#ifdef MemCheck_MEMCHECK
    MemCheck_SetReadChecking(0);
#endif
    if ( xconnect_get_fixed( "smtp", &port ) != 0 )
      port = htons( 25 );
#ifdef MemCheck_MEMCHECK
    MemCheck_SetReadChecking(1);
#endif
  }
  return xconnect( server, port, 0, status );
}
