/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * HTTP (c.config)
 *
 * © Acorn Computers Ltd. 1998
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "kernel.h"
#include "swis.h"
#include "sys/errno.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "sys/dcistructs.h"
#include "Interface/AcornSSL.h"
#include "module.h"
#include "socklib.h"
#include "security.h"

static int ssl_module_present;

static int sec_socketioctl(int, unsigned long, ...);
static int sec_socket(int domain, int type, int protocol);
static int sec_connect(int s, const struct sockaddr *name, int namelen);
static int sec_shutdown(int s, int how);
static int sec_socketclose(int d);
static int sec_getsockopt(int s, int level, int optname,
		      void *optval, int *optlen);
static int sec_socketwrite(int s, const void *buf, unsigned int len);
static int sec_recv(int s, void *data, size_t size, int flags);
static int sec_setsockopt(int s, int level, int optname, const void *optval, int optlen);

int security_ssl_available(void)
{
        return ssl_module_present;
}

void security_init(void)
{
        int ver = 0; /* If error, use zero */

        _swix(AcornSSL_Version, _OUT(0), &ver);
        security_ssl_arrived(ver);
}

int security_ssl_arrived(int ver)
{
        if (ver < 3) {
                /* We do not support the 'old' interface */
                return security_ssl_gone();
        }
        ssl_module_present = ver;
        return ssl_module_present;
}

int security_ssl_gone(void)
{
        ssl_module_present = 0;
        return ssl_module_present;
}

const sock_vtbl *security_get_ssl_vtable(void)
{
        static const sock_vtbl vtable = {
                sec_socketioctl,
                sec_socket,
                sec_connect,
                sec_shutdown,
                sec_socketclose,
                sec_getsockopt,
                sec_socketwrite,
                sec_recv,
                sec_setsockopt
        };
        return &vtable;
}

const sock_vtbl *security_get_normal_vtable(void)
{
        static const sock_vtbl vtable = {
                socketioctl,
                socket,
                connect,
                shutdown,
                socketclose,
                getsockopt,
                socketwrite,
                recv,
                setsockopt
        };
        return &vtable;
}

/* ---- implementation veneers to the secure socket stuff */

#ifdef FAKE_SSL_MODULE
/* Dummy implementation */

static int sec_socketioctl(int s, unsigned long op, ...)
{
        void *data;
        va_list ap;
        va_start(ap, op);
	data = va_arg(ap, void *);
        va_end(ap);
        return socketioctl(s, op, data);
}

static int sec_socket(int domain, int type, int protocol)
{
        return socket(domain, type, protocol);
}

static int sec_connect(int s, const struct sockaddr *name, int namelen)
{
        return connect(s, name, namelen);
}

static int sec_shutdown(int s, int how)
{
        return shutdown(s, how);
}

static int sec_socketclose(int d)
{
        return socketclose(d);
}

static int sec_getsockopt(int s, int level, int optname,
		      void *optval, int *optlen)
{
        return getsockopt(s, level, optname, optval, optlen);
}

static int sec_socketwrite(int s, const void *buf, unsigned int len)
{
        return socketwrite(s, buf, len);
}

static int sec_recv(int s, void *data, size_t size, int flags)
{
        return recv(s, data, size, flags);
}

static int sec_setsockopt(int s, int level, int optname, const void *optval, int optlen)
{
        return setsockopt(s, level, optname, optval, optlen);
}

#else

static int sec_socketioctl(int s, unsigned long op, ...)
{
        _kernel_oserror *e;
        int result;

        void *data;
        va_list ap;
        va_start(ap, op);
	data = va_arg(ap, void *);
        va_end(ap);

        e = _swix(AcornSSL_Ioctl, _INR(0,2)|_OUT(0),
        	s, op, data, &result);
        if (!e) return result;
        errno = GETDCI4ERRNO(e->errnum);
        return -1;
}

static int sec_socket(int domain, int type, int protocol)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Creat, _INR(0,2)|_OUT(0),
		domain, type, protocol,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_connect(int s, const struct sockaddr *name, int namelen)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Connect, _INR(0,2)|_OUT(0),
		s, name, namelen,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_shutdown(int s, int how)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Shutdown, _INR(0,1)|_OUT(0),
		s, how,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_socketclose(int d)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Close, _IN(0)|_OUT(0),
		d,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_getsockopt(int s, int level, int optname,
		      void *optval, int *optlen)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Getsockopt, _INR(0,4)|_OUT(0),
		s, level, optname, optval, optlen,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_socketwrite(int s, const void *buf, unsigned int len)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Write, _INR(0,2)|_OUT(0),
		s, buf, len,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_recv(int s, void *data, size_t size, int flags)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Recv, _INR(0,3)|_OUT(0),
		s, data, size, flags,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}

static int sec_setsockopt(int s, int level, int optname, const void *optval, int optlen)
{
        _kernel_oserror *e;
	int result;

	e = _swix(AcornSSL_Setsockopt, _INR(0,4)|_OUT(0),
		s, level, optname, optval, optlen,
		&result);
	if (!e) return result;
	errno = GETDCI4ERRNO(e->errnum);
	return -1;
}
#endif
