/* Support routines for multi-threaded SSL protocol engine.
 * 
 * The service thread communicates with the client over a single network
 * connection (MST link) using a simple protocol to multiplex 2 logical data 
 * streams (application and ssl data) over this link.
 *
 * Each message sent over the network link has the following structure:
 *    struct rpc_msg { 
 *      char channel;
 *      char function;
 *      short length;
 *      char data[MAX_DATA];
 *    } msg;
 *
 * The channel field designates the virtual data stream this message applies
 * to and is one of:
 *   A - Application data (payload).
 *   R - Remote client connection that initiated the SSL connection.  Encrypted
 *       data is sent over this connection.
 *   G - General data, reserved for future use.
 *
 * The data streams are half-duplex read/write and have following functions:
 *   G - Get, requests that up to msg.length bytes of data be returned.  The
 *       data is returned in the next 'C' function response that matches the
 *       requesting channel.
 *   P - Put, requests that the first msg.length bytes of msg.data be appended
 *       to the designated stream.
 *   C - Confirms a get or put.  Every get and put will get a confirm response,
 *       you cannot initiate another function on a channel until the previous
 *       operation has been confirmed.
 *
 *  The 2 channels may interleave their operations, for example:
 *        Server msg           Client msg
 *         A, Get, 4092          ---->
 *                               <----  R, get, 4092
 *         R, Confirm, {hello}   ---->
 *                               <----  R, put, {srv hello}
 *         R, Confirm, 0         ---->
 *                               .		(SSL handshake completed)
 *                               .              (read first app data).
 *                               <----  A, confirm, {http data}
 *         A, Put, {http data}   ---->
 *                               <----  A, confirm, 0
 *
 *  The length field is not permitted to be larger that 4092 bytes.
 *
 * Author: Dave Jones
 * Date:   24-AUG-1996
 * Revised: 25-SEP-1997		UPdate for 0.8.1
 * Revised: 22-APR-1999		relocate to base_code directory.
 * Revised: 17-JAN-2001		Fix memory leak in freeing c_to_s (free at last|)
 *				Add memory debug mode, enabled by setting
 *				env. var SSL_MEM_OPTION to 2.
 * Revised: 29-NOV-2001		Client cert. support.
 * Revised: 16-DEC-2001		CHAIN_VERIFY and CA_PATH support.
 */
#include <stdlib.h>
#include <stdio.h>

#include <string.h>		/* from ssltest.c */
#include <errno.h>
/*
 * Include headers for SSL library calls.  We must assume following
 * compiler qualifiers are used:
 *     /STANDARD=ANSI89/PREFIX=ALL
 *     /INCLUDE=(SOFT:[{ssl include directory}]
 *     /DEFINE=(FLAT_INC,VMS)
 *     /WARNING=DISABLE=DOLLARID
 */
#include "buffer.h"
#include "x509.h"
#include "client_cert.h"
#ifndef VMS
#define VMS
#endif
#include "ssl.h"
#include "err.h"
/*
 * Include header files from http server source tree.
 */
#include "message_service.h"			/* mst_* prototypes */

BIO *bio_err=NULL;
BIO *bio_stdout=NULL;
BIO_METHOD *BIO_s_mst();

static unsigned char dummy_context[4] = { 40, 0, 0, 0 };

/*************************************************************************/
/* Define structure for messages exchanged over network link.
 */
struct rpc_msg {		/* Should have member alignment inhibited */
   char channel;		/* 'A'-app data. 'R'-remote client 'G'-global */
   char function;		/* 'G'-get, 'P'-put, 'C'-confirm, 'X'-close */
   unsigned short int length;	/* Amount of data returned or max to return */
   char data[4092];		/* variable data */
};
#define RPC_HDR_SIZE (sizeof(struct rpc_msg) - 4092)

static SSL_CTX *s_ctx=NULL;
static int verify_mode = 0;
static int chain_verify = 0;
static char *ca_path = (char *) 0;
/*
 * Debugging memory managment.
 */
static int mem_flags;
static struct {
   void *addr;
   size_t size;
} mem_list[4000];
static int mblocks = 0;
static int malloc_count = 0;
static size_t mnet = 0;
static int mblock_trigger = 0;
int LIB$SIGNAL(), LIB$INIT_TIMER(), LIB$SHOW_TIMER();
/*
 * Substitute memory management routines.  Note that these functions are
 * not thread safe.
 */
static void *dbg_malloc ( size_t size )
{
    void *blk;
    blk = malloc ( size );
    /* printf ( "crypto-malloc(%d) = %x\n", size, blk ); */
    if ( blk && (mblocks < 4000) ) {
	mnet += size;
	malloc_count++;
	mem_list[mblocks].addr = blk;
	mem_list[mblocks++].size = size;
    }
    if ( size == mblock_trigger ) {
	printf ( "malloced trigger size, addr: %x\n", blk);
	LIB$SIGNAL(2);
    }
    return blk;
}
static void *dbg_realloc ( void *old, size_t size )
{
    void *blk;
    blk = realloc ( old, size );
    /* printf ( "crypto-realloc(%x,%d) = %x\n", old, size, blk ); */
    if ( old != blk ) {
	int i;
	for ( i = 0; i < mblocks; i++ ) if ( mem_list[i].addr == old ) {
	    mnet += (size - mem_list[i].size);
	    mblocks--;
	    mem_list[i] = mem_list[mblocks];
	    i = -1;
	    break;
	}
	if ( i >= mblocks ) {
	    printf ( "Realloc of block %x not allocated\n", old );
	}
    }
    if ( blk && (mblocks < 4000) ) {
	mem_list[mblocks].addr = blk;
	mem_list[mblocks++].size = size;
    }
    if ( size == mblock_trigger ) {
	printf ( "realloced trigger size, addr: %x\n", blk);
	LIB$SIGNAL(2);
    }
    return blk;
}
static void dbg_free ( void *blk )
{
    int i;
    free ( blk );
    /* printf ( "crypto-free(%x)\n", blk ); */
    for ( i = 0; i < mblocks; i++ ) if ( mem_list[i].addr == blk ) {
	mnet = mnet - mem_list[i].size;
	mblocks--;
	mem_list[i] = mem_list[mblocks];
	return;
    }
    printf ( "dbg_free freed block %x not allocated by dbg_malloc\n", blk );
}
static void dbg_show_blocks ( )
{
    int i;
    printf ( "Total allocations: %d, net bytes: %d\n", malloc_count,
	mnet );
    if ( mblocks == 0 ) return;
    printf ( "%d dangling memory blocks:\n", mblocks);
    for ( i = 0; i < mblocks; i++ ) {
	printf ( "   %08x, size: %d\n", mem_list[i].addr, mem_list[i].size );
    }
    mblocks = 0;
}
static int verify_override ( char *arg, SSL *s, X509 **xs, STACK chain )
{
    /* Called to override the certificate verifcation */
    printf ( "Overriding certifcate verify \n" );
    return 1;
}
static int verify_callback(int ok, X509_STORE_CTX *ctx )
{
	/* Called when x509_verify_cert fails to verify, lets us override */
	if ( !ok ) printf ( 
		"Certificate verify failure, depth: %d, code: %d overrding\n",
		X509_STORE_CTX_get_error_depth(ctx), 
		X509_STORE_CTX_get_error(ctx) );
	return ok;
}
/*
 * Retrieve client certificate info and construct message for passing to
 * SSL partner.
 */
static int format_peer_certificate ( SSL *s_ssl, 
	int msg_size, char *msg, unsigned short int *length )
{
    X509 *client_cert;
    client_cert_info *info;
    unsigned char *pp;
    int len;
    /*
     * Cast msg pointer to client_cert info for easy access to it.
     */
    info = client_cert_info_init ( msg, msg_size );
    /*
     * Call into OpenSSL to get certificate data.
     */
    client_cert = SSL_get_peer_certificate (s_ssl);
#ifdef DEBUG
    printf ("Client certificate: %x, receive buffer limit: %d\n", client_cert,
				msg_size );
#endif
    if (client_cert != NULL) {
	char *dn, *issuer;
	unsigned char digest[EVP_MAX_MD_SIZE];
	unsigned int digest_len;
	const EVP_MD *digest_type;
        int status, i;
	/*
	 * Certificate obtained, format distinguished name as text and
	 * return it as message data.
	 */
	pp = info->data;
	len = i2d_X509_NAME ( X509_get_subject_name ( client_cert ), &pp );
	if ( len < 0 ) {
	} else if ( (info->d_used+len) > info->d_limit ) {
	}
	info->d_used += len;
	info->length[0]++;
	info->length[info->length[0]] = len;

	len = i2c_ASN1_INTEGER ( X509_get_serialNumber ( client_cert ), &pp );
	if ( len < 0 ) {
	} else if ( (info->d_used+len) > info->d_limit ) {
	    len = 0;
	}
	info->d_used += len;
	info->length[0]++;
	info->length[info->length[0]] = len;

	len = i2d_X509_NAME ( X509_get_issuer_name ( client_cert ), &pp );
	if ( len < 0 ) {
	} else if ( (info->d_used+len) > info->d_limit ) {
	    len = 0;
	}
	info->d_used += len;
	info->length[0]++;
	info->length[info->length[0]] = len;

	/* dn = X509_NAME_oneline ( X509_get_subject_name (client_cert), 0, 0);*/

	X509_free ( client_cert );
    } else {
	/*
	 * No client cert, make a NULL one.
	 */
    }

    *length = (sizeof(client_cert_info) - sizeof(info->data)) + info->d_used;
    return 1;
}
/***************************************************************************/
/*
 * Scan list of keyword=value pairs in penv for first one whose keyword
 * matches var and return value if found.  If not found, return
 * getenv ( "SSL_'var'" );
 */
static char *getpenv ( const char *var, char **penv )
{
    char *result, logical[256];
    int i, j, klen;
    /*
     * Scan the penv.
     */
    klen = strlen ( var );
    if ( penv ) for ( i = 0; penv[i]; i++ ) {
	if ( strncmp ( var, penv[i], klen ) == 0 ) {
	    /* Possibile match, next character must be '=' */
	    if ( penv[i][klen] == '=' ) return &penv[i][klen+1];
	}
    }
    /*
     * Fallback to standard environment variable, construct name.
     */
    strcpy ( logical, "SSL_" );
    if ( strlen(var) < (sizeof(logical)-4) ) strcpy ( &logical[4], var );
    else { strncpy ( &logical[4], var, (sizeof(logical)-5) );
	logical[sizeof(logical)-1] = '\0'; }

    result = getenv ( logical );
    return result;
}
/***************************************************************************/
/* Initialize routine, called once at program load.
 */
int ssl_threads_init ( char * certificate, char *version, char *ca_file,
	char **penv )
{
    int status, length, pt_initialize(), n;
    char *mem_opt, *verify_opt;
    /*
     * Set up locking.
     */
    pt_initialize ( );
    mem_flags = 0;
    mem_opt = getpenv ( "MEM_OPTION", penv );
    if ( mem_opt ) mem_flags = atoi ( mem_opt );
    if ( mem_flags ) {
	/*
	 * Options:	0 - default, 1 - def+timer, 2 - debug
	 */
	LIB$INIT_TIMER();
	if ( (mem_flags&3) == 1 ) {
	} else if ( (mem_flags&2) == 2 ) {
	    CRYPTO_set_mem_functions ( dbg_malloc, dbg_realloc, dbg_free );
	}
     }
    /*
     * Initialize standard out and error files, init error messages.
     */
	if (bio_err == NULL)
		if ((bio_err=BIO_new(BIO_s_file())) != NULL)
			BIO_set_fp(bio_err,stderr,BIO_NOCLOSE);
	if (bio_stdout == NULL)
		if ((bio_stdout=BIO_new(BIO_s_file())) != NULL)
			BIO_set_fp(bio_stdout,stdout,BIO_NOCLOSE);
	SSL_load_error_strings();
	SSLeay_add_all_algorithms();
    /*
     * Create a common SSL_CTX for all threads to use and initialize
     * with caller specified certificate.  SSL version is selectable by caller.
     */
        status = 0;
	for ( n = 0; version[n] && version[n] != ':'; n++ );
	if ( strncmp ( version, "23", n ) == 0 ) {
	    s_ctx=SSL_CTX_new(SSLv23_server_method());
	} else if ( strncmp ( version, "3", n ) == 0 ) {
	    s_ctx=SSL_CTX_new(SSLv3_server_method());
	} else {	/* default */
	    s_ctx=SSL_CTX_new(SSLv2_server_method());
	}
	if (s_ctx == NULL) goto end;
#ifdef DEBUG
	/*
	 * Validate the session cache is operating.
	 */
	status = SSL_CTX_get_session_cache_mode ( s_ctx );
	length = SSL_CTX_sess_get_cache_size ( s_ctx );
	printf ( "Session cache mode: %d, size: %d\n", status, length );
#endif
	/* SSL_CTX_set_session_cache_mode ( s_ctx, SSL_SESS_CACHE_BOTH ); */
	SSL_CTX_set_session_id_context(s_ctx, dummy_context,
                                sizeof(dummy_context) );
	/*
	 * Set up the server certificate and CA chain for client verification.
	 */
	chain_verify = 0;			/* default to no chaining */
	verify_opt = getpenv ( "CHAIN_VERIFY", penv );
	if ( verify_opt ) {
	    chain_verify = atoi ( verify_opt );
	printf("chain_verify value: %d\n", chain_verify );
	}
	verify_mode = SSL_VERIFY_NONE;		/* default to noverify */
	verify_opt = getpenv ( "CLIENT_VERIFY", penv );
	if ( verify_opt ) {
	    int flags;
	    flags = atoi ( verify_opt );
	    verify_mode = SSL_VERIFY_PEER;
	    if ( flags&1 ) verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
	    if ( flags&2 ) verify_mode |= SSL_VERIFY_CLIENT_ONCE;
	}
	verify_opt = getpenv ( "CA_PATH", penv );
	if ( verify_opt ) {
	    ca_path = malloc ( strlen ( verify_opt ) + 1 );
	    strcpy ( ca_path, verify_opt );
	    printf ( "Using CA path of '%s'\n", ca_path );
	} else ca_path = (char *) 0;

#ifdef NOCHECK_CA
	/* Provide our own verifcation callback that always accepts */
	SSL_CTX_set_cert_verify_callback ( s_ctx, verify_override, "" );
#else
	if ( ca_file ) {
	    SSL_CTX_set_client_CA_list(s_ctx,
		SSL_load_client_CA_file(ca_file));
	    status = SSL_CTX_load_verify_locations(s_ctx, ca_file, ca_path );
	    if ( !status ) printf ( "CA file load (%s) failed!\n", ca_file );
	    /*
	     * Force default to verify.
	     */
	    if ( !verify_opt ) verify_mode = SSL_VERIFY_PEER;
	}
	else printf ( "No client CA list specified\n" );
#endif
	SSL_CTX_set_verify ( s_ctx, verify_mode, NULL );


	if ( version[n] ) {
	    /* Take portion after first colon as cipher list */
	    status = SSL_CTX_set_cipher_list ( s_ctx, &version[n+1] );
	}
	/*
	 * Locate the certificate.
	 */
	if ( chain_verify ) {
	    status = SSL_CTX_use_certificate_chain_file
		(s_ctx,certificate);	/* must be PEM */
	} else {
	    status = SSL_CTX_use_certificate_file
		(s_ctx,certificate,SSL_FILETYPE_PEM);
	}
	if (status == 1) status = SSL_CTX_use_RSAPrivateKey_file
		(s_ctx,certificate,SSL_FILETYPE_PEM);

end:
    /*
     * Reset memory block list since the stuff allocated so far is 'permanent'
     */
    mblocks = 0;
    if ( mem_flags ) {
   	LIB$SHOW_TIMER();
	if ( (mem_flags&3) == 2 ) dbg_show_blocks();
    }
    return status;
}
/***************************************************************************/
/*  Handle an SSL session, called concurrently by multiple threads.
 *  Each new network connection creates a new thread which then calls
 *  ssl_do_thread.
 */
int ssl_do_thread ( void *link, 	/* handle for network/mst connection */
	int link_type, 			/* handle type: *ignored* */
	int *id )			/* Thread Id */
{
    int status, length, link_state, written, pt_set_identity();
     struct rpc_msg msg;
	SSL *s_ssl=NULL;
	BIO *c_to_s=NULL;
	BIO *s_to_c=NULL;
	BIO *s_bio=NULL;
	int i, verify_type; char *vflag;
	int extra_length;
	char extra[200];
	/*                 !
	 * Create an SSL server context using our saved certificate info,
	 */
	pt_set_identity ( id );
	s_ssl=SSL_new(s_ctx);
	/* printf ( "id: %d, s_ctx: %x, s_ssl: %x\n", *id, s_ctx, s_ssl ); */
	if (s_ssl == NULL) return 0;
	/*
	 * Set client mode, turn on verify if enabled by environment var.
	 */
#ifdef DEBUG
	printf ( "ctx stats: connect=%d/%d/%d accept: %d/%d/%d\n",
	    s_ctx->stats.sess_connect, s_ctx->stats.sess_connect_renegotiate,
		s_ctx->stats.sess_connect_good, s_ctx->stats.sess_accept,
		s_ctx->stats.sess_accept_renegotiate,s_ctx->stats.sess_accept_good);
	printf ( "   cache miss: %d, timeout=%d full=%d, hit: %d/%d\n",
		s_ctx->stats.sess_miss, s_ctx->stats.sess_timeout,
		s_ctx->stats.sess_cache_full, s_ctx->stats.sess_hit,
		s_ctx->stats.sess_cb_hit );
#endif
	/*
	 * Create 2 I/O streams for moving data into and out of the
	 * the SSL interpreter via the communication link with our partner.
	 */
	c_to_s=BIO_new(BIO_s_mst());
	s_to_c=BIO_new(BIO_s_mst());
	if ((s_to_c == NULL) || (c_to_s == NULL)) goto err;
	BIO_set_fd ( c_to_s, link_type, (long) link );
	BIO_set_fd ( s_to_c, link_type, (long) link );
	/*
	 * Create ssl stream.
	 */
	s_bio=BIO_new(BIO_f_ssl());
	if (s_bio == NULL) goto err;

	SSL_set_accept_state(s_ssl);
	SSL_set_bio(s_ssl,c_to_s,s_to_c);
	BIO_set_ssl(s_bio,s_ssl,BIO_CLOSE);
	/*
	 * Let partner process/thread drive connection.
	 * Link states: 0-idle, 1-read pending, 2-write pending, 3-closed.
	 */
	extra_length = 0;
	for (link_state = 0; link_state < 3; ) {
	    /*
	     * Wait for remote end to request data action on A channel.
	     */
	    while ( link_state == 0 ) {
		status = mst_read ( (mst_link_t) link,
			(char *) &msg, sizeof(msg), &length );
		if ( (status&1) == 0 ) {
		    if ( status != MST_DISCONNECT ) 
			printf("Error in main loop get: %d\n", status );
		    link_state = 3;
		    break;
		}
	   	if ( length < RPC_HDR_SIZE ) {
		    printf("Error in main loop get size: %d\n", length );
		    link_state = 3;
		    break;
		}
	   	if ( msg.channel != 'A' ) {
		    if ( (msg.channel == 'G') && (msg.function == 'G') ) {
			/*
			 * Return the client certificate data for the
			 * connection.  Do zero-length read to force
			 * handshake to complete.
			 */
			if ( verify_mode == SSL_VERIFY_NONE )
			    SSL_set_verify ( s_ssl, SSL_VERIFY_PEER, 
				verify_callback );
			if ( extra_length == 0 ) {
			    extra_length = BIO_read ( s_bio, extra,
				sizeof(extra) );
			    if ( extra_length < 0 ) {
				extra_length = 0;
#ifdef DEBUG
			        if (bio_err == NULL)
                			bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
				ERR_print_errors ( bio_err );
#endif
			    } else {
				SSL_set_session_id_context(s_ssl, dummy_context,
                                                 sizeof(dummy_context) );
			    }
			}
			if ( 0 == format_peer_certificate ( s_ssl, msg.length,
				msg.data, &msg.length ) ) {
			    /*
			     * Error getting certificate.
			     */
			    msg.length = 0;		/* include final NUL */
			}

			msg.channel = 'G';
			msg.function = 'C';		/* confirm */

		        status = mst_write ( (mst_link_t) link,
				(char *) &msg, msg.length+RPC_HDR_SIZE, 
				&written );
		        if ( (status&1) == 0 ) break;
		        link_state = 0;
			continue;
		    }
		    printf("Error in main loop, unexpected channel: %c\n", 
			msg.channel );
		    break;
		    link_state = 3;
		}
		if ( msg.function == 'G' ) {
		    link_state = 1;
		} else if ( msg.function == 'P' ) {
		    link_state = 2;	/* write pending */
		} else if ( msg.function == 'X' ) {
		    link_state = 3;
		} else {
		    link_state = 3;
		}
	    }
	    if ( link_state == 1 ) {
		/*
		 * Remote end want applciation data.
		 */
		if ( extra_length > 0 ) {
		    if ( msg.length >= extra_length ) {
			/* User's buffer big enough for whole thing */
			i = extra_length;
			memcpy ( msg.data, extra, i );
		    } else {
			i = msg.length;
			memcpy ( msg.data, extra, i );
			memmove ( extra, &extra[i], extra_length - i );
		    }
		    extra_length = extra_length - i;
		} else {
		    i = BIO_read ( s_bio, msg.data, msg.length );
		}
		if ( i < 0 ) link_state = 3;
		else {
		    msg.channel = 'A';
		    msg.function = 'C';		/* confirm */
		    msg.length = i;
		    status = mst_write ( (mst_link_t) link,
				(char *) &msg, i+RPC_HDR_SIZE, &written );
		    if ( (status&1) == 0 ) break;
		    link_state = 0;
		}
	    } else if ( link_state == 2 ) {
		i = BIO_write ( s_bio, msg.data, msg.length );
		if ( i < 0 ) link_state = 3;
		else {
		    msg.channel = 'A';
		    msg.function = 'C';		/* confirm */
		    msg.length = 0;
		    status = mst_write ( (mst_link_t) link,
				(char *) &msg, RPC_HDR_SIZE, &written );
		    if ( (status&1) == 0 ) break;
		    link_state = 0;
		}
	    }
	}
err:
	/* We have to set the BIO's to NULL otherwise they will be
	 * free()ed twice.  Once when the s_ssl is SSL_free()ed and
	 * again when s_to_c is SSL_free()ed.
	 * This is a hack required because s_ssl and c_ssl are sharing the same
	 * BIO structure and SSL_set_bio() and SSL_free() automatically
	 * BIO_free non NULL entries.
	 * You should not normally do this or be required to do this */
	if ( (c_to_s->references > 1) ) {
	    /*
	     * The BIO_set_ssl call incremented c_to_s's reference count,
	     * Manually reduce it so the memory gets freed.
	     */
	    CRYPTO_add ( &c_to_s->references, -1, CRYPTO_LOCK_BIO );
	}
	s_ssl->rbio=NULL;
	s_ssl->wbio=NULL;

	if (c_to_s != NULL) BIO_free(c_to_s);
	if (s_to_c != NULL) BIO_free(s_to_c);
	if (s_bio != NULL) BIO_free(s_bio);
	if ( (mem_flags&3) == 2 ) {
	    /* Debug mode, check for memory leaks */
	    dbg_show_blocks();
	    mblock_trigger = 0 /*64*/;
	}
	return(0);
}

/* Copyright (C) 1995-1996 Eric Young (eay@mincom.oz.au)
 * All rights reserved.
 * 
 * This file is part of an SSL implementation written
 * by Eric Young (eay@mincom.oz.au).
 * The implementation was written so as to conform with Netscapes SSL
 * specification.  This library and applications are
 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
 * as long as the following conditions are aheared to.
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.  If this code is used in a product,
 * Eric Young should be given attribution as the author of the parts used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Eric Young (eay@mincom.oz.au)
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */
