/* Socket routines

   Cygnus IRC Services - Copyright (c) 2001-2002 Darcy Grexton
   Contact: skold@habber.net, skold @ HabberNet

   See doc/LICENSE for licensing details.
 */

#include "../inc/services.h"

fd_set readfds, nullfds;

static int read_server (int, char *);
static void process_packet (char *);

#if !HAVE_GETHOSTBYNAME
/* Translate an IP dotted-quad address to a 4-byte character string.
   Return NULL if the given string is not in dotted-quad format.
 */
static char *pack_ip (const char *ipaddr)
{
    static char ipbuf[4];
    int tmp[4], i;

    if (sscanf (ipaddr, "%d.%d.%d.%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3]) != 4)
	return NULL;

    for (i = 0; i < 4; i++)
    { 
	if (tmp[i] < 0 || tmp[i] > 255)
	    return NULL;

	ipbuf[i] = tmp[i];
    }

    return ipbuf;
}
#endif

/* Disconnect from the server */
void disconn (int s)
{
    shutdown (s, 2);
    close (s);
}

#ifndef WIN32
/* Connect to our uplink */
int16 conn (const char *host, int port, const char *lhost, int lport)
{  
#if HAVE_GETHOSTBYNAME
    struct hostent *hp;
#else
    char *addr;
#endif
    struct sockaddr_in sa, lsa;
    int sock;

    memset(&lsa, 0, sizeof(lsa));

    if (lhost)
    {
#if HAVE_GETHOSTBYNAME
	if ((hp = gethostbyname (lhost)) != NULL)
	    memcpy ((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
#else
	if (addr = pack_ip (lhost))
	    memcpy ((char *)&sa.sin_addr, addr, 4);
#endif
	else
	    lhost = NULL;
    }

    if (lport)
	sa.sin_port = htons (lport);

    memset (&sa, 0, sizeof (sa));
        
#if HAVE_GETHOSTBYNAME
    if (!(hp = gethostbyname (host)))
	return -1;

    memcpy ((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
    sa.sin_family = hp->h_addrtype;
#else
    if (!(addr = pack_ip (host)))
    {
	log ("conn(): `%s' is not a valid IP address", host);
	errno = EINVAL;
	return -1;
    }

    memcpy ((char *)&sa.sin_addr, addr, 4);
    sa.sin_family = AF_INET;
#endif
    sa.sin_port = htons ((unsigned short)port);

    if ((sock = socket (sa.sin_family, SOCK_STREAM, 0)) < 0)
	return -1;

    if ((lhost || lport) && bind (sock, (struct sockaddr *)&lsa,sizeof(sa)) < 0)
    {
	int errno_save = errno;
	close (sock);
	errno = errno_save;
	return -1;
    }

    if (connect (sock, (struct sockaddr *)&sa, sizeof(sa)) < 0)
    {
	int errno_save = errno;
	close (sock);
	errno = errno_save;
	return -1;
    }

    return sock;
}
#endif /* WIN32 */

/* Read data from the server, and parse it. */
void read_loop ()
{
    int sresult;
    struct timeval timeout;
    static char buf[BUFSIZE + 1];

    while (!(runflags & (RUN_RESTART | RUN_SHUTDOWN)))
    {
	memset (buf, '\0', BUFSIZE + 1);

	/* Check for any timeouts and such */
	chk ();

	FD_ZERO (&readfds);

	timeout.tv_sec = 1;
	timeout.tv_usec = 0L;

	FD_SET (servsock, &readfds);	

	if ((sresult = select (FD_SETSIZE, &readfds, &nullfds, &nullfds,
	    &timeout)) > 0)
	{
	    if (!read_server (servsock, buf))
	    {
		log (RPL_LOST_CONNECTION, me.uplink);
		return;
	    }

	    process_packet (buf);
	}
	else
	{
	    if (!(!sresult) || ((sresult == -1) && (errno == EINTR)))
	    {
		log (RPL_LOST_CONNECTION, me.uplink);
		return;
	    }
	}
    }
}

/* Send the given text to the server. */
void sendtoserver (char *fmt,...)
{
    va_list args;
    char buf[BUFSIZE];

    va_start (args, fmt);
    vsnprintf (buf, BUFSIZE, fmt, args);

    sstrlcat (buf, "\n", BUFSIZE);

    if ((write (servsock, buf, strlen (buf))) == -1)
    {
	log (RPL_WRITE_ERROR);
	close (servsock);
    }

    va_end (args);
}

/* Read data from the server. */
static int read_server (int fd, char *buffer)
{
    int n = read (fd, buffer, BUFSIZE);

    buffer[n] = '\0';

    return n;
}

/* Do initial processing on the buffer, then throw it into
   process().
 */
static void process_packet (char *buffer)
{
    char *ptr;
    static char tmp[BUFSIZE * 8];

    while ((ptr = strchr (buffer, '\n')))
    {
	*ptr = '\0';

	if (*(ptr - 1) == '\r')
	    *(ptr - 1) = '\0';

	snprintf (inbuf, (BUFSIZE * 8), "%s%s", tmp, buffer);

	*tmp = '\0';
	process ();
	buffer = ptr + 1;
    }

    if (*buffer)
	sstrlcpy (tmp, buffer, (BUFSIZE * 8) - strlen (tmp));
}
