This commit is contained in:
2020-11-23 23:10:34 +01:00
commit 1af0dae049
61 changed files with 30015 additions and 0 deletions

111
lib/web/geo.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web;
//! Geo plug-in
class Geo extends \Prefab {
/**
* Return information about specified Unix time zone
* @return array
* @param $zone string
**/
function tzinfo($zone) {
$ref=new \DateTimeZone($zone);
$loc=$ref->getLocation();
$trn=$ref->getTransitions($now=time(),$now);
$out=[
'offset'=>$ref->
getOffset(new \DateTime('now',new \DateTimeZone('UTC')))/3600,
'country'=>$loc['country_code'],
'latitude'=>$loc['latitude'],
'longitude'=>$loc['longitude'],
'dst'=>$trn[0]['isdst']
];
unset($ref);
return $out;
}
/**
* Return geolocation data based on specified/auto-detected IP address
* @return array|FALSE
* @param $ip string
**/
function location($ip=NULL) {
$fw=\Base::instance();
$web=\Web::instance();
if (!$ip)
$ip=$fw->IP;
$public=filter_var($ip,FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|
FILTER_FLAG_NO_RES_RANGE|FILTER_FLAG_NO_PRIV_RANGE);
if (function_exists('geoip_db_avail') &&
geoip_db_avail(GEOIP_CITY_EDITION_REV1) &&
$out=@geoip_record_by_name($ip)) {
$out['request']=$ip;
$out['region_code']=$out['region'];
$out['region_name']='';
if (!empty($out['country_code']) && !empty($out['region']))
$out['region_name']=geoip_region_name_by_code(
$out['country_code'],$out['region']
);
unset($out['country_code3'],$out['region'],$out['postal_code']);
return $out;
}
if (($req=$web->request('http://www.geoplugin.net/json.gp'.
($public?('?ip='.$ip):''))) &&
$data=json_decode($req['body'],TRUE)) {
$out=[];
foreach ($data as $key=>$val)
if (!strpos($key,'currency') && $key!=='geoplugin_status'
&& $key!=='geoplugin_region')
$out[$fw->snakecase(substr($key, 10))]=$val;
return $out;
}
return FALSE;
}
/**
* Return weather data based on specified latitude/longitude
* @return array|FALSE
* @param $latitude float
* @param $longitude float
* @param $key string
**/
function weather($latitude,$longitude,$key) {
$fw=\Base::instance();
$web=\Web::instance();
$query=[
'lat'=>$latitude,
'lon'=>$longitude,
'APPID'=>$key,
'units'=>'metric'
];
return ($req=$web->request(
'http://api.openweathermap.org/data/2.5/weather?'.
http_build_query($query)))?
json_decode($req['body'],TRUE):
FALSE;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web\Google;
//! Google ReCAPTCHA v2 plug-in
class Recaptcha {
const
//! API URL
URL_Recaptcha='https://www.google.com/recaptcha/api/siteverify';
/**
* Verify reCAPTCHA response
* @param string $secret
* @param string $response
* @return bool
**/
static function verify($secret,$response=NULL) {
$fw=\Base::instance();
if (!isset($response))
$response=$fw->{'POST.g-recaptcha-response'};
if (!$response)
return FALSE;
$web=\Web::instance();
$out=$web->request(self::URL_Recaptcha,[
'method'=>'POST',
'content'=>http_build_query([
'secret'=>$secret,
'response'=>$response,
'remoteip'=>$fw->IP
]),
]);
return isset($out['body']) &&
($json=json_decode($out['body'],TRUE)) &&
isset($json['success']) && $json['success'];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web\Google;
//! Google Static Maps API v2 plug-in
class StaticMap {
const
//! API URL
URL_Static='http://maps.googleapis.com/maps/api/staticmap';
protected
//! Query arguments
$query=array();
/**
* Specify API key-value pair via magic call
* @return object
* @param $func string
* @param $args array
**/
function __call($func,array $args) {
$this->query[]=array($func,$args[0]);
return $this;
}
/**
* Generate map
* @return string
**/
function dump() {
$fw=\Base::instance();
$web=\Web::instance();
$out='';
return ($req=$web->request(
self::URL_Static.'?'.array_reduce(
$this->query,
function($out,$item) {
return ($out.=($out?'&':'').
urlencode($item[0]).'='.urlencode($item[1]));
}
))) && $req['body']?$req['body']:FALSE;
}
}

163
lib/web/oauth2.php Normal file
View File

@@ -0,0 +1,163 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web;
//! Lightweight OAuth2 client
class OAuth2 extends \Magic {
protected
//! Scopes and claims
$args=[],
//! Encoding
$enc_type = PHP_QUERY_RFC1738;
/**
* Return OAuth2 authentication URI
* @return string
* @param $endpoint string
* @param $query bool
**/
function uri($endpoint,$query=TRUE) {
return $endpoint.($query?('?'.
http_build_query($this->args,null,'&',$this->enc_type)):'');
}
/**
* Send request to API/token endpoint
* @return string|FALSE
* @param $uri string
* @param $method string
* @param $token array
**/
function request($uri,$method,$token=NULL) {
$web=\Web::instance();
$options=[
'method'=>$method,
'content'=>http_build_query($this->args,null,'&',$this->enc_type),
'header'=>['Accept: application/json']
];
if ($token)
array_push($options['header'],'Authorization: Bearer '.$token);
elseif ($method=='POST' && isset($this->args['client_id']))
array_push($options['header'],'Authorization: Basic '.
base64_encode(
$this->args['client_id'].':'.
$this->args['client_secret']
)
);
$response=$web->request($uri,$options);
if ($response['error'])
user_error($response['error'],E_USER_ERROR);
if (isset($response['body'])) {
if (preg_grep('/^Content-Type:.*application\/json/i',
$response['headers'])) {
$token=json_decode($response['body'],TRUE);
if (isset($token['error_description']))
user_error($token['error_description'],E_USER_ERROR);
if (isset($token['error']))
user_error($token['error'],E_USER_ERROR);
return $token;
}
else
return $response['body'];
}
return FALSE;
}
/**
* Parse JSON Web token
* @return array
* @param $token string
**/
function jwt($token) {
return json_decode(
base64_decode(
str_replace(['-','_'],['+','/'],explode('.',$token)[1])
),
TRUE
);
}
/**
* change default url encoding type, i.E. PHP_QUERY_RFC3986
* @param $type
*/
function setEncoding($type) {
$this->enc_type = $type;
}
/**
* URL-safe base64 encoding
* @return array
* @param $data string
**/
function b64url($data) {
return trim(strtr(base64_encode($data),'+/','-_'),'=');
}
/**
* Return TRUE if scope/claim exists
* @return bool
* @param $key string
**/
function exists($key) {
return isset($this->args[$key]);
}
/**
* Bind value to scope/claim
* @return string
* @param $key string
* @param $val string
**/
function set($key,$val) {
return $this->args[$key]=$val;
}
/**
* Return value of scope/claim
* @return mixed
* @param $key string
**/
function &get($key) {
if (isset($this->args[$key]))
$val=&$this->args[$key];
else
$val=NULL;
return $val;
}
/**
* Remove scope/claim
* @return NULL
* @param $key string
**/
function clear($key=NULL) {
if ($key)
unset($this->args[$key]);
else
$this->args=[];
}
}

248
lib/web/openid.php Normal file
View File

@@ -0,0 +1,248 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web;
//! OpenID consumer
class OpenID extends \Magic {
protected
//! OpenID provider endpoint URL
$url,
//! HTTP request parameters
$args=[];
/**
* Determine OpenID provider
* @return string|FALSE
* @param $proxy string
**/
protected function discover($proxy) {
// Normalize
if (!preg_match('/https?:\/\//i',$this->args['endpoint']))
$this->args['endpoint']='http://'.$this->args['endpoint'];
$url=parse_url($this->args['endpoint']);
// Remove fragment; reconnect parts
$this->args['endpoint']=$url['scheme'].'://'.
(isset($url['user'])?
($url['user'].
(isset($url['pass'])?(':'.$url['pass']):'').'@'):'').
strtolower($url['host']).(isset($url['path'])?$url['path']:'/').
(isset($url['query'])?('?'.$url['query']):'');
// HTML-based discovery of OpenID provider
$req=\Web::instance()->
request($this->args['endpoint'],['proxy'=>$proxy]);
if (!$req)
return FALSE;
$type=array_values(preg_grep('/Content-Type:/',$req['headers']));
if ($type &&
preg_match('/application\/xrds\+xml|text\/xml/',$type[0]) &&
($sxml=simplexml_load_string($req['body'])) &&
($xrds=json_decode(json_encode($sxml),TRUE)) &&
isset($xrds['XRD'])) {
// XRDS document
$svc=$xrds['XRD']['Service'];
if (isset($svc[0]))
$svc=$svc[0];
$svc_type=is_array($svc['Type'])?$svc['Type']:array($svc['Type']);
if (preg_grep('/http:\/\/specs\.openid\.net\/auth\/2.0\/'.
'(?:server|signon)/',$svc_type)) {
$this->args['provider']=$svc['URI'];
if (isset($svc['LocalID']))
$this->args['localidentity']=$svc['LocalID'];
elseif (isset($svc['CanonicalID']))
$this->args['localidentity']=$svc['CanonicalID'];
}
$this->args['server']=$svc['URI'];
if (isset($svc['Delegate']))
$this->args['delegate']=$svc['Delegate'];
}
else {
$len=strlen($req['body']);
$ptr=0;
// Parse document
while ($ptr<$len)
if (preg_match(
'/^<link\b((?:\h+\w+\h*=\h*'.
'(?:"(?:.+?)"|\'(?:.+?)\'))*)\h*\/?>/is',
substr($req['body'],$ptr),$parts)) {
if ($parts[1] &&
// Process attributes
preg_match_all('/\b(rel|href)\h*=\h*'.
'(?:"(.+?)"|\'(.+?)\')/s',$parts[1],$attr,
PREG_SET_ORDER)) {
$node=[];
foreach ($attr as $kv)
$node[$kv[1]]=isset($kv[2])?$kv[2]:$kv[3];
if (isset($node['rel']) &&
preg_match('/openid2?\.(\w+)/',
$node['rel'],$var) &&
isset($node['href']))
$this->args[$var[1]]=$node['href'];
}
$ptr+=strlen($parts[0]);
}
else
$ptr++;
}
// Get OpenID provider's endpoint URL
if (isset($this->args['provider'])) {
// OpenID 2.0
$this->args['ns']='http://specs.openid.net/auth/2.0';
if (isset($this->args['localidentity']))
$this->args['identity']=$this->args['localidentity'];
if (isset($this->args['trust_root']))
$this->args['realm']=$this->args['trust_root'];
}
elseif (isset($this->args['server'])) {
// OpenID 1.1
$this->args['ns']='http://openid.net/signon/1.1';
if (isset($this->args['delegate']))
$this->args['identity']=$this->args['delegate'];
}
if (isset($this->args['provider'])) {
// OpenID 2.0
if (empty($this->args['claimed_id']))
$this->args['claimed_id']=$this->args['identity'];
return $this->args['provider'];
}
elseif (isset($this->args['server']))
// OpenID 1.1
return $this->args['server'];
else
return FALSE;
}
/**
* Initiate OpenID authentication sequence; Return FALSE on failure
* or redirect to OpenID provider URL
* @return bool
* @param $proxy string
* @param $attr array
* @param $reqd string|array
**/
function auth($proxy=NULL,$attr=[],array $reqd=NULL) {
$fw=\Base::instance();
$root=$fw->SCHEME.'://'.$fw->HOST;
if (empty($this->args['trust_root']))
$this->args['trust_root']=$root.$fw->BASE.'/';
if (empty($this->args['return_to']))
$this->args['return_to']=$root.$_SERVER['REQUEST_URI'];
$this->args['mode']='checkid_setup';
if ($this->url=$this->discover($proxy)) {
if ($attr) {
$this->args['ns.ax']='http://openid.net/srv/ax/1.0';
$this->args['ax.mode']='fetch_request';
foreach ($attr as $key=>$val)
$this->args['ax.type.'.$key]=$val;
$this->args['ax.required']=is_string($reqd)?
$reqd:implode(',',$reqd);
}
$var=[];
foreach ($this->args as $key=>$val)
$var['openid.'.$key]=$val;
$fw->reroute($this->url.'?'.http_build_query($var));
}
return FALSE;
}
/**
* Return TRUE if OpenID verification was successful
* @return bool
* @param $proxy string
**/
function verified($proxy=NULL) {
preg_match_all('/(?<=^|&)openid\.([^=]+)=([^&]+)/',
$_SERVER['QUERY_STRING'],$matches,PREG_SET_ORDER);
foreach ($matches as $match)
$this->args[$match[1]]=urldecode($match[2]);
if (isset($this->args['mode']) &&
$this->args['mode']!='error' &&
$this->url=$this->discover($proxy)) {
$this->args['mode']='check_authentication';
$var=[];
foreach ($this->args as $key=>$val)
$var['openid.'.$key]=$val;
$req=\Web::instance()->request(
$this->url,
[
'method'=>'POST',
'content'=>http_build_query($var),
'proxy'=>$proxy
]
);
return (bool)preg_match('/is_valid:true/i',$req['body']);
}
return FALSE;
}
/**
* Return OpenID response fields
* @return array
**/
function response() {
return $this->args;
}
/**
* Return TRUE if OpenID request parameter exists
* @return bool
* @param $key string
**/
function exists($key) {
return isset($this->args[$key]);
}
/**
* Bind value to OpenID request parameter
* @return string
* @param $key string
* @param $val string
**/
function set($key,$val) {
return $this->args[$key]=$val;
}
/**
* Return value of OpenID request parameter
* @return mixed
* @param $key string
**/
function &get($key) {
if (isset($this->args[$key]))
$val=&$this->args[$key];
else
$val=NULL;
return $val;
}
/**
* Remove OpenID request parameter
* @return NULL
* @param $key
**/
function clear($key) {
unset($this->args[$key]);
}
}

176
lib/web/pingback.php Normal file
View File

@@ -0,0 +1,176 @@
<?php
/*
Copyright (c) 2009-2019 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
This is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or later.
Fat-Free Framework is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Web;
//! Pingback 1.0 protocol (client and server) implementation
class Pingback extends \Prefab {
protected
//! Transaction history
$log;
/**
* Return TRUE if URL points to a pingback-enabled resource
* @return bool
* @param $url
**/
protected function enabled($url) {
$web=\Web::instance();
$req=$web->request($url);
$found=FALSE;
if ($req['body']) {
// Look for pingback header
foreach ($req['headers'] as $header)
if (preg_match('/^X-Pingback:\h*(.+)/',$header,$href)) {
$found=$href[1];
break;
}
if (!$found &&
// Scan page for pingback link tag
preg_match('/<link\h+(.+?)\h*\/?>/i',$req['body'],$parts) &&
preg_match('/rel\h*=\h*"pingback"/i',$parts[1]) &&
preg_match('/href\h*=\h*"\h*(.+?)\h*"/i',$parts[1],$href))
$found=$href[1];
}
return $found;
}
/**
* Load local page contents, parse HTML anchor tags, find permalinks,
* and send XML-RPC calls to corresponding pingback servers
* @return NULL
* @param $source string
**/
function inspect($source) {
$fw=\Base::instance();
$web=\Web::instance();
$parts=parse_url($source);
if (empty($parts['scheme']) || empty($parts['host']) ||
$parts['host']==$fw->HOST) {
$req=$web->request($source);
$doc=new \DOMDocument('1.0',$fw->ENCODING);
$doc->stricterrorchecking=FALSE;
$doc->recover=TRUE;
if (@$doc->loadhtml($req['body'])) {
// Parse anchor tags
$links=$doc->getelementsbytagname('a');
foreach ($links as $link) {
$permalink=$link->getattribute('href');
// Find pingback-enabled resources
if ($permalink && $found=$this->enabled($permalink)) {
$req=$web->request($found,
[
'method'=>'POST',
'header'=>'Content-Type: application/xml',
'content'=>xmlrpc_encode_request(
'pingback.ping',
[$source,$permalink],
['encoding'=>$fw->ENCODING]
)
]
);
if ($req['body'])
$this->log.=date('r').' '.
$permalink.' [permalink:'.$found.']'.PHP_EOL.
$req['body'].PHP_EOL;
}
}
}
unset($doc);
}
}
/**
* Receive ping, check if local page is pingback-enabled, verify
* source contents, and return XML-RPC response
* @return string
* @param $func callback
* @param $path string
**/
function listen($func,$path=NULL) {
$fw=\Base::instance();
if (PHP_SAPI!='cli') {
header('X-Powered-By: '.$fw->PACKAGE);
header('Content-Type: application/xml; '.
'charset='.$charset=$fw->ENCODING);
}
if (!$path)
$path=$fw->BASE;
$web=\Web::instance();
$args=xmlrpc_decode_request($fw->BODY,$method,$charset);
$options=['encoding'=>$charset];
if ($method=='pingback.ping' && isset($args[0],$args[1])) {
list($source,$permalink)=$args;
$doc=new \DOMDocument('1.0',$fw->ENCODING);
// Check local page if pingback-enabled
$parts=parse_url($permalink);
if ((empty($parts['scheme']) ||
$parts['host']==$fw->HOST) &&
preg_match('/^'.preg_quote($path,'/').'/'.
($fw->CASELESS?'i':''),$parts['path']) &&
$this->enabled($permalink)) {
// Check source
$parts=parse_url($source);
if ((empty($parts['scheme']) ||
$parts['host']==$fw->HOST) &&
($req=$web->request($source)) &&
$doc->loadhtml($req['body'])) {
$links=$doc->getelementsbytagname('a');
foreach ($links as $link) {
if ($link->getattribute('href')==$permalink) {
call_user_func_array($func,[$source,$req['body']]);
// Success
die(xmlrpc_encode_request(NULL,$source,$options));
}
}
// No link to local page
die(xmlrpc_encode_request(NULL,0x11,$options));
}
// Source failure
die(xmlrpc_encode_request(NULL,0x10,$options));
}
// Doesn't exist (or not pingback-enabled)
die(xmlrpc_encode_request(NULL,0x21,$options));
}
// Access denied
die(xmlrpc_encode_request(NULL,0x31,$options));
}
/**
* Return transaction history
* @return string
**/
function log() {
return $this->log;
}
/**
* Instantiate class
* @return object
**/
function __construct() {
// Suppress errors caused by invalid HTML structures
libxml_use_internal_errors(TRUE);
}
}