/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */

/* 
 * nsssl.c --
 *
 *	Call internal Ns_DriverInit.
 *
 */

#include "ns.h"
#include "ssl.h"

#ifdef SSL_EXPORT 
#define DRIVER_NAME "nsssle"
#else
#define DRIVER_NAME "nsssl"
#endif

static Ns_DriverProc SSLProc;

NS_EXPORT int Ns_ModuleVersion = 1;


/*
 *----------------------------------------------------------------------
 *
 * Ns_ModuleInit --
 *
 *	Initialize the SSL driver.
 *
 * Results:
 *	NS_OK if initialized ok, NS_ERROR otherwise.
 *
 * Side effects:
 *	See Ns_DriverInit.
 *
 *----------------------------------------------------------------------
 */

NS_EXPORT int
Ns_ModuleInit(char *server, char *module)
{
    Ns_DriverInitData init;
    char *cert, *key, *path;
    void *dssl;
    int n;

    /*
     * Initialize the global and per-driver SSL.
     */

    path = Ns_ConfigGetPath(server, module, NULL);
    if (NsSSLInitialize(server, module) != NS_OK) { 
        Ns_Log(Error, "%s: failed to initialize ssl driver", module);
        return NS_ERROR; 
    } 
    cert = Ns_ConfigGet(path, "certfile"); 

    if (cert == NULL) { 
        Ns_Log(Warning, "%s: certfile not specified", module); 
        return NS_OK; 
    } 
#ifdef SSL_EXPORT 
    n = 40;
#else
    n = 128;
#endif
    Ns_Log(Notice, "%s: initialized with %d-bit encryption", module, n);

#ifdef HAVE_SWIFT
    Ns_Log(Notice, "%s: SUPPORTS RAINBOW CRYPTOSWIFT HARDWARE SSL ACCELERATOR",
	   module);
#endif
    key = Ns_ConfigGet(path, "keyfile"); 
    dssl = NsSSLCreateServer(cert, key);
    if (dssl == NULL) {
        return NS_ERROR;
    }

    /*
     * Initialize the driver without the async option so all I/O
     * happens in the connection thread, avoiding any possible
     * blocking in the driver thread due to SSL overhead.  Set
     * the SSL option to use the SSL port and protocol defaults.
     */

    init.version = NS_DRIVER_VERSION_1;
    init.name = DRIVER_NAME;
    init.proc = SSLProc;
    init.arg = dssl;
    init.opts = NS_DRIVER_SSL;
    init.path = NULL;

    return Ns_DriverInit(server, module, &init);
}


/*
 *----------------------------------------------------------------------
 *
 * SSLProc --
 *
 *	SSL driver callback proc.  This driver performs the necessary
 *	handshake and encryption of SSL.
 *
 * Results:
 *	For close, always 0.  For keep, 0 if connection could be
 *	properly flushed, -1 otherwise.  For send and recv, # of bytes
 *	processed or -1 on error.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SSLProc(Ns_DriverCmd cmd, Ns_Sock *sock, struct iovec *bufs, int nbufs)
{
    Ns_Driver *driver = sock->driver;
    int n, total;

    switch (cmd) {
    case DriverRecv:
    case DriverSend:
	/*
	 * On first I/O, initialize the connection context.
	 */

	if (sock->arg == NULL) { 
	    n = driver->recvwait;
	    if (n > driver->sendwait) {
		n = driver->sendwait;
	    }
	    sock->arg = NsSSLCreateConn(sock->sock, n, driver->arg);
	    if (sock->arg == NULL) { 
		return -1;
	    } 
	}

	/*
	 * Process each buffer one at a time.
	 */

	total = 0;
	do {
	    if (cmd == DriverSend) {
		n = NsSSLSend(sock->arg, bufs->iov_base, bufs->iov_len);
	    } else {
		n = NsSSLRecv(sock->arg, bufs->iov_base, bufs->iov_len);
	    }
	    if (n < 0 && total > 0) {
		/* NB: Mask error if some bytes were read. */
		n = 0;
	    }
	    ++bufs;
	    total += n;
	} while (n > 0 && --nbufs > 0);
	n = total;
	break;

    case DriverKeep:
	if (sock->arg != NULL && NsSSLFlush(sock->arg) == NS_OK) {
	    n = 0;
	} else {
	    n = -1;
	}
	break;

    case DriverClose:
	if (sock->arg != NULL) { 
	    (void) NsSSLFlush(sock->arg); 
	    NsSSLDestroyConn(sock->arg); 
	    sock->arg = NULL;
	}
	n = 0;
	break;

    default:
	/* Unsupported command. */
	n = -1;
	break;
    }
    return n;
}
