<?php
define('USER_AGENT', 'Disqus-WPPlugin/2.0-dev');
define('SOCKET_TIMEOUT', 10);

function dsq_get_query_string($postdata) {
	$postdata_str = '';

	if($postdata) {
		foreach($postdata as $key=>$value) {
			$postdata_str .= urlencode($key) . '=' . urlencode($value) . '&';
		}
	}

	return $postdata_str;
}


function dsq_get_post_content($boundary, $postdata, $file_name, $file_field) {
	if(empty($file_name) || empty($file_field)) {
		return dsq_get_query_string($postdata);
	}

	$content = array();
	$content[] = '--' . $boundary;
	foreach($postdata as $key=>$value) {
		$content[] = 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n" . $value;
		$content[] = '--' . $boundary;
	}
	$content[] = 'Content-Disposition: form-data; name="' . $file_field . '"; filename="' . $file_name . '"';
	// HACK: We only need to handle text/plain files right now.
	$content[] = "Content-Type: text/plain\r\n";
	$content[] = file_get_contents($file_name);
	$content[] = '--' . $boundary . '--';
	$content = implode("\r\n", $content);
	return $content;
}


function dsq_get_http_headers_for_request($boundary, $content, $file_name, $file_field) {
	$headers = array();
	$headers[] = 'User-Agent: ' . USER_AGENT;
	$headers[] = 'Connection: close';
	if($content) {
		$headers[] = 'Content-Length: ' . strlen($content);
		if($file_name && $file_field) {
			$headers[] = 'Content-Type: multipart/form-data; boundary=' . $boundary;
		} else {
			$headers[] = 'Content-Type: application/x-www-form-urlencoded';
		}
	}
	return implode("\r\n", $headers);
}


function _dsq_curl_urlopen($url, $postdata, &$response, $file_name, $file_field) {
	$c = curl_init($url);
	$postdata_str = dsq_get_query_string($postdata);

	$c_options = array(
		CURLOPT_USERAGENT		=> USER_AGENT,
		CURLOPT_RETURNTRANSFER	=> true,
		CURLOPT_POST			=> ($postdata_str ? 1 : 0),
		CURLOPT_HEADER			=> true,
		CURLOPT_HTTPHEADER		=> array('Expect:'),
		CURLOPT_TIMEOUT 		=> SOCKET_TIMEOUT
	);
	if($postdata) {
		$c_options[CURLOPT_POSTFIELDS] = $postdata_str;
	}
	if($file_name && $file_field) {
		$postdata[$file_field] = '@' . $file_name;
		$c_options[CURLOPT_POSTFIELDS] = $postdata;
		$c_options[CURLOPT_RETURNTRANSFER] = 1;
	}
	curl_setopt_array($c, $c_options);

	$data = curl_exec($c);
	list($headers, $response['data']) = explode("\r\n\r\n", $data, 2);
	
	$response['headers'] = _dsq_get_response_headers($headers, $response);
	$response['code'] = curl_getinfo($c, CURLINFO_HTTP_CODE);
}

function _dsq_get_response_headers($headers, &$response) {
	$headers = explode("\r\n", $headers);
	list($unused, $response['code'], $unused) = explode(' ', $headers[0], 3);
	$headers = array_slice($headers, 1);

	// Convert headers into associative array.
	foreach($headers as $unused=>$header) {
		$header = explode(':', $header);
		$header[0] = trim($header[0]);
		$header[1] = trim($header[1]);
		$headers[strtolower($header[0])] = $header[1];
	}
	
	return $headers;
}

function _dsq_fsockopen_urlopen($url, $postdata, &$response, $file_name, $file_field) {
	$buf = '';
	$req = '';
	$length = 0;
	$boundary = '----------' . md5(time());
	$postdata_str = dsq_get_post_content($boundary, $postdata, $file_name, $file_field);
	$url_pieces = parse_url($url);

	// Set default port for supported schemes if none is provided.
	if(!isset($url_pieces['port'])) {
		switch($url_pieces['scheme']) {
			case 'http':
				$url_pieces['port'] = 80;
				break;
			case 'https':
				$url_pieces['port'] = 443;
				$url_pieces['host'] = 'ssl://' . $url_pieces['host'];
				break;
		}
	}

	// Set default path if trailing slash is not provided.
	if(!isset($url_pieces['path'])) { $url_pieces['path'] = '/'; }

	// Determine if we need to include the port in the Host header or not.
	if(($url_pieces['port'] == 80  && $url_pieces['scheme'] == 'http') ||
		($url_pieces['port'] == 443 && $url_pieces['scheme'] == 'https')) {
		$host = $url_pieces['host'];
	} else {
		$host = $url_pieces['host'] . ':' . $url_pieces['port'];
	}

	$fp = @fsockopen($url_pieces['host'], $url_pieces['port'], $errno, $errstr, SOCKET_TIMEOUT);
	if(!$fp) { return false; }

	$path = $url_pieces['path'];
	if ($url_pieces['query']) $path .= '?'.$url_pieces['query'];

	$req .= ($postdata_str ? 'POST' : 'GET') . ' ' . $path . " HTTP/1.1\r\n";
	$req .= 'Host: ' . $host . "\r\n";
	$req .=  dsq_get_http_headers_for_request($boundary, $postdata_str, $file_name, $file_field);
	if($postdata_str) {
		$req .= "\r\n\r\n" . $postdata_str;
	}
	$req .= "\r\n\r\n";

	fwrite($fp, $req);
	while(!feof($fp)) {
		$buf .= fgets($fp, 4096);
	}

	// Parse headers from the response buffers.
	list($headers, $response['data']) = explode("\r\n\r\n", $buf, 2);

	// Get status code from headers.
	$headers = _dsq_get_response_headers($headers, $response);

	// If transfer-coding is set to chunked, we need to join the message body
	// together.
	if(isset($headers['transfer-encoding']) && 'chunked' == strtolower($headers['transfer-encoding'])) {
		$chunk_data = $response['data'];
		$joined_data = '';
		while(true) {
			// Strip length from body.
			list($chunk_length, $chunk_data) = explode("\r\n", $chunk_data, 2);
			$chunk_length = hexdec($chunk_length);
			if(!$chunk_length || !strlen($chunk_data)) { break; }

			$joined_data .= substr($chunk_data, 0, $chunk_length);
			$chunk_data = substr($chunk_data, $chunk_length + 1);
			$length += $chunk_length;
		}
		$response['data'] = $joined_data;
	} else {
		$length = $headers['content-length'];
	}
	$response['headers'] = $headers;
}


function _dsq_fopen_urlopen($url, $postdata, &$response, $file_name, $file_field) {
	$params = array();
	if($file_name && $file_field) {
		$boundary = '----------' . md5(time());
		$content = dsq_get_post_content($boundary, $postdata, $file_name, $file_field);
		$header = dsq_get_http_headers_for_request($boundary, $content, $file_name, $file_field);

		$params = array('http' => array(
			'method'	=> 'POST',
			'header'	=> $header,
			'content'	=> $content,
			'timeout'	=> SOCKET_TIMEOUT
		));
	} else {
		if($postdata) {
			$params = array('http' => array(
				'method'	=> 'POST',
				'header'	=> 'Content-Type: application/x-www-form-urlencoded',
				'content'	=> dsq_get_query_string($postdata),
				'timeout'	=> SOCKET_TIMEOUT
			));
		}
	}


	ini_set('user_agent', USER_AGENT);
	$ctx = stream_context_create($params);
	$fp = fopen($url, 'rb', false, $ctx);
	if(!$fp) {
		return false;
	}

	// Get status code from headers.
	list($unused, $response['code'], $unused) = explode(' ', $http_response_header[0], 3);
	$headers = array_slice($http_response_header, 1);

	// Convert headers into associative array.
	foreach($headers as $unused=>$header) {
		$header = explode(':', $header);
		$header[0] = trim($header[0]);
		$header[1] = trim($header[1]);
		$headers[strtolower($header[0])] = strtolower($header[1]);
	}

	$response['data'] = stream_get_contents($fp);
	$response['headers'] = $headers;
}


/**
 * Wrapper to provide a single interface for making an HTTP request.
 *
 * Attempts to use cURL, fopen(), or fsockopen(), whichever is available
 * first.
 *
 * @param	string	$url		URL to make request to.
 * @param	array	$postdata	(optional) If postdata is provided, the request
 *								method is POST with the key/value pairs as
 *								the data.
 * @param	array	$file		(optional) Should provide associative array
 *								with two keys: name and field.  Name should
 *								be the name of the file and field is the name
 *								of the field to POST.
 */
function dsq_urlopen($url, $postdata=false, $file=false) {
	$response = array(
		'data' => '',
		'code' => 0
	);

	if($file) {
		extract($file, EXTR_PREFIX_ALL, 'file');
	}
	if(empty($file_name) || empty($file_field)) {
		$file_name = false;
		$file_field = false;
	}

//

	// Try curl, fsockopen, fopen + stream (PHP5 only), exec wget
	if(function_exists('curl_init')) {
		if (!function_exists('curl_setopt_array')) {
			function curl_setopt_array(&$ch, $curl_options)
			{
				foreach ($curl_options as $option => $value) {
					if (!curl_setopt($ch, $option, $value)) {
						return false;
					}
				}
				return true;
			}
		}
		_dsq_curl_urlopen($url, $postdata, $response, $file_name, $file_field);
	} else if(ini_get('allow_url_fopen') && function_exists('stream_get_contents')) {
		_dsq_fopen_urlopen($url, $postdata, $response, $file_name, $file_field);
	} else {
		// TODO: Find the failure condition for fsockopen() (sockets?)
		_dsq_fsockopen_urlopen($url, $postdata, $response, $file_name, $file_field);
	}

// returns array with keys data and code (from headers)

	return $response;
}

function dsq_url_method() {
	if(function_exists('curl_init')) {
		return 'curl';
	} else if(ini_get('allow_url_fopen') && function_exists('stream_get_contents')) {
		return 'fopen';
	} else {
		return 'fsockopen';
	}
}
?>
