/*
** network.c
**
** Copyright (C) 1997, Howard Chu <hyc@highlandsun.com>
**
** This file can be redistributed under the terms of the GNU General
** Public Licence.
*/

#include <unistd.h>
#include <osbind.h>
#include <mintbind.h>
#include <aesbind.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "module.h"
#include "hostinfo.h"
#include "network.h"
#include "weblib.h"

#ifdef USE_SSL
#include <ssl.h>
static SSL_CTX *ssl_ctx;
static char     ssl_version[6];
#endif

#define	LOG(x)	if (debug) printf x

extern int      h_errno;

sockB           sockList[2], *Conn = sockList;
int             currSock;

/* look up the IP address of a given host */
int 
resolveHost(hostB * host)
{
    struct hostent *hp;
    char          **ap;
    int             i;

    if ((hp = gethostbyname(host->name.txt)) == NULL)
	return -1;
    for (i = 0, ap = hp->h_addr_list; i < 4 && ap && *ap; i++, ap++)
	memcpy(&host->addr[i], *ap, 4);
    return 0;
}

/*
**  Open a socket to the specified service.
*/
int
openConn(urlB * u)
{
    urlB           *nu;
    int             i, j;
    struct sockaddr_in server;
    size_t          len;

#ifndef USE_SSL
    /* Don't even try, if we don't have SSL available ... */
    if (u->flags & U_USE_SSL)
    {
	errno = ESOCKTNOSUPPORT;
	return -1;
    }
#endif

    if (u->flags & U_USE_PROXY)
	nu = &proxyHosts[u->request];
    else
	nu = u;

    if (!nu->host)
    {
	LOG(("Can't openConn to NULL host!\n"));
	errno = EPATH;
	return -1;
    }
    /* Do we already have a connection to this host and port? */
    if (Conn->host == nu->host && Conn->port == nu->port)
    {
	LOG(("Re-using current connection\n"));
	return 0;
    }
    currSock ^= 1;
    Conn = &sockList[currSock];
    if (Conn->host == nu->host && Conn->port == nu->port)
    {
	LOG(("Re-using previous connection\n"));
	return 0;
    }
    if (Conn->host)
	closeConn(Conn);
    if (!nu->host->addr[0] && resolveHost(nu->host))
    {
	LOG(("Unknown host %s h_errno %d\n", nu->host->name.txt, h_errno));
	errno = EPATH;
	return -1;
    }
    /* Set up the socket address. */
    (void) memset((char *) &server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_port = nu->port;

    /* Loop through the address list, trying to connect. */
    for (i = 0; i < 4 && nu->host->addr[i]; i++)
    {
	/* Make a socket and try to connect. */
	if ((j = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	    break;
	memcpy((char *) &server.sin_addr, &nu->host->addr[i], 4);
	if (connect(j, (struct sockaddr *) & server, sizeof server) == 0)
	{
	    Conn->s = j;
#ifdef USE_SSL
	    if (u->flags & U_USE_SSL)
	    {
		Conn->ssl_con = SSL_new(ssl_ctx);
		SSL_set_fd(Conn->ssl_con, j);
		if (SSL_connect(Conn->ssl_con) < 0)
		{
		    LOG(("SSL_connect failed\n"));
		    if (debug)
			ERR_print_errors_fp(stdout);
		    closeConn(Conn);
		    return -1;
		}
	    }
#endif
	    Conn->host = nu->host;
	    Conn->port = nu->port;
	    Conn->sp = fdopen(Conn->s, "w");
	    return j;
	}
	(void) close(j);
    }
    LOG(("Can't connect\n"));
    return -1;
}

void 
closeConn(sockB * sock)
{
    if (!sock->port)
	return;
    sock->host = NULL;
    sock->port = 0;
    shutdown(sock->s, 2);
    if (sock->sp)
    {
	fclose(sock->sp);
	sock->sp = NULL;
    } else
    {
	close(sock->s);
    }
    sock->s = 0;
#ifdef USE_SSL
    if (sock->ssl_con)
    {
	SSL_free(sock->ssl_con);
	sock->ssl_con = NULL;
    }
#endif
}

long 
netGetChar(sockB * sock)
{
    long            b;

#ifdef USE_SSL
    if (sock->ssl_con)
    {
	char            d;
	b = SSL_read(sock->ssl_con, &d, 1);
	if (b == 1)
	    b = d;
    } else
#endif
	b = Fgetchar(sock->s, 0);
    return b;
}

long 
netFlush(sockB * sock)
{
    long            e;
#ifdef USE_SSL
    if (sock->ssl_con)
    {
	e = SSL_write(sock->ssl_con, sock->sp->_base, sock->sp->_cnt);
	if (e != sock->sp->_cnt)
	    e = EOF;
	else
	    e = 0;
	sock->sp->_cnt = 0;
	sock->sp->_ptr = sock->sp->_base;
    } else
#endif
    {
	e = fflush(sock->sp);
    }
    return e;
}

long 
netRead(sockB * sock, char *buf, size_t num)
{
#ifdef USE_SSL
    if (sock->ssl_con)
	return SSL_read(sock->ssl_con, buf, num);
    else
#endif
	return Fread(sock->s, num, buf);
}

char           *agent;

void 
netInit()
{
    char            ax[256];
    long            i;

#ifdef USE_SSL
    i = SSLeay();
    sprintf(ssl_version, "%x.%02x", i >> 12, (i >> 4) & 0xff);
    SSLeay_add_ssl_algorithms();
    SSL_load_error_strings();
    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
    SSL_CTX_set_default_verify_paths(ssl_ctx);
#endif
    sprintf(ax, "CAB/%s MiNTnetOVL/%s"
#ifdef USE_SSL
	    " SSLeay/%s"
#endif
	    ,cab_version, version
#ifdef USE_SSL
	    ,ssl_version
#endif
	);
    agent = malloc(strlen(ax) + 1);
    strcpy(agent, ax);

}
