#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stddef.h>
#include <errno.h>

#include <curl/curl.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#ifndef WIN32
#define stricmp strcasecmp
#endif

/* The SOAP query to send. For the moment, use a known-good query */
const char *soap = "<?xml version='1.0' encoding='UTF-8'?>\n\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/1999/XMLSchema\">\n<SOAP-ENV:Body>\n<ns1:doGoogleSearch xmlns:ns1=\"urn:GoogleSearch\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n<key xsi:type=\"xsd:string\">1uLxMnpQFHIGgqomK6o82/ewswSPddlW</key>\n<q xsi:type=\"xsd:string\"><![CDATA[Ford XR8 +site:ford.com.au]]></q>\n<start xsi:type=\"xsd:int\">0</start>\n<maxResults xsi:type=\"xsd:int\">10</maxResults>\n<filter xsi:type=\"xsd:boolean\">true</filter>\n<restrict xsi:type=\"xsd:string\"></restrict>\n<safeSearch xsi:type=\"xsd:boolean\">false</safeSearch>\n<lr xsi:type=\"xsd:string\"></lr>\n<ie xsi:type=\"xsd:string\">latin1</ie>\n<oe xsi:type=\"xsd:string\">latin1</oe>\n</ns1:doGoogleSearch>\n</SOAP-ENV:Body>\n</SOAP-ENV:Envelope>\n";

/* Curl callbacks */
size_t curl_writefcn(void *ptr, size_t size, size_t nmemb, void *stream);
size_t curl_readfcn(void *ptr, size_t size, size_t nmemb, void *stream);
/* int curl_debugfcn(CURL *handle, curl_infotype it, char *debug_str, size_t sz, void *v); */

FILE *hDebug = NULL;
size_t recv_sz = 0;
char *recvdata = NULL;

int
main(int argc, char *argv[])
{
	CURL *hCurl = NULL;
	CURLcode rc = 0, hc = 0;
	struct curl_slist *http_headers = NULL;
	long int http_status = 0;
	size_t qlen = 0;
	char buff[64];
	
	if ( (hDebug = fopen("output.log", "wb")) == NULL) {
		fprintf(stderr, "Failed to open output.log wb: %s\n",
			strerror(errno));
		abort();
	}
	
	if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
		fprintf(stderr, "Fail: curl_global_init(CURL_GLOBAL_ALL)\n");
		exit(-1);
	}
	
	if ( (hCurl = curl_easy_init()) == NULL) {
		fprintf(stderr, "Fail: curl_easy_init == NULL\n");
		exit(-1);
	}
	
	qlen = strlen(soap);
	
	http_headers = curl_slist_append(http_headers, "Content-Type: text/xml; charset=utf-8");
	http_headers = curl_slist_append(http_headers, "SOAPAction: urn:GoogleSearchAction");
	_snprintf(buff, 63, "Content-Length: %d", qlen);
	http_headers = curl_slist_append(http_headers, buff);
	
	curl_easy_setopt(hCurl, CURLOPT_HEADER, TRUE);
	curl_easy_setopt(hCurl, CURLOPT_POST, TRUE);
	curl_easy_setopt(hCurl, CURLOPT_VERBOSE, TRUE);
	/* curl_easy_setopt(hCurl, CURLOPT_DEBUGFUNCTION, curl_debugfcn); */
	curl_easy_setopt(hCurl, CURLOPT_URL, "http://api.google.com/search/beta2");
	curl_easy_setopt(hCurl, CURLOPT_READFUNCTION, curl_readfcn);
	curl_easy_setopt(hCurl, CURLOPT_WRITEFUNCTION, curl_writefcn);
	
	curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, http_headers);

	rc = curl_easy_perform(hCurl);
	hc = curl_easy_getinfo(hCurl, CURLINFO_RESPONSE_CODE, &http_status);
	
	if (rc != CURLE_OK) {
		puts("rc != CURLE_OK");
	}
	else {
		puts("rc == CURLE_OK");
	}
	
	if (hc != CURLE_OK) {
		puts("hc != CURLE_OK, something seriously wrong!");
		exit(-1);
	}
	
	printf("%s\nHTTP Status Code: %d\n",
		((rc != CURLE_OK) ? curl_easy_strerror(rc) : "No error from CURL"),
		http_status);
	
	fclose(hDebug);
	free(recvdata);

	curl_slist_free_all(http_headers);
	curl_easy_cleanup(hCurl);
	curl_global_cleanup();
	
	return (0);
}

size_t curl_writefcn(void *ptr, size_t size, size_t nmemb, void *stream)
{
	if (size * nmemb == 0)
		return 0;
	
	if (recvdata == NULL) {
		recvdata = malloc(size * nmemb);
		if (recvdata == NULL) {
			fprintf(stderr, "curl_writefcn malloc(%u) failed: %s\n",
				size * nmemb, strerror(errno));
			abort();
		}
		memset(recvdata, 0, size * nmemb);
	}
	else {
		recvdata = realloc(recvdata, recv_sz + size * nmemb);
		if (recvdata == NULL) {
			fprintf(stderr, "curl_writefcn realloc(%u) failed: %s\n",
				recv_sz + size * nmemb, strerror(errno));
			abort();
		}
	}
	
	memcpy(recvdata + recv_sz, ptr, size * nmemb);
	recv_sz += size * nmemb;
	
	/*
	char buff[256];
	recv_sz += size * nmemb;
	_snprintf(buff, 254, "*** curl_writefcn called with size = %u nmemb = %u recv_sz = %u\n",
		size, nmemb, recv_sz);
	
	printf(buff);
	*/
	
	fwrite(ptr, size, nmemb, hDebug);
	
	return size * nmemb;
}

size_t curl_readfcn(void *ptr, size_t size, size_t nmemb, void *stream)
{
	if (strlen(soap) > size * nmemb) {
		fprintf(stderr, "query size (%u bytes) too large for curl (%u bytes max)\n",
			strlen(soap), size * nmemb);
		abort();
	}
	
	strncpy(ptr, soap, strlen(soap));
	
	return strlen(soap);
}

/*
int curl_debugfcn(CURL *handle, curl_infotype it, char *debug_str, size_t sz, void *v)
{
	switch (it) {
		case CURLINFO_TEXT:
			puts("CURLINFO_TEXT");
			fwrite(debug_str, 1, sz, stderr);
			putchar('\n');
			break;
		case CURLINFO_HEADER_IN:
			puts("CURLINFO_HEADER_IN");
			fwrite(debug_str, 1, sz, stderr);
			putchar('\n');
			break;
		case CURLINFO_HEADER_OUT:
			puts("CURLINFO_HEADER_OUT");
			fwrite(debug_str, 1, sz, stderr);
			putchar('\n');
			break;
		case CURLINFO_DATA_IN:
			puts("CURLINFO_DATA_IN");
			fwrite(debug_str, 1, sz, stderr);
			putchar('\n');
			break;
		case CURLINFO_DATA_OUT:
			puts("CURLINFO_DATA_OUT");
			fwrite(debug_str, 1, sz, stderr);
			putchar('\n');
			break;
		default:
			fprintf(stderr, "Unknown debugging info, probably SSL\n");
	}
	
	return 0;
}
*/