| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- <?php
- define('ROOT_PATH', dirname(__FILE__).'/../../');
- class _dzRestful {
- private $apiParam;
- private $postParam;
- public $token;
- public $tokenData;
- private $_appId;
- private $_m;
- const SignTTL = 300;
- const TokenTTL = 3600;
- const TokenSaveTTL = 86400 * 30;
- const AuthTokenTTL = 300;
- const TokenPre = 'rToken_';
- const AuthTokenPre = 'rAuthToken_';
- const AppIdConfPre = 'rApp_';
- const ApiConfPre = 'rApi_';
- const ApiFreqPre = 'rFreq_';
- const ApiFreqTTL = 60;
- const AuthPre = 'rAuth_';
- const AuthTTL = 60;
- const Error = [
- -100 => 'run: script is exception',
- -101 => 'checkSign: param is missing',
- -102 => 'checkSign: appid is invalid',
- -103 => 'checkSign: sign is expire',
- -104 => 'checkSign: sign is invalid',
- -105 => 'checkToken: token is missing',
- -106 => 'checkToken: token is expire',
- -107 => 'checkToken: appid is error',
- -108 => 'getTokenData: token is error',
- -109 => 'getApiParam: api is invalid',
- -110 => 'getAppidPerm: appid is invalid',
- -111 => 'getSecret: appid is invalid',
- -112 => 'parseQuery: api url is empty',
- -113 => 'parseQuery: api url is error',
- -114 => 'initParam: api is invalid',
- -115 => 'apiPermCheck: api is invalid',
- -116 => 'apiFreqCheck: out of frequency',
- -117 => 'scriptCheck: script is empty',
- -118 => 'scriptCheck: script format is error',
- -119 => 'callback: authtoken is invalid',
- ];
- const Developer = false;
- public function __construct($postParam = []) {
- require_once ROOT_PATH.'./source/function/function_core.php';
- $this->postParam = $postParam;
- }
- public function parseQuery() {
- $s = rawurldecode($_SERVER['QUERY_STRING']);
- if(!$s) {
- $this->error(-112);
- }
- if(str_ends_with($s, '=')) {
- $s = substr($s, 0, -1);
- }
- if(!str_starts_with($s, '/')) {
- $s = '/'.$s;
- }
- $e = explode('/', $s);
- $c = count($e);
- if(preg_match('/^v\d+$/', $e[$c - 1])) {
- $api = array_slice($e, 1, -1);
- $ver = $e[$c - 1];
- } else {
- $api = array_slice($e, 1);
- $ver = 'v1';
- }
- if(!$api || !$ver) {
- $this->error(-113);
- }
- return [$api, $ver];
- }
- public function initParam($api, $ver) {
- $apiParams = $this->_getApiParam($api, $ver);
- $key = '/'.implode('/', $api);
- if(empty($apiParams[$key])) {
- $this->error(-114);
- }
- $params = $apiParams[$key];
- $params['perms'] = [$key.'/'.$ver];
- $this->apiParam = $params;
- return $params['script'];
- }
- public function paramDecode($key) {
- if(empty($this->apiParam[$key])) {
- return [];
- }
- if(is_array($this->apiParam[$key])) {
- $v = $this->apiParam[$key];
- } else {
- $_tmp = unserialize($this->apiParam[$key]);
- $v = $_tmp === false ? $this->apiParam[$key] : $_tmp;
- }
- return $this->_replacePostParams($v);
- }
- public function getRequestParam() {
- return !empty($this->postParam['_REQUEST']) ? json_decode($this->postParam['_REQUEST'], true) : [];
- }
- public function getShutdownFunc() {
- $output = !empty($this->apiParam['output']) ? $this->apiParam['output'] : [];
- $rawOutput = !empty($this->apiParam['raw']);
- $shutdownFunc = 'showOutput';
- if($rawOutput) {
- $shutdownFunc = 'rawOutput';
- } elseif($output) {
- $shutdownFunc = 'convertOutput';
- }
- return [$shutdownFunc, $output];
- }
- private function _replacePostParams($v) {
- foreach($v as $_k => $_v) {
- if(is_array($_v)) {
- $v[$_k] = $this->_replacePostParams($_v);
- } elseif(is_string($_v)) {
- if(str_starts_with($_v, '{') && preg_match('/^\{:(\w+):\}$/', $_v, $r)) {
- $v[$_k] = !empty($this->postParam[$r[1]]) ? $this->postParam[$r[1]] : null;
- }
- }
- }
- return $v;
- }
- public function apiPermCheck() {
- if(!empty($this->tokenData['_conf']['apis']) && !array_intersect($this->apiParam['perms'], $this->tokenData['_conf']['apis'])) {
- $this->error(-115);
- }
- }
- public function apiFreqCheck() {
- $api = $this->apiParam['perms'][0];
- if(!empty($this->tokenData['_conf']['freq'][$api])) {
- $key = self::ApiFreqPre.$this->_appId.'_'.$api;
- $v = $this->_memory('get', [$key]);
- if(!$v) {
- $this->_memory('inc', [$key]);
- $this->_memory('expire', [$key, 10]);//self::ApiFreqTTL
- } elseif($v >= $this->tokenData['_conf']['freq'][$api]) {
- $this->error(-116);
- } else {
- $this->_memory('inc', [$key]);
- }
- }
- }
- public function scriptCheck() {
- if(empty($this->apiParam['script'])) {
- $this->error(-117);
- }
- if(!preg_match('/^\w+$/', $this->apiParam['script'])) {
- $this->error(-118);
- }
- return $this->apiParam['script'];
- }
- public function error($code) {
- $this->output([
- 'ret' => $code,
- 'msg' => !empty(self::Error[$code]) ? self::Error[$code] : '',
- ]);
- }
- public function output($value) {
- $this->_setSysVar($return['data'], $value);
- echo json_encode($value);
- exit;
- }
- public function showOutput() {
- $return = ['ret' => 0];
- $s = ob_get_contents();
- ob_end_clean();
- $return['data']['content'] = $s;
- $this->plugin('after', $return['data']);
- $this->output($return);
- }
- public function rawOutput() {
- $newTokenData = $this->updateTokenData();
- if($newTokenData) {
- $ttl = $this->tokenData['refreshExptime'] - time();
- $this->setToken($this->token, $newTokenData, $ttl);
- }
- exit;
- }
- public function convertOutput($output) {
- ob_end_clean();
- $return = ['ret' => 0];
- $tmp = $GLOBALS;
- foreach($output as $k => $v) {
- if(str_contains($k, '/')) {
- $return['data'][$v] = $this->_arrayVar($tmp, $k);
- } else {
- $return['data'][$v] = $this->_singleVar($tmp, $k);
- }
- }
- $this->plugin('after', $return['data']);
- $this->output($return);
- }
- public function plugin($type, &$data) {
- if(empty($this->apiParam['plugin'][$type])) {
- return;
- }
- foreach($this->apiParam['plugin'][$type] as $method => $value) {
- $vars = explode(':', $method);
- if(count($vars) == 2) {
- if(!preg_match('/^\w+$/', $vars[0])) {
- continue;
- }
- require_once ROOT_PATH.'./source/function/function_path.php';
- $f = ROOT_PATH.PLUGIN_ROOT.$vars[0].'/restful.class.php';
- $c = 'restful_'.$vars[0];
- $m = $vars[1];
- } else {
- $f = ROOT_PATH.'./source/class/class_restfulplugin.php';
- $c = 'restfulplugin';
- $m = $method;
- }
- if(!file_exists($f)) {
- continue;
- }
- @require_once $f;
- if(!class_exists($c) || !method_exists($c, $m)) {
- continue;
- }
- call_user_func_array([$c, $m], [&$data, explode(',', $value['param'] ?? '')]);
- }
- }
- public function sessionDecode() {
- $session = unserialize(base64_decode($this->tokenData['_session']));
- if(!empty($this->postParam['_authSign'])) {
- $this->decodeAuthSign($session);
- }
- return $session;
- }
- public function checkSign() {
- if(empty($_SERVER['HTTP_APPID']) ||
- empty($_SERVER['HTTP_SIGN']) ||
- empty($_SERVER['HTTP_NONCE']) ||
- empty($_SERVER['HTTP_T'])) {
- $this->error(-101);
- }
- $this->_getAppidPerm($_SERVER['HTTP_APPID']);
- $secret = $this->_getSecret();
- if(!$secret) {
- $this->error(-102);
- }
- if(time() - $_SERVER['HTTP_T'] > self::SignTTL) {
- $this->error(-103);
- }
- if($_SERVER['HTTP_SIGN'] != base64_encode(hash('sha256', $_SERVER['HTTP_NONCE'].$_SERVER['HTTP_T'].$secret))) {
- $this->error(-104);
- }
- }
- public function checkToken() {
- if(empty($this->_getToken())) {
- $this->error(-105);
- }
- $v = $this->_getTokenData();
- if(time() >= $v['exptime']) {
- $this->error(-106);
- }
- $this->tokenData = $v;
- if(!$v['_appid'] || $v['_appid'] != $_SERVER['HTTP_APPID']) {
- $this->error(-107);
- }
- $this->_getAppidPerm($v['_appid']);
- $this->postParam['formhash'] = !empty($this->tokenData['_formhash']) ? $this->tokenData['_formhash'] : '';
- $this->token = $this->_getToken();
- }
- public function setToken($key, $value, $ttl = self::TokenSaveTTL) {
- $this->_memory('set', [self::TokenPre.$key, serialize($value), $ttl]);
- }
- public function setAuthToken($token, $value) {
- if($this->getAuthToken($token)) {
- return false;
- }
- global $_G;
- $value = authcode(serialize($value), 'ENCODE', md5($_G['config']['security']['authkey']));
- $this->_memory('set', [self::AuthTokenPre.$token, $value, self::AuthTokenTTL]);
- return true;
- }
- public function getAuthToken($token) {
- global $_G;
- $value = $this->_memory('get', [self::AuthTokenPre.$token]);
- return $value ? dunserialize(authcode($value, 'DECODE', md5($_G['config']['security']['authkey']))) : [];
- }
- public function isRefreshToken() {
- return !empty($this->_getToken());
- }
- private function _getToken() {
- return !empty($_SERVER['HTTP_TOKEN']) ? $_SERVER['HTTP_TOKEN'] : (!empty($this->postParam['token']) ? $this->postParam['token'] : '');
- }
- private function _getTokenData() {
- $v = dunserialize($this->_memory('get', [self::TokenPre.$this->_getToken()]));
- if(empty($v)) {
- $this->error(-108);
- }
- return $v;
- }
- public function refreshTokenData() {
- $v = $this->_getTokenData();
- $data['_session'] = $v['_session'];
- $data['_formhash'] = $this->_singleVar($_G, 'formhash');
- $data['_appid'] = $_SERVER['HTTP_APPID'];
- $data['_conf'] = $this->tokenData['_conf'];
- $data['exptime'] = time() + self::TokenTTL;
- $data['refreshExptime'] = time() + self::TokenSaveTTL;
- return serialize($data);
- }
- public function delTokenData() {
- $this->_memory('rm', [self::TokenPre.$this->_getToken()]);
- }
- public function newTokenData() {
- global $_G;
- $data = [];
- $data['_session'] = $this->_sessionEncode($_COOKIE);
- $data['_formhash'] = $this->_singleVar($_G, 'formhash');
- $data['_appid'] = $_SERVER['HTTP_APPID'];
- $data['_conf'] = $this->tokenData['_conf'];
- $data['exptime'] = time() + self::TokenTTL;
- $data['refreshExptime'] = time() + self::TokenSaveTTL;
- return serialize($data);
- }
- private function _getApiParam($api, $ver) {
- if(self::Developer) {
- return $this->_getApiParam_Developer($api, $ver);
- }
- $v = $this->_memory('get', [self::ApiConfPre.'/'.$api[0].'_'.$ver]);
- if(!$v) {
- $this->error(-109);
- }
- return $this->apiParam = dunserialize($v);
- }
- private function _getAppidPerm($appid) {
- $v = $this->_memory('get', [self::AppIdConfPre.$appid]);
- if(!$v) {
- if(!empty($GLOBALS['discuz'])) {
- require_once libfile('function/cache');
- updatecache('restful');
- $v = $this->_memory('get', [self::AppIdConfPre.$appid]);
- if(!$v) {
- $this->error(-110);
- }
- } else {
- $this->error(-110);
- }
- }
- $this->_appId = $appid;
- $v = dunserialize($v);
- $this->tokenData['_conf'] = $v;
- }
- public function updateTokenData() {
- global $_G;
- $data = $this->tokenData;
- $_session = $this->_sessionEncode($_COOKIE);
- if(!empty($this->tokenData['_session']) && $_session == $this->tokenData['_session']) {
- return null;
- }
- $data['_session'] = $_session;
- $data['_formhash'] = $this->_singleVar($_G, 'formhash');
- return serialize($data);
- }
- private function _getSecret() {
- if(empty($this->tokenData['_conf']['secret'])) {
- $this->error(-111);
- }
- return $this->tokenData['_conf']['secret'];
- }
- private function _sessionEncode($v) {
- return base64_encode(serialize($v));
- }
- private function _setSysVar(&$data, &$output = []) {
- global $_G;
- $newTokenData = $this->updateTokenData();
- if(!empty($this->tokenData['refreshExptime']) && $newTokenData) {
- $ttl = $this->tokenData['refreshExptime'] - time();
- $this->setToken($this->token, $newTokenData, $ttl);
- }
- if(!empty($_G['_multi'])) {
- $output['multi'] = $_G['_multi'];
- }
- unset($_G['config'],
- $_G['setting']['siteuniqueid'],
- $_G['setting']['ec_tenpay_opentrans_chnid'],
- $_G['setting']['ec_tenpay_opentrans_key'],
- $_G['setting']['ec_tenpay_bargainor'],
- $_G['setting']['ec_tenpay_key'],
- $_G['setting']['ec_account'],
- $_G['setting']['ec_contract']);
- }
- private function _singleVar(&$var, $k) {
- return $var[$k] ?? null;
- }
- private function _arrayVar(&$var, $k) {
- unset($GLOBALS['_L']['_G']);
- $value = null;
- $sVar = &$var;
- $e = explode('/', $k);
- $count = count($e);
- foreach($e as $i => $_k) {
- if($_k == '*') {
- foreach($sVar as $_k3 => $_v3) {
- $nKey = implode('/', array_slice($e, $i + 1));
- $value[$_k3] = $this->_arrayVar($_v3, $nKey);
- }
- break;
- }
- $isMulti = str_contains($_k, ',');
- if(!isset($sVar[$_k]) && !$isMulti) {
- break;
- }
- if($isMulti) {
- $value = null;
- foreach(explode(',', $_k) as $_k2) {
- $value[$_k2] = $this->_singleVar($sVar, $_k2);
- }
- break;
- } else {
- if($count - 1 == $i) {
- $value = $this->_singleVar($sVar, $_k);
- }
- $sVar = &$sVar[$_k];
- }
- }
- return $value;
- }
- private function _memory($method, $params = []) {
- if($this->_m == null) {
- require_once ROOT_PATH.'./source/class/memory/memory_driver_redis.php';
- $_config = [];
- require ROOT_PATH.'./config/config_global.php';
- $this->_m = new memory_driver_redis();
- if(empty($_config['memory']['redis'])) {
- return null;
- }
- $this->_m->init($_config['memory']['redis']);
- if(!$this->_m->enable) {
- return null;
- }
- $this->_config = $_config;
- }
- if($this->_m == null) {
- return null;
- }
- if(!method_exists($this->_m, $method)) {
- return null;
- }
- return call_user_func_array([$this->_m, $method], $params);
- }
- // for Developer
- private function _getApiParam_Developer($api, $ver) {
- $xml = ROOT_PATH.'./data/discuz_restful.xml';
- require_once ROOT_PATH.'./source/class/class_xml.php';
- $data = file_get_contents($xml);
- $xmldata = xml2array($data);
- foreach($xmldata['Data']['api'] as $ver => $apis) {
- if(!preg_match('/^v(\d+)$/', $ver, $r)) {
- continue;
- }
- $this->_parseApis($apis, $ver, '/');
- }
- if(empty($_ENV['api'][$ver]['/'.$api[0]])) {
- $this->error(-109);
- }
- return $this->apiParam = $_ENV['api'][$ver]['/'.$api[0]];
- }
- private function _parseApis($apis, $ver, $uriPre = '/') {
- if(!is_array($apis)) {
- return;
- }
- foreach($apis as $uri => $api) {
- $k = $uriPre.$uri;
- list(, $baseuri) = explode('/', $k);
- $baseuri = '/'.$baseuri;
- if(!empty($api['script']) && is_string($api['script'])) {
- $_ENV['api'][$ver][$baseuri][$k] = [];
- $_ENV['api'][$ver][$baseuri][$k] = $api;
- } else {
- $this->_parseApis($api, $ver, $k.'/');
- }
- }
- }
- public function getAuthSign() {
- static $authSign = null;
- if($authSign !== null) {
- return $authSign;
- }
- global $_G;
- $this->_memory('set', [self::AuthPre.$_G['cookie']['sid'], serialize([
- 'auth' => $_G['cookie']['auth'],
- 'saltkey' => $_G['cookie']['saltkey'],
- 'sid' => $_G['cookie']['sid']
- ]), self::AuthTTL]);
- return $authSign = authcode($_G['cookie']['sid'], 'ENCODE', md5($_G['config']['security']['authkey']), self::AuthTTL);
- }
- public function decodeAuthSign(&$session) {
- $sid = authcode($this->postParam['_authSign'], 'DECODE', md5($this->_config['security']['authkey']));
- if(!$sid) {
- return;
- }
- $data = $this->_memory('get', [self::AuthPre.$sid]);
- if(!$data) {
- return;
- }
- $cookiepre = $this->_config['cookie']['cookiepre'].substr(md5($this->_config['cookie']['cookiepath'].'|'.$this->_config['cookie']['cookiedomain']), 0, 4).'_';
- $session[$cookiepre.'auth'] = $data['auth'];
- $session[$cookiepre.'saltkey'] = $data['saltkey'];
- $session[$cookiepre.'sid'] = $data['sid'];
- }
- }
|