/*******************************************************************************
 * Copyright (C) 2004-2006 Intel Corp. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - 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.
 *
 *  - Neither the name of Intel Corp. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 Intel Corp. OR THE 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.
 *******************************************************************************/

/**
 * @author Anas Nashif
 * @author Vadim Revyakin
 */

#ifdef HAVE_CONFIG_H
#include <wsman_config.h>
#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "u/libu.h"
#include "wsman-client-transport.h"
#include "wsman-soap.h"
#include "wsman-client.h"

static char *auth_methods[] = {
  _WS_NO_AUTH,
  _WS_BASIC_AUTH,
  _WS_DIGEST_AUTH,
  _WS_PASS_AUTH,
  _WS_NTLM_AUTH,
  _WS_GSSNEGOTIATE_AUTH,
	NULL,
};


extern void wsmc_handler(WsManClient * cl, WsXmlDocH rqstDoc,
				 void *user_data);

#ifdef BENCHMARK
static long long transfer_time = 0;
#endif

int wsman_send_request(WsManClient * cl, WsXmlDocH request)
{
        int ret = 0;
#ifdef BENCHMARK
	struct timeval tv0, tv1;
	long long t0, t1;
#endif

	if (wsmc_lock(cl) != 0 ) {
		error("Client busy");
		return 1;
	}
	wsmc_reinit_conn(cl);

#ifdef BENCHMARK
	gettimeofday(&tv0, NULL);
#endif

	wsmc_handler(cl, request, NULL);
        if (cl->last_error != WS_LASTERR_OK) {
          _warning("Couldn't send request to client: %s\n", cl->fault_string);
          ret = 1;
        }
#ifdef BENCHMARK
	gettimeofday(&tv1, NULL);
	t0 = tv0.tv_sec * 10000000 + tv0.tv_usec;
	t1 = tv1.tv_sec * 10000000 + tv1.tv_usec;
	transfer_time += t1 - t0;
#endif
	wsmc_unlock(cl);
	return ret;
}

#ifdef BENCHMARK
long long get_transfer_time()
{
	long long l = transfer_time;
	transfer_time = 0;
	return l;
}
#endif


const char *wsmc_transport_get_auth_name(wsman_auth_type_t auth)
{
	switch (auth) {
	case WS_NO_AUTH:
		return _WS_NO_AUTH;
	case WS_BASIC_AUTH:
		return _WS_BASIC_AUTH;
	case WS_DIGEST_AUTH:
		return _WS_DIGEST_AUTH;
	case WS_PASS_AUTH:
		return _WS_PASS_AUTH;
	case WS_NTLM_AUTH:
		return _WS_NTLM_AUTH;
	case WS_GSSNEGOTIATE_AUTH:
		return _WS_GSSNEGOTIATE_AUTH;
	default:;
	}
	return "Unknown";
}

void wsmc_transport_set_auth_request_func(WsManClient * cl,
				  wsman_auth_request_func_t f)
{
	cl->authentication.auth_request_func = f;
}


void wsman_transport_set_agent(WsManClient * cl, const char *arg)
{
	u_free(cl->user_agent);
	cl->user_agent = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_agent(WsManClient * cl)
{
	if (cl->user_agent)
		return u_strdup(cl->user_agent);
	else
		return u_strdup(DEFAULT_USER_AGENT);
}


void wsman_transport_set_proxy(WsManClient * cl, const char *arg)
{
	u_free(cl->proxy_data.proxy);
	cl->proxy_data.proxy = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_proxy(WsManClient *cl)
{
	return cl->proxy_data.proxy ? u_strdup( cl->proxy_data.proxy ) : NULL;
}

void wsman_transport_set_userName(WsManClient * cl, const char *arg)
{
	u_free(cl->data.user);
	cl->data.user = arg ? u_strdup(arg) : NULL;
}

char *wsman_transport_get_userName(WsManClient * cl)
{
	return cl->data.user ? u_strdup(cl->data.user) : NULL;
}

void wsman_transport_set_password(WsManClient * cl, const char *arg)
{
	u_cleanfree(cl->data.pwd);
	cl->data.pwd = arg ? u_strdup(arg) : NULL;
}

char *wsman_transport_get_password(WsManClient * cl)
{
	return cl->data.pwd ? u_strdup(cl->data.pwd) : NULL;
}

void wsman_transport_set_proxyauth(WsManClient * cl, const char *arg)
{
	u_free(cl->proxy_data.proxy_auth);
	cl->proxy_data.proxy_auth = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_proxyauth(WsManClient *cl)
{
	return cl->proxy_data.proxy_auth ? u_strdup( cl->proxy_data.proxy_auth ) : NULL;
}


unsigned long wsman_transport_get_timeout(WsManClient * cl)
{
	return cl->transport_timeout;
}

void wsman_transport_set_timeout(WsManClient * cl, unsigned long arg)
{
	cl->transport_timeout = arg;
}


char *wsman_transport_get_auth_method(WsManClient * cl)
{
  return cl->authentication.method ? u_strdup(cl->authentication.method) : NULL;
}

void wsman_transport_set_auth_method(WsManClient * cl, const char *arg)
{
	u_free(cl->authentication.method);
	cl->authentication.method = arg ? u_strdup( arg ) : NULL;
}

int wsman_is_auth_method(WsManClient * cl, int method)
{
	if (cl->authentication.method == NULL) {
		return 1;
	}
	if (method >= WS_MAX_AUTH) {
		return 0;
	}
	return (!strncasecmp
		(cl->authentication.method, auth_methods[method],
		 strlen(cl->authentication.method)));
}

wsman_auth_type_t wsmc_transport_get_auth_value(WsManClient * cl)
{
	char *m = cl->authentication.method;
	wsman_auth_type_t i;

	if (m == NULL) {
		return WS_NO_AUTH;
	}
	for (i = WS_NO_AUTH; auth_methods[i] != NULL; i++) {
		if (!strcasecmp(m, auth_methods[i])) {
			return i;
		}
	}
	return WS_MAX_AUTH;
}


void wsman_transport_set_verify_peer(WsManClient * cl, unsigned int arg)
{
	cl->authentication.verify_peer = arg;
}

unsigned int wsman_transport_get_verify_peer(WsManClient *cl)
{
	return cl->authentication.verify_peer;
}


void wsman_transport_set_verify_host(WsManClient * cl, unsigned int arg)
{
	cl->authentication.verify_host = arg;
}

unsigned int wsman_transport_get_verify_host(WsManClient *cl)
{
	return cl->authentication.verify_host;
}

void wsman_transport_set_crlcheck(WsManClient * cl, unsigned int arg)
{
	cl->authentication.crl_check = arg;
}

unsigned int wsman_transport_get_crlcheck(WsManClient * cl)
{
        return cl->authentication.crl_check;
}

#ifndef _WIN32
void wsman_transport_set_crlfile(WsManClient * cl, const char *arg)
{
        u_free(cl->authentication.crl_file);
        cl->authentication.crl_file = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_crlfile(WsManClient *cl)
{
  return cl->authentication.crl_file ? u_strdup(cl->authentication.crl_file) : NULL; 
}
#endif



void wsman_transport_set_cainfo(WsManClient * cl, const char *arg)
{
	u_free(cl->authentication.cainfo);
	cl->authentication.cainfo = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_cainfo(WsManClient *cl)
{
	return cl->authentication.cainfo ? u_strdup( cl->authentication.cainfo ) : NULL;
}

static int hexadecimal2raw(const char *str, unsigned char *dest, int len) {
        int i = 0;
        unsigned char v1 =0 , v2 = 0;
        while(*str != 0 && *(str+1) != 0 && i < len) {
                if(*str <= '9' && *str >= '0')
                        v1 = *str - '0';
                else if(*str <= 'f' && *str >= 'a')
                        v1 = *str - 'a' + 10;
                else if(*str <= 'F' && *str >= 'A')
                        v1 = *str - 'A' + 10;
                v1 <<= 4;
                str++;
                if(*str <= '9' && *str >= '0')
                        v2 = *str - '0';
                else if(*str <= 'f' && *str >= 'a')
                        v2 = *str - 'a' + 10;
                else if(*str <= 'F' && *str >= 'A')
                        v2 = *str - 'A' + 10;
                str++;
                dest[i] = v1 + v2;
                i++;
        }
        return i;
}

static char * raw2hexadecimal(unsigned char *dest, int len)
{
        int i = 0;
        char v;
        char * str = calloc(1, len*2+1);
        char * p = str;
        if(str == NULL) return str;
        while(i < len) {
                v = (dest[i] & 0xf0) >> 4;
                if(v <= 9)
                        *str = '0' + v;
                else
                        *str = 'a' + v - 10;
                str++;
                v = dest[i] & 0x0f;
                if(v <= 9)
                        *str = '0' + v;
                else
                        *str = 'a' + v - 10;
                str++;
                i++;
        }
        return p;
}


void wsman_transport_set_certhumbprint(WsManClient *cl, const char *arg)
{
	if(arg == NULL)
		return;
	hexadecimal2raw(arg, cl->authentication.certificatethumbprint, 20);
}

char *wsman_transport_get_certhumbprint(WsManClient *cl)
{
	return raw2hexadecimal(cl->authentication.certificatethumbprint, 20);
}

void wsman_transport_set_capath(WsManClient *cl, const char *arg)
{
	u_free(cl->authentication.capath);
	cl->authentication.capath = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_capath(WsManClient *cl)
{
	return cl->authentication.capath ? u_strdup( cl->authentication.capath ) : NULL;
}


void wsman_transport_set_caoid(WsManClient *cl, const char *arg)
{
	u_free(cl->authentication.caoid);
	cl->authentication.caoid = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_caoid(WsManClient *cl)
{
	return cl->authentication.caoid ? u_strdup( cl->authentication.caoid ) : NULL;
}


#ifdef _WIN32
void wsman_transport_set_calocal(WsManClient *cl, BOOL local)
{
	cl->authentication.calocal = local;
}

BOOL wsman_transport_get_calocal(WsManClient *cl)
{
	return cl->authentication.calocal;
}
#endif

char *wsman_transport_get_proxy_username(WsManClient *cl)
{
  return cl->proxy_data.proxy_username ? u_strdup(cl->proxy_data.proxy_username) : NULL;
}

void wsman_transport_set_proxy_username(WsManClient *cl, const char *proxy_username )
{
  u_free(cl->proxy_data.proxy_username);
  cl->proxy_data.proxy_username = proxy_username ? u_strdup(proxy_username) : NULL;
}

char *wsman_transport_get_proxy_password(WsManClient *cl)
{
  return cl->proxy_data.proxy_password ? u_strdup(cl->proxy_data.proxy_password) : NULL;
}

void wsman_transport_set_proxy_password(WsManClient *cl, const char *proxy_password )
{
  u_cleanfree(cl->proxy_data.proxy_password);
  cl->proxy_data.proxy_password = proxy_password ? u_strdup(proxy_password) : NULL;
}



void wsman_transport_set_cert(WsManClient * cl, const char *arg)
{
	u_free(cl->authentication.sslcert);
	cl->authentication.sslcert = arg ? u_strdup( arg ) : NULL;
}

char *wsman_transport_get_cert(WsManClient *cl)
{
	return cl->authentication.sslcert ? u_strdup( cl->authentication.sslcert ) : NULL;
}


void wsman_transport_set_key(WsManClient *cl, const char *key)
{
	u_free(cl->authentication.sslkey);
	cl->authentication.sslkey = key ? u_strdup( key ) : NULL;
}

char *wsman_transport_get_key(WsManClient *cl)
{
	return cl->authentication.sslkey ? u_strdup( cl->authentication.sslkey ) : NULL;
}


char *wsman_transport_get_last_error_string(WS_LASTERR_Code err)
{
	switch (err) {
	case WS_LASTERR_OK:
		return "Everything OK";
	case WS_LASTERR_FAILED_INIT:
		return "Transport initialization failed";
	case WS_LASTERR_UNSUPPORTED_PROTOCOL:
		return "Unsupported protocol";
	case WS_LASTERR_URL_MALFORMAT:
		return "URL malformat";
	case WS_LASTERR_COULDNT_RESOLVE_PROXY:
		return "Could not resolve proxy";
	case WS_LASTERR_COULDNT_RESOLVE_HOST:
		return "Could not resolve host";
	case WS_LASTERR_COULDNT_CONNECT:
		return "Could not connect";
	case WS_LASTERR_HTTP_RETURNED_ERROR:
		return "HTTP returned error";
	case WS_LASTERR_WRITE_ERROR:
		return "Write error";
	case WS_LASTERR_READ_ERROR:
		return "Read error";
	case WS_LASTERR_OUT_OF_MEMORY:
		return "Could not alloc memory";
	case WS_LASTERR_OPERATION_TIMEOUTED:
		return "Operation timeout reached";
	case WS_LASTERR_HTTP_POST_ERROR:
		return "HTTP POST error";
	case WS_LASTERR_BAD_DOWNLOAD_RESUME:
		return "Could not resume download";
	case WS_LASTERR_TOO_MANY_REDIRECTS:
		return "Catch endless re-direct loop";
	case WS_LASTERR_SSL_CONNECT_ERROR:
		return "SSL connection error";
	case WS_LASTERR_SSL_PEER_CERTIFICATE:
		return "Peer's certificate wasn't OK";
	case WS_LASTERR_SSL_ENGINE_NOTFOUND:
		return "SSL crypto engine not found";
	case WS_LASTERR_SSL_ENGINE_SETFAILED:
		return "Can't set SSL crypto engine default";
	case WS_LASTERR_SSL_CERTPROBLEM:
		return "Problem with the local certificate";
	case WS_LASTERR_SSL_CACERT:
		return "Problem with the CA certificate";
	case WS_LASTERR_SSL_ENGINE_INITFAILED:
		return "Failed to initialise SSL engine";
	case WS_LASTERR_SEND_ERROR:
		return "Failed sending network data";
	case WS_LASTERR_RECV_ERROR:
		return "Failure in receiving network data";
	case WS_LASTERR_BAD_CONTENT_ENCODING:
		return "Unrecognized transfer encoding";
	case WS_LASTERR_LOGIN_DENIED:
		return "User, password or similar was not accepted";
	case WS_LASTERR_CURL_BAD_FUNCTION_ARG:
		return "Internal error: Wrong function argument in call to CURL";
	default:
		return "Unrecognized error";
	}
}
