<?php

class WHMCS_Api{

	public $cache = '';
	public $method = 'POST';
	public $post_array = null;
	public $remote_host = '';
	public $ssl_verify = false;
	public $timeout = 120;
	public $whmcs_request_url = '';

	function __construct WHMCS_Api($remote_host = ''){
		$this->remote_host = $remote_host;

		$this->http_patch();

		add_action('init', array($this, 'on_init') );

		add_filter('http_request_timeout', array(&$this,'on_timeout'));
		add_filter('https_ssl_verify', array(&$this,'ssl_verify'));


	}

	/**
	* on_init -  Calls init hook functions.
	*
	*/
	function on_init(){

		//setup cache files for current session
		if(! session_id()) session_start();
		$sid = session_id();
		//Setup session specific cookies for WHMCS
		if(! is_dir(WHMCS_INTEGRATION_CACHE_DIR)) mkdir(WHMCS_INTEGRATION_CACHE_DIR, 0755);
		if(! is_writable(WHMCS_INTEGRATION_CACHE_DIR) ) chmod(WHMCS_INTEGRATION_CACHE_DIR, 0755);
		//if(! is_writable(WHMCS_INTEGRATION_CACHE_DIR) ) chmod(WHMCS_INTEGRATION_CACHE_DIR, 0777);
		$this->cache = WHMCS_INTEGRATION_CACHE_DIR . "$sid.txt";

		//Clean out old cache files
		foreach(glob(WHMCS_INTEGRATION_CACHE_DIR . '*.*') as $fname){
			$age = time() - filemtime($fname);
			if(($age > 24 * 60 * 60 * 7) &&  (basename($fname) != 'index.php')) { //Don't erase our blocking index.php file
				unlink($fname); // more than a week old;
			}
		}
	}

	function remote_call($url = '', $args = array() ){
		//grab WP_Http
		add_action('http_api_curl',array(&$this,'cache_cookies'));
		add_filter('http_curl_headers', array(&$this,'filter_headers'));
		add_filter('http_api_redirect', array(&$this,'filter_redirect'));

		$this->whmcs_request_url = $url;

		$body = null;

		$headers = array('X-Forwarded-For' => $_SERVER['REMOTE_ADDR']);

		$this->method = (empty($args['method'] ) ? $this->method : $args['method'] ;

		switch ($this->method){

			case 'POST' : {

				//Only figure out the post array once
				if(is_null($this->post_array)){
					$this->post_array = (empty($args['body'])) ? $_POST : $args['body'];

					//Pass on the upload files
					foreach($_FILES as $field => $filespecs){
						foreach($filespecs['name'] as $key => $filename){

							if ( empty($filespecs['error'][$key])){ //good file
								$actual_file = WHMCS_INTEGRATION_CACHE_DIR . $filespecs['name'][$key];
								move_uploaded_file($filespecs['tmp_name'][$key], $actual_file);
								$this->post_array[$field . "[$key]"] = "@$actual_file;type=" . $filespecs['type'][$key];
							}
						}
					}
				}

				$response = wp_remote_request($url,array(
				'method' => 'POST',
				'headers' => $headers,
				'body' => $this->post_array,
				'timeout' => apply_filters('http_request_timeout',60),
				));
				break;
			}
			case 'GET': {
				$response = wp_remote_get($url, array(
				'headers' => $headers,
				));
				break;
			}
			case 'HEAD': {
				$response = wp_remote_request($url, array(
				'method' => 'HEAD',
				'headers' => $headers,
				'timeout' => apply_filters('http_request_timeout',60),
				));
				break;
			}
			case 'PUT': {
				$response = wp_remote_put($url, array(
				'headers' => $headers,
				));
				break;
			}
			default: {
				break;
			}
		}

		//print_r(http_build_query($_POST));
		//Debug display here so we don't get the wrong base
		if($this->debug){
			//remove the <base> tag so it doesn't screw relative urls.
			//$response['body'] = preg_replace("/<base[^>]+\>/i", "", $response['body']);
			$this->debug_print($response);
		}

		if (is_wp_error($response)){
		}	else {
			if (in_array($response['response']['code'], array(300, 301, 302, 303, 307) ) ){
				$newurl = url_to_absolute($this->remote_host,$response['headers']['location']);

				remove_filter('http_api_redirect', array($this,'filter_redirect'));
				remove_filter('http_curl_headers', array($this,'filter_headers'));
				remove_action('http_api_curl',array($this,'cache_cookies'));

				if(in_array($response['response']['code'], array(302, 303 ) ) ) $this->method = 'GET';

				$response =  $this->redirect_request( $newurl , $this->post_fields );
			}
		}

		// Give WP_Http back
		remove_filter('http_api_redirect', array($this,'filter_redirect'));
		remove_filter('http_curl_headers', array($this,'filter_headers'));
		remove_action('http_api_curl',array($this,'cache_cookies'));
		return $response;

	}

	/**
	* Makes a minor patch to WP_Curl turning off errors when a 301, 302 redirect occurs
	* A redirect is not an error and it messes up catching page changes at WHMCS
	* Also inserts a filter so that headers can be filtered in curl.
	* And a filter to turn off WP_Http's internal redirect when safe_mode or open_basedir are set in PHP.
	*/
	function http_patch($hard = false){

		//Check the file stats for an updated file
		$this->settings = get_option(WHMCS_SETTINGS_NAME);
		$fname = ABSPATH . WPINC . '/class-http.php';
		clearstatcache();
		$stat = stat($fname);
		$result = false;
		//if different update the file and save the new signature
		if($this->settings['http_sig'] != $stat['size'].$stat['mtime'].$stat['ctime'] || $hard){
			$fs = file_get_contents($fname);
			if($fs !== false){

				$fs = preg_replace('#(302\s*\)\s*\)\s*\)\s*)(return)#','$1;//$2'	,$fs); //comment out the error

				//** Header filter necessary to support multipart forms. If not patched genereates a 413: Request Entity Too Large error
				//Header filter for 3.3x
				$fs = preg_replace('#(true\s*\)\s*;\s*)(if\s*\(\s*\!\s*empty\s*\(\s*\$r\[\'headers\'\])#',
				"$1\$r['headers'] = apply_filters('http_curl_headers', \$r['headers']); //Added by WHMCS Integration\n\n\t\t$2", $fs); //Add header filter

				//Header filter for 3.4x
				$fs = preg_replace('#(\}\s*)(if\s*\(\s*\!\s*empty\s*\(\s*\$r\[\'headers\'\])#',
				"$1\$r['headers'] = apply_filters('http_curl_headers', \$r['headers']); //Added by WHMCS Integration\n\n\t\t$2", $fs); //Add header filter

				//obsolete after WPv3.4
				$fs = preg_replace('#(>\s*0\s*\)\s*\{\s*)(return\s*\$this->request\(\s*\$theHeaders)#',
				"$1if(apply_filters('http_api_redirect', true)) //Added by WHMCS Integration\n\t\t\t\t$2", $fs); //Add redirect filter

				//For WPv3.4
				$fs = preg_replace('#(>\s*0\s*\)\s*\{\s*)(return\s*\$this->request\(\s*WP_HTTP\:\:make_absolute_url\(\s*\$theHeaders)#',
				"$1if(apply_filters('http_api_redirect', true)) //Added by WHMCS Integration\n\t\t\t\t$2", $fs); //Add redirect filter

				if($fs){
					chmod($fname, 0666);
					$result = @file_put_contents($fname, $fs);
					clearstatcache();
					$stat = stat($fname);
					$this->settings['http_sig'] = $stat['size'].$stat['mtime'].$stat['ctime'];
					update_option(WHMCS_SETTINGS_NAME, $this->settings);
				}
			}

		}
		return $result;
	}

	function filter_headers($headers){

		if ( count($_FILES) >  0 ){

			//Multipart post. these will set themselves in curl
			unset($headers['Content-Type']);
			unset($headers['Content-Length']);
		}

		return $headers;
	}

	function filter_redirect($doredirect){
		return false;
	}

	function cache_cookies($handle){
		curl_setopt( $handle, CURLOPT_COOKIEJAR, $this->cache );
		curl_setopt( $handle, CURLOPT_COOKIEFILE, $this->cache );
		curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); // Need to follow redirects explicitly

		//If this is a multipart post this is our chance to fixup curl
		if ( count($_FILES) >  0 ){
			curl_setopt( $handle, CURLOPT_POSTFIELDS, $this->post_array ); //Multipart post
		}
	}

	function ssl_verify($sslverify){
		return $this->ssl_verify;
	}

	function on_timeout($timeout){
		return $this->timeout;
	}

}