mirror of
https://github.com/e107inc/e107.git
synced 2025-01-16 20:28:28 +01:00
1392 lines
30 KiB
PHP
1392 lines
30 KiB
PHP
<?php
|
|
/*
|
|
* e107 website system
|
|
*
|
|
* Copyright (C) 2008-2013 e107 Inc (e107.org)
|
|
* Released under the terms and conditions of the
|
|
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
|
|
*
|
|
* Application store client
|
|
*
|
|
*/
|
|
|
|
e107::coreLan('theme', true);
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class e_marketplace
|
|
{
|
|
/**
|
|
* Protocol type, defaults to WSDL, fallback to 'xmlrpc' if soap extension not installed
|
|
* @var e_marketplace_adapter_abstract
|
|
*/
|
|
protected $adapter = null;
|
|
|
|
/**
|
|
* Adapter identifier
|
|
* @var string wsdl|xmlrpc
|
|
*/
|
|
protected $_adapter_name = null;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param string $force force adapter wsdl|xmlrpc, omit to switch to auto-detection
|
|
*/
|
|
public function __construct($force = null)
|
|
{
|
|
if(null !== $force)
|
|
{
|
|
$this->_adapter_name = $force === 'wsdl' ? 'wsdl' : 'xmlrpc';
|
|
}
|
|
elseif(!class_exists('SoapClient')) $this->_adapter_name = 'xmlrpc';
|
|
else
|
|
{
|
|
$this->_adapter_name = 'wsdl';
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Set authorization key
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function generateAuthKey($username, $password)
|
|
{
|
|
if(trim($username) == '' || trim($password) == '')
|
|
{
|
|
return false;
|
|
}
|
|
$this->setAuthKey($this->makeAuthKey($username, $password, true));
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set authorization key
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function setAuthKey($authkey)
|
|
{
|
|
$this->adapter->setAuthKey($authkey);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function hasAuthKey()
|
|
{
|
|
return $this->adapter->hasAuthKey();
|
|
}
|
|
|
|
/**
|
|
* Make authorization key from user credentials
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function makeAuthKey($username, $password = '', $plain = false)
|
|
{
|
|
$now = gmdate('y-m-d H');
|
|
if($plain && !empty($password)) $password = md5($password);
|
|
return sha1($username.$password.$now);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Have the admin enter their e107.org login details in order to create the authorization key.
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function renderLoginForm()
|
|
{
|
|
|
|
$text = '
|
|
<div class="" id="loginModal">
|
|
<div class="well">
|
|
<img src="'.e_IMAGE_ABS.'admin_images/credits_logo.png" alt="" style="margin-bottom:15px" />
|
|
<ul class="nav nav-tabs">
|
|
<li class="active"><a href="#login" data-toggle="tab" data-bs-toggle="tab">Login</a></li>
|
|
<li><a href="#create" data-toggle="tab" data-bs-toggle="tab">Create Account</a></li>
|
|
</ul>
|
|
<div id="myTabContent" class="tab-content">
|
|
<div class="tab-pane active in" id="login">
|
|
<form class="form-horizontal" action="" method="POST">
|
|
<fieldset>
|
|
<div id="legend">
|
|
<legend class="">Login</legend>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label class="control-label" for="username">Username</label>
|
|
<div class="controls">
|
|
<input type="text" id="username" name="username" placeholder="" class="input-xlarge">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<label class="control-label" for="password">Password</label>
|
|
<div class="controls">
|
|
<input type="password" id="password" name="password" placeholder="" class="input-xlarge">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
<div class="controls">
|
|
<button class="btn btn-success">Login</button>
|
|
</div>
|
|
</div>
|
|
|
|
</fieldset>
|
|
</form>
|
|
</div>';
|
|
|
|
//TODO Use Form handler for INPUT tags.
|
|
//XXX TBD OR do we just redirect to the signup page on the website, in an iframe?
|
|
|
|
$text .= '
|
|
<div class="tab-pane fade" id="create">
|
|
<form class="form-horizontal" id="tab">
|
|
<div class="control-group">
|
|
<label class="control-label">Username</label>
|
|
<div class="controls">
|
|
<input type="text" value="" class="input-xlarge">
|
|
</div>
|
|
</div>
|
|
<div class="control-group">
|
|
<label class="control-label">Password</label>
|
|
<div class="controls">
|
|
<input type="password" value="" class="input-xlarge">
|
|
</div>
|
|
</div>
|
|
<div class="control-group">
|
|
<label class="control-label">Email</label>
|
|
<div class="controls">
|
|
<input type="text" value="" class="input-xlarge">
|
|
</div>
|
|
</div>
|
|
<div class="control-group">
|
|
<div class="controls">
|
|
<button class="btn btn-primary">Create Account</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
';
|
|
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Retrieve currently used adapter
|
|
* @param e_marketplace_adapter_abstract
|
|
* @return \e_marketplace_adapter_abstract
|
|
*/
|
|
public function adapter()
|
|
{
|
|
if(null === $this->adapter)
|
|
{
|
|
$className = 'e_marketplace_adapter_'.$this->_adapter_name;
|
|
$this->adapter = new $className();
|
|
}
|
|
return $this->adapter;
|
|
}
|
|
|
|
/**
|
|
* Retrieve currently used adapter
|
|
* @param $method
|
|
* @param $data
|
|
* @param bool $apply
|
|
* @return mixed
|
|
*/
|
|
public function call($method, $data, $apply = true)
|
|
{
|
|
if(E107_DEBUG_LEVEL > 0)
|
|
{
|
|
e107::getDebug()->log("Calling e107.org using <b> ".$this->_adapter_name."</b> adapter");
|
|
}
|
|
return $this->adapter()->call($method, $data, $apply);
|
|
}
|
|
|
|
/**
|
|
* Adapter proxy
|
|
*/
|
|
public function download($id, $mode, $type)
|
|
{
|
|
return $this->adapter()->download($id, $mode, $type);
|
|
}
|
|
|
|
/**
|
|
* Direct adapter()->call() execution - experimental stage
|
|
*/
|
|
public function __call($method, $arguments)
|
|
{
|
|
if(strpos($method, 'get') === 0 || strpos($method, 'do') === 0)
|
|
{
|
|
return $this->adapter()->call($method, $arguments);
|
|
}
|
|
throw new Exception("Error Processing Request", 10);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->adapter = null;
|
|
//echo "Adapter destroyed", PHP_EOL;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $data - e107.org plugin/theme feed data.
|
|
* @return bool|string
|
|
*/
|
|
public function getDownloadModal($type='plugin',$data=array())
|
|
{
|
|
|
|
$url = false;
|
|
|
|
if($type === 'plugin')
|
|
{
|
|
|
|
if(empty($data['plugin_id']))
|
|
{
|
|
|
|
$srcData = array(
|
|
'plugin_id' => $data['params']['id'],
|
|
'plugin_folder' => $data['folder'],
|
|
'plugin_price' => $data['price'],
|
|
'plugin_mode' => $data['params']['mode'],
|
|
'plugin_url' => $data['url'],
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$srcData = $data;
|
|
}
|
|
|
|
$d = http_build_query($srcData,false,'&');
|
|
|
|
// if(deftrue('e_DEBUG_PLUGMANAGER'))
|
|
{
|
|
$url = e_ADMIN.'plugin.php?mode=online&action=download&e-token='.e_TOKEN.'&src='.base64_encode($d);
|
|
}
|
|
// else
|
|
{
|
|
// $url = e_ADMIN.'plugin.php?mode=download&src='.base64_encode($d);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if($type === 'theme')
|
|
{
|
|
$srcData = array(
|
|
'id' => $data['params']['id'],
|
|
'url' => $data['url'],
|
|
'mode' => 'addon',
|
|
'price' => $data['price']
|
|
);
|
|
|
|
$d = http_build_query($srcData,false,'&');
|
|
$url = e_ADMIN.'theme.php?mode=main&action=download&e-token='.e_TOKEN.'&src='.base64_encode($d);//$url.'&action=download';
|
|
|
|
}
|
|
|
|
|
|
return $url;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $type
|
|
* @return array|string[]
|
|
*/
|
|
public function getVersionList($type='plugin')
|
|
{
|
|
$cache = e107::getCache();
|
|
$cache->setMD5('_', false);
|
|
|
|
$tag = 'Versions_'.$type;
|
|
|
|
if($data = $cache->retrieve($tag,(60 * 12), true, true))
|
|
{
|
|
return e107::unserialize($data);
|
|
}
|
|
|
|
// $mp = $this->getMarketplace();
|
|
// $mp->generateAuthKey($e107SiteUsername, $e107SiteUserpass);
|
|
e107::getDebug()->log("Retrieving ".$type." version list from e107.org");
|
|
|
|
$xdata = $this->call('getList', array(
|
|
'type' => $type,
|
|
'params' => array('limit' => 200, 'search' => null, 'from' => 0)
|
|
));
|
|
|
|
$arr = array();
|
|
|
|
if(!empty($xdata['data']))
|
|
{
|
|
|
|
foreach($xdata['data'] as $row)
|
|
{
|
|
$k = $row['folder'];
|
|
$arr[$k] = $row;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if(empty($arr))
|
|
{
|
|
$arr = array('-unable-to-connect'); // make sure something is cached so further lookups stop.
|
|
}
|
|
|
|
$data = e107::serialize($arr, 'json');
|
|
$cache->set($tag, $data, true, true, true);
|
|
|
|
return $arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
abstract class e_marketplace_adapter_abstract
|
|
{
|
|
/**
|
|
* e107.org download URL
|
|
* @var string
|
|
*/
|
|
protected $downloadUrl = 'https://e107.org/request/';
|
|
|
|
/**
|
|
* e107.org service URL [adapter implementation required]
|
|
* @var string
|
|
*/
|
|
protected $serviceUrl = null;
|
|
|
|
/**
|
|
* Request method POST || GET [adapter implementation required]
|
|
* @var string
|
|
*/
|
|
public $requestMethod = null;
|
|
|
|
|
|
/**
|
|
* @var eAuth
|
|
*/
|
|
protected $_auth = null;
|
|
|
|
/**
|
|
* e107.org authorization key
|
|
* @deprecated subject of removal
|
|
* @var string
|
|
*/
|
|
protected $authKey = null;
|
|
|
|
/**
|
|
* @param $input
|
|
* @return mixed
|
|
*/
|
|
abstract public function test($input);
|
|
//abstract public function call($method, $data, $apply);
|
|
|
|
/**
|
|
* @param $method
|
|
* @param $data
|
|
* @param $apply
|
|
* @return mixed
|
|
*/
|
|
abstract public function call($method, $data, $apply = true); // Fix issue #490
|
|
|
|
/**
|
|
* @param $method
|
|
* @param $result
|
|
* @return mixed
|
|
*/
|
|
abstract public function fetch($method, &$result);
|
|
|
|
/**
|
|
* Authorization object
|
|
* @return eAuth
|
|
*/
|
|
public function auth()
|
|
{
|
|
if(null === $this->_auth)
|
|
{
|
|
$this->_auth = new eAuth;
|
|
$this->_auth->loadSysCredentials();
|
|
$this->_auth->requestMethod = $this->requestMethod;
|
|
}
|
|
return $this->_auth;
|
|
}
|
|
|
|
/**
|
|
* Set authorization key
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function setAuthKey($authkey)
|
|
{
|
|
$this->authKey = $authkey;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function hasAuthKey()
|
|
{
|
|
return $this->authKey !== null;
|
|
}
|
|
|
|
/**
|
|
* @deprecated subject of removal
|
|
*/
|
|
public function getAuthKey()
|
|
{
|
|
return $this->authKey;
|
|
}
|
|
|
|
|
|
/**
|
|
* Download a Plugin or Theme to Temp, then test and move to plugin/theme folder and backup to system backup folder.
|
|
* XXX better way to return status (e.g. getError(), getStatus() service call before download)
|
|
* XXX temp is not well cleaned
|
|
* XXX themes/plugins not well tested after unzip (example - Headline 1.0, non-default structure, same applies to most FS net free themes)
|
|
* This method is direct outputting the status. If not needed - use buffer
|
|
* @param $id
|
|
* @param $mode
|
|
* @param string $type plugin or theme
|
|
* @return bool
|
|
*/
|
|
public function download($id, $mode, $type)
|
|
{
|
|
$tp = e107::getParser();
|
|
$mes = e107::getMessage();
|
|
$fl = e107::getFile();
|
|
|
|
$id = intval($id);
|
|
$qry = 'id='.$id.'&type='.$type.'&mode='.$mode;
|
|
$remotefile = $this->downloadUrl."?auth=".$this->getAuthKey()."&".$qry;
|
|
|
|
$localfile = md5($remotefile.time()).".zip";
|
|
$mes->addSuccess(LAN_DOWNLOADING."...");
|
|
|
|
// FIXME call the service, check status first, then download (if status OK), else retireve the error break and show it
|
|
|
|
$result = $this->getRemoteFile($remotefile, $localfile);
|
|
|
|
if(!$result)
|
|
{
|
|
if(filesize(e_TEMP.$localfile))
|
|
{
|
|
$contents = file_get_contents(e_TEMP.$localfile);
|
|
$contents = explode('REQ_', $contents);
|
|
$mes->addError('[#'.trim($contents[1]).'] '.trim($contents[0])); flush();
|
|
}
|
|
|
|
@unlink(e_TEMP.$localfile);
|
|
return false;
|
|
}
|
|
|
|
|
|
if(!file_exists(e_TEMP.$localfile))
|
|
{
|
|
$srch = array("[", "]");
|
|
$repl = array("<a href='".$remotefile."'>", "</a>");
|
|
|
|
$mes->addError( TPVLAN_83." ".str_replace($srch, $repl, TPVLAN_84));
|
|
|
|
if(E107_DEBUG_LEVEL > 0)
|
|
{
|
|
$mes->addDebug('local='.$localfile); // ; flush();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
if($fl->unzipArchive($localfile,$type, true))
|
|
{
|
|
$lan = defset('LAN_DOWNLOAD_COMPLETE', 'Download Complete!');
|
|
$mes->addSuccess($lan);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$mes->addSuccess( "<a href='".$remotefile."'>".TPVLAN_84."</a>");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// Grab a remote file and save it in the /temp directory. requires CURL
|
|
|
|
/**
|
|
* @param $remote_url
|
|
* @param $local_file
|
|
* @param $type
|
|
* @return bool
|
|
*/
|
|
function getRemoteFile($remote_url, $local_file, $type='temp')
|
|
{
|
|
// FIXME - different methods (see xml handler getRemoteFile()), error handling, appropriate error messages,
|
|
if (!function_exists("curl_init"))
|
|
{
|
|
return false;
|
|
}
|
|
$path = ($type == 'media') ? e_MEDIA : e_TEMP;
|
|
|
|
$fp = fopen($path.$local_file, 'w'); // media-directory is the root.
|
|
//$fp1 = fopen(e_TEMP.'/curllog.txt', 'w');
|
|
|
|
|
|
$cp = e107::getFile()->initCurl($remote_url);
|
|
curl_setopt($cp, CURLOPT_FILE, $fp);
|
|
/* $cp = curl_init($remote_url);
|
|
|
|
|
|
//curl_setopt($ch, CURLOPT_VERBOSE, 1);
|
|
//curl_setopt($ch, CURLOPT_STDERR, $fp1);
|
|
|
|
curl_setopt($cp, CURLOPT_REFERER, e_REQUEST_HTTP);
|
|
curl_setopt($cp, CURLOPT_HEADER, 0);
|
|
curl_setopt($cp, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
|
|
curl_setopt($cp, CURLOPT_COOKIEFILE, e_SYSTEM.'cookies.txt');*/
|
|
|
|
$buffer = curl_exec($cp);
|
|
|
|
curl_close($cp);
|
|
fclose($fp);
|
|
//fclose($fp1);
|
|
|
|
if($buffer)
|
|
{
|
|
$size = filesize($path.$local_file);
|
|
if($size < 400) $buffer = false;
|
|
}
|
|
|
|
return ($buffer) ? true : false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class e_marketplace_adapter_wsdl extends e_marketplace_adapter_abstract
|
|
{
|
|
/**
|
|
* e107.org WSDL URL
|
|
* @var string
|
|
*/
|
|
protected $serviceUrl = 'https://e107.org/service?wsdl';
|
|
|
|
/**
|
|
* Request method POST || GET
|
|
* @var string
|
|
*/
|
|
public $requestMethod = 'POST';
|
|
|
|
/**
|
|
* Soap client instance
|
|
* @var SoapClient
|
|
*/
|
|
protected $client = null;
|
|
|
|
public function __construct()
|
|
{
|
|
ini_set('soap.wsdl_cache_enabled', 0);
|
|
ini_set('soap.wsdl_cache_ttl', 0);
|
|
|
|
$options = array(
|
|
"trace" => true,
|
|
'exception' => true,
|
|
"uri" => "http://server.soap.e107.inc.com/",
|
|
'cache_wsdl' => WSDL_CACHE_NONE,
|
|
'connection_timeout' => 5,
|
|
);
|
|
|
|
|
|
try
|
|
{
|
|
//libxml_disable_entity_loader(false);
|
|
$this->client = new SoapClient($this->serviceUrl, $options);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
$message = deftrue('LAN_ERROR_CONNECTION', "Unable to connect for updates. Please check firewall and/or internet connection.");
|
|
e107::getMessage()->addInfo($message);
|
|
e107::getMessage()->addDebug($e->getMessage());
|
|
}
|
|
|
|
|
|
|
|
if(function_exists('xdebug_disable'))
|
|
{
|
|
xdebug_disable();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $input
|
|
* @return string
|
|
*/
|
|
public function test($input)
|
|
{
|
|
try
|
|
{
|
|
$res = $this->client->get_echo($input);
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
$res = $e->getMessage();
|
|
}
|
|
return $res;
|
|
|
|
}
|
|
|
|
/**
|
|
* Generic call method
|
|
*/
|
|
public function _call($method, $args, $apply = true)
|
|
{
|
|
$result = array(
|
|
'data' => null,
|
|
//'error'=> array('code' => 0, 'message' => null)
|
|
);
|
|
$ret = null;
|
|
|
|
// authorize on every call, service class decides what to do on every method call
|
|
$auth = new stdClass;
|
|
$auth->authKey = $this->getAuthKey();
|
|
$header = new SoapHeader('https://e107.org/services/auth', 'checkAuthHeader', $auth);
|
|
|
|
if(!is_object($this->client))
|
|
{
|
|
$result['exception'] = array();
|
|
$result['exception']['message'] = "Unable to connect at this time.";
|
|
return $result;
|
|
}
|
|
|
|
try
|
|
{
|
|
|
|
|
|
$this->client->__setSoapHeaders(array($header));
|
|
if(is_array($args) && $apply)
|
|
{
|
|
$ret = call_user_func_array(array($this->client, $method), $args);
|
|
}
|
|
else $ret = $this->client->$method($args);
|
|
|
|
$result = $ret;
|
|
if(isset($ret['exception']))
|
|
{
|
|
$result['exception'] = array();
|
|
$result['exception']['message'] = "API Exception [call::{$method}]: (#".$ret['exception']['code'].") ".$ret['exception']['message'];
|
|
$result['exception']['code'] = 'API_'.$ret['exception']['code'];
|
|
}
|
|
unset($ret);
|
|
}
|
|
catch(SoapFault $e)
|
|
{
|
|
$result['exception']['message'] = "SoapFault Exception [call::{$method}]: (#".$e->faultcode.") ".$e->faultstring;
|
|
$result['exception']['code'] = 'SOAP_'.$e->faultcode;
|
|
if(E107_DEBUG_LEVEL)
|
|
{
|
|
$result['exception']['trace'] = $e->getTraceAsString();
|
|
$result['exception']['message'] .= ". Header fault: ".($e->headerfault ? $e->headerfault : 'n/a');
|
|
}
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
$result['exception']['message'] = "Generic Exception [call::{$method}]: (#".$e->getCode().") ".$e->getMessage();
|
|
$result['exception']['code'] = 'GEN_'.$e->getCode();
|
|
if(E107_DEBUG_LEVEL)
|
|
{
|
|
$result['debug']['trace'] = $e->getTraceAsString();
|
|
}
|
|
}
|
|
if(E107_DEBUG_LEVEL)
|
|
{
|
|
$result['debug']['response'] = $this->client->__getLastResponse();
|
|
$result['debug']['request'] = $this->client->__getLastRequest();
|
|
$result['debug']['request_header'] = $this->client->__getLastRequestHeaders();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Public call method
|
|
*/
|
|
public function call($method, $data, $apply = true)
|
|
{
|
|
return $this->_call($method, $data, $apply);
|
|
}
|
|
|
|
/**
|
|
* Prepare the result, not needed for WSDL
|
|
* SUBJECT OF REMOVAL
|
|
*/
|
|
public function fetch($method, &$result)
|
|
{
|
|
if(isset($result['error']))
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
switch ($method)
|
|
{
|
|
case 'getList':
|
|
break;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->client = null;
|
|
//echo "SOAP Client destroyed", PHP_EOL;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class e_marketplace_adapter_xmlrpc extends e_marketplace_adapter_abstract
|
|
{
|
|
/**
|
|
* e107.org XML-rpc service
|
|
* @var xmlClass
|
|
*/
|
|
protected $serviceUrl = 'https://e107.org/xservice';
|
|
|
|
/**
|
|
* Request method POST || GET
|
|
* @var string
|
|
*/
|
|
public $requestMethod = 'GET';
|
|
|
|
protected $_forceArray = array();
|
|
protected $_forceNumericalArray = array();
|
|
|
|
public function __construct()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @param $input
|
|
* @return void
|
|
*/
|
|
public function test($input)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* @param $method
|
|
* @param $data
|
|
* @param $apply
|
|
* @return array|string
|
|
*/
|
|
public function call($method, $data, $apply = true)
|
|
{
|
|
$client = $this->client();
|
|
|
|
// settings based on current method
|
|
$this->prepareClient($method, $client);
|
|
|
|
// authorization data
|
|
// $data['auth'] = $this->getAuthKey();
|
|
$data['action'] = $method;
|
|
|
|
foreach($data['params'] as $k=>$v)
|
|
{
|
|
$data[$k] = $v;
|
|
}
|
|
unset($data['params']);
|
|
|
|
|
|
// build the request query
|
|
$qry = str_replace(array('s%5B', '%5D'), array('[', ']'), http_build_query($data, null, '&'));
|
|
$url = $this->serviceUrl.'?'.$qry;
|
|
$result = array();
|
|
|
|
// call it
|
|
try
|
|
{
|
|
$xmlString = $client->loadXMLfile($url,false);
|
|
$xml = new SimpleXMLIterator($xmlString);
|
|
//$result = $client->loadXMLfile($url, 'advanced');
|
|
$result = $this->fetch($method, $xml);
|
|
if(isset($result['exception']))
|
|
{
|
|
$exception = $result['exception'];
|
|
$result['exception'] = array();
|
|
$result['exception']['message'] = "API Exception [call::{$method}]: (#".$exception['code'].") ".$exception['message'];
|
|
$result['exception']['code'] = 'API_'.$exception['code'];
|
|
}
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
$result['exception']['message'] = "Generic Exception [call::{$method}]: (#".$e->getCode().") ".$e->getMessage();
|
|
$result['exception']['code'] = 'GEN_'.$e->getCode();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param $method
|
|
* @param $result
|
|
* @return array|string
|
|
*/
|
|
public function fetch($method, &$result)
|
|
{
|
|
$ret = $this->parse($result);
|
|
$this->fetchParams($ret);
|
|
|
|
switch ($method)
|
|
{
|
|
// normalize
|
|
case 'getList':
|
|
$ret['data'] = $ret['data']['item'];
|
|
break;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* New experimental XML parser, will be moved to XML handlers soon
|
|
* XXX replace xmlClass::xml2array() after this one passes all tests
|
|
* @param SimpleXmlIterator $xml
|
|
* @param string $parentName parent node name - used currently for debug only
|
|
* @return array|string
|
|
*/
|
|
public function parse($xml, $parentName = null)
|
|
{
|
|
$ret = array();
|
|
$tags = array_keys(get_object_vars($xml));
|
|
$count = $xml->count();
|
|
$tcount = count($tags);
|
|
|
|
if($count === 0)
|
|
{
|
|
$attr = (array) $xml->attributes();
|
|
if(!empty($attr))
|
|
{
|
|
$ret['@attributes'] = $attr['@attributes'];
|
|
$ret['@value'] = (string) $xml;
|
|
$ret['@value'] = trim($ret['@value']);
|
|
}
|
|
else
|
|
{
|
|
$ret = (string) $xml;
|
|
$ret = trim($ret);
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* <key>
|
|
* <value />
|
|
* <value />
|
|
* </key>
|
|
*/
|
|
if($tcount === 1 && $count > 1)
|
|
{
|
|
foreach($xml as $name => $node)
|
|
{
|
|
$_res = $this->parse($node, $name);
|
|
if(is_string($_res))
|
|
{
|
|
$_res = trim($_res);
|
|
}
|
|
|
|
$ret[$name][] = $this->parse($node, $name);
|
|
}
|
|
}
|
|
// default
|
|
else
|
|
{
|
|
foreach($xml as $name => $node)
|
|
{
|
|
if(in_array($name, $this->_forceArray))
|
|
{
|
|
$_res = $this->parse($node, $name);
|
|
if(is_string($_res))
|
|
{
|
|
$_res = trim($_res);
|
|
}
|
|
|
|
if(empty($_res))
|
|
{
|
|
$ret[$name] = array();
|
|
}
|
|
elseif(is_string($_res)) // empty
|
|
{
|
|
$ret[$name][] = $_res;
|
|
} // string
|
|
else
|
|
{
|
|
if(in_array($name, $this->_forceNumericalArray))
|
|
{
|
|
$ret[$name][] = $_res;
|
|
} //array - controlled force numerical array
|
|
else
|
|
{
|
|
$ret[$name] = $_res;
|
|
} //array, no force
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$ret[$name] = $this->parse($node, $name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
$attr = (array) $xml->attributes();
|
|
if(!empty($attr))
|
|
{
|
|
$ret['@attributes'] = $attr['@attributes'];
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Normalize parameters/attributes
|
|
* @param array $result parsed to array XML response data
|
|
*/
|
|
public function fetchParams(&$result)
|
|
{
|
|
foreach ($result as $tag => $data)
|
|
{
|
|
if($tag === 'params')
|
|
{
|
|
foreach ($data['param'] as $i => $param)
|
|
{
|
|
$result[$tag][$param['@attributes']['name']] = $param['@value'];
|
|
unset($result[$tag]['param'][$i]);
|
|
}
|
|
unset($result[$tag]['param']);
|
|
}
|
|
elseif($tag === 'exception')
|
|
{
|
|
$result['exception'] = array('code' => (int) $result['exception']['@attributes']['code'], 'message' => $result['exception']['@value']);
|
|
//unset($result['exception']);
|
|
}
|
|
elseif($tag === '@attributes')
|
|
{
|
|
$result['params'] = $result['@attributes'];
|
|
unset($result['@attributes']);
|
|
}
|
|
elseif(is_array($data))
|
|
{
|
|
$this->fetchParams($result[$tag]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $method
|
|
* @param xmlClass $client
|
|
*/
|
|
public function prepareClient($method, &$client)
|
|
{
|
|
switch ($method)
|
|
{
|
|
case 'getList':
|
|
$this->_forceArray = array('item', 'screenshots', 'image', 'data');
|
|
$this->_forceNumericalArray = array('item', 'image');
|
|
//$client->setOptArrayTags('item,screenshots,image')
|
|
// ->setOptStringTags('icon,folder,version,author,authorURL,date,compatibility,url,thumbnail,featured,livedemo,price,name,description,category,image');
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return xmlClass
|
|
*/
|
|
public function client()
|
|
{
|
|
return e107::getXml(false);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class eAuth
|
|
{
|
|
|
|
/**
|
|
* e107.org manage client credentials (Consumer Key and Secret) URL
|
|
* @var string
|
|
*/
|
|
protected $eauthConsumerUrl = 'https://e107.org/eauth/client';
|
|
|
|
/**
|
|
* URL used to make temporary credential request (Request Token and Secret) to e107.org before the authorization phase
|
|
* @var string
|
|
*/
|
|
protected $eauthRequestUrl = 'https://e107.org/eauth/initialize';
|
|
|
|
/**
|
|
* URL used to redirect and authorize the resource owner (user) on e107.org using temporary (request) token
|
|
* @var string
|
|
*/
|
|
protected $eauthAuthorizeUrl = 'https://e107.org/eauth/authorize';
|
|
|
|
/**
|
|
* URL used to obtain token credentials (Access Token and Secret) from e107.org using temporary (request) token
|
|
* @var string
|
|
*/
|
|
protected $eauthAccessUrl = 'https://e107.org/eauth/token';
|
|
|
|
/**
|
|
* Public client key (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthConsumerKey = null;
|
|
|
|
/**
|
|
* Client shared secret (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthConsumerSecret = null;
|
|
|
|
/**
|
|
* Public temporary request token (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthRequestKey = null;
|
|
|
|
/**
|
|
* Temporary request shared secret (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthRequestSecret = null;
|
|
|
|
/**
|
|
* Public access token (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthAccessToken = null;
|
|
|
|
/**
|
|
* Access shared secret (generated and obtained from e107.org)
|
|
* @var string
|
|
*/
|
|
public $eauthAccessSecret = null;
|
|
|
|
/**
|
|
* Request method POST || GET
|
|
* @var string
|
|
*/
|
|
public $requestMethod = null;
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isClient()
|
|
{
|
|
$this->loadSysCredentials();
|
|
return (!empty($this->eauthConsumerKey) && !empty($this->eauthConsumerSecret));
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isInitialized()
|
|
{
|
|
$this->loadSysCredentials();
|
|
return ($this->isClient() && !empty($this->eauthRequestKey) && !empty($this->eauthRequestSecret));
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function hasAccess()
|
|
{
|
|
$this->loadSysCredentials();
|
|
return ($this->isClient() && !empty($this->eauthAccessToken) && !empty($this->eauthAccessSecret));
|
|
}
|
|
|
|
/**
|
|
* @param $method
|
|
* @param $args
|
|
* @param $toObject
|
|
* @return array|stdClass
|
|
*/
|
|
public function serviceAuthData($method, $args, $toObject = true)
|
|
{
|
|
// The client has previously registered with the server and obtained the client identifier dpf43f3p2l4k3l03 and client secret kd94hf93k423kf44.
|
|
// It has executed the eAuth workflow and obtained an access token nnch734d00sl2jdk and token secret pfkkdhi9sl3r4s00
|
|
|
|
$date = gmdate('Y-m-d H:i:s');
|
|
$timestamp = $this->gmtTime($date);
|
|
$nonce = $this->nonce($timestamp); // create nonce
|
|
|
|
$cryptMethod = $this->cryptMethod();
|
|
$authData = array(
|
|
'eauth_consumer_key' => $this->eauthConsumerKey, // (Client Identifier) Application key
|
|
'eauth_token' => $this->eauthAccessToken, // Access Token
|
|
'eauth_nonce' => $nonce,//'kllo9940pd9333jh' 'nonce' (number used once) string
|
|
'eauth_timestamp' => $timestamp, // timestamp
|
|
'eauth_signature_method'=> $cryptMethod, // encryption method
|
|
'eauth_version' => '1.0', // signature method
|
|
);
|
|
|
|
// current request parameters
|
|
$args['action'] = $method;
|
|
|
|
// signature data for building the signature
|
|
$signatureData = $authData;
|
|
|
|
// add request parameters to the signature array
|
|
$signatureData['eauth_request_params'] = $args;
|
|
|
|
// sort all
|
|
self::array_kmultisort($signatureData);
|
|
|
|
// signature base string
|
|
$signatureBaseString = $this->requestMethod.'&'.rawurlencode($this->serviceUrl).'&'.http_build_query($signatureData, false, '&');
|
|
$secretKey = rawurlencode($this->eauthConsumerSecret).'&'.rawurlencode($this->eauthAccessSecret);
|
|
|
|
// crypt it
|
|
$signature = $this->crypt($signatureBaseString, $secretKey);
|
|
|
|
//encode it
|
|
$authData['eauth_signature'] = base64_encode($signature);
|
|
if($toObject) return self::toObject($authData);
|
|
|
|
return $authData;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $array
|
|
* @return stdClass
|
|
*/
|
|
public static function toObject($array)
|
|
{
|
|
$obj = new stdClass;
|
|
foreach ($array as $key => $value)
|
|
{
|
|
$obj->$key = $value;
|
|
}
|
|
return $obj;
|
|
}
|
|
|
|
/**
|
|
* Load credentials stored in a system file
|
|
* @param boolean $force
|
|
* @return eAuth
|
|
*/
|
|
public function loadSysCredentials($force = false)
|
|
{
|
|
if($force || null === $this->eauthConsumerKey)
|
|
{
|
|
$data = e107::getArrayStorage()->load('eauth');
|
|
if(empty($data)) $data = array();
|
|
$this->eauthConsumerKey = varset($data['consumer_key'], '');
|
|
$this->eauthConsumerSecret = varset($data['consumer_secret'], '');
|
|
$this->eauthAccessToken = varset($data['access_token'], '');
|
|
$this->eauthAccessSecret = varset($data['access_secret'], '');
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param $credentials
|
|
* @return bool
|
|
*/
|
|
public function storeSysCredentials($credentials = null)
|
|
{
|
|
if(null === $credentials)
|
|
{
|
|
$credentials = array(
|
|
'consumer_key' => $this->eauthConsumerKey,
|
|
'consumer_secret' => $this->eauthConsumerSecret,
|
|
'access_token' => $this->eauthAccessToken,
|
|
'access_secret' => $this->eauthAccessSecret,
|
|
);
|
|
}
|
|
if(!is_array($credentials)) return false;
|
|
|
|
foreach ($credentials as $key => $value)
|
|
{
|
|
switch ($key)
|
|
{
|
|
case 'consumer_key':
|
|
case 'consumer_secret':
|
|
case 'access_token':
|
|
case 'access_secret':
|
|
// OK
|
|
break;
|
|
|
|
default:
|
|
unset($credentials[$key]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return e107::getArrayStorage()->store($credentials, 'eauth');
|
|
}
|
|
|
|
/**
|
|
* Retrieve available system credentials or credential value
|
|
* @param string $key [optional]
|
|
* return mixed array of all credentials or string credential value
|
|
*/
|
|
public function getCredentials($key = null)
|
|
{
|
|
$this->loadSysCredentials();
|
|
|
|
$credentials = array(
|
|
'consumer_key' => $this->eauthConsumerKey,
|
|
'consumer_secret' => $this->eauthConsumerSecret,
|
|
'access_token' => $this->eauthAccessToken,
|
|
'access_secret' => $this->eauthAccessSecret,
|
|
);
|
|
if(null !== $key) return varset($credentials[$key], null);
|
|
return $credentials;
|
|
}
|
|
|
|
/**
|
|
* @param $params
|
|
* @return string
|
|
* @throws Exception
|
|
*/
|
|
public function toAuthHeader($params)
|
|
{
|
|
$first = true;
|
|
$realm = isset($params['realm']) ? $params['realm'] : null;
|
|
if($realm)
|
|
{
|
|
$out = 'Authorization: eAuth realm="'.rawurlencode($realm).'"';
|
|
$first = false;
|
|
}
|
|
else
|
|
$out = 'Authorization: eAuth';
|
|
|
|
$total = array();
|
|
foreach($params as $k => $v)
|
|
{
|
|
if(strpos($k, "eauth") !== 0) continue;
|
|
if(is_array($v))
|
|
{
|
|
throw new Exception('Arrays not supported in headers', 200);
|
|
}
|
|
$out .= ($first) ? ' ' : ',';
|
|
$out .= rawurlencode($k).'="'.rawurlencode($v).'"';
|
|
$first = false;
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function cryptMethod()
|
|
{
|
|
return function_exists('hash_hmac') ? 'HMAC-SHA1' : 'SHA1';
|
|
}
|
|
|
|
/**
|
|
* @param $bits
|
|
* @return string
|
|
*/
|
|
function random($bits = 256)
|
|
{
|
|
$bytes = ceil($bits / 8);
|
|
$ret = '';
|
|
for ($i = 0; $i < $bytes; $i++)
|
|
{
|
|
$ret .= chr(mt_rand(0, 255));
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @param $string
|
|
* @param $secretKey
|
|
* @return false|string
|
|
*/
|
|
public function crypt($string, $secretKey)
|
|
{
|
|
$cMethod = $this->cryptMethod();
|
|
// Append secret if it's sha1
|
|
if($cMethod == 'SHA1')
|
|
{
|
|
return sha1($string.$secretKey);
|
|
}
|
|
// use secret key if HMAC-SHA1
|
|
return hash_hmac('sha1', $string, $secretKey);
|
|
}
|
|
|
|
/**
|
|
* @param $timestamp
|
|
* @return false|string
|
|
*/
|
|
public function nonce($timestamp)
|
|
{
|
|
return $this->crypt($this->random().$timestamp, $this->eauthAccessSecret.$this->eauthConsumerSecret);
|
|
}
|
|
|
|
/**
|
|
* @param $string
|
|
* @return false|int
|
|
*/
|
|
public function gmtTime($string)
|
|
{
|
|
$ret = false;
|
|
// mask - Y-m-d H:i:s
|
|
if(preg_match('#(.*?)-(.*?)-(.*?) (.*?):(.*?):(.*?)$#', $string, $matches))
|
|
{
|
|
$ret = gmmktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1]);
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* @param $array
|
|
* @param $order
|
|
* @return void
|
|
*/
|
|
public static function array_kmultisort(&$array, $order = 'asc')
|
|
{
|
|
$func = $order == 'asc' ? 'ksort' : 'krsort';
|
|
$func($array);
|
|
foreach ($array as $key => $value)
|
|
{
|
|
if(is_array($value))
|
|
{
|
|
self::array_kmultisort($value, $order);
|
|
$array[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|