fullmsg = $msg; parent::__construct((is_array($msg) ? $msg['error'] : $msg)); } public function getError() { return $this->fullmsg; } public function getInfo() { $err = $this->fullmsg; if (isset($err['errordiags']['info'])) { return $err['errordiags']['info']; } if (isset($err['errordiags']['reason'])) { return $err['errordiags']['reason']; } return $this->getMessage(); } } class CCClient { private $version = '1.4'; private $default_api_version = '0.1'; private $default_mode = 'test'; private $serverURL_format = 'https://%s.apicontrol.co.uk/%s/%s'; private $numeric_mode_map = array('1'=>'live', '0'=>'test'); private $url; private $ua = NULL; private $auth; private $raise_error = 0; private $use_json = 0; private $client_addr = NULL; public $last_error = NULL; public $request = NULL; public $response = NULL; public $http_code = NULL; public function __construct($user, $pass, $opt = array()) { if (!is_array($opt)) { throw new Exception("Options not an array", 1); } $mode = $this->default_mode; $apiver = $this->default_api_version; if (array_key_exists('mode',$opt)) { $mode = $opt['mode']; unset($opt['mode']); } if (array_key_exists('version',$opt)) { $apiver = $opt['version']; unset($opt['version']); } if (array_key_exists('raise_error',$opt)) { if (preg_match('/^(0|1|2|full)$/i', $opt['raise_error'])) { $this->raise_error = (strtolower($opt['raise_error']) === 'full' ? 2 : intval($opt['raise_error'])); unset($opt['raise_error']); } else { throw new Exception('Bad value for raise_error: '.$opt['raise_error'], 1); } } if (array_key_exists($mode, $this->numeric_mode_map)) { $mode = $this->numeric_mode_map[$mode]; } if (!preg_match('/^[\w-]+$/', $mode)) { throw new Exception("Non-valid mode: $mode", 1); } if (function_exists('json_encode') && function_exists('json_decode')) { $this->use_json = 1; } if (array_key_exists('REMOTE_ADDR', $_SERVER)) { $this->client_addr = $_SERVER['REMOTE_ADDR']; if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { $this->client_addr .= ', '.$_SERVER['HTTP_X_FORWARDED_FOR']; } } $this->url = sprintf($this->serverURL_format, $mode, $apiver, ($this->use_json ? 'json' : 'phpserialize')); $this->auth = array_merge($opt, array('username'=>$user, 'password'=>$pass)); $this->ua = "php/".get_class($this)."/".$this->version; } public function __call($func, $args) { if ($args) { if (count($args) > 1) { throw new Exception("Too many arguments passed to $func", 2); } $args = array_shift($args); } $request = array ( 'auth' => $this->auth, 'func' => str_replace('_', '.', $func), 'args' => $args ); $request = $this->_encode($request); $this->request = &$request; $POST = $this->_HTTPS_POST($this->url, $request); $this->response = &$POST['response']; if ($POST['error']) { throw new Exception("Transport error: {$POST['error']}", 3); } $result = $this->_decode($POST['response']); if ($result === false) { throw new Exception("Cannot decode response: {$POST['response']}", 3); } if (!isset($result['success']) || !$result['success']) { $this->last_error = $result; if ($this->raise_error) { throw new CCAPI_Error($this->raise_error > 1 ? $result : $result['error']); } } return $result; } private function &_HTTPS_POST($url, &$data) { $hdrs = array('User-Agent'=>$this->ua); if ($this->client_addr) { $hdrs['X-Forwarded-For'] = $this->client_addr; } $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE); foreach ($hdrs as $hdr=>$val) { curl_setopt($ch, CURLOPT_HTTPHEADER, array("$hdr: $val")); } $result = array ('response'=>curl_exec($ch), 'error'=>curl_error($ch), 'http_code'=>curl_getinfo($ch, CURLINFO_HTTP_CODE)); curl_close ($ch); if ($result['http_code'] != 400) { $result['success'] = strpos($result['http_code'], '2') === 0; return $result; } // we've probably hit the php-curl SNI bug; see if we can get Perl to do this for us: return $this->_Perl_POST($url, $data, $hdrs); } private function &_Perl_POST($url, &$data, $hdrs = array()) { $perlcode = ' use strict; use warnings; use LWP::UserAgent; use HTTP::Request::Common; my $default_timeout = 60; local $/; my $req = ; my @args = (); my %hdrs = (); foreach my $arg (@ARGV) { if ($arg =~ /^--([^=]+)(=(.*))?$/) { my ($field, $val) = ($1, $3); $field =~ s/-/_/g; $hdrs{$field} = $val; } else { push (@args, $arg); } } my $url = shift(@args) || die "no URL in args"; my $ua = LWP::UserAgent->new(agent=>"Perl/CCAPI_Post/1.0"); $ua->timeout(delete($hdrs{"timeout"}) || $default_timeout); my $result = $ua->request(POST $url, %hdrs, Content=>$req); print $result->code, " ", $result->message, "\\n\\n", $result->content;'; $cmdparts = array('perl', '-e', $perlcode, $url); foreach ($hdrs as $key=>$val) { $cmdparts[] = "--$key=$val"; } foreach ($cmdparts as &$part) { $part = escapeshellarg($part); } $cmd = join(' ', $cmdparts); $pipespec = array(0=>array('pipe','r'), 1=>array('pipe','w'), 2=>array('pipe','w')); $proc = proc_open($cmd, $pipespec, $pipes); $written = fwrite($pipes[0], $data, strlen($data)); fclose($pipes[0]); $stdout = stream_get_contents($pipes[1]); $stderr = stream_get_contents($pipes[2]); $resultcode = proc_close($proc); # zero if no error if ($resultcode > 0) { $err = error_get_last(); return (object) array('http_code'=>500, 'error'=>$err['message']."\n$stderr", 'http_msg'=>'Transport error', 'response'=>$stdout); } $headerend = strpos($stdout, "\n\n"); $code = $msg = NULL; if (preg_match('/^(\d+)(\s+(.+))?/', substr($stdout, 0, $headerend), $bits)) { $code = $bits[1]; $msg = $bits[3]; } $res = array('http_code'=>$code, 'http_msg'=>$msg, 'error'=>$stderr, 'response'=>substr($stdout, $headerend+2)); return $res; } private function _encode(&$struct) { return ($this->use_json ? json_encode($struct) : serialize($struct)); } private function _decode(&$struct) { return ($this->use_json ? json_decode($struct,1) : unserialize($struct)); } }