/***************************************************************************
  
  gbcurl.c
  
  (c) 2003-2008 Daniel Campos Fernández <dcamposf@gmail.com>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.
  
***************************************************************************/

#define __GB_CURL_C

/*****************************
 NOTE THAT :
 libcurl <= 7.10.7 lacks CURLE_LDAP_INVALID_URL and CURLE_FILESIZE_EXCEEDED constants
 libcurl <= 7.10.6 lacks proxy authentication support
 libcurl <= 7.10.5 lacks user authentication support
 *****************************/
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include "gbcurl.h"
#include "CCurl.h"

CURL_PROXY CURL_default_proxy = { CURLPROXY_HTTP, CURLAUTH_NONE, NULL, NULL, NULL, NULL };

static char *_protocols[] = { "ftp://", "ftps://", "http://", "https://", "dict://", NULL };

static void warning(const char *msg)
{
	fprintf(stderr, "gb.net.curl: warning: %s\n", msg);
}

#ifndef CURLAUTH_NONE
static void warning_auth(void)
{
	warning("This component was compiled without authentication support.");
	warning("Use libcurl version 7.10.7 or later.");
}
#define CURLAUTH_NONE 0
#define LACKS_AUTH
#warning #######################################################################
#warning COMPILING WITHOUT AUTHENTICATION SUPPORT - YOU MUST USE LIBCURL>=7.10.6
#warning #######################################################################
#endif


static void warning_proxy_auth(void)
{
	warning("This component was compiled without proxy authentication support.");
	warning("Use libcurl version 7.10.8 or later.");
}


static char *CURL_get_protocol(char *url, char *default_protocol)
{
	char **p;
	char *pos;
	
	for (p = _protocols; *p; p++)
	{
		if (!strncmp(url, *p, strlen(*p)))
			return *p;
	}
	
	pos = strstr(url, "://");
	if (pos)
		return "?";
	
	return default_protocol;
}

bool CURL_set_url(void *_object, const char *src, int len)
{
	char *url, *tmp, *protocol;
	
	if (len == 0)
		goto UNKNOWN_PROTOCOL;
	
	url = GB.NewString(src, len);
	
	if (GB.Is(THIS, GB.FindClass("FtpClient")))
	{
		protocol = CURL_get_protocol(url, "ftp://");
		if (strcmp(protocol, "ftp://") && strcmp(protocol, "ftps://"))
			goto UNKNOWN_PROTOCOL;
	}
	else if (GB.Is(THIS, GB.FindClass("HttpClient")))
	{
		protocol = CURL_get_protocol(url, "http://");
		if (strcmp(protocol, "http://") && strcmp(protocol, "https://"))
			goto UNKNOWN_PROTOCOL;
	}
	else if (GB.Is(THIS, GB.FindClass("DictClient")))
	{
		protocol = CURL_get_protocol(url, "dict://");
		if (strcmp(protocol, "dict://"))
			goto UNKNOWN_PROTOCOL;
	}
	else
		goto UNKNOWN_PROTOCOL;

	if (strncmp(url, protocol, strlen(protocol)))
	{
		tmp = GB.NewZeroString(protocol);
		tmp = GB.AddString(tmp, url, GB.StringLength(url));
		GB.FreeString(&url);
		url = tmp;
	}
	
	GB.FreeString(&THIS_URL);
	THIS_URL = url;
	return FALSE;
	
UNKNOWN_PROTOCOL:

	GB.Error("Unknown protocol");
	return TRUE;
}

#if 0
void Adv_correct_url(char **buf,char *protocol)
{
	char *buftmp;
	int len;
	int myloop,myloop2;
	int pos=-1;
	int myok=1;

	len=strlen(*buf);
	for (myloop=0;myloop<len;myloop++)
	{
		if ( (*buf)[myloop]==':' )
		{
			if (myloop==(len-1))
			{
				pos=myloop;
				break;
			}
			else
			{
				if ( (*buf)[myloop+1]=='/' )
				{
					pos=myloop;
					break;
				}
				else
				{
					for (myloop2=myloop+1;myloop2<len;myloop2++)
					{
						if ( (*buf)[myloop2]=='/' ) break;
						if ( ((*buf)[myloop2]<0x30) || ((*buf)[myloop2]>0x39) )
						{
							myok=0;
							break;
						}
					}
					if (!myok) pos=myloop;
					break;
				}
			}
		}
	}

	myok=0;

	if (pos==-1)
	{

		GB.Alloc((void**)POINTER(&buftmp),len+1);
		strcpy(buftmp,*buf);
		GB.Free((void**)POINTER(buf));
		GB.Alloc((void**)POINTER(buf),len+strlen(protocol)+1);
	}
	else
	{
		GB.Alloc((void**)POINTER(&buftmp),(len-pos)+1);
		strcpy(buftmp,*buf+pos+1);
		GB.Free((void**)POINTER(buf));
		GB.Alloc((void**)POINTER(buf),strlen(buftmp)+strlen(protocol)+1);
	}

	strcpy(*buf,protocol);
	if (strlen(buftmp)>=2)
	{
		if ( buftmp[0]=='/') myok++;
		if ( buftmp[1]=='/') myok++;
	}
	strcat(*buf,buftmp+myok);
	GB.Free((void**)POINTER(&buftmp));
}
#endif

bool CURL_check_userpwd(CURL_USER *user)
{
	char *tmp = NULL;
	bool ret;
	
	if (user->user || user->pwd)
	{
		tmp = GB.AddString(tmp, user->user, 0);
		tmp = GB.AddChar(tmp, ':');
		tmp = GB.AddString(tmp, user->pwd, 0);
	}
	
	if (tmp && user->userpwd)
		ret = (strcmp(tmp, user->userpwd) != 0);
	else
		ret = (tmp != user->userpwd);
	
	GB.FreeString(&tmp);
	return ret;
}

/***************************************************************************/

void CURL_proxy_init(CURL_PROXY *proxy)
{
	*proxy = CURL_default_proxy;
	GB.RefString(proxy->host);
	GB.RefString(proxy->user);
	GB.RefString(proxy->pwd);
	GB.RefString(proxy->userpwd);
}

void CURL_proxy_clear(CURL_PROXY *proxy)
{
	GB.FreeString(&proxy->host);
	GB.FreeString(&proxy->user);
	GB.FreeString(&proxy->pwd);
	GB.FreeString(&proxy->userpwd);
}

void CURL_default_proxy_clear()
{
	CURL_proxy_clear(&CURL_default_proxy);
}


bool CURL_proxy_set(CURL_PROXY *proxy, CURL *curl)
{
	GB.FreeString(&proxy->userpwd);
	
	if (proxy->user || proxy->pwd)
	{
		proxy->userpwd = GB.AddString(proxy->userpwd, proxy->user, 0);
		proxy->userpwd = GB.AddChar(proxy->userpwd, ':');
		proxy->userpwd = GB.AddString(proxy->userpwd, proxy->pwd, 0);
	}
	
	if (!proxy->host)
	{
		if (CURL_set_option(curl, CURLOPT_PROXY, NULL))
			return TRUE;
		
		if ( LIBCURL_VERSION_NUM >= 0x070a08 )
		{
			if (CURL_set_option(curl, 111, CURLAUTH_NONE))
				return TRUE;
		}
		
		return FALSE;
	}

	if (CURL_set_option(curl, CURLOPT_PROXYTYPE, (long)proxy->type) || CURL_set_option(curl, CURLOPT_PROXY, proxy->host))
		return TRUE;
	
	if ( LIBCURL_VERSION_NUM >= 0x070a08 )
	{
		if (CURL_set_option(curl, CURLOPT_PROXYUSERPWD, proxy->userpwd) || CURL_set_option(curl, 111, (long)proxy->auth))
			return TRUE;
	}
	else
		warning_proxy_auth();
	
	return FALSE;
}

bool CURL_proxy_set_auth(CURL_PROXY *proxy, int auth)
{
	#ifdef LACKS_AUTH
	return FALSE;
	#else
	if (LIBCURL_VERSION_NUM < 0x070a08)
	{
		if (auth) 
			warning_proxy_auth();
		return FALSE;
	}
	
	switch (auth)
	{
		case CURLAUTH_NONE:
		case CURLAUTH_BASIC:
		case CURLAUTH_NTLM:
			proxy->auth=auth; 
			return FALSE;
		default: 
			return TRUE;
	}
	#endif
}

bool CURL_proxy_set_type(CURL_PROXY *proxy, int type)
{
	switch (type)
	{
		case CURLPROXY_HTTP:
		case CURLPROXY_SOCKS5:
			proxy->type = type; 
			return FALSE;
		default: 
			return TRUE;
	}
}

/***************************************************************************/

void CURL_user_init(CURL_USER *user)
{
	user->auth = CURLAUTH_NONE;
	user->user = NULL;
	user->pwd = NULL;
	user->userpwd = NULL;
}


void CURL_user_clear(CURL_USER *user)
{
	GB.FreeString(&user->user);
	GB.FreeString(&user->pwd);
	GB.FreeString(&user->userpwd);
}


bool CURL_user_set(CURL_USER *user, CURL *curl)
{
	#ifdef LACKS_AUTH
	return;
	#else
	
	if (user->auth == CURLAUTH_NONE)
		return CURL_set_option(curl, CURLOPT_USERPWD, NULL) || CURL_set_option(curl, CURLOPT_HTTPAUTH, CURLAUTH_NONE);
	
	GB.FreeString(&user->userpwd);
	user->userpwd = GB.AddString(user->userpwd, user->user, 0);
	user->userpwd = GB.AddChar(user->userpwd, ':');
	user->userpwd = GB.AddString(user->userpwd, user->pwd, 0);

	return CURL_set_option(curl, CURLOPT_USERPWD, user->userpwd) || CURL_set_option(curl, CURLOPT_HTTPAUTH, (long)user->auth);
	
	#endif
}

bool CURL_user_set_auth(CURL_USER *user, int auth)
{
	#ifdef LACKS_AUTH
	if (auth) 
		warning_auth();
	return FALSE;
	#else
	switch (auth)
	{
		case CURLAUTH_NONE:
		case CURLAUTH_BASIC:
		case CURLAUTH_NTLM:
		case CURLAUTH_GSSNEGOTIATE:
		case CURLAUTH_DIGEST:
			user->auth = auth;
			return FALSE;
			
		default: 
			return TRUE;
	}
	#endif
}
