acf
This commit is contained in:
1
plugins/system/tgeoip/db/index.html
Normal file
1
plugins/system/tgeoip/db/index.html
Normal file
@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title><!-- e4aNe339 -->
|
||||
39
plugins/system/tgeoip/field/lastupdated.php
Normal file
39
plugins/system/tgeoip/field/lastupdated.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
// No direct access to this file
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Form\FormField;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Filesystem\File;
|
||||
|
||||
class JFormFieldTG_LastUpdated extends FormField
|
||||
{
|
||||
/**
|
||||
* Method to render the input field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
$file = JPATH_PLUGINS . '/system/tgeoip/db/GeoLite2-City.mmdb';
|
||||
|
||||
if (!file_exists($file))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return Factory::getDate(@filemtime($file))->format('d M Y H:m');
|
||||
}
|
||||
}
|
||||
108
plugins/system/tgeoip/field/lookup.php
Normal file
108
plugins/system/tgeoip/field/lookup.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
// No direct access to this file
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Form\FormField;
|
||||
use Joomla\CMS\Session\Session;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use NRFramework\User;
|
||||
|
||||
class JFormFieldTG_Lookup extends FormField
|
||||
{
|
||||
/**
|
||||
* GeoIP Class
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $geoIP;
|
||||
|
||||
/**
|
||||
* Method to render the input field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
// JavaScript
|
||||
$ajaxURL = Uri::base() . 'index.php?option=com_ajax&format=raw&plugin=tgeoip&task=get&' . Session::getFormToken() . '=1';
|
||||
|
||||
Factory::getDocument()->addScriptDeclaration('
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.addEventListener("click", function(e) {
|
||||
var btn = e.target.closest(".tGeoIPtest button");
|
||||
if (!btn) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
ip = document.querySelector(".tGeoIPtest input").value;
|
||||
|
||||
if (!ip) {
|
||||
alert("Please enter a valid IP address");
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = new FormData();
|
||||
data.append("ip", ip);
|
||||
|
||||
fetch("' . $ajaxURL . '",
|
||||
{
|
||||
method: "POST",
|
||||
body: data
|
||||
})
|
||||
.then(function(res){ return res.json(); })
|
||||
.then(function(response){
|
||||
if (response) {
|
||||
if (response.continent) {
|
||||
document.querySelector(".tGeoIPtest .continent").innerHTML = response.continent.names.en;
|
||||
}
|
||||
|
||||
if (response.city) {
|
||||
document.querySelector(".tGeoIPtest .city").innerHTML = response.city.names.en;
|
||||
}
|
||||
|
||||
if (response.country) {
|
||||
document.querySelector(".tGeoIPtest .country").innerHTML = response.country.names.en;
|
||||
document.querySelector(".tGeoIPtest .country_code").innerHTML = response.country.iso_code;
|
||||
}
|
||||
|
||||
document.querySelector(".tGeoIPtest .results").style.display = "block";
|
||||
} else {
|
||||
alert("Invalid IP address");
|
||||
document.querySelector(".tGeoIPtest .results").style.display = "none";
|
||||
}
|
||||
})
|
||||
|
||||
return false;
|
||||
})
|
||||
});
|
||||
');
|
||||
|
||||
// HTML
|
||||
$ip = User::getIP();
|
||||
|
||||
return '<div class="tGeoIPtest">
|
||||
<input class="form-control input-medium" type="text" value="' . $ip . '"/>
|
||||
<button class="btn btn-outline-secondary">Lookup</button>
|
||||
<ul class="results" style="margin-top:20px; display:none;">
|
||||
<li>Continent: <span class="continent"></span></li>
|
||||
<li>Country: <span class="country"></span></li>
|
||||
<li>Country Code: <span class="country_code"></span></li>
|
||||
<li>City: <span class="city"></span></li>
|
||||
<ul>
|
||||
</div>';
|
||||
}
|
||||
}
|
||||
129
plugins/system/tgeoip/field/updatebutton.php
Normal file
129
plugins/system/tgeoip/field/updatebutton.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
// No direct access to this file
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Form\FormField;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Session\Session;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
class JFormFieldTG_UpdateButton extends FormField
|
||||
{
|
||||
/**
|
||||
* Method to render the input field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
if (!NRFramework\Extension::pluginIsEnabled('tgeoip'))
|
||||
{
|
||||
return '<span class="label label-warning" style="margin-top:4px;">' . Text::_('PLG_SYSTEM_TGEOIP_ENABLE_PLUGIN') . '</span>';
|
||||
}
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/joomla4.css', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
$ajaxURL = Uri::base() . 'index.php?option=com_ajax&format=raw&plugin=tgeoip&task=update&license_key=USER_LICENSE_KEY&' . Session::getFormToken() . '=1';
|
||||
|
||||
Text::script('PLG_SYSTEM_TGEOIP_DATABASE_UPDATED');
|
||||
Text::script('PLG_SYSTEM_TGEOIP_PLEASE_WAIT');
|
||||
|
||||
Factory::getDocument()->addScriptDeclaration('
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.addEventListener("click", function(e) {
|
||||
var btn = e.target.closest(".geo button");
|
||||
if (!btn) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var license_key = e.target.closest("form").querySelector("#jform_params_license_key").value;
|
||||
if (!license_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
var alert = document.querySelector(".geo .alert");
|
||||
|
||||
var url = "' . $ajaxURL . '";
|
||||
url = url.replace("USER_LICENSE_KEY", license_key);
|
||||
|
||||
// before request
|
||||
alert.style.display = "none";
|
||||
btn.querySelector("span").innerHTML = Joomla.Text._("PLG_SYSTEM_TGEOIP_PLEASE_WAIT");
|
||||
btn.classList.add("btn-working");
|
||||
|
||||
fetch(url,
|
||||
{
|
||||
method: "POST"
|
||||
})
|
||||
.then(function(res){ return res.text(); })
|
||||
.then(function(response){
|
||||
if (response == "1") {
|
||||
alert.innerHTML = Joomla.Text._("PLG_SYSTEM_TGEOIP_DATABASE_UPDATED");
|
||||
alert.style.display = "block";
|
||||
alert.classList.remove("alert-danger");
|
||||
alert.classList.add("alert-success");
|
||||
} else {
|
||||
alert.innerHTML = response;
|
||||
alert.style.display = "block";
|
||||
alert.classList.remove("alert-success");
|
||||
alert.classList.add("alert-danger");
|
||||
}
|
||||
|
||||
btn.classList.remove("btn-working");
|
||||
btn.querySelector("span").innerHTML = btn.dataset.label;
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
');
|
||||
|
||||
Factory::getDocument()->addStyleDeclaration('
|
||||
.geo .btn-working {
|
||||
pointer-events:none;
|
||||
}
|
||||
.geo .alert {
|
||||
display:none;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.geo button {
|
||||
outline:none !important;
|
||||
width: auto;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
}
|
||||
.geo button:before {
|
||||
margin-right:5px;
|
||||
position:relative;
|
||||
top:1px;
|
||||
}
|
||||
#wrapper .geo .icon-refresh {
|
||||
margin-right: 5px;
|
||||
}
|
||||
');
|
||||
|
||||
return '
|
||||
<div class="geo">
|
||||
<div class="alert alert-danger"></div>
|
||||
<button class="btn btn-primary" data-label="' . Text::_('PLG_SYSTEM_TGEOIP_UPDATE_DATABASE') . '">
|
||||
<em class="icon-refresh"></em>
|
||||
<span>' . Text::_('PLG_SYSTEM_TGEOIP_UPDATE_DATABASE') . '</span>
|
||||
</button>
|
||||
</div>';
|
||||
}
|
||||
}
|
||||
132
plugins/system/tgeoip/helper/fakebcmath.php
Normal file
132
plugins/system/tgeoip/helper/fakebcmath.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
if (!function_exists('bcadd'))
|
||||
{
|
||||
function bcadd($Num1,$Num2,$Scale=null)
|
||||
{
|
||||
// check if they're valid positive numbers, extract the whole numbers and decimals
|
||||
if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
|
||||
!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');
|
||||
|
||||
// this is where the result is stored
|
||||
$Output=array();
|
||||
|
||||
// remove ending zeroes from decimals and remove point
|
||||
$Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
|
||||
$Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';
|
||||
|
||||
// calculate the longest length of decimals
|
||||
$DLen=max(strlen($Dec1),strlen($Dec2));
|
||||
|
||||
// if $Scale is null, automatically set it to the amount of decimal places for accuracy
|
||||
if($Scale==null) $Scale=$DLen;
|
||||
|
||||
// remove leading zeroes and reverse the whole numbers, then append padded decimals on the end
|
||||
$Num1=strrev(ltrim($Tmp1[1],'0').str_pad($Dec1,$DLen,'0'));
|
||||
$Num2=strrev(ltrim($Tmp2[1],'0').str_pad($Dec2,$DLen,'0'));
|
||||
|
||||
// calculate the longest length we need to process
|
||||
$MLen=max(strlen($Num1),strlen($Num2));
|
||||
|
||||
// pad the two numbers so they are of equal length (both equal to $MLen)
|
||||
$Num1=str_pad($Num1,$MLen,'0');
|
||||
$Num2=str_pad($Num2,$MLen,'0');
|
||||
|
||||
// process each digit, keep the ones, carry the tens (remainders)
|
||||
for($i=0;$i<$MLen;$i++) {
|
||||
$Sum=((int)$Num1[$i]+(int)$Num2[$i]);
|
||||
if(isset($Output[$i])) $Sum+=$Output[$i];
|
||||
$Output[$i]=$Sum%10;
|
||||
if($Sum>9) $Output[$i+1]=1;
|
||||
}
|
||||
|
||||
// convert the array to string and reverse it
|
||||
$Output=strrev(implode($Output));
|
||||
|
||||
// substring the decimal digits from the result, pad if necessary (if $Scale > amount of actual decimals)
|
||||
// next, since actual zero values can cause a problem with the substring values, if so, just simply give '0'
|
||||
// next, append the decimal value, if $Scale is defined, and return result
|
||||
$Decimal=str_pad(substr($Output,-$DLen,$Scale),$Scale,'0');
|
||||
$Output=(($MLen-$DLen<1)?'0':substr($Output,0,-$DLen));
|
||||
$Output.=(($Scale>0)?".{$Decimal}":'');
|
||||
return($Output);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('bcmul'))
|
||||
{
|
||||
function bcmul($Num1='0',$Num2='0') {
|
||||
// check if they're both plain numbers
|
||||
if(!preg_match("/^\d+$/",$Num1)||!preg_match("/^\d+$/",$Num2)) return(0);
|
||||
|
||||
// remove zeroes from beginning of numbers
|
||||
for($i=0;$i<strlen($Num1);$i++) if(@$Num1[$i]!='0') {$Num1=substr($Num1,$i);break;}
|
||||
for($i=0;$i<strlen($Num2);$i++) if(@$Num2[$i]!='0') {$Num2=substr($Num2,$i);break;}
|
||||
|
||||
// get both number lengths
|
||||
$Len1=strlen($Num1);
|
||||
$Len2=strlen($Num2);
|
||||
|
||||
// $Rema is for storing the calculated numbers and $Rema2 is for carrying the remainders
|
||||
$Rema=$Rema2=array();
|
||||
|
||||
// we start by making a $Len1 by $Len2 table (array)
|
||||
for($y=$i=0;$y<$Len1;$y++)
|
||||
for($x=0;$x<$Len2;$x++)
|
||||
// we use the classic lattice method for calculating the multiplication..
|
||||
// this will multiply each number in $Num1 with each number in $Num2 and store it accordingly
|
||||
@$Rema[$i++%$Len2].=sprintf('%02d',(int)$Num1[$y]*(int)$Num2[$x]);
|
||||
|
||||
// cycle through each stored number
|
||||
for($y=0;$y<$Len2;$y++)
|
||||
for($x=0;$x<$Len1*2;$x++)
|
||||
// add up the numbers in the diagonal fashion the lattice method uses
|
||||
@$Rema2[Floor(($x-1)/2)+1+$y]+=(int)$Rema[$y][$x];
|
||||
|
||||
// reverse the results around
|
||||
$Rema2=array_reverse($Rema2);
|
||||
|
||||
// cycle through all the results again
|
||||
for($i=0;$i<count($Rema2);$i++) {
|
||||
// reverse this item, split, keep the first digit, spread the other digits down the array
|
||||
$Rema3=str_split(strrev($Rema2[$i]));
|
||||
for($o=0;$o<count($Rema3);$o++)
|
||||
if($o==0) @$Rema2[$i+$o]=$Rema3[$o];
|
||||
else @$Rema2[$i+$o]+=$Rema3[$o];
|
||||
}
|
||||
// implode $Rema2 so it's a string and reverse it, this is the result!
|
||||
$Rema2=strrev(implode($Rema2));
|
||||
|
||||
// just to make sure, we delete the zeros from the beginning of the result and return
|
||||
while(strlen($Rema2)>1&&$Rema2[0]=='0') $Rema2=substr($Rema2,1);
|
||||
|
||||
return($Rema2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('bcpow'))
|
||||
{
|
||||
function bcpow($num, $power)
|
||||
{
|
||||
$answer = "1";
|
||||
|
||||
while ($power)
|
||||
{
|
||||
$answer = bcmul($answer, $num, 100);
|
||||
$power--;
|
||||
}
|
||||
return rtrim($answer, '0.');
|
||||
}
|
||||
}
|
||||
551
plugins/system/tgeoip/helper/tgeoip.php
Normal file
551
plugins/system/tgeoip/helper/tgeoip.php
Normal file
@ -0,0 +1,551 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Database\Reader;
|
||||
use Tassos\Vendor\splitbrain\PHPArchive\Tar;
|
||||
use NRFramework\User;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Filesystem\Path;
|
||||
use Joomla\CMS\Http\HttpFactory;
|
||||
|
||||
class TGeoIP
|
||||
{
|
||||
/**
|
||||
* The MaxMind GeoLite database reader
|
||||
*
|
||||
* @var Reader
|
||||
*/
|
||||
private $reader = null;
|
||||
|
||||
/**
|
||||
* Records for IP addresses already looked up
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
*/
|
||||
private $lookups = array();
|
||||
|
||||
/**
|
||||
* Max Age Database before it needs an update
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxAge = 30;
|
||||
|
||||
/**
|
||||
* Database File name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $DBFileName = 'GeoLite2-City';
|
||||
|
||||
/**
|
||||
* Database Remote URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $DBUpdateURL = 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=USER_LICENSE_KEY&suffix=tar.gz';
|
||||
|
||||
/**
|
||||
* GeoIP Enable Geolocations Documentation URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $TGeoIPEnableDocURL = 'https://www.tassos.gr/kb/general/how-to-enable-geolocation-features-in-tassos-gr-extensions';
|
||||
|
||||
/**
|
||||
* The IP address to look up
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $ip;
|
||||
|
||||
/**
|
||||
* The License Key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* Public constructor. Loads up the GeoLite2 database.
|
||||
*/
|
||||
public function __construct($ip = null)
|
||||
{
|
||||
if (!function_exists('bcadd') || !function_exists('bcmul') || !function_exists('bcpow'))
|
||||
{
|
||||
require_once __DIR__ . '/fakebcmath.php';
|
||||
}
|
||||
|
||||
// Check we have a valid GeoLite2 database
|
||||
$filePath = $this->getDBPath();
|
||||
|
||||
if (!file_exists($filePath))
|
||||
{
|
||||
$this->reader = null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->reader = new Reader($filePath);
|
||||
}
|
||||
// If anything goes wrong, MaxMind will raise an exception, resulting in a WSOD. Let's be sure to catch everything.
|
||||
catch(\Exception $e)
|
||||
{
|
||||
$this->reader = null;
|
||||
}
|
||||
|
||||
// Setup IP
|
||||
$this->ip = $ip ?: User::getIP();
|
||||
|
||||
if (in_array($this->ip, array('127.0.0.1', '::1')))
|
||||
{
|
||||
$this->ip = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the license key
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function setKey($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getKey()
|
||||
{
|
||||
if ($this->key)
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
$plugin = PluginHelper::getPlugin('system', 'tgeoip');
|
||||
$params = new Registry($plugin->params);
|
||||
|
||||
return $params->get('license_key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IP to look up
|
||||
*
|
||||
* @param string $ip The IP to look up
|
||||
*/
|
||||
public function setIP($ip)
|
||||
{
|
||||
$this->ip = $ip;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ISO country code from an IP address
|
||||
*
|
||||
* @return mixed A string with the country ISO code if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getCountryCode()
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $record->country->isoCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the country name from an IP address
|
||||
*
|
||||
* @param string $locale The locale of the country name, e.g 'de' to return the country names in German. If not specified the English (US) names are returned.
|
||||
*
|
||||
* @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getCountryName($locale = null)
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($locale))
|
||||
{
|
||||
return $record->country->name;
|
||||
}
|
||||
|
||||
return $record->country->names[$locale];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the continent ISO code from an IP address
|
||||
*
|
||||
* @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getContinentCode($locale = null)
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $record->continent->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the continent name from an IP address
|
||||
*
|
||||
* @param string $locale The locale of the continent name, e.g 'de' to return the country names in German. If not specified the English (US) names are returned.
|
||||
*
|
||||
* @return mixed A string with the country name if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getContinentName($locale = null)
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($locale))
|
||||
{
|
||||
return $record->continent;
|
||||
}
|
||||
|
||||
return $record->continent->names[$locale];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a raw record from an IP address
|
||||
*
|
||||
* @return mixed A \GeoIp2\Model\City record if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getRecord()
|
||||
{
|
||||
if (empty($this->ip))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$ip = $this->ip;
|
||||
|
||||
$needsToLoad = !array_key_exists($ip, $this->lookups);
|
||||
|
||||
if ($needsToLoad)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!is_null($this->reader))
|
||||
{
|
||||
$this->lookups[$ip] = $this->reader->city($ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->lookups[$ip] = null;
|
||||
}
|
||||
}
|
||||
catch (Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException $e)
|
||||
{
|
||||
$this->lookups[$ip] = false;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// GeoIp2 could throw several different types of exceptions. Let's be sure that we're going to catch them all
|
||||
$this->lookups[$ip] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lookups[$ip];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the city's name from an IP address
|
||||
*
|
||||
* @param string $locale The locale of the city's name, e.g 'de' to return the city names in German. If not specified the English (US) names are returned.
|
||||
* @return mixed A string with the city name if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getCity($locale = null)
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($locale))
|
||||
{
|
||||
return $record->city->name;
|
||||
}
|
||||
|
||||
return $record->city->names[$locale];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a geographical region's (i.e. a country's province/state) name from an IP address
|
||||
*
|
||||
* @param string $locale The locale of the regions's name, e.g 'de' to return region names in German. If not specified the English (US) names are returned.
|
||||
* @return mixed A string with the region's name if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getRegionName($locale = null)
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// MaxMind stores region information in a 'Subdivision' object (also found in $record->city->subdivision)
|
||||
// http://maxmind.github.io/GeoIP2-php/doc/v2.9.0/class-GeoIp2.Record.Subdivision.html
|
||||
if (empty($locale))
|
||||
{
|
||||
return $record->mostSpecificSubdivision->name;
|
||||
}
|
||||
|
||||
return $record->mostSpecificSubdivision->names[$locale];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a geographical region's (i.e. a country's province/state) ISO 3611-2 (alpha-2) code from an IP address
|
||||
*
|
||||
* @return mixed A string with the region's code if found, false if the IP address is not found, null if the db can't be loaded
|
||||
*/
|
||||
public function getRegionCode()
|
||||
{
|
||||
$record = $this->getRecord();
|
||||
|
||||
if ($record === false || is_null($record))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// MaxMind stores region information in a 'Subdivision' object
|
||||
// http://maxmind.github.io/GeoIP2-php/doc/v2.9.0/class-GeoIp2.Record.Subdivision.html
|
||||
return $record->mostSpecificSubdivision->isoCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads and installs a fresh copy of the GeoLite2 City database
|
||||
*
|
||||
* @return mixed True on success, error string on failure
|
||||
*/
|
||||
public function updateDatabase()
|
||||
{
|
||||
// Try to download the package, if I get any exception I'll simply stop here and display the error
|
||||
try
|
||||
{
|
||||
$compressed = $this->downloadDatabase();
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
// Write the downloaded file to a temporary location
|
||||
$target = $this->getTempFolder() . $this->DBFileName . '.tar.gz';
|
||||
if (File::write($target, $compressed) === false)
|
||||
{
|
||||
return Text::_('PLG_SYSTEM_TGEOIP_ERR_WRITEFAILED');
|
||||
}
|
||||
|
||||
// Unzip database to the same temporary location
|
||||
$tar = new Tar;
|
||||
$tar->open($target);
|
||||
$extracted_files = $tar->extract($this->getTempFolder());
|
||||
|
||||
$database_file = '';
|
||||
$extracted_folder = '';
|
||||
|
||||
// Loop through extracted files to find the name of the extracted folder and the name of the database file
|
||||
foreach ($extracted_files as $key => $extracted_file)
|
||||
{
|
||||
if ($extracted_file->getIsdir())
|
||||
{
|
||||
$extracted_folder = $extracted_file->getPath();
|
||||
}
|
||||
|
||||
if (strpos($extracted_file->getPath(), '.mmdb') === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$database_file = $extracted_file->getPath();
|
||||
}
|
||||
|
||||
// Move database file to the correct location
|
||||
if (!File::move($this->getTempFolder() . $database_file, $this->getDBPath()))
|
||||
{
|
||||
return Text::sprintf('PLG_SYSTEM_TGEOIP_ERR_CANTWRITE', $this->getDBPath());
|
||||
}
|
||||
|
||||
// Make sure the database is readable
|
||||
if (!$this->dbIsValid())
|
||||
{
|
||||
return Text::_('PLG_SYSTEM_TGEOIP_ERR_INVALIDDB');
|
||||
}
|
||||
|
||||
// Delete leftovers
|
||||
File::delete($target);
|
||||
Folder::delete($this->getTempFolder() . $extracted_folder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Double check if MaxMind can actually read and validate the downloaded database
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function dbIsValid()
|
||||
{
|
||||
try
|
||||
{
|
||||
$reader = new Reader($this->getDBPath());
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the compressed database for the provider
|
||||
*
|
||||
* @return string The compressed data
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function downloadDatabase()
|
||||
{
|
||||
// Make sure we have enough memory limit
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$license_key = $this->getKey();
|
||||
|
||||
if (empty($license_key))
|
||||
{
|
||||
throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_LICENSE_KEY_EMPTY') . ' <a href="' . $this->TGeoIPEnableDocURL . '" target="_blank">' . Text::_('PLG_SYSTEM_TGEOIP_ENABLE_DOC_LINK_LABEL') . '</a>');
|
||||
}
|
||||
|
||||
$http = HttpFactory::getHttp();
|
||||
|
||||
$this->DBUpdateURL = str_replace('USER_LICENSE_KEY', $license_key, $this->DBUpdateURL);
|
||||
|
||||
// Let's bubble up the exception, we will take care in the caller
|
||||
$response = $http->get($this->DBUpdateURL);
|
||||
$compressed = $response->body;
|
||||
|
||||
// 401 is thrown if you have incorrect credentials or wrong license key
|
||||
if ($response->code == 401)
|
||||
{
|
||||
throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_WRONG_LICENSE_KEY'));
|
||||
}
|
||||
|
||||
// Generic check on valid HTTP code
|
||||
if ($response->code > 299)
|
||||
{
|
||||
throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_MAXMIND_GENERIC'));
|
||||
}
|
||||
|
||||
// An empty file indicates a problem with MaxMind's servers
|
||||
if (empty($compressed))
|
||||
{
|
||||
throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_EMPTYDOWNLOAD'));
|
||||
}
|
||||
|
||||
// Sometimes you get a rate limit exceeded
|
||||
if (stristr($compressed, 'Rate limited exceeded') !== false)
|
||||
{
|
||||
throw new \Exception(Text::_('PLG_SYSTEM_TGEOIP_ERR_MAXMINDRATELIMIT'));
|
||||
}
|
||||
|
||||
return $compressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads (and checks) the temp Joomla folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getTempFolder()
|
||||
{
|
||||
$ds = DIRECTORY_SEPARATOR;
|
||||
|
||||
$tmpdir = Factory::getConfig()->get('tmp_path');
|
||||
|
||||
if (realpath($tmpdir) == $ds . 'tmp')
|
||||
{
|
||||
$tmpdir = JPATH_SITE . $ds . 'tmp';
|
||||
}
|
||||
|
||||
elseif (!is_dir($tmpdir))
|
||||
{
|
||||
$tmpdir = JPATH_SITE . $ds . 'tmp';
|
||||
}
|
||||
|
||||
return Path::clean(trim($tmpdir) . $ds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Database local file path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getDBPath()
|
||||
{
|
||||
return JPATH_ROOT . '/plugins/system/tgeoip/db/' . $this->DBFileName . '.mmdb';
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the GeoIP database need update?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function needsUpdate()
|
||||
{
|
||||
// Get the modification time of the database file
|
||||
$modTime = @filemtime($this->getDBPath());
|
||||
|
||||
// This is now
|
||||
$now = time();
|
||||
|
||||
// Minimum time difference
|
||||
$threshold = $this->maxAge * 24 * 3600;
|
||||
|
||||
// Do we need an update?
|
||||
$needsUpdate = ($now - $modTime) > $threshold;
|
||||
|
||||
return $needsUpdate;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
; @package Advanced Custom Fields
|
||||
; @version 2.8.8 Pro
|
||||
;
|
||||
; @author Tassos Marinos <info@tassos.gr>
|
||||
; @link http://www.tassos.gr
|
||||
; @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
; @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
|
||||
TGEOIP="Tassos GeoIP"
|
||||
PLG_SYSTEM_TGEOIP="System - Tassos GeoIP"
|
||||
PLG_SYSTEM_TGEOIP_DESC="This plugin provides GeoIP features (finding out the country of an IP address) for Tassos.gr extensions. Without it the GeoIP features will not be available. This plugin includes GeoLite2 data created by MaxMind <a href='http://www.maxmind.com'>http://www.maxmind.com</a>."
|
||||
PLG_SYSTEM_TGEOIP_ERR_NOGZSUPPORT="Your server does not support extraction of GZip (.gz) files. The GeoLite2 Country database update cannot proceed."
|
||||
PLG_SYSTEM_TGEOIP_ERR_EMPTYDOWNLOAD="Downloading the GeoLite2 Country database failed: empty file retrieved from server. Please contact your host."
|
||||
PLG_SYSTEM_TGEOIP_ERR_WRITEFAILED="Writing the temporary file failed. Please make sure that the temporary directory defined in your site's Global Configuration is writeable. The GeoLite2 Country database update cannot proceed."
|
||||
PLG_SYSTEM_TGEOIP_ERR_CANTUNCOMPRESS="Cannot decompress the GeoLite2 Country database file. Probably a corrupt download? The GeoLite2 Country database update cannot proceed."
|
||||
PLG_SYSTEM_TGEOIP_ERR_MAXMINDRATELIMIT="MaxMind's servers are busy. Please retry updating the GeoLite2 Country database in 24 hours."
|
||||
PLG_SYSTEM_TGEOIP_ERR_MAXMIND_GENERIC="A connection error occurred. Please retry updating the GeoLite2 Country database in 24 hours."
|
||||
PLG_SYSTEM_TGEOIP_ERR_INVALIDDB="Downloaded database seems to be invalid. Please retry updating the GeoLite2 Country database in 24 hours."
|
||||
PLG_SYSTEM_TGEOIP_ERR_WRONG_LICENSE_KEY="Your MaxMind license key appears to be incorrect."
|
||||
PLG_SYSTEM_TGEOIP_ERR_CANTWRITE="Moving the database file failed. Please make sure that the database directory is writeable: %s"
|
||||
PLG_SYSTEM_TGEOIP_UPDATE_DATABASE="Update Database"
|
||||
PLG_SYSTEM_TGEOIP_UPDATE_DATABASE_DESC="Update the GeoLite2 database from MaxMind servers. This might take several seconds to finish. Please be patient."
|
||||
PLG_SYSTEM_TGEOIP_DATABASE="Database"
|
||||
PLG_SYSTEM_TGEOIP_LAST_UPDATED="Last Updated"
|
||||
PLG_SYSTEM_TGEOIP_LAST_UPDATED_DESC="Indicates the last datetime the database updated."
|
||||
PLG_SYSTEM_TGEOIP_CHECK_IP="Lookup IP Address"
|
||||
PLG_SYSTEM_TGEOIP_CHECK_IP_DESC="Test drive the GeoIP plugin by looking up an IP address."
|
||||
PLG_SYSTEM_TGEOIP_MAINTENANCE="GeoIP Database Maintenance"
|
||||
PLG_SYSTEM_TGEOIP_MAINTENANCE_DESC="%s finds the country of your visitors' IP addresses using the MaxMind GeoLite2 Country database. You are advised to update it at least once per month. On most servers you can perform the update by clicking the button below."
|
||||
PLG_SYSTEM_TGEOIP_DATABASE_UPDATED="GeoIP database successfully updated!"
|
||||
PLG_SYSTEM_TGEOIP_LICENSE_KEY="License Key"
|
||||
PLG_SYSTEM_TGEOIP_LICENSE_KEY_DESC="Get your free License Key to download the latest MaxMind GeoLite2 Database."
|
||||
PLG_SYSTEM_TGEOIP_LICENSE_KEY_GET="Get a free License Key"
|
||||
PLG_SYSTEM_TGEOIP_LICENSE_KEY_EMPTY="Please enter a valid MaxMind License Key."
|
||||
PLG_SYSTEM_TGEOIP_ENABLE_PLUGIN="To be able to update the database you will need to enable this plugin first"
|
||||
PLG_SYSTEM_TGEOIP_ENABLE_DOC_LINK_LABEL="Click to learn how to enable Geolocation features in Tassos.gr extensions"
|
||||
PLG_SYSTEM_TGEOIP_PLEASE_WAIT="Please wait..."
|
||||
@ -0,0 +1,11 @@
|
||||
; @package Advanced Custom Fields
|
||||
; @version 2.8.8 Pro
|
||||
;
|
||||
; @author Tassos Marinos <info@tassos.gr>
|
||||
; @link http://www.tassos.gr
|
||||
; @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
; @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
|
||||
TGEOIP="Tassos GeoIP"
|
||||
PLG_SYSTEM_TGEOIP="System - Tassos GeoIP"
|
||||
PLG_SYSTEM_TGEOIP_DESC="This plugin provides GeoIP features (finding out the country of an IP address) for Tassos.gr extensions. Without it the GeoIP features will not be available. This plugin includes GeoLite2 data created by MaxMind <a href='http://www.maxmind.com'>http://www.maxmind.com</a>."
|
||||
691
plugins/system/tgeoip/script.install.helper.php
Normal file
691
plugins/system/tgeoip/script.install.helper.php
Normal file
@ -0,0 +1,691 @@
|
||||
<?php
|
||||
/**
|
||||
* Installer Script Helper
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2016 Tassos Marinos All Rights Reserved
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Installer\Installer;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
|
||||
class PlgSystemTgeoipInstallerScriptHelper
|
||||
{
|
||||
public $name = '';
|
||||
public $alias = '';
|
||||
public $extname = '';
|
||||
public $extension_type = '';
|
||||
public $plugin_folder = 'system';
|
||||
public $module_position = 'status';
|
||||
public $client_id = 1;
|
||||
public $install_type = 'install';
|
||||
public $show_message = true;
|
||||
public $autopublish = true;
|
||||
public $db = null;
|
||||
public $app = null;
|
||||
public $installedVersion;
|
||||
|
||||
public function __construct(&$params)
|
||||
{
|
||||
$this->extname = $this->extname ?: $this->alias;
|
||||
$this->db = Factory::getDbo();
|
||||
$this->app = Factory::getApplication();
|
||||
$this->installedVersion = $this->getVersion($this->getInstalledXMLFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Preflight event
|
||||
*
|
||||
* @param string
|
||||
* @param JAdapterInstance
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function preflight($route, $adapter)
|
||||
{
|
||||
if (!in_array($route, array('install', 'update')))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Factory::getLanguage()->load('plg_system_novaraininstaller', JPATH_PLUGINS . '/system/novaraininstaller');
|
||||
|
||||
if ($this->show_message && $this->isInstalled())
|
||||
{
|
||||
$this->install_type = 'update';
|
||||
}
|
||||
|
||||
if ($this->onBeforeInstall() === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preflight event
|
||||
*
|
||||
* @param string
|
||||
* @param JAdapterInstance
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function postflight($route, $adapter)
|
||||
{
|
||||
Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, $this->getMainFolder());
|
||||
|
||||
if (!in_array($route, array('install', 'update')))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->onAfterInstall() === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($route == 'install' && $this->autopublish)
|
||||
{
|
||||
$this->publishExtension();
|
||||
}
|
||||
|
||||
if ($this->show_message)
|
||||
{
|
||||
$this->addInstalledMessage();
|
||||
}
|
||||
|
||||
Factory::getCache()->clean('com_plugins');
|
||||
Factory::getCache()->clean('_system');
|
||||
}
|
||||
|
||||
public function isInstalled()
|
||||
{
|
||||
if (!is_file($this->getInstalledXMLFile()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = $this->db->getQuery(true)
|
||||
->select('extension_id')
|
||||
->from('#__extensions')
|
||||
->where($this->db->quoteName('type') . ' = ' . $this->db->quote($this->extension_type))
|
||||
->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->getElementName()));
|
||||
$this->db->setQuery($query, 0, 1);
|
||||
$result = $this->db->loadResult();
|
||||
|
||||
return empty($result) ? false : true;
|
||||
}
|
||||
|
||||
public function getMainFolder()
|
||||
{
|
||||
switch ($this->extension_type)
|
||||
{
|
||||
case 'plugin' :
|
||||
return JPATH_SITE . '/plugins/' . $this->plugin_folder . '/' . $this->extname;
|
||||
|
||||
case 'component' :
|
||||
return JPATH_ADMINISTRATOR . '/components/com_' . $this->extname;
|
||||
|
||||
case 'module' :
|
||||
return JPATH_ADMINISTRATOR . '/modules/mod_' . $this->extname;
|
||||
|
||||
case 'library' :
|
||||
return JPATH_SITE . '/libraries/' . $this->extname;
|
||||
}
|
||||
}
|
||||
|
||||
public function getInstalledXMLFile()
|
||||
{
|
||||
return $this->getXMLFile($this->getMainFolder());
|
||||
}
|
||||
|
||||
public function getCurrentXMLFile()
|
||||
{
|
||||
return $this->getXMLFile(__DIR__);
|
||||
}
|
||||
|
||||
public function getXMLFile($folder)
|
||||
{
|
||||
switch ($this->extension_type)
|
||||
{
|
||||
case 'module' :
|
||||
return $folder . '/mod_' . $this->extname . '.xml';
|
||||
default :
|
||||
return $folder . '/' . $this->extname . '.xml';
|
||||
}
|
||||
}
|
||||
|
||||
public function foldersExist($folders = array())
|
||||
{
|
||||
foreach ($folders as $folder)
|
||||
{
|
||||
if (is_dir($folder))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function publishExtension()
|
||||
{
|
||||
switch ($this->extension_type)
|
||||
{
|
||||
case 'plugin' :
|
||||
$this->publishPlugin();
|
||||
|
||||
case 'module' :
|
||||
$this->publishModule();
|
||||
}
|
||||
}
|
||||
|
||||
public function publishPlugin()
|
||||
{
|
||||
$query = $this->db->getQuery(true)
|
||||
->update('#__extensions')
|
||||
->set($this->db->quoteName('enabled') . ' = 1')
|
||||
->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin'))
|
||||
->where($this->db->quoteName('element') . ' = ' . $this->db->quote($this->extname))
|
||||
->where($this->db->quoteName('folder') . ' = ' . $this->db->quote($this->plugin_folder));
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
}
|
||||
|
||||
public function publishModule()
|
||||
{
|
||||
// Get module id
|
||||
$query = $this->db->getQuery(true)
|
||||
->select('id')
|
||||
->from('#__modules')
|
||||
->where($this->db->quoteName('module') . ' = ' . $this->db->quote('mod_' . $this->extname))
|
||||
->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id);
|
||||
$this->db->setQuery($query, 0, 1);
|
||||
$id = $this->db->loadResult();
|
||||
|
||||
if (!$id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check if module is already in the modules_menu table (meaning is is already saved)
|
||||
$query->clear()
|
||||
->select('moduleid')
|
||||
->from('#__modules_menu')
|
||||
->where($this->db->quoteName('moduleid') . ' = ' . (int) $id);
|
||||
$this->db->setQuery($query, 0, 1);
|
||||
$exists = $this->db->loadResult();
|
||||
|
||||
if ($exists)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get highest ordering number in position
|
||||
$query->clear()
|
||||
->select('ordering')
|
||||
->from('#__modules')
|
||||
->where($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position))
|
||||
->where($this->db->quoteName('client_id') . ' = ' . (int) $this->client_id)
|
||||
->order('ordering DESC');
|
||||
$this->db->setQuery($query, 0, 1);
|
||||
$ordering = $this->db->loadResult();
|
||||
$ordering++;
|
||||
|
||||
// publish module and set ordering number
|
||||
$query->clear()
|
||||
->update('#__modules')
|
||||
->set($this->db->quoteName('published') . ' = 1')
|
||||
->set($this->db->quoteName('ordering') . ' = ' . (int) $ordering)
|
||||
->set($this->db->quoteName('position') . ' = ' . $this->db->quote($this->module_position))
|
||||
->where($this->db->quoteName('id') . ' = ' . (int) $id);
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
|
||||
// add module to the modules_menu table
|
||||
$query->clear()
|
||||
->insert('#__modules_menu')
|
||||
->columns(array($this->db->quoteName('moduleid'), $this->db->quoteName('menuid')))
|
||||
->values((int) $id . ', 0');
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
}
|
||||
|
||||
public function addInstalledMessage()
|
||||
{
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
Text::sprintf(
|
||||
Text::_($this->install_type == 'update' ? 'NRI_THE_EXTENSION_HAS_BEEN_UPDATED_SUCCESSFULLY' : 'NRI_THE_EXTENSION_HAS_BEEN_INSTALLED_SUCCESSFULLY'),
|
||||
'<strong>' . Text::_($this->name) . '</strong>',
|
||||
'<strong>' . $this->getVersion() . '</strong>',
|
||||
$this->getFullType()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getPrefix()
|
||||
{
|
||||
switch ($this->extension_type)
|
||||
{
|
||||
case 'plugin';
|
||||
return Text::_('plg_' . strtolower($this->plugin_folder));
|
||||
|
||||
case 'component':
|
||||
return Text::_('com');
|
||||
|
||||
case 'module':
|
||||
return Text::_('mod');
|
||||
|
||||
case 'library':
|
||||
return Text::_('lib');
|
||||
|
||||
default:
|
||||
return $this->extension_type;
|
||||
}
|
||||
}
|
||||
|
||||
public function getElementName($type = null, $extname = null)
|
||||
{
|
||||
$type = is_null($type) ? $this->extension_type : $type;
|
||||
$extname = is_null($extname) ? $this->extname : $extname;
|
||||
|
||||
switch ($type)
|
||||
{
|
||||
case 'component' :
|
||||
return 'com_' . $extname;
|
||||
|
||||
case 'module' :
|
||||
return 'mod_' . $extname;
|
||||
|
||||
case 'plugin' :
|
||||
default:
|
||||
return $extname;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFullType()
|
||||
{
|
||||
return Text::_('NRI_' . strtoupper($this->getPrefix()));
|
||||
}
|
||||
|
||||
public function isPro()
|
||||
{
|
||||
$versionFile = __DIR__ . "/version.php";
|
||||
|
||||
// If version file does not exist we assume a PRO version
|
||||
if (!is_file($versionFile))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load version file
|
||||
require_once $versionFile;
|
||||
return (bool) $NR_PRO;
|
||||
}
|
||||
|
||||
public function getVersion($file = '')
|
||||
{
|
||||
$file = $file ?: $this->getCurrentXMLFile();
|
||||
|
||||
if (!is_file($file))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$xml = Installer::parseXMLInstallFile($file);
|
||||
|
||||
if (!$xml || !isset($xml['version']))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return $xml['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wether the extension can be installed or not
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canInstall()
|
||||
{
|
||||
// The extension is not installed yet. Accept Install.
|
||||
if (!$installed_version = $this->getVersion($this->getInstalledXMLFile()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Path to extension's version file
|
||||
$versionFile = $this->getMainFolder() . "/version.php";
|
||||
$NR_PRO = true;
|
||||
|
||||
// If version file does not exist we assume we have a PRO version installed
|
||||
if (file_exists($versionFile))
|
||||
{
|
||||
require_once($versionFile);
|
||||
}
|
||||
|
||||
// The free version is installed. Accept install.
|
||||
if (!(bool)$NR_PRO)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Current package is a PRO version. Accept install.
|
||||
if ($this->isPro())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// User is trying to update from PRO version to FREE. Do not accept install.
|
||||
Factory::getLanguage()->load($this->getPrefix() . '_' . $this->extname, __DIR__);
|
||||
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
Text::_('NRI_ERROR_PRO_TO_FREE'), 'error'
|
||||
);
|
||||
|
||||
Factory::getApplication()->enqueueMessage(
|
||||
html_entity_decode(
|
||||
Text::sprintf(
|
||||
'NRI_ERROR_UNINSTALL_FIRST',
|
||||
'<a href="http://www.tassos.gr/joomla-extensions/' . $this->getUrlAlias() . '" target="_blank">',
|
||||
'</a>',
|
||||
Text::_($this->name)
|
||||
)
|
||||
), 'error'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL alias of the extension.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getUrlAlias()
|
||||
{
|
||||
$alias = $this->alias;
|
||||
|
||||
switch ($alias)
|
||||
{
|
||||
case 'smilepack':
|
||||
$alias = 'smile-pack';
|
||||
break;
|
||||
case 'convertforms':
|
||||
$alias = 'convert-forms';
|
||||
break;
|
||||
case 'rstbox':
|
||||
$alias = 'engagebox';
|
||||
break;
|
||||
case 'gsd':
|
||||
$alias = 'google-structured-data';
|
||||
break;
|
||||
}
|
||||
|
||||
// ACF
|
||||
if ($this->plugin_folder === 'fields' && ($alias === 'acf' || $this->startsWith($alias, 'acf')))
|
||||
{
|
||||
$alias = 'advanced-custom-fields';
|
||||
}
|
||||
|
||||
return $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether string starts with substring.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $query
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($string, $query)
|
||||
{
|
||||
return substr($string, 0, strlen($query)) === $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current version is newer than the installed one
|
||||
* Used for Novarain Framework
|
||||
*
|
||||
* @return boolean [description]
|
||||
*/
|
||||
public function isNewer()
|
||||
{
|
||||
if (!$installed_version = $this->getVersion($this->getInstalledXMLFile()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$package_version = $this->getVersion();
|
||||
|
||||
return version_compare($installed_version, $package_version, '<=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method triggered before installation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function onBeforeInstall()
|
||||
{
|
||||
if (!$this->canInstall())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method triggered after installation
|
||||
*/
|
||||
public function onAfterInstall()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete files
|
||||
*
|
||||
* @param array $folders
|
||||
*/
|
||||
public function deleteFiles($files = array())
|
||||
{
|
||||
foreach ($files as $key => $file)
|
||||
{
|
||||
if (!is_file($file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
File::delete($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes folders
|
||||
*
|
||||
* @param array $folders
|
||||
*/
|
||||
public function deleteFolders($folders = array())
|
||||
{
|
||||
foreach ($folders as $folder)
|
||||
{
|
||||
if (!is_dir($folder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Folder::delete($folder);
|
||||
}
|
||||
}
|
||||
|
||||
public function dropIndex($table, $index)
|
||||
{
|
||||
$db = $this->db;
|
||||
|
||||
// Check if index exists first
|
||||
$query = 'SHOW INDEX FROM ' . $db->quoteName('#__' . $table) . ' WHERE KEY_NAME = ' . $db->quote($index);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
if (!$db->loadResult())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove index
|
||||
$query = 'ALTER TABLE ' . $db->quoteName('#__' . $table) . ' DROP INDEX ' . $db->quoteName($index);
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
}
|
||||
|
||||
public function dropUnwantedTables($tables) {
|
||||
|
||||
if (!$tables) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$query = "DROP TABLE IF EXISTS #__".$this->db->escape($table);
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
}
|
||||
}
|
||||
|
||||
public function dropUnwantedColumns($table, $columns) {
|
||||
|
||||
if (!$columns || !$table) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->db;
|
||||
|
||||
// Check if columns exists in database
|
||||
function qt($n) {
|
||||
return(Factory::getDBO()->quote($n));
|
||||
}
|
||||
|
||||
$query = 'SHOW COLUMNS FROM #__'.$table.' WHERE Field IN ('.implode(",", array_map("qt", $columns)).')';
|
||||
$db->setQuery($query);
|
||||
$rows = $db->loadColumn(0);
|
||||
|
||||
// Abort if we don't have any rows
|
||||
if (!$rows) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's remove the columns
|
||||
$q = "";
|
||||
foreach ($rows as $key => $column) {
|
||||
$comma = (($key+1) < count($rows)) ? "," : "";
|
||||
$q .= "drop ".$this->db->escape($column).$comma;
|
||||
}
|
||||
|
||||
$query = "alter table #__".$table." $q";
|
||||
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
}
|
||||
|
||||
public function fetch($table, $columns = "*", $where = null, $singlerow = false) {
|
||||
if (!$table) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->db;
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query
|
||||
->select($columns)
|
||||
->from("#__$table");
|
||||
|
||||
if (isset($where)) {
|
||||
$query->where("$where");
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
return ($singlerow) ? $db->loadObject() : $db->loadObjectList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Novarain Framework
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function loadFramework()
|
||||
{
|
||||
if (is_file(JPATH_PLUGINS . '/system/nrframework/autoload.php'))
|
||||
{
|
||||
include_once JPATH_PLUGINS . '/system/nrframework/autoload.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-orders plugin after passed array of plugins
|
||||
*
|
||||
* @param string $plugin Plugin element name
|
||||
* @param array $lowerPluginOrder Array of plugin element names
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function pluginOrderAfter($lowerPluginOrder)
|
||||
{
|
||||
|
||||
if (!is_array($lowerPluginOrder) || !count($lowerPluginOrder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->db;
|
||||
|
||||
// Get plugins max order
|
||||
$query = $db->getQuery(true);
|
||||
$query
|
||||
->select($db->quoteName('b.ordering'))
|
||||
->from($db->quoteName('#__extensions', 'b'))
|
||||
->where($db->quoteName('b.element') . ' IN ("'.implode("\",\"",$lowerPluginOrder).'")')
|
||||
->order('b.ordering desc');
|
||||
|
||||
$db->setQuery($query);
|
||||
$maxOrder = $db->loadResult();
|
||||
|
||||
if (is_null($maxOrder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get plugin details
|
||||
$query
|
||||
->clear()
|
||||
->select(array($db->quoteName('extension_id'), $db->quoteName('ordering')))
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote($this->alias));
|
||||
|
||||
$db->setQuery($query);
|
||||
$pluginInfo = $db->loadObject();
|
||||
|
||||
if (!isset($pluginInfo->ordering) || $pluginInfo->ordering > $maxOrder)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the new plugin order
|
||||
$object = new stdClass();
|
||||
$object->extension_id = $pluginInfo->extension_id;
|
||||
$object->ordering = ($maxOrder + 1);
|
||||
|
||||
try {
|
||||
$db->updateObject('#__extensions', $object, 'extension_id');
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
plugins/system/tgeoip/script.install.php
Normal file
19
plugins/system/tgeoip/script.install.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
require_once __DIR__ . '/script.install.helper.php';
|
||||
|
||||
class PlgSystemTGeoIPInstallerScript extends PlgSystemTGeoIPInstallerScriptHelper
|
||||
{
|
||||
public $name = 'TGEOIP';
|
||||
public $alias = 'tgeoip';
|
||||
public $extension_type = 'plugin';
|
||||
public $show_message = false;
|
||||
}
|
||||
121
plugins/system/tgeoip/tgeoip.php
Normal file
121
plugins/system/tgeoip/tgeoip.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Advanced Custom Fields
|
||||
* @version 2.8.8 Pro
|
||||
*
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2024 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
defined( '_JEXEC' ) or die( 'Restricted access' );
|
||||
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\Session\Session;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
class plgSystemTGeoIP extends CMSPlugin
|
||||
{
|
||||
/**
|
||||
* Joomla Application Object
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Auto load plugin language
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $autoloadLanguage = true;
|
||||
|
||||
/**
|
||||
* GeoIP Class
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $geoIP;
|
||||
|
||||
/**
|
||||
* Load GeoIP Classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadGeoIP()
|
||||
{
|
||||
$path = JPATH_PLUGINS . '/system/tgeoip';
|
||||
|
||||
if (!class_exists('TGeoIP'))
|
||||
{
|
||||
if (@file_exists($path . '/helper/tgeoip.php'))
|
||||
{
|
||||
if (@include_once($path . '/vendor/autoload.php'))
|
||||
{
|
||||
@include_once $path . '/helper/tgeoip.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->geoIP = new TGeoIP();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to AJAX requests on ?option=com_ajax&format=raw&plugin=tgeoip
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onAjaxTgeoip()
|
||||
{
|
||||
Session::checkToken('request') or die('Invalid Token');
|
||||
|
||||
// Only in admin
|
||||
if (!$this->app->isClient('administrator'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loadGeoIP();
|
||||
|
||||
$task = $this->app->input->get('task', 'update');
|
||||
|
||||
$this->geoIP->setKey($this->app->input->get('license_key', ''));
|
||||
|
||||
switch ($task)
|
||||
{
|
||||
// Update database and redirect
|
||||
case 'update-red':
|
||||
|
||||
$result = $this->geoIP->updateDatabase();
|
||||
|
||||
if ($result === true)
|
||||
{
|
||||
$msg = Text::_('PLG_SYSTEM_TGEOIP_DATABASE_UPDATED');
|
||||
$msgType = 'message';
|
||||
} else
|
||||
{
|
||||
$msgType = 'error';
|
||||
$msg = $result;
|
||||
}
|
||||
|
||||
$return = base64_decode($this->app->input->get->getBase64('return', null));
|
||||
|
||||
$this->app->enqueueMessage($msg, $msgType);
|
||||
$this->app->redirect($return);
|
||||
break;
|
||||
|
||||
// Update database
|
||||
case 'update':
|
||||
echo $this->geoIP->updateDatabase();
|
||||
break;
|
||||
|
||||
// IP Lookup
|
||||
case 'get':
|
||||
$ip = $this->app->input->get('ip');
|
||||
echo json_encode($this->geoIP->setIP($ip)->getRecord());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
plugins/system/tgeoip/tgeoip.xml
Normal file
62
plugins/system/tgeoip/tgeoip.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<extension version="3.4" type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_tgeoip</name>
|
||||
<description>PLG_SYSTEM_TGEOIP_DESC</description>
|
||||
<version>2.2.5</version>
|
||||
<creationDate>06 Mar 2017</creationDate>
|
||||
<author>Tassos Marinos</author>
|
||||
<copyright>Copyright © 2024 Tassos Marinos All Rights Reserved</copyright>
|
||||
<license>http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL</license>
|
||||
<authorEmail>info@tassos.gr</authorEmail>
|
||||
<authorUrl>http://www.tassos.gr</authorUrl>
|
||||
<scriptfile>script.install.php</scriptfile>
|
||||
<files>
|
||||
<filename plugin="tgeoip">tgeoip.php</filename>
|
||||
<filename>script.install.helper.php</filename>
|
||||
<folder>db</folder>
|
||||
<folder>field</folder>
|
||||
<folder>helper</folder>
|
||||
<folder>language</folder>
|
||||
<folder>vendor</folder>
|
||||
</files>
|
||||
<config>
|
||||
<fields name="params" addfieldpath="/plugins/system/tgeoip/field">
|
||||
<fieldset name="basic" addfieldpath="/plugins/system/nrframework/fields">
|
||||
<field name="blockStart" type="nr_well"
|
||||
label="PLG_SYSTEM_TGEOIP_DATABASE"
|
||||
description="PLG_SYSTEM_TGEOIP_UPDATE_DATABASE_DESC"
|
||||
/>
|
||||
<field name="license_key" type="nrtext"
|
||||
label="PLG_SYSTEM_TGEOIP_LICENSE_KEY"
|
||||
description="PLG_SYSTEM_TGEOIP_LICENSE_KEY_DESC"
|
||||
class="input-xlarge"
|
||||
urltext="PLG_SYSTEM_TGEOIP_LICENSE_KEY_GET"
|
||||
url="https://www.tassos.gr/kb/general/how-to-enable-geolocation-features-in-tassos-gr-extensions#create_maxmind_license_key"
|
||||
required="true"
|
||||
/>
|
||||
<field name="updatebutton" type="tg_updatebutton"
|
||||
label="PLG_SYSTEM_TGEOIP_UPDATE_DATABASE"
|
||||
description="PLG_SYSTEM_TGEOIP_UPDATE_DATABASE_DESC"
|
||||
/>
|
||||
<field name="lastupdated" type="tg_lastupdated"
|
||||
label="PLG_SYSTEM_TGEOIP_LAST_UPDATED"
|
||||
description="PLG_SYSTEM_TGEOIP_LAST_UPDATED_DESC"
|
||||
/>
|
||||
<field name="blockEnd" type="nr_well"
|
||||
end="1"
|
||||
/>
|
||||
<field name="blockStart1" type="nr_well"
|
||||
label="PLG_SYSTEM_TGEOIP_CHECK_IP"
|
||||
description="PLG_SYSTEM_TGEOIP_CHECK_IP_DESC"
|
||||
/>
|
||||
<field name="testdrive" type="tg_lookup"
|
||||
label="PLG_SYSTEM_TGEOIP_CHECK_IP"
|
||||
description="PLG_SYSTEM_TGEOIP_CHECK_IP_DESC"
|
||||
/>
|
||||
<field name="blockEnd1" type="nr_well"
|
||||
end="1"
|
||||
/>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
25
plugins/system/tgeoip/vendor/autoload.php
vendored
Normal file
25
plugins/system/tgeoip/vendor/autoload.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d::getLoader();
|
||||
579
plugins/system/tgeoip/vendor/composer/ClassLoader.php
vendored
Normal file
579
plugins/system/tgeoip/vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,579 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var string|null */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* List of PSR-0 prefixes
|
||||
*
|
||||
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
|
||||
*
|
||||
* @var array<string, array<string, list<string>>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var list<string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var string|null */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var array<string, self>
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param string|null $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string> Array of classname => path
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $classMap Class to filename map
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
$paths = (array) $paths;
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
$paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
$paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param list<string>|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param list<string>|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders keyed by their corresponding vendor directories.
|
||||
*
|
||||
* @return array<string, self>
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
313
plugins/system/tgeoip/vendor/composer/InstalledVersions.php
vendored
Normal file
313
plugins/system/tgeoip/vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Tassos\Vendor\Composer;
|
||||
|
||||
use Tassos\Vendor\Composer\Autoload\ClassLoader;
|
||||
use Tassos\Vendor\Composer\Semver\VersionParser;
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = \array_keys($installed['versions']);
|
||||
}
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
return \array_keys(\array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $packagesByType;
|
||||
}
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = \true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === \false;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (\array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = \array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (\array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = \array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (\array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = \array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
return \implode(' || ', $ranges);
|
||||
}
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@\trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', \E_USER_DEPRECATED);
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (\substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = (include __DIR__ . '/installed.php');
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
return self::$installed;
|
||||
}
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = \method_exists('Tassos\\Vendor\\Composer\\Autoload\\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
$installed = array();
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (\is_file($vendorDir . '/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = (require $vendorDir . '/composer/installed.php');
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && \strtr($vendorDir . '/composer', '\\', '/') === \strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[\count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (\substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = (require __DIR__ . '/installed.php');
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
10
plugins/system/tgeoip/vendor/composer/autoload_classmap.php
vendored
Normal file
10
plugins/system/tgeoip/vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
);
|
||||
9
plugins/system/tgeoip/vendor/composer/autoload_namespaces.php
vendored
Normal file
9
plugins/system/tgeoip/vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
15
plugins/system/tgeoip/vendor/composer/autoload_psr4.php
vendored
Normal file
15
plugins/system/tgeoip/vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Tassos\\Vendor\\splitbrain\\PHPArchive\\' => array($vendorDir . '/splitbrain/php-archive/src'),
|
||||
'Tassos\\Vendor\\MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'),
|
||||
'Tassos\\Vendor\\MaxMind\\Exception\\' => array($vendorDir . '/maxmind/web-service-common/src/Exception'),
|
||||
'Tassos\\Vendor\\MaxMind\\Db\\' => array($vendorDir . '/maxmind-db/reader/src/MaxMind/Db'),
|
||||
'Tassos\\Vendor\\GeoIp2\\' => array($vendorDir . '/geoip2/geoip2/src'),
|
||||
'Tassos\\Vendor\\Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
|
||||
);
|
||||
38
plugins/system/tgeoip/vendor/composer/autoload_real.php
vendored
Normal file
38
plugins/system/tgeoip/vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
61
plugins/system/tgeoip/vendor/composer/autoload_static.php
vendored
Normal file
61
plugins/system/tgeoip/vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'T' =>
|
||||
array (
|
||||
'Tassos\\Vendor\\splitbrain\\PHPArchive\\' => 36,
|
||||
'Tassos\\Vendor\\MaxMind\\WebService\\' => 33,
|
||||
'Tassos\\Vendor\\MaxMind\\Exception\\' => 32,
|
||||
'Tassos\\Vendor\\MaxMind\\Db\\' => 25,
|
||||
'Tassos\\Vendor\\GeoIp2\\' => 21,
|
||||
'Tassos\\Vendor\\Composer\\CaBundle\\' => 32,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Tassos\\Vendor\\splitbrain\\PHPArchive\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/splitbrain/php-archive/src',
|
||||
),
|
||||
'Tassos\\Vendor\\MaxMind\\WebService\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/WebService',
|
||||
),
|
||||
'Tassos\\Vendor\\MaxMind\\Exception\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/Exception',
|
||||
),
|
||||
'Tassos\\Vendor\\MaxMind\\Db\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/maxmind-db/reader/src/MaxMind/Db',
|
||||
),
|
||||
'Tassos\\Vendor\\GeoIp2\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/geoip2/geoip2/src',
|
||||
),
|
||||
'Tassos\\Vendor\\Composer\\CaBundle\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/composer/ca-bundle/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit087ac1c88c9dd7b872309175c7c60d7d::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
3372
plugins/system/tgeoip/vendor/composer/ca-bundle/res/cacert.pem
vendored
Normal file
3372
plugins/system/tgeoip/vendor/composer/ca-bundle/res/cacert.pem
vendored
Normal file
File diff suppressed because it is too large
Load Diff
361
plugins/system/tgeoip/vendor/composer/ca-bundle/src/CaBundle.php
vendored
Normal file
361
plugins/system/tgeoip/vendor/composer/ca-bundle/src/CaBundle.php
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of composer/ca-bundle.
|
||||
*
|
||||
* (c) Composer <https://github.com/composer>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
namespace Tassos\Vendor\Composer\CaBundle;
|
||||
|
||||
use Tassos\Vendor\Psr\Log\LoggerInterface;
|
||||
use Tassos\Vendor\Symfony\Component\Process\PhpProcess;
|
||||
/**
|
||||
* @author Chris Smith <chris@cs278.org>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class CaBundle
|
||||
{
|
||||
/** @var string|null */
|
||||
private static $caPath;
|
||||
/** @var array<string, bool> */
|
||||
private static $caFileValidity = array();
|
||||
/** @var bool|null */
|
||||
private static $useOpensslParse;
|
||||
/**
|
||||
* Returns the system CA bundle path, or a path to the bundled one
|
||||
*
|
||||
* This method was adapted from Sslurp.
|
||||
* https://github.com/EvanDotPro/Sslurp
|
||||
*
|
||||
* (c) Evan Coury <me@evancoury.com>
|
||||
*
|
||||
* For the full copyright and license information, please see below:
|
||||
*
|
||||
* Copyright (c) 2013, Evan Coury
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
* @return string path to a CA bundle file or directory
|
||||
*/
|
||||
public static function getSystemCaRootBundlePath(LoggerInterface $logger = null)
|
||||
{
|
||||
if (self::$caPath !== null) {
|
||||
return self::$caPath;
|
||||
}
|
||||
$caBundlePaths = array();
|
||||
// If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_FILE');
|
||||
// If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
|
||||
// This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
|
||||
$caBundlePaths[] = self::getEnvVariable('SSL_CERT_DIR');
|
||||
$caBundlePaths[] = \ini_get('openssl.cafile');
|
||||
$caBundlePaths[] = \ini_get('openssl.capath');
|
||||
$otherLocations = array(
|
||||
'/etc/pki/tls/certs/ca-bundle.crt',
|
||||
// Fedora, RHEL, CentOS (ca-certificates package)
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
// Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
|
||||
'/etc/ssl/ca-bundle.pem',
|
||||
// SUSE, openSUSE (ca-certificates package)
|
||||
'/usr/local/share/certs/ca-root-nss.crt',
|
||||
// FreeBSD (ca_root_nss_package)
|
||||
'/usr/ssl/certs/ca-bundle.crt',
|
||||
// Cygwin
|
||||
'/opt/local/share/curl/curl-ca-bundle.crt',
|
||||
// OS X macports, curl-ca-bundle package
|
||||
'/usr/local/share/curl/curl-ca-bundle.crt',
|
||||
// Default cURL CA bunde path (without --with-ca-bundle option)
|
||||
'/usr/share/ssl/certs/ca-bundle.crt',
|
||||
// Really old RedHat?
|
||||
'/etc/ssl/cert.pem',
|
||||
// OpenBSD
|
||||
'/usr/local/etc/ssl/cert.pem',
|
||||
// FreeBSD 10.x
|
||||
'/usr/local/etc/openssl/cert.pem',
|
||||
// OS X homebrew, openssl package
|
||||
'/usr/local/etc/openssl@1.1/cert.pem',
|
||||
);
|
||||
foreach ($otherLocations as $location) {
|
||||
$otherLocations[] = \dirname($location);
|
||||
}
|
||||
$caBundlePaths = \array_merge($caBundlePaths, $otherLocations);
|
||||
foreach ($caBundlePaths as $caBundle) {
|
||||
if ($caBundle && self::caFileUsable($caBundle, $logger)) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
if ($caBundle && self::caDirUsable($caBundle, $logger)) {
|
||||
return self::$caPath = $caBundle;
|
||||
}
|
||||
}
|
||||
return self::$caPath = static::getBundledCaBundlePath();
|
||||
// Bundled CA file, last resort
|
||||
}
|
||||
/**
|
||||
* Returns the path to the bundled CA file
|
||||
*
|
||||
* In case you don't want to trust the user or the system, you can use this directly
|
||||
*
|
||||
* @return string path to a CA bundle file
|
||||
*/
|
||||
public static function getBundledCaBundlePath()
|
||||
{
|
||||
$caBundleFile = __DIR__ . '/../res/cacert.pem';
|
||||
// cURL does not understand 'phar://' paths
|
||||
// see https://github.com/composer/ca-bundle/issues/10
|
||||
if (0 === \strpos($caBundleFile, 'phar://')) {
|
||||
$tempCaBundleFile = \tempnam(\sys_get_temp_dir(), 'openssl-ca-bundle-');
|
||||
if (\false === $tempCaBundleFile) {
|
||||
throw new \RuntimeException('Could not create a temporary file to store the bundled CA file');
|
||||
}
|
||||
\file_put_contents($tempCaBundleFile, \file_get_contents($caBundleFile));
|
||||
\register_shutdown_function(function () use($tempCaBundleFile) {
|
||||
@\unlink($tempCaBundleFile);
|
||||
});
|
||||
$caBundleFile = $tempCaBundleFile;
|
||||
}
|
||||
return $caBundleFile;
|
||||
}
|
||||
/**
|
||||
* Validates a CA file using opensl_x509_parse only if it is safe to use
|
||||
*
|
||||
* @param string $filename
|
||||
* @param LoggerInterface $logger optional logger for information about which CA files were loaded
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateCaFile($filename, LoggerInterface $logger = null)
|
||||
{
|
||||
static $warned = \false;
|
||||
if (isset(self::$caFileValidity[$filename])) {
|
||||
return self::$caFileValidity[$filename];
|
||||
}
|
||||
$contents = \file_get_contents($filename);
|
||||
// assume the CA is valid if php is vulnerable to
|
||||
// https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
|
||||
if (!static::isOpensslParseSafe()) {
|
||||
if (!$warned && $logger) {
|
||||
$logger->warning(\sprintf('Your version of PHP, %s, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.', \PHP_VERSION));
|
||||
$warned = \true;
|
||||
}
|
||||
$isValid = !empty($contents);
|
||||
} elseif (\is_string($contents) && \strlen($contents) > 0) {
|
||||
$contents = \preg_replace("/^(\\-+(?:BEGIN|END))\\s+TRUSTED\\s+(CERTIFICATE\\-+)\$/m", '$1 $2', $contents);
|
||||
if (null === $contents) {
|
||||
// regex extraction failed
|
||||
$isValid = \false;
|
||||
} else {
|
||||
$isValid = (bool) \openssl_x509_parse($contents);
|
||||
}
|
||||
} else {
|
||||
$isValid = \false;
|
||||
}
|
||||
if ($logger) {
|
||||
$logger->debug('Checked CA file ' . \realpath($filename) . ': ' . ($isValid ? 'valid' : 'invalid'));
|
||||
}
|
||||
return self::$caFileValidity[$filename] = $isValid;
|
||||
}
|
||||
/**
|
||||
* Test if it is safe to use the PHP function openssl_x509_parse().
|
||||
*
|
||||
* This checks if OpenSSL extensions is vulnerable to remote code execution
|
||||
* via the exploit documented as CVE-2013-6420.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isOpensslParseSafe()
|
||||
{
|
||||
if (null !== self::$useOpensslParse) {
|
||||
return self::$useOpensslParse;
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 50600) {
|
||||
return self::$useOpensslParse = \true;
|
||||
}
|
||||
// Vulnerable:
|
||||
// PHP 5.3.0 - PHP 5.3.27
|
||||
// PHP 5.4.0 - PHP 5.4.22
|
||||
// PHP 5.5.0 - PHP 5.5.6
|
||||
if (\PHP_VERSION_ID < 50400 && \PHP_VERSION_ID >= 50328 || \PHP_VERSION_ID < 50500 && \PHP_VERSION_ID >= 50423 || \PHP_VERSION_ID >= 50507) {
|
||||
// This version of PHP has the fix for CVE-2013-6420 applied.
|
||||
return self::$useOpensslParse = \true;
|
||||
}
|
||||
if (\defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
// Windows is probably insecure in this case.
|
||||
return self::$useOpensslParse = \false;
|
||||
}
|
||||
$compareDistroVersionPrefix = function ($prefix, $fixedVersion) {
|
||||
$regex = '{^' . \preg_quote($prefix) . '([0-9]+)$}';
|
||||
if (\preg_match($regex, \PHP_VERSION, $m)) {
|
||||
return (int) $m[1] >= $fixedVersion;
|
||||
}
|
||||
return \false;
|
||||
};
|
||||
// Hard coded list of PHP distributions with the fix backported.
|
||||
if ($compareDistroVersionPrefix('5.3.3-7+squeeze', 18) || $compareDistroVersionPrefix('5.4.4-14+deb7u', 7) || $compareDistroVersionPrefix('5.3.10-1ubuntu3.', 9)) {
|
||||
return self::$useOpensslParse = \true;
|
||||
}
|
||||
// Symfony Process component is missing so we assume it is unsafe at this point
|
||||
if (!\class_exists('Tassos\\Vendor\\Symfony\\Component\\Process\\PhpProcess')) {
|
||||
return self::$useOpensslParse = \false;
|
||||
}
|
||||
// This is where things get crazy, because distros backport security
|
||||
// fixes the chances are on NIX systems the fix has been applied but
|
||||
// it's not possible to verify that from the PHP version.
|
||||
//
|
||||
// To verify exec a new PHP process and run the issue testcase with
|
||||
// known safe input that replicates the bug.
|
||||
// Based on testcase in https://github.com/php/php-src/commit/c1224573c773b6845e83505f717fbf820fc18415
|
||||
// changes in https://github.com/php/php-src/commit/76a7fd893b7d6101300cc656058704a73254d593
|
||||
$cert = 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVwRENDQTR5Z0F3SUJBZ0lKQUp6dThyNnU2ZUJjTUEwR0NTcUdTSWIzRFFFQkJRVUFNSUhETVFzd0NRWUQKVlFRR0V3SkVSVEVjTUJvR0ExVUVDQXdUVG05eVpISm9aV2x1TFZkbGMzUm1ZV3hsYmpFUU1BNEdBMVVFQnd3SApTOE9Ed3Jac2JqRVVNQklHQTFVRUNnd0xVMlZyZEdsdmJrVnBibk14SHpBZEJnTlZCQXNNRmsxaGJHbGphVzkxCmN5QkRaWEowSUZObFkzUnBiMjR4SVRBZkJnTlZCQU1NR0cxaGJHbGphVzkxY3k1elpXdDBhVzl1WldsdWN5NWsKWlRFcU1DZ0dDU3FHU0liM0RRRUpBUlliYzNSbFptRnVMbVZ6YzJWeVFITmxhM1JwYjI1bGFXNXpMbVJsTUhVWQpaREU1TnpBd01UQXhNREF3TURBd1dnQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBCkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEKQUFBQUFBQVhEVEUwTVRFeU9ERXhNemt6TlZvd2djTXhDekFKQmdOVkJBWVRBa1JGTVJ3d0dnWURWUVFJREJOTwpiM0prY21obGFXNHRWMlZ6ZEdaaGJHVnVNUkF3RGdZRFZRUUhEQWRMdzRQQ3RteHVNUlF3RWdZRFZRUUtEQXRUClpXdDBhVzl1UldsdWN6RWZNQjBHQTFVRUN3d1dUV0ZzYVdOcGIzVnpJRU5sY25RZ1UyVmpkR2x2YmpFaE1COEcKQTFVRUF3d1liV0ZzYVdOcGIzVnpMbk5sYTNScGIyNWxhVzV6TG1SbE1Tb3dLQVlKS29aSWh2Y05BUWtCRmh0egpkR1ZtWVc0dVpYTnpaWEpBYzJWcmRHbHZibVZwYm5NdVpHVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRERBZjNobDdKWTBYY0ZuaXlFSnBTU0RxbjBPcUJyNlFQNjV1c0pQUnQvOFBhRG9xQnUKd0VZVC9OYSs2ZnNnUGpDMHVLOURaZ1dnMnRIV1dvYW5TYmxBTW96NVBINlorUzRTSFJaN2UyZERJalBqZGhqaAowbUxnMlVNTzV5cDBWNzk3R2dzOWxOdDZKUmZIODFNTjJvYlhXczROdHp0TE11RDZlZ3FwcjhkRGJyMzRhT3M4CnBrZHVpNVVhd1Raa3N5NXBMUEhxNWNNaEZHbTA2djY1Q0xvMFYyUGQ5K0tBb2tQclBjTjVLTEtlYno3bUxwazYKU01lRVhPS1A0aWRFcXh5UTdPN2ZCdUhNZWRzUWh1K3ByWTNzaTNCVXlLZlF0UDVDWm5YMmJwMHdLSHhYMTJEWAoxbmZGSXQ5RGJHdkhUY3lPdU4rblpMUEJtM3ZXeG50eUlJdlZBZ01CQUFHalFqQkFNQWtHQTFVZEV3UUNNQUF3CkVRWUpZSVpJQVliNFFnRUJCQVFEQWdlQU1Bc0dBMVVkRHdRRUF3SUZvREFUQmdOVkhTVUVEREFLQmdnckJnRUYKQlFjREFqQU5CZ2txaGtpRzl3MEJBUVVGQUFPQ0FRRUFHMGZaWVlDVGJkajFYWWMrMVNub2FQUit2SThDOENhRAo4KzBVWWhkbnlVNGdnYTBCQWNEclk5ZTk0ZUVBdTZacXljRjZGakxxWFhkQWJvcHBXb2NyNlQ2R0QxeDMzQ2tsClZBcnpHL0t4UW9oR0QySmVxa2hJTWxEb214SE83a2EzOStPYThpMnZXTFZ5alU4QVp2V01BcnVIYTRFRU55RzcKbFcyQWFnYUZLRkNyOVRuWFRmcmR4R1ZFYnY3S1ZRNmJkaGc1cDVTanBXSDErTXEwM3VSM1pYUEJZZHlWODMxOQpvMGxWajFLRkkyRENML2xpV2lzSlJvb2YrMWNSMzVDdGQwd1lCY3BCNlRac2xNY09QbDc2ZHdLd0pnZUpvMlFnClpzZm1jMnZDMS9xT2xOdU5xLzBUenprVkd2OEVUVDNDZ2FVK1VYZTRYT1Z2a2NjZWJKbjJkZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K';
|
||||
$script = <<<'EOT'
|
||||
|
||||
error_reporting(-1);
|
||||
$info = openssl_x509_parse(base64_decode('%s'));
|
||||
var_dump(PHP_VERSION, $info['issuer']['emailAddress'], $info['validFrom_time_t']);
|
||||
|
||||
EOT;
|
||||
$script = '<' . "?php\n" . \sprintf($script, $cert);
|
||||
try {
|
||||
$process = new PhpProcess($script);
|
||||
$process->mustRun();
|
||||
} catch (\Exception $e) {
|
||||
// In the case of any exceptions just accept it is not possible to
|
||||
// determine the safety of openssl_x509_parse and bail out.
|
||||
return self::$useOpensslParse = \false;
|
||||
}
|
||||
$output = \preg_split('{\\r?\\n}', \trim($process->getOutput()));
|
||||
$errorOutput = \trim($process->getErrorOutput());
|
||||
if (\is_array($output) && \count($output) === 3 && $output[0] === \sprintf('string(%d) "%s"', \strlen(\PHP_VERSION), \PHP_VERSION) && $output[1] === 'string(27) "stefan.esser@sektioneins.de"' && $output[2] === 'int(-1)' && \preg_match('{openssl_x509_parse\\(\\): illegal (?:ASN1 data type for|length in) timestamp in - on line \\d+}', $errorOutput)) {
|
||||
// This PHP has the fix backported probably by a distro security team.
|
||||
return self::$useOpensslParse = \true;
|
||||
}
|
||||
return self::$useOpensslParse = \false;
|
||||
}
|
||||
/**
|
||||
* Resets the static caches
|
||||
* @return void
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$caFileValidity = array();
|
||||
self::$caPath = null;
|
||||
self::$useOpensslParse = null;
|
||||
}
|
||||
/**
|
||||
* @param string $name
|
||||
* @return string|false
|
||||
*/
|
||||
private static function getEnvVariable($name)
|
||||
{
|
||||
if (isset($_SERVER[$name])) {
|
||||
return (string) $_SERVER[$name];
|
||||
}
|
||||
if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== \false && $value !== null) {
|
||||
return (string) $value;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* @param string|false $certFile
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function caFileUsable($certFile, LoggerInterface $logger = null)
|
||||
{
|
||||
return $certFile && static::isFile($certFile, $logger) && static::isReadable($certFile, $logger) && static::validateCaFile($certFile, $logger);
|
||||
}
|
||||
/**
|
||||
* @param string|false $certDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function caDirUsable($certDir, LoggerInterface $logger = null)
|
||||
{
|
||||
return $certDir && static::isDir($certDir, $logger) && static::isReadable($certDir, $logger) && static::glob($certDir . '/*', $logger);
|
||||
}
|
||||
/**
|
||||
* @param string $certFile
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isFile($certFile, LoggerInterface $logger = null)
|
||||
{
|
||||
$isFile = @\is_file($certFile);
|
||||
if (!$isFile && $logger) {
|
||||
$logger->debug(\sprintf('Checked CA file %s does not exist or it is not a file.', $certFile));
|
||||
}
|
||||
return $isFile;
|
||||
}
|
||||
/**
|
||||
* @param string $certDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isDir($certDir, LoggerInterface $logger = null)
|
||||
{
|
||||
$isDir = @\is_dir($certDir);
|
||||
if (!$isDir && $logger) {
|
||||
$logger->debug(\sprintf('Checked directory %s does not exist or it is not a directory.', $certDir));
|
||||
}
|
||||
return $isDir;
|
||||
}
|
||||
/**
|
||||
* @param string $certFileOrDir
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function isReadable($certFileOrDir, LoggerInterface $logger = null)
|
||||
{
|
||||
$isReadable = @\is_readable($certFileOrDir);
|
||||
if (!$isReadable && $logger) {
|
||||
$logger->debug(\sprintf('Checked file or directory %s is not readable.', $certFileOrDir));
|
||||
}
|
||||
return $isReadable;
|
||||
}
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param LoggerInterface|null $logger
|
||||
* @return bool
|
||||
*/
|
||||
private static function glob($pattern, LoggerInterface $logger = null)
|
||||
{
|
||||
$certs = \glob($pattern);
|
||||
if ($certs === \false) {
|
||||
if ($logger) {
|
||||
$logger->debug(\sprintf("An error occurred while trying to find certificates for pattern: %s", $pattern));
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
if (\count($certs) === 0) {
|
||||
if ($logger) {
|
||||
$logger->debug(\sprintf("No CA files found for pattern: %s", $pattern));
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
327
plugins/system/tgeoip/vendor/composer/installed.json
vendored
Normal file
327
plugins/system/tgeoip/vendor/composer/installed.json
vendored
Normal file
@ -0,0 +1,327 @@
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer\/ca-bundle",
|
||||
"version": "1.3.5",
|
||||
"version_normalized": "1.3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https:\/\/github.com\/composer\/ca-bundle.git",
|
||||
"reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https:\/\/api.github.com\/repos\/composer\/ca-bundle\/zipball\/74780ccf8c19d6acb8d65c5f39cd72110e132bbd",
|
||||
"reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"ext-pcre": "*",
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan\/phpstan": "^0.12.55",
|
||||
"psr\/log": "^1.0",
|
||||
"symfony\/phpunit-bridge": "^4.2 || ^5",
|
||||
"symfony\/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
|
||||
},
|
||||
"time": "2023-01-11T08:27:00+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tassos\\Vendor\\Composer\\CaBundle\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https:\/\/packagist.org\/downloads\/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http:\/\/seld.be"
|
||||
}
|
||||
],
|
||||
"description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.",
|
||||
"keywords": [
|
||||
"cabundle",
|
||||
"cacert",
|
||||
"certificate",
|
||||
"ssl",
|
||||
"tls"
|
||||
],
|
||||
"support": {
|
||||
"irc": "irc:\/\/irc.freenode.org\/composer",
|
||||
"issues": "https:\/\/github.com\/composer\/ca-bundle\/issues",
|
||||
"source": "https:\/\/github.com\/composer\/ca-bundle\/tree\/1.3.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https:\/\/packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https:\/\/github.com\/composer",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https:\/\/tidelift.com\/funding\/github\/packagist\/composer\/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"install-path": ".\/ca-bundle"
|
||||
},
|
||||
{
|
||||
"name": "geoip2\/geoip2",
|
||||
"version": "v2.13.0",
|
||||
"version_normalized": "2.13.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:maxmind\/GeoIP2-php.git",
|
||||
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https:\/\/api.github.com\/repos\/maxmind\/GeoIP2-php\/zipball\/6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
|
||||
"reference": "6a41d8fbd6b90052bc34dff3b4252d0f88067b23",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"maxmind-db\/reader": "~1.8",
|
||||
"maxmind\/web-service-common": "~0.8",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp\/php-cs-fixer": "3.*",
|
||||
"phpstan\/phpstan": "*",
|
||||
"phpunit\/phpunit": "^8.0 || ^9.0",
|
||||
"squizlabs\/php_codesniffer": "3.*"
|
||||
},
|
||||
"time": "2022-08-05T20:32:58+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tassos\\Vendor\\GeoIp2\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https:\/\/packagist.org\/downloads\/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gregory J. Oschwald",
|
||||
"email": "goschwald@maxmind.com",
|
||||
"homepage": "https:\/\/www.maxmind.com\/"
|
||||
}
|
||||
],
|
||||
"description": "MaxMind GeoIP2 PHP API",
|
||||
"homepage": "https:\/\/github.com\/maxmind\/GeoIP2-php",
|
||||
"keywords": [
|
||||
"IP",
|
||||
"geoip",
|
||||
"geoip2",
|
||||
"geolocation",
|
||||
"maxmind"
|
||||
],
|
||||
"install-path": "..\/geoip2\/geoip2"
|
||||
},
|
||||
{
|
||||
"name": "maxmind-db\/reader",
|
||||
"version": "v1.11.0",
|
||||
"version_normalized": "1.11.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php.git",
|
||||
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https:\/\/api.github.com\/repos\/maxmind\/MaxMind-DB-Reader-php\/zipball\/b1f3c0699525336d09cc5161a2861268d9f2ae5b",
|
||||
"reference": "b1f3c0699525336d09cc5161a2861268d9f2ae5b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-maxminddb": "<1.10.1,>=2.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp\/php-cs-fixer": "3.*",
|
||||
"php-coveralls\/php-coveralls": "^2.1",
|
||||
"phpstan\/phpstan": "*",
|
||||
"phpunit\/phpcov": ">=6.0.0",
|
||||
"phpunit\/phpunit": ">=8.0.0,<10.0.0",
|
||||
"squizlabs\/php_codesniffer": "3.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
|
||||
"ext-gmp": "bcmath or gmp is required for decoding larger integers with the pure PHP decoder",
|
||||
"ext-maxminddb": "A C-based database decoder that provides significantly faster lookups"
|
||||
},
|
||||
"time": "2021-10-18T15:23:10+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tassos\\Vendor\\MaxMind\\Db\\": "src\/MaxMind\/Db"
|
||||
}
|
||||
},
|
||||
"notification-url": "https:\/\/packagist.org\/downloads\/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gregory J. Oschwald",
|
||||
"email": "goschwald@maxmind.com",
|
||||
"homepage": "https:\/\/www.maxmind.com\/"
|
||||
}
|
||||
],
|
||||
"description": "MaxMind DB Reader API",
|
||||
"homepage": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php",
|
||||
"keywords": [
|
||||
"database",
|
||||
"geoip",
|
||||
"geoip2",
|
||||
"geolocation",
|
||||
"maxmind"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php\/issues",
|
||||
"source": "https:\/\/github.com\/maxmind\/MaxMind-DB-Reader-php\/tree\/v1.11.0"
|
||||
},
|
||||
"install-path": "..\/maxmind-db\/reader"
|
||||
},
|
||||
{
|
||||
"name": "maxmind\/web-service-common",
|
||||
"version": "v0.9.0",
|
||||
"version_normalized": "0.9.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https:\/\/github.com\/maxmind\/web-service-common-php.git",
|
||||
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https:\/\/api.github.com\/repos\/maxmind\/web-service-common-php\/zipball\/4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
|
||||
"reference": "4dc5a3e8df38aea4ca3b1096cee3a038094e9b53",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer\/ca-bundle": "^1.0.3",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp\/php-cs-fixer": "3.*",
|
||||
"phpstan\/phpstan": "*",
|
||||
"phpunit\/phpunit": "^8.0 || ^9.0",
|
||||
"squizlabs\/php_codesniffer": "3.*"
|
||||
},
|
||||
"time": "2022-03-28T17:43:20+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tassos\\Vendor\\MaxMind\\Exception\\": "src\/Exception",
|
||||
"Tassos\\Vendor\\MaxMind\\WebService\\": "src\/WebService"
|
||||
}
|
||||
},
|
||||
"notification-url": "https:\/\/packagist.org\/downloads\/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gregory Oschwald",
|
||||
"email": "goschwald@maxmind.com"
|
||||
}
|
||||
],
|
||||
"description": "Internal MaxMind Web Service API",
|
||||
"homepage": "https:\/\/github.com\/maxmind\/web-service-common-php",
|
||||
"support": {
|
||||
"issues": "https:\/\/github.com\/maxmind\/web-service-common-php\/issues",
|
||||
"source": "https:\/\/github.com\/maxmind\/web-service-common-php\/tree\/v0.9.0"
|
||||
},
|
||||
"install-path": "..\/maxmind\/web-service-common"
|
||||
},
|
||||
{
|
||||
"name": "splitbrain\/php-archive",
|
||||
"version": "1.3.1",
|
||||
"version_normalized": "1.3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https:\/\/github.com\/splitbrain\/php-archive.git",
|
||||
"reference": "d274e5190ba309777926348900cf9578d9e533c9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https:\/\/api.github.com\/repos\/splitbrain\/php-archive\/zipball\/d274e5190ba309777926348900cf9578d9e533c9",
|
||||
"reference": "d274e5190ba309777926348900cf9578d9e533c9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-bz2": "*",
|
||||
"ext-zip": "*",
|
||||
"mikey179\/vfsstream": "^1.6",
|
||||
"phpunit\/phpunit": "^8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bz2": "For bz2 compression",
|
||||
"ext-iconv": "Used for proper filename encode handling",
|
||||
"ext-mbstring": "Can be used alternatively for handling filename encoding",
|
||||
"ext-zlib": "For zlib compression"
|
||||
},
|
||||
"time": "2022-03-23T09:21:55+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tassos\\Vendor\\splitbrain\\PHPArchive\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https:\/\/packagist.org\/downloads\/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"description": "Pure-PHP implementation to read and write TAR and ZIP archives",
|
||||
"keywords": [
|
||||
"archive",
|
||||
"extract",
|
||||
"tar",
|
||||
"unpack",
|
||||
"unzip",
|
||||
"zip"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https:\/\/github.com\/splitbrain\/php-archive\/issues",
|
||||
"source": "https:\/\/github.com\/splitbrain\/php-archive\/tree\/1.3.1"
|
||||
},
|
||||
"install-path": "..\/splitbrain\/php-archive"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": []
|
||||
}
|
||||
5
plugins/system/tgeoip/vendor/composer/installed.php
vendored
Normal file
5
plugins/system/tgeoip/vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor;
|
||||
|
||||
return array('root' => array('name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => '015eb26a70321a57e0c514ec4742cbe8a2580208', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => \true), 'versions' => array('__root__' => array('pretty_version' => 'dev-master', 'version' => 'dev-master', 'reference' => '015eb26a70321a57e0c514ec4742cbe8a2580208', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => \false), 'composer/ca-bundle' => array('pretty_version' => '1.3.5', 'version' => '1.3.5.0', 'reference' => '74780ccf8c19d6acb8d65c5f39cd72110e132bbd', 'type' => 'library', 'install_path' => __DIR__ . '/./ca-bundle', 'aliases' => array(), 'dev_requirement' => \false), 'geoip2/geoip2' => array('pretty_version' => 'v2.13.0', 'version' => '2.13.0.0', 'reference' => '6a41d8fbd6b90052bc34dff3b4252d0f88067b23', 'type' => 'library', 'install_path' => __DIR__ . '/../geoip2/geoip2', 'aliases' => array(), 'dev_requirement' => \false), 'maxmind-db/reader' => array('pretty_version' => 'v1.11.0', 'version' => '1.11.0.0', 'reference' => 'b1f3c0699525336d09cc5161a2861268d9f2ae5b', 'type' => 'library', 'install_path' => __DIR__ . '/../maxmind-db/reader', 'aliases' => array(), 'dev_requirement' => \false), 'maxmind/web-service-common' => array('pretty_version' => 'v0.9.0', 'version' => '0.9.0.0', 'reference' => '4dc5a3e8df38aea4ca3b1096cee3a038094e9b53', 'type' => 'library', 'install_path' => __DIR__ . '/../maxmind/web-service-common', 'aliases' => array(), 'dev_requirement' => \false), 'splitbrain/php-archive' => array('pretty_version' => '1.3.1', 'version' => '1.3.1.0', 'reference' => 'd274e5190ba309777926348900cf9578d9e533c9', 'type' => 'library', 'install_path' => __DIR__ . '/../splitbrain/php-archive', 'aliases' => array(), 'dev_requirement' => \false)));
|
||||
26
plugins/system/tgeoip/vendor/composer/platform_check.php
vendored
Normal file
26
plugins/system/tgeoip/vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 70200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
23
plugins/system/tgeoip/vendor/geoip2/geoip2/examples/benchmark.php
vendored
Normal file
23
plugins/system/tgeoip/vendor/geoip2/geoip2/examples/benchmark.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
use Tassos\Vendor\GeoIp2\Database\Reader;
|
||||
\srand(0);
|
||||
$reader = new Reader('GeoIP2-City.mmdb');
|
||||
$count = 500000;
|
||||
$startTime = \microtime(\true);
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$ip = \long2ip(\rand(0, 2 ** 32 - 1));
|
||||
try {
|
||||
$t = $reader->city($ip);
|
||||
} catch (\Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException $e) {
|
||||
}
|
||||
if ($i % 10000 === 0) {
|
||||
echo $i . ' ' . $ip . "\n";
|
||||
}
|
||||
}
|
||||
$endTime = \microtime(\true);
|
||||
$duration = $endTime - $startTime;
|
||||
echo 'Requests per second: ' . $count / $duration . "\n";
|
||||
246
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Database/Reader.php
vendored
Normal file
246
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Database/Reader.php
vendored
Normal file
@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Database;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException;
|
||||
use Tassos\Vendor\GeoIp2\Model\AbstractModel;
|
||||
use Tassos\Vendor\GeoIp2\Model\AnonymousIp;
|
||||
use Tassos\Vendor\GeoIp2\Model\Asn;
|
||||
use Tassos\Vendor\GeoIp2\Model\City;
|
||||
use Tassos\Vendor\GeoIp2\Model\ConnectionType;
|
||||
use Tassos\Vendor\GeoIp2\Model\Country;
|
||||
use Tassos\Vendor\GeoIp2\Model\Domain;
|
||||
use Tassos\Vendor\GeoIp2\Model\Enterprise;
|
||||
use Tassos\Vendor\GeoIp2\Model\Isp;
|
||||
use Tassos\Vendor\GeoIp2\ProviderInterface;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader as DbReader;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader\InvalidDatabaseException;
|
||||
/**
|
||||
* Instances of this class provide a reader for the GeoIP2 database format.
|
||||
* IP addresses can be looked up using the database specific methods.
|
||||
*
|
||||
* ## Usage ##
|
||||
*
|
||||
* The basic API for this class is the same for every database. First, you
|
||||
* create a reader object, specifying a file name. You then call the method
|
||||
* corresponding to the specific database, passing it the IP address you want
|
||||
* to look up.
|
||||
*
|
||||
* If the request succeeds, the method call will return a model class for
|
||||
* the method you called. This model in turn contains multiple record classes,
|
||||
* each of which represents part of the data returned by the database. If
|
||||
* the database does not contain the requested information, the attributes
|
||||
* on the record class will have a `null` value.
|
||||
*
|
||||
* If the address is not in the database, an
|
||||
* {@link \GeoIp2\Exception\AddressNotFoundException} exception will be
|
||||
* thrown. If an invalid IP address is passed to one of the methods, a
|
||||
* SPL {@link \InvalidArgumentException} will be thrown. If the database is
|
||||
* corrupt or invalid, a {@link \MaxMind\Db\Reader\InvalidDatabaseException}
|
||||
* will be thrown.
|
||||
*/
|
||||
class Reader implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var DbReader
|
||||
*/
|
||||
private $dbReader;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $dbType;
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $locales;
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $filename the path to the GeoIP2 database file
|
||||
* @param array $locales list of locale codes to use in name property
|
||||
* from most preferred to least preferred
|
||||
*
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function __construct(string $filename, array $locales = ['en'])
|
||||
{
|
||||
$this->dbReader = new DbReader($filename);
|
||||
$this->dbType = $this->dbReader->metadata()->databaseType;
|
||||
$this->locales = $locales;
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 City model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function city(string $ipAddress) : City
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->modelFor(City::class, 'City', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 Country model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function country(string $ipAddress) : Country
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->modelFor(Country::class, 'Country', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 Anonymous IP model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function anonymousIp(string $ipAddress) : AnonymousIp
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->flatModelFor(AnonymousIp::class, 'GeoIP2-Anonymous-IP', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoLite2 ASN model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function asn(string $ipAddress) : Asn
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->flatModelFor(Asn::class, 'GeoLite2-ASN', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 Connection Type model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function connectionType(string $ipAddress) : ConnectionType
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->flatModelFor(ConnectionType::class, 'GeoIP2-Connection-Type', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 Domain model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function domain(string $ipAddress) : Domain
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->flatModelFor(Domain::class, 'GeoIP2-Domain', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 Enterprise model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function enterprise(string $ipAddress) : Enterprise
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method returns a GeoIP2 ISP model.
|
||||
*
|
||||
* @param string $ipAddress an IPv4 or IPv6 address as a string
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address is
|
||||
* not in the database
|
||||
* @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
|
||||
* is corrupt or invalid
|
||||
*/
|
||||
public function isp(string $ipAddress) : Isp
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->flatModelFor(Isp::class, 'GeoIP2-ISP', $ipAddress);
|
||||
}
|
||||
private function modelFor(string $class, string $type, string $ipAddress) : AbstractModel
|
||||
{
|
||||
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
|
||||
$record['traits']['ip_address'] = $ipAddress;
|
||||
$record['traits']['prefix_len'] = $prefixLen;
|
||||
return new $class($record, $this->locales);
|
||||
}
|
||||
private function flatModelFor(string $class, string $type, string $ipAddress) : AbstractModel
|
||||
{
|
||||
[$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
|
||||
$record['ip_address'] = $ipAddress;
|
||||
$record['prefix_len'] = $prefixLen;
|
||||
return new $class($record);
|
||||
}
|
||||
private function getRecord(string $class, string $type, string $ipAddress) : array
|
||||
{
|
||||
if (\strpos($this->dbType, $type) === \false) {
|
||||
$method = \lcfirst((new \ReflectionClass($class))->getShortName());
|
||||
throw new \BadMethodCallException("The {$method} method cannot be used to open a {$this->dbType} database");
|
||||
}
|
||||
[$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress);
|
||||
if ($record === null) {
|
||||
throw new AddressNotFoundException("The address {$ipAddress} is not in the database.");
|
||||
}
|
||||
if (!\is_array($record)) {
|
||||
// This can happen on corrupt databases. Generally,
|
||||
// MaxMind\Db\Reader will throw a
|
||||
// MaxMind\Db\Reader\InvalidDatabaseException, but occasionally
|
||||
// the lookup may result in a record that looks valid but is not
|
||||
// an array. This mostly happens when the user is ignoring all
|
||||
// exceptions and the more frequent InvalidDatabaseException
|
||||
// exceptions go unnoticed.
|
||||
throw new InvalidDatabaseException("Expected an array when looking up {$ipAddress} but received: " . \gettype($record));
|
||||
}
|
||||
return [$record, $prefixLen];
|
||||
}
|
||||
/**
|
||||
* @throws \InvalidArgumentException if arguments are passed to the method
|
||||
* @throws \BadMethodCallException if the database has been closed
|
||||
*
|
||||
* @return \MaxMind\Db\Reader\Metadata object for the database
|
||||
*/
|
||||
public function metadata() : DbReader\Metadata
|
||||
{
|
||||
return $this->dbReader->metadata();
|
||||
}
|
||||
/**
|
||||
* Closes the GeoIP2 database and returns the resources to the system.
|
||||
*/
|
||||
public function close() : void
|
||||
{
|
||||
$this->dbReader->close();
|
||||
}
|
||||
}
|
||||
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AddressNotFoundException.php
vendored
Normal file
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AddressNotFoundException.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class AddressNotFoundException extends GeoIp2Exception
|
||||
{
|
||||
}
|
||||
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AuthenticationException.php
vendored
Normal file
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/AuthenticationException.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class AuthenticationException extends GeoIp2Exception
|
||||
{
|
||||
}
|
||||
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/GeoIp2Exception.php
vendored
Normal file
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/GeoIp2Exception.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class GeoIp2Exception extends \Exception
|
||||
{
|
||||
}
|
||||
22
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/HttpException.php
vendored
Normal file
22
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/HttpException.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an HTTP transport error.
|
||||
*/
|
||||
class HttpException extends GeoIp2Exception
|
||||
{
|
||||
/**
|
||||
* The URI queried.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $uri;
|
||||
public function __construct(string $message, int $httpStatus, string $uri, \Exception $previous = null)
|
||||
{
|
||||
$this->uri = $uri;
|
||||
parent::__construct($message, $httpStatus, $previous);
|
||||
}
|
||||
}
|
||||
23
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/InvalidRequestException.php
vendored
Normal file
23
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/InvalidRequestException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an error returned by MaxMind's GeoIP2
|
||||
* web service.
|
||||
*/
|
||||
class InvalidRequestException extends HttpException
|
||||
{
|
||||
/**
|
||||
* The code returned by the MaxMind web service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $error;
|
||||
public function __construct(string $message, string $error, int $httpStatus, string $uri, \Exception $previous = null)
|
||||
{
|
||||
$this->error = $error;
|
||||
parent::__construct($message, $httpStatus, $uri, $previous);
|
||||
}
|
||||
}
|
||||
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/OutOfQueriesException.php
vendored
Normal file
11
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Exception/OutOfQueriesException.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic error.
|
||||
*/
|
||||
class OutOfQueriesException extends GeoIp2Exception
|
||||
{
|
||||
}
|
||||
60
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AbstractModel.php
vendored
Normal file
60
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AbstractModel.php
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
abstract class AbstractModel implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $raw;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
$this->raw = $raw;
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get(string $field)
|
||||
{
|
||||
if (isset($this->raw[$field])) {
|
||||
return $this->raw[$field];
|
||||
}
|
||||
if (\preg_match('/^is_/', $field)) {
|
||||
return \false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
if ($attr !== 'instance' && \property_exists($this, $attr)) {
|
||||
return $this->{$attr};
|
||||
}
|
||||
throw new \RuntimeException("Unknown attribute: {$attr}");
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __isset(string $attr) : bool
|
||||
{
|
||||
return $attr !== 'instance' && isset($this->{$attr});
|
||||
}
|
||||
public function jsonSerialize() : array
|
||||
{
|
||||
return $this->raw;
|
||||
}
|
||||
}
|
||||
80
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AnonymousIp.php
vendored
Normal file
80
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/AnonymousIp.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* This class provides the GeoIP2 Anonymous IP model.
|
||||
*
|
||||
* @property-read bool $isAnonymous This is true if the IP address belongs to
|
||||
* any sort of anonymous network.
|
||||
* @property-read bool $isAnonymousVpn This is true if the IP address is
|
||||
* registered to an anonymous VPN provider. If a VPN provider does not
|
||||
* register subnets under names associated with them, we will likely only
|
||||
* flag their IP ranges using the isHostingProvider property.
|
||||
* @property-read bool $isHostingProvider This is true if the IP address belongs
|
||||
* to a hosting or VPN provider (see description of isAnonymousVpn property).
|
||||
* @property-read bool $isPublicProxy This is true if the IP address belongs to
|
||||
* a public proxy.
|
||||
* @property-read bool $isResidentialProxy This is true if the IP address is
|
||||
* on a suspected anonymizing network and belongs to a residential ISP.
|
||||
* @property-read bool $isTorExitNode This is true if the IP address is a Tor
|
||||
* exit node.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class AnonymousIp extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAnonymous;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isAnonymousVpn;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isHostingProvider;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isPublicProxy;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isResidentialProxy;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isTorExitNode;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->isAnonymous = $this->get('is_anonymous');
|
||||
$this->isAnonymousVpn = $this->get('is_anonymous_vpn');
|
||||
$this->isHostingProvider = $this->get('is_hosting_provider');
|
||||
$this->isPublicProxy = $this->get('is_public_proxy');
|
||||
$this->isResidentialProxy = $this->get('is_residential_proxy');
|
||||
$this->isTorExitNode = $this->get('is_tor_exit_node');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
||||
51
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Asn.php
vendored
Normal file
51
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Asn.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* This class provides the GeoLite2 ASN model.
|
||||
*
|
||||
* @property-read int|null $autonomousSystemNumber The autonomous system number
|
||||
* associated with the IP address.
|
||||
* @property-read string|null $autonomousSystemOrganization The organization
|
||||
* associated with the registered autonomous system number for the IP
|
||||
* address.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class Asn extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
protected $autonomousSystemNumber;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $autonomousSystemOrganization;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->autonomousSystemNumber = $this->get('autonomous_system_number');
|
||||
$this->autonomousSystemOrganization = $this->get('autonomous_system_organization');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
||||
105
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/City.php
vendored
Normal file
105
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/City.php
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* Model class for the data returned by City Plus web service and City
|
||||
* database.
|
||||
*
|
||||
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
|
||||
* details.
|
||||
*
|
||||
* @property-read \GeoIp2\Record\City $city City data for the requested IP
|
||||
* address.
|
||||
* @property-read \GeoIp2\Record\Location $location Location data for the
|
||||
* requested IP address.
|
||||
* @property-read \GeoIp2\Record\Postal $postal Postal data for the
|
||||
* requested IP address.
|
||||
* @property-read array $subdivisions An array \GeoIp2\Record\Subdivision
|
||||
* objects representing the country subdivisions for the requested IP
|
||||
* address. The number and type of subdivisions varies by country, but a
|
||||
* subdivision is typically a state, province, county, etc. Subdivisions
|
||||
* are ordered from most general (largest) to most specific (smallest).
|
||||
* If the response did not contain any subdivisions, this method returns
|
||||
* an empty array.
|
||||
* @property-read \GeoIp2\Record\Subdivision $mostSpecificSubdivision An object
|
||||
* representing the most specific subdivision returned. If the response
|
||||
* did not contain any subdivisions, this method returns an empty
|
||||
* \GeoIp2\Record\Subdivision object.
|
||||
*/
|
||||
class City extends Country
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var \GeoIp2\Record\City
|
||||
*/
|
||||
protected $city;
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var \GeoIp2\Record\Location
|
||||
*/
|
||||
protected $location;
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var \GeoIp2\Record\Postal
|
||||
*/
|
||||
protected $postal;
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<\GeoIp2\Record\Subdivision>
|
||||
*/
|
||||
protected $subdivisions = [];
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw, array $locales = ['en'])
|
||||
{
|
||||
parent::__construct($raw, $locales);
|
||||
$this->city = new \Tassos\Vendor\GeoIp2\Record\City($this->get('city'), $locales);
|
||||
$this->location = new \Tassos\Vendor\GeoIp2\Record\Location($this->get('location'));
|
||||
$this->postal = new \Tassos\Vendor\GeoIp2\Record\Postal($this->get('postal'));
|
||||
$this->createSubdivisions($raw, $locales);
|
||||
}
|
||||
private function createSubdivisions(array $raw, array $locales) : void
|
||||
{
|
||||
if (!isset($raw['subdivisions'])) {
|
||||
return;
|
||||
}
|
||||
foreach ($raw['subdivisions'] as $sub) {
|
||||
$this->subdivisions[] = new \Tassos\Vendor\GeoIp2\Record\Subdivision($sub, $locales);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
if ($attr === 'mostSpecificSubdivision') {
|
||||
return $this->{$attr}();
|
||||
}
|
||||
return parent::__get($attr);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __isset(string $attr) : bool
|
||||
{
|
||||
if ($attr === 'mostSpecificSubdivision') {
|
||||
// We always return a mostSpecificSubdivision, even if it is the
|
||||
// empty subdivision
|
||||
return \true;
|
||||
}
|
||||
return parent::__isset($attr);
|
||||
}
|
||||
private function mostSpecificSubdivision() : \Tassos\Vendor\GeoIp2\Record\Subdivision
|
||||
{
|
||||
return empty($this->subdivisions) ? new \Tassos\Vendor\GeoIp2\Record\Subdivision([], $this->locales) : \end($this->subdivisions);
|
||||
}
|
||||
}
|
||||
44
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/ConnectionType.php
vendored
Normal file
44
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/ConnectionType.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* This class provides the GeoIP2 Connection-Type model.
|
||||
*
|
||||
* @property-read string|null $connectionType The connection type may take the
|
||||
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular".
|
||||
* Additional values may be added in the future.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class ConnectionType extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $connectionType;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->connectionType = $this->get('connection_type');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
||||
74
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Country.php
vendored
Normal file
74
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Country.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* Model class for the data returned by GeoIP2 Country web service and database.
|
||||
*
|
||||
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details.
|
||||
*
|
||||
* @property-read \GeoIp2\Record\Continent $continent Continent data for the
|
||||
* requested IP address.
|
||||
* @property-read \GeoIp2\Record\Country $country Country data for the requested
|
||||
* IP address. This object represents the country where MaxMind believes the
|
||||
* end user is located.
|
||||
* @property-read \GeoIp2\Record\MaxMind $maxmind Data related to your MaxMind
|
||||
* account.
|
||||
* @property-read \GeoIp2\Record\Country $registeredCountry Registered country
|
||||
* data for the requested IP address. This record represents the country
|
||||
* where the ISP has registered a given IP block and may differ from the
|
||||
* user's country.
|
||||
* @property-read \GeoIp2\Record\RepresentedCountry $representedCountry
|
||||
* Represented country data for the requested IP address. The represented
|
||||
* country is used for things like military bases. It is only present when
|
||||
* the represented country differs from the country.
|
||||
* @property-read \GeoIp2\Record\Traits $traits Data for the traits of the
|
||||
* requested IP address.
|
||||
* @property-read array $raw The raw data from the web service.
|
||||
*/
|
||||
class Country extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var \GeoIp2\Record\Continent
|
||||
*/
|
||||
protected $continent;
|
||||
/**
|
||||
* @var \GeoIp2\Record\Country
|
||||
*/
|
||||
protected $country;
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $locales;
|
||||
/**
|
||||
* @var \GeoIp2\Record\MaxMind
|
||||
*/
|
||||
protected $maxmind;
|
||||
/**
|
||||
* @var \GeoIp2\Record\Country
|
||||
*/
|
||||
protected $registeredCountry;
|
||||
/**
|
||||
* @var \GeoIp2\Record\RepresentedCountry
|
||||
*/
|
||||
protected $representedCountry;
|
||||
/**
|
||||
* @var \GeoIp2\Record\Traits
|
||||
*/
|
||||
protected $traits;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw, array $locales = ['en'])
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->continent = new \Tassos\Vendor\GeoIp2\Record\Continent($this->get('continent'), $locales);
|
||||
$this->country = new \Tassos\Vendor\GeoIp2\Record\Country($this->get('country'), $locales);
|
||||
$this->maxmind = new \Tassos\Vendor\GeoIp2\Record\MaxMind($this->get('maxmind'));
|
||||
$this->registeredCountry = new \Tassos\Vendor\GeoIp2\Record\Country($this->get('registered_country'), $locales);
|
||||
$this->representedCountry = new \Tassos\Vendor\GeoIp2\Record\RepresentedCountry($this->get('represented_country'), $locales);
|
||||
$this->traits = new \Tassos\Vendor\GeoIp2\Record\Traits($this->get('traits'));
|
||||
$this->locales = $locales;
|
||||
}
|
||||
}
|
||||
44
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Domain.php
vendored
Normal file
44
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Domain.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* This class provides the GeoIP2 Domain model.
|
||||
*
|
||||
* @property-read string|null $domain The second level domain associated with the
|
||||
* IP address. This will be something like "example.com" or
|
||||
* "example.co.uk", not "foo.example.com".
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class Domain extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $domain;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->domain = $this->get('domain');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
||||
14
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Enterprise.php
vendored
Normal file
14
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Enterprise.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* Model class for the data returned by GeoIP2 Enterprise database lookups.
|
||||
*
|
||||
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for more
|
||||
* details.
|
||||
*/
|
||||
class Enterprise extends City
|
||||
{
|
||||
}
|
||||
14
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Insights.php
vendored
Normal file
14
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Insights.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
/**
|
||||
* Model class for the data returned by GeoIP2 Insights web service.
|
||||
*
|
||||
* See https://dev.maxmind.com/geoip/docs/web-services?lang=en for
|
||||
* more details.
|
||||
*/
|
||||
class Insights extends City
|
||||
{
|
||||
}
|
||||
81
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Isp.php
vendored
Normal file
81
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Model/Isp.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Model;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* This class provides the GeoIP2 ISP model.
|
||||
*
|
||||
* @property-read int|null $autonomousSystemNumber The autonomous system number
|
||||
* associated with the IP address.
|
||||
* @property-read string|null $autonomousSystemOrganization The organization
|
||||
* associated with the registered autonomous system number for the IP
|
||||
* address.
|
||||
* @property-read string|null $isp The name of the ISP associated with the IP
|
||||
* address.
|
||||
* @property-read string|null $mobileCountryCode The [mobile country code
|
||||
* (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
|
||||
* the IP address and ISP.
|
||||
* @property-read string|null $mobileNetworkCode The [mobile network code
|
||||
* (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
|
||||
* the IP address and ISP.
|
||||
* @property-read string|null $organization The name of the organization associated
|
||||
* with the IP address.
|
||||
* @property-read string $ipAddress The IP address that the data in the model is
|
||||
* for.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
*/
|
||||
class Isp extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
protected $autonomousSystemNumber;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $autonomousSystemOrganization;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $isp;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $mobileCountryCode;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $mobileNetworkCode;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $organization;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ipAddress;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $network;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(array $raw)
|
||||
{
|
||||
parent::__construct($raw);
|
||||
$this->autonomousSystemNumber = $this->get('autonomous_system_number');
|
||||
$this->autonomousSystemOrganization = $this->get('autonomous_system_organization');
|
||||
$this->isp = $this->get('isp');
|
||||
$this->mobileCountryCode = $this->get('mobile_country_code');
|
||||
$this->mobileNetworkCode = $this->get('mobile_network_code');
|
||||
$this->organization = $this->get('organization');
|
||||
$ipAddress = $this->get('ip_address');
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->network = Util::cidr($ipAddress, $this->get('prefix_len'));
|
||||
}
|
||||
}
|
||||
20
plugins/system/tgeoip/vendor/geoip2/geoip2/src/ProviderInterface.php
vendored
Normal file
20
plugins/system/tgeoip/vendor/geoip2/geoip2/src/ProviderInterface.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2;
|
||||
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $ipAddress an IPv4 or IPv6 address to lookup
|
||||
*
|
||||
* @return \GeoIp2\Model\Country a Country model for the requested IP address
|
||||
*/
|
||||
public function country(string $ipAddress) : Model\Country;
|
||||
/**
|
||||
* @param string $ipAddress an IPv4 or IPv6 address to lookup
|
||||
*
|
||||
* @return \GeoIp2\Model\City a City model for the requested IP address
|
||||
*/
|
||||
public function city(string $ipAddress) : Model\City;
|
||||
}
|
||||
57
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractPlaceRecord.php
vendored
Normal file
57
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractPlaceRecord.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
abstract class AbstractPlaceRecord extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $locales;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(?array $record, array $locales = ['en'])
|
||||
{
|
||||
$this->locales = $locales;
|
||||
parent::__construct($record);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
if ($attr === 'name') {
|
||||
return $this->name();
|
||||
}
|
||||
return parent::__get($attr);
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __isset(string $attr) : bool
|
||||
{
|
||||
if ($attr === 'name') {
|
||||
return $this->firstSetNameLocale() !== null;
|
||||
}
|
||||
return parent::__isset($attr);
|
||||
}
|
||||
private function name() : ?string
|
||||
{
|
||||
$locale = $this->firstSetNameLocale();
|
||||
// @phpstan-ignore-next-line
|
||||
return $locale === null ? null : $this->names[$locale];
|
||||
}
|
||||
private function firstSetNameLocale() : ?string
|
||||
{
|
||||
foreach ($this->locales as $locale) {
|
||||
if (isset($this->names[$locale])) {
|
||||
return $locale;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
56
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractRecord.php
vendored
Normal file
56
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/AbstractRecord.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
abstract class AbstractRecord implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $record;
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function __construct(?array $record)
|
||||
{
|
||||
$this->record = isset($record) ? $record : [];
|
||||
}
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
// XXX - kind of ugly but greatly reduces boilerplate code
|
||||
$key = $this->attributeToKey($attr);
|
||||
if ($this->__isset($attr)) {
|
||||
return $this->record[$key];
|
||||
}
|
||||
if ($this->validAttribute($attr)) {
|
||||
if (\preg_match('/^is_/', $key)) {
|
||||
return \false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
throw new \RuntimeException("Unknown attribute: {$attr}");
|
||||
}
|
||||
public function __isset(string $attr) : bool
|
||||
{
|
||||
return $this->validAttribute($attr) && isset($this->record[$this->attributeToKey($attr)]);
|
||||
}
|
||||
private function attributeToKey(string $attr) : string
|
||||
{
|
||||
return \strtolower(\preg_replace('/([A-Z])/', '_\1', $attr));
|
||||
}
|
||||
private function validAttribute(string $attr) : bool
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return \in_array($attr, $this->validAttributes, \true);
|
||||
}
|
||||
public function jsonSerialize() : ?array
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
}
|
||||
32
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/City.php
vendored
Normal file
32
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/City.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* City-level data associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases besides
|
||||
* Country.
|
||||
*
|
||||
* @property-read int|null $confidence A value from 0-100 indicating MaxMind's
|
||||
* confidence that the city is correct. This attribute is only available
|
||||
* from the Insights service and the GeoIP2 Enterprise database.
|
||||
* @property-read int|null $geonameId The GeoName ID for the city. This attribute
|
||||
* is returned by all location services and databases.
|
||||
* @property-read string|null $name The name of the city based on the locales list
|
||||
* passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class City extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['confidence', 'geonameId', 'names'];
|
||||
}
|
||||
31
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Continent.php
vendored
Normal file
31
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Continent.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the continent record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read string|null $code A two character continent code like "NA" (North
|
||||
* America) or "OC" (Oceania). This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read int|null $geonameId The GeoName ID for the continent. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read string|null $name Returns the name of the continent based on the
|
||||
* locales list passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class Continent extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['code', 'geonameId', 'names'];
|
||||
}
|
||||
37
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Country.php
vendored
Normal file
37
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Country.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the country record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read int|null $confidence A value from 0-100 indicating MaxMind's
|
||||
* confidence that the country is correct. This attribute is only available
|
||||
* from the Insights service and the GeoIP2 Enterprise database.
|
||||
* @property-read int|null $geonameId The GeoName ID for the country. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read bool $isInEuropeanUnion This is true if the country is a
|
||||
* member state of the European Union. This attribute is returned by all
|
||||
* location services and databases.
|
||||
* @property-read string|null $isoCode The two-character ISO 3166-1 alpha code
|
||||
* for the country. See https://en.wikipedia.org/wiki/ISO_3166-1. This
|
||||
* attribute is returned by all location services and databases.
|
||||
* @property-read string|null $name The name of the country based on the locales
|
||||
* list passed to the constructor. This attribute is returned by all location
|
||||
* services and databases.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* services and databases.
|
||||
*/
|
||||
class Country extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['confidence', 'geonameId', 'isInEuropeanUnion', 'isoCode', 'names'];
|
||||
}
|
||||
45
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Location.php
vendored
Normal file
45
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Location.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the location record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases besides
|
||||
* Country.
|
||||
*
|
||||
* @property-read int|null $averageIncome The average income in US dollars
|
||||
* associated with the requested IP address. This attribute is only available
|
||||
* from the Insights service.
|
||||
* @property-read int|null $accuracyRadius The approximate accuracy radius in
|
||||
* kilometers around the latitude and longitude for the IP address. This is
|
||||
* the radius where we have a 67% confidence that the device using the IP
|
||||
* address resides within the circle centered at the latitude and longitude
|
||||
* with the provided radius.
|
||||
* @property-read float|null $latitude The approximate latitude of the location
|
||||
* associated with the IP address. This value is not precise and should not be
|
||||
* used to identify a particular address or household.
|
||||
* @property-read float|null $longitude The approximate longitude of the location
|
||||
* associated with the IP address. This value is not precise and should not be
|
||||
* used to identify a particular address or household.
|
||||
* @property-read int|null $populationDensity The estimated population per square
|
||||
* kilometer associated with the IP address. This attribute is only available
|
||||
* from the Insights service.
|
||||
* @property-read int|null $metroCode The metro code of the location if the location
|
||||
* is in the US. MaxMind returns the same metro codes as the
|
||||
* Google AdWords API. See
|
||||
* https://developers.google.com/adwords/api/docs/appendix/cities-DMAregions.
|
||||
* @property-read string|null $timeZone The time zone associated with location, as
|
||||
* specified by the IANA Time Zone Database, e.g., "America/New_York". See
|
||||
* https://www.iana.org/time-zones.
|
||||
*/
|
||||
class Location extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['averageIncome', 'accuracyRadius', 'latitude', 'longitude', 'metroCode', 'populationDensity', 'postalCode', 'postalConfidence', 'timeZone'];
|
||||
}
|
||||
22
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/MaxMind.php
vendored
Normal file
22
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/MaxMind.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data about your account.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read int|null $queriesRemaining The number of remaining queries you
|
||||
* have for the service you are calling.
|
||||
*/
|
||||
class MaxMind extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['queriesRemaining'];
|
||||
}
|
||||
29
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Postal.php
vendored
Normal file
29
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Postal.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the postal record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location databases and services besides
|
||||
* Country.
|
||||
*
|
||||
* @property-read string|null $code The postal code of the location. Postal codes
|
||||
* are not available for all countries. In some countries, this will only
|
||||
* contain part of the postal code. This attribute is returned by all location
|
||||
* databases and services besides Country.
|
||||
* @property-read int|null $confidence A value from 0-100 indicating MaxMind's
|
||||
* confidence that the postal code is correct. This attribute is only
|
||||
* available from the Insights service and the GeoIP2 Enterprise
|
||||
* database.
|
||||
*/
|
||||
class Postal extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['code', 'confidence'];
|
||||
}
|
||||
25
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/RepresentedCountry.php
vendored
Normal file
25
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/RepresentedCountry.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the represented country associated with an IP address.
|
||||
*
|
||||
* This class contains the country-level data associated with an IP address
|
||||
* for the IP's represented country. The represented country is the country
|
||||
* represented by something like a military base.
|
||||
*
|
||||
* @property-read string|null $type A string indicating the type of entity that is
|
||||
* representing the country. Currently we only return <code>military</code>
|
||||
* but this could expand to include other types in the future.
|
||||
*/
|
||||
class RepresentedCountry extends Country
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['confidence', 'geonameId', 'isInEuropeanUnion', 'isoCode', 'names', 'type'];
|
||||
}
|
||||
38
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Subdivision.php
vendored
Normal file
38
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Subdivision.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
/**
|
||||
* Contains data for the subdivisions associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location databases and services besides
|
||||
* Country.
|
||||
*
|
||||
* @property-read int|null $confidence This is a value from 0-100 indicating
|
||||
* MaxMind's confidence that the subdivision is correct. This attribute is
|
||||
* only available from the Insights service and the GeoIP2 Enterprise
|
||||
* database.
|
||||
* @property-read int|null $geonameId This is a GeoName ID for the subdivision.
|
||||
* This attribute is returned by all location databases and services besides
|
||||
* Country.
|
||||
* @property-read string|null $isoCode This is a string up to three characters long
|
||||
* contain the subdivision portion of the ISO 3166-2 code. See
|
||||
* https://en.wikipedia.org/wiki/ISO_3166-2. This attribute is returned by all
|
||||
* location databases and services except Country.
|
||||
* @property-read string|null $name The name of the subdivision based on the
|
||||
* locales list passed to the constructor. This attribute is returned by all
|
||||
* location databases and services besides Country.
|
||||
* @property-read array|null $names An array map where the keys are locale codes
|
||||
* and the values are names. This attribute is returned by all location
|
||||
* databases and services besides Country.
|
||||
*/
|
||||
class Subdivision extends AbstractPlaceRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['confidence', 'geonameId', 'isoCode', 'names'];
|
||||
}
|
||||
131
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Traits.php
vendored
Normal file
131
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Record/Traits.php
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\Record;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Util;
|
||||
/**
|
||||
* Contains data for the traits record associated with an IP address.
|
||||
*
|
||||
* This record is returned by all location services and databases.
|
||||
*
|
||||
* @property-read int|null $autonomousSystemNumber The autonomous system number
|
||||
* associated with the IP address. See
|
||||
* https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This attribute
|
||||
* is only available from the City Plus and Insights web services and the
|
||||
* GeoIP2 Enterprise database.
|
||||
* @property-read string|null $autonomousSystemOrganization The organization
|
||||
* associated with the registered autonomous system number for the IP address.
|
||||
* See https://en.wikipedia.org/wiki/Autonomous_system_(Internet%29. This
|
||||
* attribute is only available from the City Plus and Insights web services and
|
||||
* the GeoIP2 Enterprise database.
|
||||
* @property-read string|null $connectionType The connection type may take the
|
||||
* following values: "Dialup", "Cable/DSL", "Corporate", "Cellular".
|
||||
* Additional values may be added in the future. This attribute is only
|
||||
* available in the GeoIP2 Enterprise database.
|
||||
* @property-read string|null $domain The second level domain associated with the
|
||||
* IP address. This will be something like "example.com" or "example.co.uk",
|
||||
* not "foo.example.com". This attribute is only available from the
|
||||
* City Plus and Insights web services and the GeoIP2 Enterprise
|
||||
* database.
|
||||
* @property-read string $ipAddress The IP address that the data in the model
|
||||
* is for. If you performed a "me" lookup against the web service, this
|
||||
* will be the externally routable IP address for the system the code is
|
||||
* running on. If the system is behind a NAT, this may differ from the IP
|
||||
* address locally assigned to it. This attribute is returned by all end
|
||||
* points.
|
||||
* @property-read bool $isAnonymous This is true if the IP address belongs to
|
||||
* any sort of anonymous network. This property is only available from GeoIP2
|
||||
* Insights.
|
||||
* @property-read bool $isAnonymousProxy *Deprecated.* Please see our GeoIP2
|
||||
* Anonymous IP database
|
||||
* (https://www.maxmind.com/en/geoip2-anonymous-ip-database) to determine
|
||||
* whether the IP address is used by an anonymizing service.
|
||||
* @property-read bool $isAnonymousVpn This is true if the IP address is
|
||||
* registered to an anonymous VPN provider. If a VPN provider does not register
|
||||
* subnets under names associated with them, we will likely only flag their IP
|
||||
* ranges using the isHostingProvider property. This property is only available
|
||||
* from GeoIP2 Insights.
|
||||
* @property-read bool $isHostingProvider This is true if the IP address belongs
|
||||
* to a hosting or VPN provider (see description of isAnonymousVpn property).
|
||||
* This property is only available from GeoIP2 Insights.
|
||||
* @property-read bool $isLegitimateProxy This attribute is true if MaxMind
|
||||
* believes this IP address to be a legitimate proxy, such as an internal
|
||||
* VPN used by a corporation. This attribute is only available in the GeoIP2
|
||||
* Enterprise database.
|
||||
* @property-read bool $isPublicProxy This is true if the IP address belongs to
|
||||
* a public proxy. This property is only available from GeoIP2 Insights.
|
||||
* @property-read bool $isResidentialProxy This is true if the IP address is
|
||||
* on a suspected anonymizing network and belongs to a residential ISP. This
|
||||
* property is only available from GeoIP2 Insights.
|
||||
* @property-read bool $isSatelliteProvider *Deprecated.* Due to the
|
||||
* increased coverage by mobile carriers, very few satellite providers now
|
||||
* serve multiple countries. As a result, the output does not provide
|
||||
* sufficiently relevant data for us to maintain it.
|
||||
* @property-read bool $isTorExitNode This is true if the IP address is a Tor
|
||||
* exit node. This property is only available from GeoIP2 Insights.
|
||||
* @property-read string|null $isp The name of the ISP associated with the IP
|
||||
* address. This attribute is only available from the City Plus and Insights
|
||||
* web services and the GeoIP2 Enterprise database.
|
||||
* @property-read string $network The network in CIDR notation associated with
|
||||
* the record. In particular, this is the largest network where all of the
|
||||
* fields besides $ipAddress have the same value.
|
||||
* @property-read string|null $organization The name of the organization
|
||||
* associated with the IP address. This attribute is only available from the
|
||||
* City Plus and Insights web services and the GeoIP2 Enterprise database.
|
||||
* @property-read string|null $mobileCountryCode The [mobile country code
|
||||
* (MCC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
|
||||
* the IP address and ISP. This property is available from the City Plus and
|
||||
* Insights web services and the GeoIP2 Enterprise database.
|
||||
* @property-read string|null $mobileNetworkCode The [mobile network code
|
||||
* (MNC)](https://en.wikipedia.org/wiki/Mobile_country_code) associated with
|
||||
* the IP address and ISP. This property is available from the City Plus and
|
||||
* Insights web services and the GeoIP2 Enterprise database.
|
||||
* @property-read float|null $staticIpScore An indicator of how static or
|
||||
* dynamic an IP address is. This property is only available from GeoIP2
|
||||
* Insights.
|
||||
* @property-read int|null $userCount The estimated number of users sharing
|
||||
* the IP/network during the past 24 hours. For IPv4, the count is for the
|
||||
* individual IP. For IPv6, the count is for the /64 network. This property is
|
||||
* only available from GeoIP2 Insights.
|
||||
* @property-read string|null $userType <p>The user type associated with the IP
|
||||
* address. This can be one of the following values:</p>
|
||||
* <ul>
|
||||
* <li>business
|
||||
* <li>cafe
|
||||
* <li>cellular
|
||||
* <li>college
|
||||
* <li>consumer_privacy_network
|
||||
* <li>content_delivery_network
|
||||
* <li>dialup
|
||||
* <li>government
|
||||
* <li>hosting
|
||||
* <li>library
|
||||
* <li>military
|
||||
* <li>residential
|
||||
* <li>router
|
||||
* <li>school
|
||||
* <li>search_engine_spider
|
||||
* <li>traveler
|
||||
* </ul>
|
||||
* <p>
|
||||
* This attribute is only available from the Insights web service and the
|
||||
* GeoIP2 Enterprise database.
|
||||
* </p>
|
||||
*/
|
||||
class Traits extends AbstractRecord
|
||||
{
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validAttributes = ['autonomousSystemNumber', 'autonomousSystemOrganization', 'connectionType', 'domain', 'ipAddress', 'isAnonymous', 'isAnonymousProxy', 'isAnonymousVpn', 'isHostingProvider', 'isLegitimateProxy', 'isp', 'isPublicProxy', 'isResidentialProxy', 'isSatelliteProvider', 'isTorExitNode', 'mobileCountryCode', 'mobileNetworkCode', 'network', 'organization', 'staticIpScore', 'userCount', 'userType'];
|
||||
public function __construct(?array $record)
|
||||
{
|
||||
if (!isset($record['network']) && isset($record['ip_address'], $record['prefix_len'])) {
|
||||
$record['network'] = Util::cidr($record['ip_address'], $record['prefix_len']);
|
||||
}
|
||||
parent::__construct($record);
|
||||
}
|
||||
}
|
||||
32
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Util.php
vendored
Normal file
32
plugins/system/tgeoip/vendor/geoip2/geoip2/src/Util.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2;
|
||||
|
||||
class Util
|
||||
{
|
||||
/**
|
||||
* This returns the network in CIDR notation for the given IP and prefix
|
||||
* length. This is for internal use only.
|
||||
*
|
||||
* @internal
|
||||
* @ignore
|
||||
*/
|
||||
public static function cidr(string $ipAddress, int $prefixLen) : string
|
||||
{
|
||||
$ipBytes = \inet_pton($ipAddress);
|
||||
$networkBytes = \str_repeat("\x00", \strlen($ipBytes));
|
||||
$curPrefix = $prefixLen;
|
||||
for ($i = 0; $i < \strlen($ipBytes) && $curPrefix > 0; $i++) {
|
||||
$b = $ipBytes[$i];
|
||||
if ($curPrefix < 8) {
|
||||
$shiftN = 8 - $curPrefix;
|
||||
$b = \chr(0xff & \ord($b) >> $shiftN << $shiftN);
|
||||
}
|
||||
$networkBytes[$i] = $b;
|
||||
$curPrefix -= 8;
|
||||
}
|
||||
$network = \inet_ntop($networkBytes);
|
||||
return "{$network}/{$prefixLen}";
|
||||
}
|
||||
}
|
||||
207
plugins/system/tgeoip/vendor/geoip2/geoip2/src/WebService/Client.php
vendored
Normal file
207
plugins/system/tgeoip/vendor/geoip2/geoip2/src/WebService/Client.php
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\GeoIp2\WebService;
|
||||
|
||||
use Tassos\Vendor\GeoIp2\Exception\AddressNotFoundException;
|
||||
use Tassos\Vendor\GeoIp2\Exception\AuthenticationException;
|
||||
use Tassos\Vendor\GeoIp2\Exception\GeoIp2Exception;
|
||||
use Tassos\Vendor\GeoIp2\Exception\HttpException;
|
||||
use Tassos\Vendor\GeoIp2\Exception\InvalidRequestException;
|
||||
use Tassos\Vendor\GeoIp2\Exception\OutOfQueriesException;
|
||||
use Tassos\Vendor\GeoIp2\Model\City;
|
||||
use Tassos\Vendor\GeoIp2\Model\Country;
|
||||
use Tassos\Vendor\GeoIp2\Model\Insights;
|
||||
use Tassos\Vendor\GeoIp2\ProviderInterface;
|
||||
use Tassos\Vendor\MaxMind\WebService\Client as WsClient;
|
||||
/**
|
||||
* This class provides a client API for all the GeoIP2 web services.
|
||||
* The services are Country, City Plus, and Insights. Each service returns
|
||||
* a different set of data about an IP address, with Country returning the
|
||||
* least data and Insights the most.
|
||||
*
|
||||
* Each web service is represented by a different model class, and these model
|
||||
* classes in turn contain multiple record classes. The record classes have
|
||||
* attributes which contain data about the IP address.
|
||||
*
|
||||
* If the web service does not return a particular piece of data for an IP
|
||||
* address, the associated attribute is not populated.
|
||||
*
|
||||
* The web service may not return any information for an entire record, in
|
||||
* which case all of the attributes for that record class will be empty.
|
||||
*
|
||||
* ## Usage ##
|
||||
*
|
||||
* The basic API for this class is the same for all of the web service end
|
||||
* points. First you create a web service object with your MaxMind `$accountId`
|
||||
* and `$licenseKey`, then you call the method corresponding to a specific end
|
||||
* point, passing it the IP address you want to look up.
|
||||
*
|
||||
* If the request succeeds, the method call will return a model class for
|
||||
* the service you called. This model in turn contains multiple record
|
||||
* classes, each of which represents part of the data returned by the web
|
||||
* service.
|
||||
*
|
||||
* If the request fails, the client class throws an exception.
|
||||
*/
|
||||
class Client implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private $locales;
|
||||
/**
|
||||
* @var WsClient
|
||||
*/
|
||||
private $client;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $basePath = '/geoip/v2.1';
|
||||
public const VERSION = 'v2.13.0';
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $accountId your MaxMind account ID
|
||||
* @param string $licenseKey your MaxMind license key
|
||||
* @param array $locales list of locale codes to use in name property
|
||||
* from most preferred to least preferred
|
||||
* @param array $options array of options. Valid options include:
|
||||
* * `host` - The host to use when querying the web
|
||||
* service. To query the GeoLite2 web service
|
||||
* instead of the GeoIP2 web service, set the
|
||||
* host to `geolite.info`.
|
||||
* * `timeout` - Timeout in seconds.
|
||||
* * `connectTimeout` - Initial connection timeout in seconds.
|
||||
* * `proxy` - The HTTP proxy to use. May include a schema, port,
|
||||
* username, and password, e.g.,
|
||||
* `http://username:password@127.0.0.1:10`.
|
||||
*/
|
||||
public function __construct(int $accountId, string $licenseKey, array $locales = ['en'], array $options = [])
|
||||
{
|
||||
$this->locales = $locales;
|
||||
// This is for backwards compatibility. Do not remove except for a
|
||||
// major version bump.
|
||||
// @phpstan-ignore-next-line
|
||||
if (\is_string($options)) {
|
||||
$options = ['host' => $options];
|
||||
}
|
||||
if (!isset($options['host'])) {
|
||||
$options['host'] = 'geoip.maxmind.com';
|
||||
}
|
||||
$options['userAgent'] = $this->userAgent();
|
||||
$this->client = new WsClient($accountId, $licenseKey, $options);
|
||||
}
|
||||
private function userAgent() : string
|
||||
{
|
||||
return 'GeoIP2-API/' . self::VERSION;
|
||||
}
|
||||
/**
|
||||
* This method calls the City Plus service.
|
||||
*
|
||||
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
|
||||
* address is provided, the address that the web service is called
|
||||
* from will be used.
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address you
|
||||
* provided is not in our database (e.g., a private address).
|
||||
* @throws \GeoIp2\Exception\AuthenticationException if there is a problem
|
||||
* with the account ID or license key that you provided
|
||||
* @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
|
||||
* of queries
|
||||
* @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is
|
||||
* invalid for some other reason. This may indicate an issue
|
||||
* with this API. Please report the error to MaxMind.
|
||||
* @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned.
|
||||
* This could indicate a problem with the connection between
|
||||
* your server and the web service or that the web service
|
||||
* returned an invalid document or 500 error code
|
||||
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
|
||||
* class to the above exceptions. It will be thrown directly
|
||||
* if a 200 status code is returned but the body is invalid.
|
||||
*/
|
||||
public function city(string $ipAddress = 'me') : City
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->responseFor('city', City::class, $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method calls the Country service.
|
||||
*
|
||||
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
|
||||
* address is provided, the address that the web service is called
|
||||
* from will be used.
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address you provided is not in our database (e.g.,
|
||||
* a private address).
|
||||
* @throws \GeoIp2\Exception\AuthenticationException if there is a problem
|
||||
* with the account ID or license key that you provided
|
||||
* @throws \GeoIp2\Exception\OutOfQueriesException if your account is out of queries
|
||||
* @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is
|
||||
* invalid for some other reason. This may indicate an
|
||||
* issue with this API. Please report the error to MaxMind.
|
||||
* @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error
|
||||
* code or message was returned. This could indicate a problem
|
||||
* with the connection between your server and the web service
|
||||
* or that the web service returned an invalid document or 500
|
||||
* error code.
|
||||
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent class to the above exceptions. It
|
||||
* will be thrown directly if a 200 status code is returned but
|
||||
* the body is invalid.
|
||||
*/
|
||||
public function country(string $ipAddress = 'me') : Country
|
||||
{
|
||||
return $this->responseFor('country', Country::class, $ipAddress);
|
||||
}
|
||||
/**
|
||||
* This method calls the Insights service. Insights is only supported by
|
||||
* the GeoIP2 web service. The GeoLite2 web service does not support it.
|
||||
*
|
||||
* @param string $ipAddress IPv4 or IPv6 address as a string. If no
|
||||
* address is provided, the address that the web service is called
|
||||
* from will be used.
|
||||
*
|
||||
* @throws \GeoIp2\Exception\AddressNotFoundException if the address you
|
||||
* provided is not in our database (e.g., a private address).
|
||||
* @throws \GeoIp2\Exception\AuthenticationException if there is a problem
|
||||
* with the account ID or license key that you provided
|
||||
* @throws \GeoIp2\Exception\OutOfQueriesException if your account is out
|
||||
* of queries
|
||||
* @throws \GeoIp2\Exception\InvalidRequestException} if your request was received by the web service but is
|
||||
* invalid for some other reason. This may indicate an
|
||||
* issue with this API. Please report the error to MaxMind.
|
||||
* @throws \GeoIp2\Exception\HttpException if an unexpected HTTP error code or message was returned.
|
||||
* This could indicate a problem with the connection between
|
||||
* your server and the web service or that the web service
|
||||
* returned an invalid document or 500 error code
|
||||
* @throws \GeoIp2\Exception\GeoIp2Exception This serves as the parent
|
||||
* class to the above exceptions. It will be thrown directly
|
||||
* if a 200 status code is returned but the body is invalid.
|
||||
*/
|
||||
public function insights(string $ipAddress = 'me') : Insights
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->responseFor('insights', Insights::class, $ipAddress);
|
||||
}
|
||||
private function responseFor(string $endpoint, string $class, string $ipAddress) : Country
|
||||
{
|
||||
$path = \implode('/', [self::$basePath, $endpoint, $ipAddress]);
|
||||
try {
|
||||
$service = (new \ReflectionClass($class))->getShortName();
|
||||
$body = $this->client->get('GeoIP2 ' . $service, $path);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\IpAddressNotFoundException $ex) {
|
||||
throw new AddressNotFoundException($ex->getMessage(), $ex->getStatusCode(), $ex);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\AuthenticationException $ex) {
|
||||
throw new AuthenticationException($ex->getMessage(), $ex->getStatusCode(), $ex);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\InsufficientFundsException $ex) {
|
||||
throw new OutOfQueriesException($ex->getMessage(), $ex->getStatusCode(), $ex);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\InvalidRequestException $ex) {
|
||||
throw new InvalidRequestException($ex->getMessage(), $ex->getErrorCode(), $ex->getStatusCode(), $ex->getUri(), $ex);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\HttpException $ex) {
|
||||
throw new HttpException($ex->getMessage(), $ex->getStatusCode(), $ex->getUri(), $ex);
|
||||
} catch (\Tassos\Vendor\MaxMind\Exception\WebServiceException $ex) {
|
||||
throw new GeoIp2Exception($ex->getMessage(), $ex->getCode(), $ex);
|
||||
}
|
||||
return new $class($body, $this->locales);
|
||||
}
|
||||
}
|
||||
42
plugins/system/tgeoip/vendor/maxmind-db/reader/autoload.php
vendored
Normal file
42
plugins/system/tgeoip/vendor/maxmind-db/reader/autoload.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor;
|
||||
|
||||
/**
|
||||
* PSR-4 autoloader implementation for the MaxMind\DB namespace.
|
||||
* First we define the 'mmdb_autoload' function, and then we register
|
||||
* it with 'spl_autoload_register' so that PHP knows to use it.
|
||||
*
|
||||
* @param mixed $class
|
||||
*/
|
||||
/**
|
||||
* Automatically include the file that defines <code>class</code>.
|
||||
*
|
||||
* @param string $class
|
||||
* the name of the class to load
|
||||
*/
|
||||
function mmdb_autoload($class) : void
|
||||
{
|
||||
/*
|
||||
* A project-specific mapping between the namespaces and where
|
||||
* they're located. By convention, we include the trailing
|
||||
* slashes. The one-element array here simply makes things easy
|
||||
* to extend in the future if (for example) the test classes
|
||||
* begin to use one another.
|
||||
*/
|
||||
$namespace_map = ['MaxMind\\Db\\' => __DIR__ . '/src/MaxMind/Db/'];
|
||||
foreach ($namespace_map as $prefix => $dir) {
|
||||
// First swap out the namespace prefix with a directory...
|
||||
$path = \str_replace($prefix, $dir, $class);
|
||||
// replace the namespace separator with a directory separator...
|
||||
$path = \str_replace('\\', '/', $path);
|
||||
// and finally, add the PHP file extension to the result.
|
||||
$path = $path . '.php';
|
||||
// $path should now contain the path to a PHP file defining $class
|
||||
if (\file_exists($path)) {
|
||||
include $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
\spl_autoload_register('mmdb_autoload');
|
||||
40
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.m4
vendored
Normal file
40
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.m4
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
PHP_ARG_WITH(maxminddb,
|
||||
[Whether to enable the MaxMind DB Reader extension],
|
||||
[ --with-maxminddb Enable MaxMind DB Reader extension support])
|
||||
|
||||
PHP_ARG_ENABLE(maxminddb-debug, for MaxMind DB debug support,
|
||||
[ --enable-maxminddb-debug Enable enable MaxMind DB deubg support], no, no)
|
||||
|
||||
if test $PHP_MAXMINDDB != "no"; then
|
||||
|
||||
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
|
||||
|
||||
AC_MSG_CHECKING(for libmaxminddb)
|
||||
if test -x "$PKG_CONFIG" && $PKG_CONFIG --exists libmaxminddb; then
|
||||
dnl retrieve build options from pkg-config
|
||||
if $PKG_CONFIG libmaxminddb --atleast-version 1.0.0; then
|
||||
LIBMAXMINDDB_INC=`$PKG_CONFIG libmaxminddb --cflags`
|
||||
LIBMAXMINDDB_LIB=`$PKG_CONFIG libmaxminddb --libs`
|
||||
LIBMAXMINDDB_VER=`$PKG_CONFIG libmaxminddb --modversion`
|
||||
AC_MSG_RESULT(found version $LIBMAXMINDDB_VER)
|
||||
else
|
||||
AC_MSG_ERROR(system libmaxminddb must be upgraded to version >= 1.0.0)
|
||||
fi
|
||||
PHP_EVAL_LIBLINE($LIBMAXMINDDB_LIB, MAXMINDDB_SHARED_LIBADD)
|
||||
PHP_EVAL_INCLINE($LIBMAXMINDDB_INC)
|
||||
else
|
||||
AC_MSG_RESULT(pkg-config information missing)
|
||||
AC_MSG_WARN(will use libmaxmxinddb from compiler default path)
|
||||
|
||||
PHP_CHECK_LIBRARY(maxminddb, MMDB_open)
|
||||
PHP_ADD_LIBRARY(maxminddb, 1, MAXMINDDB_SHARED_LIBADD)
|
||||
fi
|
||||
|
||||
if test $PHP_MAXMINDDB_DEBUG != "no"; then
|
||||
CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Werror"
|
||||
fi
|
||||
|
||||
PHP_SUBST(MAXMINDDB_SHARED_LIBADD)
|
||||
|
||||
PHP_NEW_EXTENSION(maxminddb, maxminddb.c, $ext_shared)
|
||||
fi
|
||||
10
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.w32
vendored
Normal file
10
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/config.w32
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
ARG_WITH("maxminddb", "Enable MaxMind DB Reader extension support", "no");
|
||||
|
||||
if (PHP_MAXMINDDB == "yes") {
|
||||
if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") &&
|
||||
CHECK_LIB("libmaxminddb.lib", "maxminddb", PHP_MAXMINDDB)) {
|
||||
EXTENSION("maxminddb", "maxminddb.c");
|
||||
} else {
|
||||
WARNING('Could not find maxminddb.h or libmaxminddb.lib; skipping');
|
||||
}
|
||||
}
|
||||
811
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/maxminddb.c
vendored
Normal file
811
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/maxminddb.c
vendored
Normal file
@ -0,0 +1,811 @@
|
||||
/* MaxMind, Inc., licenses this file to you under the Apache License, Version
|
||||
* 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include "php_maxminddb.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <php.h>
|
||||
#include <zend.h>
|
||||
|
||||
#include "Zend/zend_exceptions.h"
|
||||
#include "Zend/zend_types.h"
|
||||
#include "ext/spl/spl_exceptions.h"
|
||||
#include "ext/standard/info.h"
|
||||
#include <maxminddb.h>
|
||||
|
||||
#ifdef ZTS
|
||||
#include <TSRM.h>
|
||||
#endif
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db")
|
||||
#define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader")
|
||||
#define PHP_MAXMINDDB_METADATA_NS \
|
||||
ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata")
|
||||
#define PHP_MAXMINDDB_READER_EX_NS \
|
||||
ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException")
|
||||
|
||||
#define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv))
|
||||
typedef size_t strsize_t;
|
||||
typedef zend_object free_obj_t;
|
||||
|
||||
/* For PHP 8 compatibility */
|
||||
#if PHP_VERSION_ID < 80000
|
||||
|
||||
#define PROP_OBJ(zv) (zv)
|
||||
|
||||
#else
|
||||
|
||||
#define PROP_OBJ(zv) Z_OBJ_P(zv)
|
||||
|
||||
#define TSRMLS_C
|
||||
#define TSRMLS_CC
|
||||
#define TSRMLS_DC
|
||||
|
||||
/* End PHP 8 compatibility */
|
||||
#endif
|
||||
|
||||
#ifndef ZEND_ACC_CTOR
|
||||
#define ZEND_ACC_CTOR 0
|
||||
#endif
|
||||
|
||||
/* IS_MIXED was added in 2020 */
|
||||
#ifndef IS_MIXED
|
||||
#define IS_MIXED IS_UNDEF
|
||||
#endif
|
||||
|
||||
/* ZEND_THIS was added in 7.4 */
|
||||
#ifndef ZEND_THIS
|
||||
#define ZEND_THIS (&EX(This))
|
||||
#endif
|
||||
|
||||
typedef struct _maxminddb_obj {
|
||||
MMDB_s *mmdb;
|
||||
zend_object std;
|
||||
} maxminddb_obj;
|
||||
|
||||
PHP_FUNCTION(maxminddb);
|
||||
|
||||
static int
|
||||
get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len);
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_array(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_map(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC);
|
||||
|
||||
#define CHECK_ALLOCATED(val) \
|
||||
if (!val) { \
|
||||
zend_error(E_ERROR, "Out of memory"); \
|
||||
return; \
|
||||
}
|
||||
|
||||
static zend_object_handlers maxminddb_obj_handlers;
|
||||
static zend_class_entry *maxminddb_ce, *maxminddb_exception_ce, *metadata_ce;
|
||||
|
||||
static inline maxminddb_obj *
|
||||
php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) {
|
||||
return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std));
|
||||
}
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_construct, 0, 0, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, db_file, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader, __construct) {
|
||||
char *db_file = NULL;
|
||||
strsize_t name_len;
|
||||
zval *_this_zval = NULL;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
getThis(),
|
||||
"Os",
|
||||
&_this_zval,
|
||||
maxminddb_ce,
|
||||
&db_file,
|
||||
&name_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != php_check_open_basedir(db_file TSRMLS_CC) ||
|
||||
0 != access(db_file, R_OK)) {
|
||||
zend_throw_exception_ex(
|
||||
spl_ce_InvalidArgumentException,
|
||||
0 TSRMLS_CC,
|
||||
"The file \"%s\" does not exist or is not readable.",
|
||||
db_file);
|
||||
return;
|
||||
}
|
||||
|
||||
MMDB_s *mmdb = (MMDB_s *)ecalloc(1, sizeof(MMDB_s));
|
||||
uint16_t status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb);
|
||||
|
||||
if (MMDB_SUCCESS != status) {
|
||||
zend_throw_exception_ex(
|
||||
maxminddb_exception_ce,
|
||||
0 TSRMLS_CC,
|
||||
"Error opening database file (%s). Is this a valid "
|
||||
"MaxMind DB file?",
|
||||
db_file);
|
||||
efree(mmdb);
|
||||
return;
|
||||
}
|
||||
|
||||
maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(ZEND_THIS);
|
||||
mmdb_obj->mmdb = mmdb;
|
||||
}
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
|
||||
arginfo_maxminddbreader_get, 0, 1, IS_MIXED, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader, get) {
|
||||
int prefix_len = 0;
|
||||
get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len);
|
||||
}
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(
|
||||
arginfo_maxminddbreader_getWithPrefixLen, 0, 1, IS_ARRAY, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, ip_address, IS_STRING, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) {
|
||||
zval record, z_prefix_len;
|
||||
|
||||
int prefix_len = 0;
|
||||
if (get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, &record, &prefix_len) ==
|
||||
FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
array_init(return_value);
|
||||
add_next_index_zval(return_value, &record);
|
||||
|
||||
ZVAL_LONG(&z_prefix_len, prefix_len);
|
||||
add_next_index_zval(return_value, &z_prefix_len);
|
||||
}
|
||||
|
||||
static int
|
||||
get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) {
|
||||
char *ip_address = NULL;
|
||||
strsize_t name_len;
|
||||
zval *this_zval = NULL;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
getThis(),
|
||||
"Os",
|
||||
&this_zval,
|
||||
maxminddb_ce,
|
||||
&ip_address,
|
||||
&name_len) == FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(ZEND_THIS);
|
||||
|
||||
MMDB_s *mmdb = mmdb_obj->mmdb;
|
||||
|
||||
if (NULL == mmdb) {
|
||||
zend_throw_exception_ex(spl_ce_BadMethodCallException,
|
||||
0 TSRMLS_CC,
|
||||
"Attempt to read from a closed MaxMind DB.");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_flags = AI_NUMERICHOST,
|
||||
/* We set ai_socktype so that we only get one result back */
|
||||
.ai_socktype = SOCK_STREAM};
|
||||
|
||||
struct addrinfo *addresses = NULL;
|
||||
int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses);
|
||||
if (gai_status) {
|
||||
zend_throw_exception_ex(spl_ce_InvalidArgumentException,
|
||||
0 TSRMLS_CC,
|
||||
"The value \"%s\" is not a valid IP address.",
|
||||
ip_address);
|
||||
return FAILURE;
|
||||
}
|
||||
if (!addresses || !addresses->ai_addr) {
|
||||
zend_throw_exception_ex(
|
||||
spl_ce_InvalidArgumentException,
|
||||
0 TSRMLS_CC,
|
||||
"getaddrinfo was successful but failed to set the addrinfo");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
int sa_family = addresses->ai_addr->sa_family;
|
||||
|
||||
int mmdb_error = MMDB_SUCCESS;
|
||||
MMDB_lookup_result_s result =
|
||||
MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
|
||||
|
||||
freeaddrinfo(addresses);
|
||||
|
||||
if (MMDB_SUCCESS != mmdb_error) {
|
||||
zend_class_entry *ex;
|
||||
if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) {
|
||||
ex = spl_ce_InvalidArgumentException;
|
||||
} else {
|
||||
ex = maxminddb_exception_ce;
|
||||
}
|
||||
zend_throw_exception_ex(ex,
|
||||
0 TSRMLS_CC,
|
||||
"Error looking up %s. %s",
|
||||
ip_address,
|
||||
MMDB_strerror(mmdb_error));
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
*prefix_len = result.netmask;
|
||||
|
||||
if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) {
|
||||
/* We return the prefix length given the IPv4 address. If there is
|
||||
no IPv4 subtree, we return a prefix length of 0. */
|
||||
*prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0;
|
||||
}
|
||||
|
||||
if (!result.found_entry) {
|
||||
ZVAL_NULL(record);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
MMDB_entry_data_list_s *entry_data_list = NULL;
|
||||
int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
|
||||
|
||||
if (MMDB_SUCCESS != status) {
|
||||
zend_throw_exception_ex(maxminddb_exception_ce,
|
||||
0 TSRMLS_CC,
|
||||
"Error while looking up data for %s. %s",
|
||||
ip_address,
|
||||
MMDB_strerror(status));
|
||||
MMDB_free_entry_data_list(entry_data_list);
|
||||
return FAILURE;
|
||||
} else if (NULL == entry_data_list) {
|
||||
zend_throw_exception_ex(
|
||||
maxminddb_exception_ce,
|
||||
0 TSRMLS_CC,
|
||||
"Error while looking up data for %s. Your database may "
|
||||
"be corrupt or you have found a bug in libmaxminddb.",
|
||||
ip_address);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
const MMDB_entry_data_list_s *rv =
|
||||
handle_entry_data_list(entry_data_list, record TSRMLS_CC);
|
||||
if (rv == NULL) {
|
||||
/* We should have already thrown the exception in handle_entry_data_list
|
||||
*/
|
||||
return FAILURE;
|
||||
}
|
||||
MMDB_free_entry_data_list(entry_data_list);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_maxminddbreader_void, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader, metadata) {
|
||||
zval *this_zval = NULL;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
getThis(),
|
||||
"O",
|
||||
&this_zval,
|
||||
maxminddb_ce) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxminddb_obj *const mmdb_obj =
|
||||
(maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
|
||||
|
||||
if (NULL == mmdb_obj->mmdb) {
|
||||
zend_throw_exception_ex(spl_ce_BadMethodCallException,
|
||||
0 TSRMLS_CC,
|
||||
"Attempt to read from a closed MaxMind DB.");
|
||||
return;
|
||||
}
|
||||
|
||||
object_init_ex(return_value, metadata_ce);
|
||||
|
||||
MMDB_entry_data_list_s *entry_data_list;
|
||||
MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list);
|
||||
|
||||
zval metadata_array;
|
||||
const MMDB_entry_data_list_s *rv =
|
||||
handle_entry_data_list(entry_data_list, &metadata_array TSRMLS_CC);
|
||||
if (rv == NULL) {
|
||||
return;
|
||||
}
|
||||
MMDB_free_entry_data_list(entry_data_list);
|
||||
zend_call_method_with_1_params(PROP_OBJ(return_value),
|
||||
metadata_ce,
|
||||
&metadata_ce->constructor,
|
||||
ZEND_CONSTRUCTOR_FUNC_NAME,
|
||||
NULL,
|
||||
&metadata_array);
|
||||
zval_ptr_dtor(&metadata_array);
|
||||
}
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader, close) {
|
||||
zval *this_zval = NULL;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
getThis(),
|
||||
"O",
|
||||
&this_zval,
|
||||
maxminddb_ce) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(this_zval);
|
||||
|
||||
if (NULL == mmdb_obj->mmdb) {
|
||||
zend_throw_exception_ex(spl_ce_BadMethodCallException,
|
||||
0 TSRMLS_CC,
|
||||
"Attempt to close a closed MaxMind DB.");
|
||||
return;
|
||||
}
|
||||
MMDB_close(mmdb_obj->mmdb);
|
||||
efree(mmdb_obj->mmdb);
|
||||
mmdb_obj->mmdb = NULL;
|
||||
}
|
||||
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
switch (entry_data_list->entry_data.type) {
|
||||
case MMDB_DATA_TYPE_MAP:
|
||||
return handle_map(entry_data_list, z_value TSRMLS_CC);
|
||||
case MMDB_DATA_TYPE_ARRAY:
|
||||
return handle_array(entry_data_list, z_value TSRMLS_CC);
|
||||
case MMDB_DATA_TYPE_UTF8_STRING:
|
||||
ZVAL_STRINGL(z_value,
|
||||
(char *)entry_data_list->entry_data.utf8_string,
|
||||
entry_data_list->entry_data.data_size);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_BYTES:
|
||||
ZVAL_STRINGL(z_value,
|
||||
(char *)entry_data_list->entry_data.bytes,
|
||||
entry_data_list->entry_data.data_size);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_DOUBLE:
|
||||
ZVAL_DOUBLE(z_value, entry_data_list->entry_data.double_value);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_FLOAT:
|
||||
ZVAL_DOUBLE(z_value, entry_data_list->entry_data.float_value);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_UINT16:
|
||||
ZVAL_LONG(z_value, entry_data_list->entry_data.uint16);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_UINT32:
|
||||
handle_uint32(entry_data_list, z_value TSRMLS_CC);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_BOOLEAN:
|
||||
ZVAL_BOOL(z_value, entry_data_list->entry_data.boolean);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_UINT64:
|
||||
handle_uint64(entry_data_list, z_value TSRMLS_CC);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_UINT128:
|
||||
handle_uint128(entry_data_list, z_value TSRMLS_CC);
|
||||
break;
|
||||
case MMDB_DATA_TYPE_INT32:
|
||||
ZVAL_LONG(z_value, entry_data_list->entry_data.int32);
|
||||
break;
|
||||
default:
|
||||
zend_throw_exception_ex(maxminddb_exception_ce,
|
||||
0 TSRMLS_CC,
|
||||
"Invalid data type arguments: %d",
|
||||
entry_data_list->entry_data.type);
|
||||
return NULL;
|
||||
}
|
||||
return entry_data_list;
|
||||
}
|
||||
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_map(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
array_init(z_value);
|
||||
const uint32_t map_size = entry_data_list->entry_data.data_size;
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < map_size && entry_data_list; i++) {
|
||||
entry_data_list = entry_data_list->next;
|
||||
|
||||
char *key = estrndup((char *)entry_data_list->entry_data.utf8_string,
|
||||
entry_data_list->entry_data.data_size);
|
||||
if (NULL == key) {
|
||||
zend_throw_exception_ex(maxminddb_exception_ce,
|
||||
0 TSRMLS_CC,
|
||||
"Invalid data type arguments");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry_data_list = entry_data_list->next;
|
||||
zval new_value;
|
||||
entry_data_list =
|
||||
handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
|
||||
if (entry_data_list != NULL) {
|
||||
add_assoc_zval(z_value, key, &new_value);
|
||||
}
|
||||
efree(key);
|
||||
}
|
||||
return entry_data_list;
|
||||
}
|
||||
|
||||
static const MMDB_entry_data_list_s *
|
||||
handle_array(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
const uint32_t size = entry_data_list->entry_data.data_size;
|
||||
|
||||
array_init(z_value);
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < size && entry_data_list; i++) {
|
||||
entry_data_list = entry_data_list->next;
|
||||
zval new_value;
|
||||
entry_data_list =
|
||||
handle_entry_data_list(entry_data_list, &new_value TSRMLS_CC);
|
||||
if (entry_data_list != NULL) {
|
||||
add_next_index_zval(z_value, &new_value);
|
||||
}
|
||||
}
|
||||
return entry_data_list;
|
||||
}
|
||||
|
||||
static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
uint64_t high = 0;
|
||||
uint64_t low = 0;
|
||||
#if MMDB_UINT128_IS_BYTE_ARRAY
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
high = (high << 8) | entry_data_list->entry_data.uint128[i];
|
||||
}
|
||||
|
||||
for (i = 8; i < 16; i++) {
|
||||
low = (low << 8) | entry_data_list->entry_data.uint128[i];
|
||||
}
|
||||
#else
|
||||
high = entry_data_list->entry_data.uint128 >> 64;
|
||||
low = (uint64_t)entry_data_list->entry_data.uint128;
|
||||
#endif
|
||||
|
||||
char *num_str;
|
||||
spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low);
|
||||
CHECK_ALLOCATED(num_str);
|
||||
|
||||
ZVAL_STRING(z_value, num_str);
|
||||
efree(num_str);
|
||||
}
|
||||
|
||||
static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
uint32_t val = entry_data_list->entry_data.uint32;
|
||||
|
||||
#if LONG_MAX >= UINT32_MAX
|
||||
ZVAL_LONG(z_value, val);
|
||||
return;
|
||||
#else
|
||||
if (val <= LONG_MAX) {
|
||||
ZVAL_LONG(z_value, val);
|
||||
return;
|
||||
}
|
||||
|
||||
char *int_str;
|
||||
spprintf(&int_str, 0, "%" PRIu32, val);
|
||||
CHECK_ALLOCATED(int_str);
|
||||
|
||||
ZVAL_STRING(z_value, int_str);
|
||||
efree(int_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list,
|
||||
zval *z_value TSRMLS_DC) {
|
||||
uint64_t val = entry_data_list->entry_data.uint64;
|
||||
|
||||
#if LONG_MAX >= UINT64_MAX
|
||||
ZVAL_LONG(z_value, val);
|
||||
return;
|
||||
#else
|
||||
if (val <= LONG_MAX) {
|
||||
ZVAL_LONG(z_value, val);
|
||||
return;
|
||||
}
|
||||
|
||||
char *int_str;
|
||||
spprintf(&int_str, 0, "%" PRIu64, val);
|
||||
CHECK_ALLOCATED(int_str);
|
||||
|
||||
ZVAL_STRING(z_value, int_str);
|
||||
efree(int_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) {
|
||||
maxminddb_obj *obj =
|
||||
php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC);
|
||||
if (obj->mmdb != NULL) {
|
||||
MMDB_close(obj->mmdb);
|
||||
efree(obj->mmdb);
|
||||
}
|
||||
|
||||
zend_object_std_dtor(&obj->std TSRMLS_CC);
|
||||
}
|
||||
|
||||
static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) {
|
||||
maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj));
|
||||
zend_object_std_init(&obj->std, type TSRMLS_CC);
|
||||
object_properties_init(&(obj->std), type);
|
||||
|
||||
obj->std.handlers = &maxminddb_obj_handlers;
|
||||
|
||||
return &obj->std;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
static zend_function_entry maxminddb_methods[] = {
|
||||
PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxminddbreader_construct,
|
||||
ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
||||
PHP_ME(MaxMind_Db_Reader, close, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(MaxMind_Db_Reader, get, arginfo_maxminddbreader_get, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxminddbreader_getWithPrefixLen, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxminddbreader_void, ZEND_ACC_PUBLIC)
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_metadata_construct, 0, 0, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, metadata, IS_ARRAY, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
PHP_METHOD(MaxMind_Db_Reader_Metadata, __construct) {
|
||||
zval *object = NULL;
|
||||
zval *metadata_array = NULL;
|
||||
zend_long node_count = 0;
|
||||
zend_long record_size = 0;
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
getThis(),
|
||||
"Oa",
|
||||
&object,
|
||||
metadata_ce,
|
||||
&metadata_array) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
zval *tmp = NULL;
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"binary_format_major_version",
|
||||
sizeof("binary_format_major_version") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"binaryFormatMajorVersion",
|
||||
sizeof("binaryFormatMajorVersion") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"binary_format_minor_version",
|
||||
sizeof("binary_format_minor_version") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"binaryFormatMinorVersion",
|
||||
sizeof("binaryFormatMinorVersion") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"build_epoch",
|
||||
sizeof("build_epoch") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"buildEpoch",
|
||||
sizeof("buildEpoch") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"database_type",
|
||||
sizeof("database_type") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"databaseType",
|
||||
sizeof("databaseType") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"description",
|
||||
sizeof("description") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"description",
|
||||
sizeof("description") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"ip_version",
|
||||
sizeof("ip_version") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"ipVersion",
|
||||
sizeof("ipVersion") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(
|
||||
HASH_OF(metadata_array), "languages", sizeof("languages") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"languages",
|
||||
sizeof("languages") - 1,
|
||||
tmp);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"record_size",
|
||||
sizeof("record_size") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"recordSize",
|
||||
sizeof("recordSize") - 1,
|
||||
tmp);
|
||||
if (Z_TYPE_P(tmp) == IS_LONG) {
|
||||
record_size = Z_LVAL_P(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (record_size != 0) {
|
||||
zend_update_property_long(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"nodeByteSize",
|
||||
sizeof("nodeByteSize") - 1,
|
||||
record_size / 4);
|
||||
}
|
||||
|
||||
if ((tmp = zend_hash_str_find(HASH_OF(metadata_array),
|
||||
"node_count",
|
||||
sizeof("node_count") - 1))) {
|
||||
zend_update_property(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"nodeCount",
|
||||
sizeof("nodeCount") - 1,
|
||||
tmp);
|
||||
if (Z_TYPE_P(tmp) == IS_LONG) {
|
||||
node_count = Z_LVAL_P(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (record_size != 0) {
|
||||
zend_update_property_long(metadata_ce,
|
||||
PROP_OBJ(object),
|
||||
"searchTreeSize",
|
||||
sizeof("searchTreeSize") - 1,
|
||||
record_size * node_count / 4);
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static zend_function_entry metadata_methods[] = {
|
||||
PHP_ME(MaxMind_Db_Reader_Metadata, __construct, arginfo_metadata_construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
PHP_MINIT_FUNCTION(maxminddb) {
|
||||
zend_class_entry ce;
|
||||
|
||||
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_EX_NS, NULL);
|
||||
maxminddb_exception_ce =
|
||||
zend_register_internal_class_ex(&ce, zend_ce_exception);
|
||||
|
||||
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods);
|
||||
maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
||||
maxminddb_ce->create_object = maxminddb_create_handler;
|
||||
|
||||
INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_METADATA_NS, metadata_methods);
|
||||
metadata_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
||||
zend_declare_property_null(metadata_ce,
|
||||
"binaryFormatMajorVersion",
|
||||
sizeof("binaryFormatMajorVersion") - 1,
|
||||
ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(metadata_ce,
|
||||
"binaryFormatMinorVersion",
|
||||
sizeof("binaryFormatMinorVersion") - 1,
|
||||
ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "buildEpoch", sizeof("buildEpoch") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(metadata_ce,
|
||||
"databaseType",
|
||||
sizeof("databaseType") - 1,
|
||||
ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "description", sizeof("description") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "ipVersion", sizeof("ipVersion") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "languages", sizeof("languages") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(metadata_ce,
|
||||
"nodeByteSize",
|
||||
sizeof("nodeByteSize") - 1,
|
||||
ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "nodeCount", sizeof("nodeCount") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(
|
||||
metadata_ce, "recordSize", sizeof("recordSize") - 1, ZEND_ACC_PUBLIC);
|
||||
zend_declare_property_null(metadata_ce,
|
||||
"searchTreeSize",
|
||||
sizeof("searchTreeSize") - 1,
|
||||
ZEND_ACC_PUBLIC);
|
||||
|
||||
memcpy(&maxminddb_obj_handlers,
|
||||
zend_get_std_object_handlers(),
|
||||
sizeof(zend_object_handlers));
|
||||
maxminddb_obj_handlers.clone_obj = NULL;
|
||||
maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std);
|
||||
maxminddb_obj_handlers.free_obj = maxminddb_free_storage;
|
||||
zend_declare_class_constant_string(maxminddb_ce,
|
||||
"MMDB_LIB_VERSION",
|
||||
sizeof("MMDB_LIB_VERSION") - 1,
|
||||
MMDB_lib_version() TSRMLS_CC);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static PHP_MINFO_FUNCTION(maxminddb) {
|
||||
php_info_print_table_start();
|
||||
|
||||
php_info_print_table_row(2, "MaxMind DB Reader", "enabled");
|
||||
php_info_print_table_row(
|
||||
2, "maxminddb extension version", PHP_MAXMINDDB_VERSION);
|
||||
php_info_print_table_row(
|
||||
2, "libmaxminddb library version", MMDB_lib_version());
|
||||
|
||||
php_info_print_table_end();
|
||||
}
|
||||
|
||||
zend_module_entry maxminddb_module_entry = {STANDARD_MODULE_HEADER,
|
||||
PHP_MAXMINDDB_EXTNAME,
|
||||
NULL,
|
||||
PHP_MINIT(maxminddb),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
PHP_MINFO(maxminddb),
|
||||
PHP_MAXMINDDB_VERSION,
|
||||
STANDARD_MODULE_PROPERTIES};
|
||||
|
||||
#ifdef COMPILE_DL_MAXMINDDB
|
||||
ZEND_GET_MODULE(maxminddb)
|
||||
#endif
|
||||
24
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/php_maxminddb.h
vendored
Normal file
24
plugins/system/tgeoip/vendor/maxmind-db/reader/ext/php_maxminddb.h
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/* MaxMind, Inc., licenses this file to you under the Apache License, Version
|
||||
* 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <zend_interfaces.h>
|
||||
|
||||
#ifndef PHP_MAXMINDDB_H
|
||||
#define PHP_MAXMINDDB_H 1
|
||||
#define PHP_MAXMINDDB_VERSION "1.10.1"
|
||||
#define PHP_MAXMINDDB_EXTNAME "maxminddb"
|
||||
|
||||
extern zend_module_entry maxminddb_module_entry;
|
||||
#define phpext_maxminddb_ptr &maxminddb_module_entry
|
||||
|
||||
#endif
|
||||
63
plugins/system/tgeoip/vendor/maxmind-db/reader/package.xml
vendored
Normal file
63
plugins/system/tgeoip/vendor/maxmind-db/reader/package.xml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0"?>
|
||||
<package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0"
|
||||
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
|
||||
|
||||
<name>maxminddb</name>
|
||||
<channel>pecl.php.net</channel>
|
||||
<summary>Reader for the MaxMind DB file format</summary>
|
||||
<description>This is the PHP extension for reading MaxMind DB files. MaxMind DB is a binary file format that stores data indexed by IP address subnets (IPv4 or IPv6).</description>
|
||||
<lead>
|
||||
<name>Greg Oschwald</name>
|
||||
<user>oschwald</user>
|
||||
<email>goschwald@maxmind.com</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<date>2021-04-14</date>
|
||||
<version>
|
||||
<release>1.10.1</release>
|
||||
<api>1.10.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
<api>stable</api>
|
||||
</stability>
|
||||
<license uri="https://github.com/maxmind/MaxMind-DB-Reader-php/blob/main/LICENSE">Apache License 2.0</license>
|
||||
<notes>* Fix a `TypeError` exception in the pure PHP reader when using large
|
||||
databases on 32-bit PHP builds with the `bcmath` extension. Reported
|
||||
by dodo1708. GitHub #124.</notes>
|
||||
<contents>
|
||||
<dir name="/">
|
||||
<file role="doc" name="LICENSE"/>
|
||||
<file role="doc" name="CHANGELOG.md"/>
|
||||
<file role="doc" name="README.md"/>
|
||||
|
||||
<dir name="ext">
|
||||
<file role="src" name="config.m4"/>
|
||||
<file role="src" name="config.w32"/>
|
||||
|
||||
<file role="src" name="maxminddb.c"/>
|
||||
<file role="src" name="php_maxminddb.h"/>
|
||||
|
||||
<dir name="tests">
|
||||
<file role="test" name="001-load.phpt"/>
|
||||
<file role="test" name="002-final.phpt"/>
|
||||
<file role="test" name="003-open-basedir.phpt"/>
|
||||
</dir>
|
||||
</dir>
|
||||
</dir>
|
||||
</contents>
|
||||
<dependencies>
|
||||
<required>
|
||||
<php>
|
||||
<min>7.2.0</min>
|
||||
</php>
|
||||
<pearinstaller>
|
||||
<min>1.10.0</min>
|
||||
</pearinstaller>
|
||||
</required>
|
||||
</dependencies>
|
||||
<providesextension>maxminddb</providesextension>
|
||||
<extsrcrelease />
|
||||
</package>
|
||||
287
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
vendored
Normal file
287
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php
vendored
Normal file
@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Db;
|
||||
|
||||
use ArgumentCountError;
|
||||
use BadMethodCallException;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader\Decoder;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader\InvalidDatabaseException;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader\Metadata;
|
||||
use Tassos\Vendor\MaxMind\Db\Reader\Util;
|
||||
use UnexpectedValueException;
|
||||
/**
|
||||
* Instances of this class provide a reader for the MaxMind DB format. IP
|
||||
* addresses can be looked up using the get method.
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private static $DATA_SECTION_SEPARATOR_SIZE = 16;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $METADATA_START_MARKER = "\xab\xcd\xefMaxMind.com";
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private static $METADATA_START_MARKER_LENGTH = 14;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private static $METADATA_MAX_SIZE = 131072;
|
||||
// 128 * 1024 = 128KiB
|
||||
/**
|
||||
* @var Decoder
|
||||
*/
|
||||
private $decoder;
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $fileHandle;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $fileSize;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $ipV4Start;
|
||||
/**
|
||||
* @var Metadata
|
||||
*/
|
||||
private $metadata;
|
||||
/**
|
||||
* Constructs a Reader for the MaxMind DB format. The file passed to it must
|
||||
* be a valid MaxMind DB file such as a GeoIp2 database file.
|
||||
*
|
||||
* @param string $database
|
||||
* the MaxMind DB file to use
|
||||
*
|
||||
* @throws InvalidArgumentException for invalid database path or unknown arguments
|
||||
* @throws InvalidDatabaseException
|
||||
* if the database is invalid or there is an error reading
|
||||
* from it
|
||||
*/
|
||||
public function __construct(string $database)
|
||||
{
|
||||
if (\func_num_args() !== 1) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
$fileHandle = @\fopen($database, 'rb');
|
||||
if ($fileHandle === \false) {
|
||||
throw new InvalidArgumentException("The file \"{$database}\" does not exist or is not readable.");
|
||||
}
|
||||
$this->fileHandle = $fileHandle;
|
||||
$fileSize = @\filesize($database);
|
||||
if ($fileSize === \false) {
|
||||
throw new UnexpectedValueException("Error determining the size of \"{$database}\".");
|
||||
}
|
||||
$this->fileSize = $fileSize;
|
||||
$start = $this->findMetadataStart($database);
|
||||
$metadataDecoder = new Decoder($this->fileHandle, $start);
|
||||
[$metadataArray] = $metadataDecoder->decode($start);
|
||||
$this->metadata = new Metadata($metadataArray);
|
||||
$this->decoder = new Decoder($this->fileHandle, $this->metadata->searchTreeSize + self::$DATA_SECTION_SEPARATOR_SIZE);
|
||||
$this->ipV4Start = $this->ipV4StartNode();
|
||||
}
|
||||
/**
|
||||
* Retrieves the record for the IP address.
|
||||
*
|
||||
* @param string $ipAddress
|
||||
* the IP address to look up
|
||||
*
|
||||
* @throws BadMethodCallException if this method is called on a closed database
|
||||
* @throws InvalidArgumentException if something other than a single IP address is passed to the method
|
||||
* @throws InvalidDatabaseException
|
||||
* if the database is invalid or there is an error reading
|
||||
* from it
|
||||
*
|
||||
* @return mixed the record for the IP address
|
||||
*/
|
||||
public function get(string $ipAddress)
|
||||
{
|
||||
if (\func_num_args() !== 1) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
[$record] = $this->getWithPrefixLen($ipAddress);
|
||||
return $record;
|
||||
}
|
||||
/**
|
||||
* Retrieves the record for the IP address and its associated network prefix length.
|
||||
*
|
||||
* @param string $ipAddress
|
||||
* the IP address to look up
|
||||
*
|
||||
* @throws BadMethodCallException if this method is called on a closed database
|
||||
* @throws InvalidArgumentException if something other than a single IP address is passed to the method
|
||||
* @throws InvalidDatabaseException
|
||||
* if the database is invalid or there is an error reading
|
||||
* from it
|
||||
*
|
||||
* @return array an array where the first element is the record and the
|
||||
* second the network prefix length for the record
|
||||
*/
|
||||
public function getWithPrefixLen(string $ipAddress) : array
|
||||
{
|
||||
if (\func_num_args() !== 1) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
if (!\is_resource($this->fileHandle)) {
|
||||
throw new BadMethodCallException('Attempt to read from a closed MaxMind DB.');
|
||||
}
|
||||
[$pointer, $prefixLen] = $this->findAddressInTree($ipAddress);
|
||||
if ($pointer === 0) {
|
||||
return [null, $prefixLen];
|
||||
}
|
||||
return [$this->resolveDataPointer($pointer), $prefixLen];
|
||||
}
|
||||
private function findAddressInTree(string $ipAddress) : array
|
||||
{
|
||||
$packedAddr = @\inet_pton($ipAddress);
|
||||
if ($packedAddr === \false) {
|
||||
throw new InvalidArgumentException("The value \"{$ipAddress}\" is not a valid IP address.");
|
||||
}
|
||||
$rawAddress = \unpack('C*', $packedAddr);
|
||||
$bitCount = \count($rawAddress) * 8;
|
||||
// The first node of the tree is always node 0, at the beginning of the
|
||||
// value
|
||||
$node = 0;
|
||||
$metadata = $this->metadata;
|
||||
// Check if we are looking up an IPv4 address in an IPv6 tree. If this
|
||||
// is the case, we can skip over the first 96 nodes.
|
||||
if ($metadata->ipVersion === 6) {
|
||||
if ($bitCount === 32) {
|
||||
$node = $this->ipV4Start;
|
||||
}
|
||||
} elseif ($metadata->ipVersion === 4 && $bitCount === 128) {
|
||||
throw new InvalidArgumentException("Error looking up {$ipAddress}. You attempted to look up an" . ' IPv6 address in an IPv4-only database.');
|
||||
}
|
||||
$nodeCount = $metadata->nodeCount;
|
||||
for ($i = 0; $i < $bitCount && $node < $nodeCount; ++$i) {
|
||||
$tempBit = 0xff & $rawAddress[($i >> 3) + 1];
|
||||
$bit = 1 & $tempBit >> 7 - $i % 8;
|
||||
$node = $this->readNode($node, $bit);
|
||||
}
|
||||
if ($node === $nodeCount) {
|
||||
// Record is empty
|
||||
return [0, $i];
|
||||
}
|
||||
if ($node > $nodeCount) {
|
||||
// Record is a data pointer
|
||||
return [$node, $i];
|
||||
}
|
||||
throw new InvalidDatabaseException('Invalid or corrupt database. Maximum search depth reached without finding a leaf node');
|
||||
}
|
||||
private function ipV4StartNode() : int
|
||||
{
|
||||
// If we have an IPv4 database, the start node is the first node
|
||||
if ($this->metadata->ipVersion === 4) {
|
||||
return 0;
|
||||
}
|
||||
$node = 0;
|
||||
for ($i = 0; $i < 96 && $node < $this->metadata->nodeCount; ++$i) {
|
||||
$node = $this->readNode($node, 0);
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
private function readNode(int $nodeNumber, int $index) : int
|
||||
{
|
||||
$baseOffset = $nodeNumber * $this->metadata->nodeByteSize;
|
||||
switch ($this->metadata->recordSize) {
|
||||
case 24:
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 3, 3);
|
||||
[, $node] = \unpack('N', "\x00" . $bytes);
|
||||
return $node;
|
||||
case 28:
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + 3 * $index, 4);
|
||||
if ($index === 0) {
|
||||
$middle = (0xf0 & \ord($bytes[3])) >> 4;
|
||||
} else {
|
||||
$middle = 0xf & \ord($bytes[0]);
|
||||
}
|
||||
[, $node] = \unpack('N', \chr($middle) . \substr($bytes, $index, 3));
|
||||
return $node;
|
||||
case 32:
|
||||
$bytes = Util::read($this->fileHandle, $baseOffset + $index * 4, 4);
|
||||
[, $node] = \unpack('N', $bytes);
|
||||
return $node;
|
||||
default:
|
||||
throw new InvalidDatabaseException('Unknown record size: ' . $this->metadata->recordSize);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function resolveDataPointer(int $pointer)
|
||||
{
|
||||
$resolved = $pointer - $this->metadata->nodeCount + $this->metadata->searchTreeSize;
|
||||
if ($resolved >= $this->fileSize) {
|
||||
throw new InvalidDatabaseException("The MaxMind DB file's search tree is corrupt");
|
||||
}
|
||||
[$data] = $this->decoder->decode($resolved);
|
||||
return $data;
|
||||
}
|
||||
/*
|
||||
* This is an extremely naive but reasonably readable implementation. There
|
||||
* are much faster algorithms (e.g., Boyer-Moore) for this if speed is ever
|
||||
* an issue, but I suspect it won't be.
|
||||
*/
|
||||
private function findMetadataStart(string $filename) : int
|
||||
{
|
||||
$handle = $this->fileHandle;
|
||||
$fstat = \fstat($handle);
|
||||
$fileSize = $fstat['size'];
|
||||
$marker = self::$METADATA_START_MARKER;
|
||||
$markerLength = self::$METADATA_START_MARKER_LENGTH;
|
||||
$minStart = $fileSize - \min(self::$METADATA_MAX_SIZE, $fileSize);
|
||||
for ($offset = $fileSize - $markerLength; $offset >= $minStart; --$offset) {
|
||||
if (\fseek($handle, $offset) !== 0) {
|
||||
break;
|
||||
}
|
||||
$value = \fread($handle, $markerLength);
|
||||
if ($value === $marker) {
|
||||
return $offset + $markerLength;
|
||||
}
|
||||
}
|
||||
throw new InvalidDatabaseException("Error opening database file ({$filename}). " . 'Is this a valid MaxMind DB file?');
|
||||
}
|
||||
/**
|
||||
* @throws InvalidArgumentException if arguments are passed to the method
|
||||
* @throws BadMethodCallException if the database has been closed
|
||||
*
|
||||
* @return Metadata object for the database
|
||||
*/
|
||||
public function metadata() : Metadata
|
||||
{
|
||||
if (\func_num_args()) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
// Not technically required, but this makes it consistent with
|
||||
// C extension and it allows us to change our implementation later.
|
||||
if (!\is_resource($this->fileHandle)) {
|
||||
throw new BadMethodCallException('Attempt to read from a closed MaxMind DB.');
|
||||
}
|
||||
return clone $this->metadata;
|
||||
}
|
||||
/**
|
||||
* Closes the MaxMind DB and returns resources to the system.
|
||||
*
|
||||
* @throws Exception
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public function close() : void
|
||||
{
|
||||
if (\func_num_args()) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 0 parameters, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
if (!\is_resource($this->fileHandle)) {
|
||||
throw new BadMethodCallException('Attempt to close a closed MaxMind DB.');
|
||||
}
|
||||
\fclose($this->fileHandle);
|
||||
}
|
||||
}
|
||||
275
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
vendored
Normal file
275
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Decoder.php
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Db\Reader;
|
||||
|
||||
// @codingStandardsIgnoreLine
|
||||
use RuntimeException;
|
||||
class Decoder
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $fileStream;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $pointerBase;
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $pointerBaseByteSize;
|
||||
/**
|
||||
* This is only used for unit testing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $pointerTestHack;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $switchByteOrder;
|
||||
private const _EXTENDED = 0;
|
||||
private const _POINTER = 1;
|
||||
private const _UTF8_STRING = 2;
|
||||
private const _DOUBLE = 3;
|
||||
private const _BYTES = 4;
|
||||
private const _UINT16 = 5;
|
||||
private const _UINT32 = 6;
|
||||
private const _MAP = 7;
|
||||
private const _INT32 = 8;
|
||||
private const _UINT64 = 9;
|
||||
private const _UINT128 = 10;
|
||||
private const _ARRAY = 11;
|
||||
private const _CONTAINER = 12;
|
||||
private const _END_MARKER = 13;
|
||||
private const _BOOLEAN = 14;
|
||||
private const _FLOAT = 15;
|
||||
/**
|
||||
* @param resource $fileStream
|
||||
*/
|
||||
public function __construct($fileStream, int $pointerBase = 0, bool $pointerTestHack = \false)
|
||||
{
|
||||
$this->fileStream = $fileStream;
|
||||
$this->pointerBase = $pointerBase;
|
||||
$this->pointerBaseByteSize = $pointerBase > 0 ? \log($pointerBase, 2) / 8 : 0;
|
||||
$this->pointerTestHack = $pointerTestHack;
|
||||
$this->switchByteOrder = $this->isPlatformLittleEndian();
|
||||
}
|
||||
public function decode(int $offset) : array
|
||||
{
|
||||
$ctrlByte = \ord(Util::read($this->fileStream, $offset, 1));
|
||||
++$offset;
|
||||
$type = $ctrlByte >> 5;
|
||||
// Pointers are a special case, we don't read the next $size bytes, we
|
||||
// use the size to determine the length of the pointer and then follow
|
||||
// it.
|
||||
if ($type === self::_POINTER) {
|
||||
[$pointer, $offset] = $this->decodePointer($ctrlByte, $offset);
|
||||
// for unit testing
|
||||
if ($this->pointerTestHack) {
|
||||
return [$pointer];
|
||||
}
|
||||
[$result] = $this->decode($pointer);
|
||||
return [$result, $offset];
|
||||
}
|
||||
if ($type === self::_EXTENDED) {
|
||||
$nextByte = \ord(Util::read($this->fileStream, $offset, 1));
|
||||
$type = $nextByte + 7;
|
||||
if ($type < 8) {
|
||||
throw new InvalidDatabaseException('Something went horribly wrong in the decoder. An extended type ' . 'resolved to a type number < 8 (' . $type . ')');
|
||||
}
|
||||
++$offset;
|
||||
}
|
||||
[$size, $offset] = $this->sizeFromCtrlByte($ctrlByte, $offset);
|
||||
return $this->decodeByType($type, $offset, $size);
|
||||
}
|
||||
private function decodeByType(int $type, int $offset, int $size) : array
|
||||
{
|
||||
switch ($type) {
|
||||
case self::_MAP:
|
||||
return $this->decodeMap($size, $offset);
|
||||
case self::_ARRAY:
|
||||
return $this->decodeArray($size, $offset);
|
||||
case self::_BOOLEAN:
|
||||
return [$this->decodeBoolean($size), $offset];
|
||||
}
|
||||
$newOffset = $offset + $size;
|
||||
$bytes = Util::read($this->fileStream, $offset, $size);
|
||||
switch ($type) {
|
||||
case self::_BYTES:
|
||||
case self::_UTF8_STRING:
|
||||
return [$bytes, $newOffset];
|
||||
case self::_DOUBLE:
|
||||
$this->verifySize(8, $size);
|
||||
return [$this->decodeDouble($bytes), $newOffset];
|
||||
case self::_FLOAT:
|
||||
$this->verifySize(4, $size);
|
||||
return [$this->decodeFloat($bytes), $newOffset];
|
||||
case self::_INT32:
|
||||
return [$this->decodeInt32($bytes, $size), $newOffset];
|
||||
case self::_UINT16:
|
||||
case self::_UINT32:
|
||||
case self::_UINT64:
|
||||
case self::_UINT128:
|
||||
return [$this->decodeUint($bytes, $size), $newOffset];
|
||||
default:
|
||||
throw new InvalidDatabaseException('Unknown or unexpected type: ' . $type);
|
||||
}
|
||||
}
|
||||
private function verifySize(int $expected, int $actual) : void
|
||||
{
|
||||
if ($expected !== $actual) {
|
||||
throw new InvalidDatabaseException("The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)");
|
||||
}
|
||||
}
|
||||
private function decodeArray(int $size, int $offset) : array
|
||||
{
|
||||
$array = [];
|
||||
for ($i = 0; $i < $size; ++$i) {
|
||||
[$value, $offset] = $this->decode($offset);
|
||||
$array[] = $value;
|
||||
}
|
||||
return [$array, $offset];
|
||||
}
|
||||
private function decodeBoolean(int $size) : bool
|
||||
{
|
||||
return $size !== 0;
|
||||
}
|
||||
private function decodeDouble(string $bytes) : float
|
||||
{
|
||||
// This assumes IEEE 754 doubles, but most (all?) modern platforms
|
||||
// use them.
|
||||
[, $double] = \unpack('E', $bytes);
|
||||
return $double;
|
||||
}
|
||||
private function decodeFloat(string $bytes) : float
|
||||
{
|
||||
// This assumes IEEE 754 floats, but most (all?) modern platforms
|
||||
// use them.
|
||||
[, $float] = \unpack('G', $bytes);
|
||||
return $float;
|
||||
}
|
||||
private function decodeInt32(string $bytes, int $size) : int
|
||||
{
|
||||
switch ($size) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
$bytes = \str_pad($bytes, 4, "\x00", \STR_PAD_LEFT);
|
||||
break;
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDatabaseException("The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)");
|
||||
}
|
||||
[, $int] = \unpack('l', $this->maybeSwitchByteOrder($bytes));
|
||||
return $int;
|
||||
}
|
||||
private function decodeMap(int $size, int $offset) : array
|
||||
{
|
||||
$map = [];
|
||||
for ($i = 0; $i < $size; ++$i) {
|
||||
[$key, $offset] = $this->decode($offset);
|
||||
[$value, $offset] = $this->decode($offset);
|
||||
$map[$key] = $value;
|
||||
}
|
||||
return [$map, $offset];
|
||||
}
|
||||
private function decodePointer(int $ctrlByte, int $offset) : array
|
||||
{
|
||||
$pointerSize = ($ctrlByte >> 3 & 0x3) + 1;
|
||||
$buffer = Util::read($this->fileStream, $offset, $pointerSize);
|
||||
$offset = $offset + $pointerSize;
|
||||
switch ($pointerSize) {
|
||||
case 1:
|
||||
$packed = \chr($ctrlByte & 0x7) . $buffer;
|
||||
[, $pointer] = \unpack('n', $packed);
|
||||
$pointer += $this->pointerBase;
|
||||
break;
|
||||
case 2:
|
||||
$packed = "\x00" . \chr($ctrlByte & 0x7) . $buffer;
|
||||
[, $pointer] = \unpack('N', $packed);
|
||||
$pointer += $this->pointerBase + 2048;
|
||||
break;
|
||||
case 3:
|
||||
$packed = \chr($ctrlByte & 0x7) . $buffer;
|
||||
// It is safe to use 'N' here, even on 32 bit machines as the
|
||||
// first bit is 0.
|
||||
[, $pointer] = \unpack('N', $packed);
|
||||
$pointer += $this->pointerBase + 526336;
|
||||
break;
|
||||
case 4:
|
||||
// We cannot use unpack here as we might overflow on 32 bit
|
||||
// machines
|
||||
$pointerOffset = $this->decodeUint($buffer, $pointerSize);
|
||||
$pointerBase = $this->pointerBase;
|
||||
if (\PHP_INT_MAX - $pointerBase >= $pointerOffset) {
|
||||
$pointer = $pointerOffset + $pointerBase;
|
||||
} else {
|
||||
throw new RuntimeException('The database offset is too large to be represented on your platform.');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDatabaseException('Unexpected pointer size ' . $pointerSize);
|
||||
}
|
||||
return [$pointer, $offset];
|
||||
}
|
||||
// @phpstan-ignore-next-line
|
||||
private function decodeUint(string $bytes, int $byteLength)
|
||||
{
|
||||
if ($byteLength === 0) {
|
||||
return 0;
|
||||
}
|
||||
$integer = 0;
|
||||
// PHP integers are signed. PHP_INT_SIZE - 1 is the number of
|
||||
// complete bytes that can be converted to an integer. However,
|
||||
// we can convert another byte if the leading bit is zero.
|
||||
$useRealInts = $byteLength <= \PHP_INT_SIZE - 1 || $byteLength === \PHP_INT_SIZE && (\ord($bytes[0]) & 0x80) === 0;
|
||||
for ($i = 0; $i < $byteLength; ++$i) {
|
||||
$part = \ord($bytes[$i]);
|
||||
// We only use gmp or bcmath if the final value is too big
|
||||
if ($useRealInts) {
|
||||
$integer = ($integer << 8) + $part;
|
||||
} elseif (\extension_loaded('gmp')) {
|
||||
$integer = \gmp_strval(\gmp_add(\gmp_mul((string) $integer, '256'), $part));
|
||||
} elseif (\extension_loaded('bcmath')) {
|
||||
$integer = \bcadd(\bcmul((string) $integer, '256'), (string) $part);
|
||||
} else {
|
||||
throw new RuntimeException('The gmp or bcmath extension must be installed to read this database.');
|
||||
}
|
||||
}
|
||||
return $integer;
|
||||
}
|
||||
private function sizeFromCtrlByte(int $ctrlByte, int $offset) : array
|
||||
{
|
||||
$size = $ctrlByte & 0x1f;
|
||||
if ($size < 29) {
|
||||
return [$size, $offset];
|
||||
}
|
||||
$bytesToRead = $size - 28;
|
||||
$bytes = Util::read($this->fileStream, $offset, $bytesToRead);
|
||||
if ($size === 29) {
|
||||
$size = 29 + \ord($bytes);
|
||||
} elseif ($size === 30) {
|
||||
[, $adjust] = \unpack('n', $bytes);
|
||||
$size = 285 + $adjust;
|
||||
} else {
|
||||
[, $adjust] = \unpack('N', "\x00" . $bytes);
|
||||
$size = $adjust + 65821;
|
||||
}
|
||||
return [$size, $offset + $bytesToRead];
|
||||
}
|
||||
private function maybeSwitchByteOrder(string $bytes) : string
|
||||
{
|
||||
return $this->switchByteOrder ? \strrev($bytes) : $bytes;
|
||||
}
|
||||
private function isPlatformLittleEndian() : bool
|
||||
{
|
||||
$testint = 0xff;
|
||||
$packed = \pack('S', $testint);
|
||||
return $testint === \current(\unpack('v', $packed));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Db\Reader;
|
||||
|
||||
use Exception;
|
||||
/**
|
||||
* This class should be thrown when unexpected data is found in the database.
|
||||
*/
|
||||
class InvalidDatabaseException extends Exception
|
||||
{
|
||||
}
|
||||
104
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Metadata.php
vendored
Normal file
104
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Metadata.php
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Db\Reader;
|
||||
|
||||
use ArgumentCountError;
|
||||
/**
|
||||
* This class provides the metadata for the MaxMind DB file.
|
||||
*/
|
||||
class Metadata
|
||||
{
|
||||
/**
|
||||
* This is an unsigned 16-bit integer indicating the major version number
|
||||
* for the database's binary format.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $binaryFormatMajorVersion;
|
||||
/**
|
||||
* This is an unsigned 16-bit integer indicating the minor version number
|
||||
* for the database's binary format.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $binaryFormatMinorVersion;
|
||||
/**
|
||||
* This is an unsigned 64-bit integer that contains the database build
|
||||
* timestamp as a Unix epoch value.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $buildEpoch;
|
||||
/**
|
||||
* This is a string that indicates the structure of each data record
|
||||
* associated with an IP address. The actual definition of these
|
||||
* structures is left up to the database creator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $databaseType;
|
||||
/**
|
||||
* This key will always point to a map (associative array). The keys of
|
||||
* that map will be language codes, and the values will be a description
|
||||
* in that language as a UTF-8 string. May be undefined for some
|
||||
* databases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $description;
|
||||
/**
|
||||
* This is an unsigned 16-bit integer which is always 4 or 6. It indicates
|
||||
* whether the database contains IPv4 or IPv6 address data.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $ipVersion;
|
||||
/**
|
||||
* An array of strings, each of which is a language code. A given record
|
||||
* may contain data items that have been localized to some or all of
|
||||
* these languages. This may be undefined.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $languages;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $nodeByteSize;
|
||||
/**
|
||||
* This is an unsigned 32-bit integer indicating the number of nodes in
|
||||
* the search tree.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $nodeCount;
|
||||
/**
|
||||
* This is an unsigned 16-bit integer. It indicates the number of bits in a
|
||||
* record in the search tree. Note that each node consists of two records.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $recordSize;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $searchTreeSize;
|
||||
public function __construct(array $metadata)
|
||||
{
|
||||
if (\func_num_args() !== 1) {
|
||||
throw new ArgumentCountError(\sprintf('%s() expects exactly 1 parameter, %d given', __METHOD__, \func_num_args()));
|
||||
}
|
||||
$this->binaryFormatMajorVersion = $metadata['binary_format_major_version'];
|
||||
$this->binaryFormatMinorVersion = $metadata['binary_format_minor_version'];
|
||||
$this->buildEpoch = $metadata['build_epoch'];
|
||||
$this->databaseType = $metadata['database_type'];
|
||||
$this->languages = $metadata['languages'];
|
||||
$this->description = $metadata['description'];
|
||||
$this->ipVersion = $metadata['ip_version'];
|
||||
$this->nodeCount = $metadata['node_count'];
|
||||
$this->recordSize = $metadata['record_size'];
|
||||
$this->nodeByteSize = $this->recordSize / 4;
|
||||
$this->searchTreeSize = $this->nodeCount * $this->nodeByteSize;
|
||||
}
|
||||
}
|
||||
27
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Util.php
vendored
Normal file
27
plugins/system/tgeoip/vendor/maxmind-db/reader/src/MaxMind/Db/Reader/Util.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Db\Reader;
|
||||
|
||||
class Util
|
||||
{
|
||||
/**
|
||||
* @param resource $stream
|
||||
*/
|
||||
public static function read($stream, int $offset, int $numberOfBytes) : string
|
||||
{
|
||||
if ($numberOfBytes === 0) {
|
||||
return '';
|
||||
}
|
||||
if (\fseek($stream, $offset) === 0) {
|
||||
$value = \fread($stream, $numberOfBytes);
|
||||
// We check that the number of bytes read is equal to the number
|
||||
// asked for. We use ftell as getting the length of $value is
|
||||
// much slower.
|
||||
if ($value !== \false && \ftell($stream) - $offset === $numberOfBytes) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
throw new InvalidDatabaseException('The MaxMind DB file contains bad data');
|
||||
}
|
||||
}
|
||||
56
plugins/system/tgeoip/vendor/maxmind/web-service-common/dev-bin/release.sh
vendored
Normal file
56
plugins/system/tgeoip/vendor/maxmind/web-service-common/dev-bin/release.sh
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu -o pipefail
|
||||
|
||||
|
||||
changelog=$(cat CHANGELOG.md)
|
||||
|
||||
regex='
|
||||
([0-9]+\.[0-9]+\.[0-9]+) \(([0-9]{4}-[0-9]{2}-[0-9]{2})\)
|
||||
-*
|
||||
|
||||
((.|
|
||||
)*)
|
||||
'
|
||||
|
||||
if [[ ! $changelog =~ $regex ]]; then
|
||||
echo "Could not find date line in change log!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${BASH_REMATCH[1]}"
|
||||
date="${BASH_REMATCH[2]}"
|
||||
notes="$(echo "${BASH_REMATCH[3]}" | sed -n -E '/^[0-9]+\.[0-9]+\.[0-9]+/,$!p')"
|
||||
|
||||
if [[ "$date" != $(date +"%Y-%m-%d") ]]; then
|
||||
echo "$date is not today!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tag="v$version"
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo ". is not clean." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
php composer.phar self-update
|
||||
php composer.phar update
|
||||
|
||||
./vendor/bin/phpunit
|
||||
|
||||
echo "Release notes for $tag:"
|
||||
echo "$notes"
|
||||
|
||||
read -e -p "Commit changes and push to origin? " should_push
|
||||
|
||||
if [ "$should_push" != "y" ]; then
|
||||
echo "Aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git push
|
||||
|
||||
gh release create --target "$(git branch --show-current)" -t "$version" -n "$notes" "$tag"
|
||||
|
||||
git push --tags
|
||||
7
plugins/system/tgeoip/vendor/maxmind/web-service-common/phpstan.neon
vendored
Normal file
7
plugins/system/tgeoip/vendor/maxmind/web-service-common/phpstan.neon
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
parameters:
|
||||
level: 6
|
||||
paths:
|
||||
- src
|
||||
- tests
|
||||
checkMissingIterableValueType: false
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an error authenticating.
|
||||
*/
|
||||
class AuthenticationException extends InvalidRequestException
|
||||
{
|
||||
}
|
||||
36
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/HttpException.php
vendored
Normal file
36
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/HttpException.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an HTTP transport error.
|
||||
*/
|
||||
class HttpException extends WebServiceException
|
||||
{
|
||||
/**
|
||||
* The URI queried.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $uri;
|
||||
/**
|
||||
* @param string $message a message describing the error
|
||||
* @param int $httpStatus the HTTP status code of the response
|
||||
* @param string $uri the URI used in the request
|
||||
* @param \Exception $previous the previous exception, if any
|
||||
*/
|
||||
public function __construct(string $message, int $httpStatus, string $uri, \Exception $previous = null)
|
||||
{
|
||||
$this->uri = $uri;
|
||||
parent::__construct($message, $httpStatus, $previous);
|
||||
}
|
||||
public function getUri() : string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
public function getStatusCode() : int
|
||||
{
|
||||
return $this->getCode();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when the account is out of credits.
|
||||
*/
|
||||
class InsufficientFundsException extends InvalidRequestException
|
||||
{
|
||||
}
|
||||
13
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/InvalidInputException.php
vendored
Normal file
13
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/InvalidInputException.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents an error in creating the request to be sent to the
|
||||
* web service. For example, if the array cannot be encoded as JSON or if there
|
||||
* is a missing or invalid field.
|
||||
*/
|
||||
class InvalidInputException extends WebServiceException
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a MaxMind web service returns an error relating to the request.
|
||||
*/
|
||||
class InvalidRequestException extends HttpException
|
||||
{
|
||||
/**
|
||||
* The code returned by the MaxMind web service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $error;
|
||||
/**
|
||||
* @param string $message the exception message
|
||||
* @param string $error the error code returned by the MaxMind web service
|
||||
* @param int $httpStatus the HTTP status code of the response
|
||||
* @param string $uri the URI queries
|
||||
* @param \Exception $previous the previous exception, if any
|
||||
*/
|
||||
public function __construct(string $message, string $error, int $httpStatus, string $uri, \Exception $previous = null)
|
||||
{
|
||||
$this->error = $error;
|
||||
parent::__construct($message, $httpStatus, $uri, $previous);
|
||||
}
|
||||
public function getErrorCode() : string
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
class IpAddressNotFoundException extends InvalidRequestException
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This exception is thrown when the service requires permission to access.
|
||||
*/
|
||||
class PermissionRequiredException extends InvalidRequestException
|
||||
{
|
||||
}
|
||||
11
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/WebServiceException.php
vendored
Normal file
11
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/Exception/WebServiceException.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\Exception;
|
||||
|
||||
/**
|
||||
* This class represents a generic web service error.
|
||||
*/
|
||||
class WebServiceException extends \Exception
|
||||
{
|
||||
}
|
||||
354
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Client.php
vendored
Normal file
354
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Client.php
vendored
Normal file
@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\WebService;
|
||||
|
||||
use Tassos\Vendor\Composer\CaBundle\CaBundle;
|
||||
use Tassos\Vendor\MaxMind\Exception\AuthenticationException;
|
||||
use Tassos\Vendor\MaxMind\Exception\HttpException;
|
||||
use Tassos\Vendor\MaxMind\Exception\InsufficientFundsException;
|
||||
use Tassos\Vendor\MaxMind\Exception\InvalidInputException;
|
||||
use Tassos\Vendor\MaxMind\Exception\InvalidRequestException;
|
||||
use Tassos\Vendor\MaxMind\Exception\IpAddressNotFoundException;
|
||||
use Tassos\Vendor\MaxMind\Exception\PermissionRequiredException;
|
||||
use Tassos\Vendor\MaxMind\Exception\WebServiceException;
|
||||
use Tassos\Vendor\MaxMind\WebService\Http\RequestFactory;
|
||||
/**
|
||||
* This class is not intended to be used directly by an end-user of a
|
||||
* MaxMind web service. Please use the appropriate client API for the service
|
||||
* that you are using.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
public const VERSION = '0.2.0';
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $caBundle;
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $connectTimeout;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $host = 'api.maxmind.com';
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useHttps = \true;
|
||||
/**
|
||||
* @var RequestFactory
|
||||
*/
|
||||
private $httpRequestFactory;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $licenseKey;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $proxy;
|
||||
/**
|
||||
* @var float|null
|
||||
*/
|
||||
private $timeout;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $userAgentPrefix;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $accountId;
|
||||
/**
|
||||
* @param int $accountId your MaxMind account ID
|
||||
* @param string $licenseKey your MaxMind license key
|
||||
* @param array $options an array of options. Possible keys:
|
||||
* * `host` - The host to use when connecting to the web service.
|
||||
* * `useHttps` - A boolean flag for sending the request via https.(True by default)
|
||||
* * `userAgent` - The prefix of the User-Agent to use in the request.
|
||||
* * `caBundle` - The bundle of CA root certificates to use in the request.
|
||||
* * `connectTimeout` - The connect timeout to use for the request.
|
||||
* * `timeout` - The timeout to use for the request.
|
||||
* * `proxy` - The HTTP proxy to use. May include a schema, port,
|
||||
* username, and password, e.g., `http://username:password@127.0.0.1:10`.
|
||||
*/
|
||||
public function __construct(int $accountId, string $licenseKey, array $options = [])
|
||||
{
|
||||
$this->accountId = $accountId;
|
||||
$this->licenseKey = $licenseKey;
|
||||
$this->httpRequestFactory = isset($options['httpRequestFactory']) ? $options['httpRequestFactory'] : new RequestFactory();
|
||||
if (isset($options['host'])) {
|
||||
$this->host = $options['host'];
|
||||
}
|
||||
if (isset($options['useHttps'])) {
|
||||
$this->useHttps = $options['useHttps'];
|
||||
}
|
||||
if (isset($options['userAgent'])) {
|
||||
$this->userAgentPrefix = $options['userAgent'] . ' ';
|
||||
}
|
||||
$this->caBundle = isset($options['caBundle']) ? $this->caBundle = $options['caBundle'] : $this->getCaBundle();
|
||||
if (isset($options['connectTimeout'])) {
|
||||
$this->connectTimeout = $options['connectTimeout'];
|
||||
}
|
||||
if (isset($options['timeout'])) {
|
||||
$this->timeout = $options['timeout'];
|
||||
}
|
||||
if (isset($options['proxy'])) {
|
||||
$this->proxy = $options['proxy'];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param string $service name of the service querying
|
||||
* @param string $path the URI path to use
|
||||
* @param array $input the data to be posted as JSON
|
||||
*
|
||||
* @throws InvalidInputException when the request has missing or invalid
|
||||
* data
|
||||
* @throws AuthenticationException when there is an issue authenticating the
|
||||
* request
|
||||
* @throws InsufficientFundsException when your account is out of funds
|
||||
* @throws InvalidRequestException when the request is invalid for some
|
||||
* other reason, e.g., invalid JSON in the POST.
|
||||
* @throws HttpException when an unexpected HTTP error occurs
|
||||
* @throws WebServiceException when some other error occurs. This also
|
||||
* serves as the base class for the above exceptions.
|
||||
*
|
||||
* @return array|null The decoded content of a successful response
|
||||
*/
|
||||
public function post(string $service, string $path, array $input) : ?array
|
||||
{
|
||||
$requestBody = \json_encode($input);
|
||||
if ($requestBody === \false) {
|
||||
throw new InvalidInputException('Error encoding input as JSON: ' . $this->jsonErrorDescription());
|
||||
}
|
||||
$request = $this->createRequest($path, ['Content-Type: application/json']);
|
||||
[$statusCode, $contentType, $responseBody] = $request->post($requestBody);
|
||||
return $this->handleResponse($statusCode, $contentType, $responseBody, $service, $path);
|
||||
}
|
||||
public function get(string $service, string $path) : ?array
|
||||
{
|
||||
$request = $this->createRequest($path);
|
||||
[$statusCode, $contentType, $responseBody] = $request->get();
|
||||
return $this->handleResponse($statusCode, $contentType, $responseBody, $service, $path);
|
||||
}
|
||||
private function userAgent() : string
|
||||
{
|
||||
$curlVersion = \curl_version();
|
||||
return $this->userAgentPrefix . 'MaxMind-WS-API/' . self::VERSION . ' PHP/' . \PHP_VERSION . ' curl/' . $curlVersion['version'];
|
||||
}
|
||||
private function createRequest(string $path, array $headers = []) : Http\Request
|
||||
{
|
||||
\array_push($headers, 'Authorization: Basic ' . \base64_encode($this->accountId . ':' . $this->licenseKey), 'Accept: application/json');
|
||||
return $this->httpRequestFactory->request($this->urlFor($path), ['caBundle' => $this->caBundle, 'connectTimeout' => $this->connectTimeout, 'headers' => $headers, 'proxy' => $this->proxy, 'timeout' => $this->timeout, 'userAgent' => $this->userAgent()]);
|
||||
}
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code of the response
|
||||
* @param string|null $contentType the Content-Type of the response
|
||||
* @param string|null $responseBody the response body
|
||||
* @param string $service the name of the service
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException when there is an issue authenticating the
|
||||
* request
|
||||
* @throws InsufficientFundsException when your account is out of funds
|
||||
* @throws InvalidRequestException when the request is invalid for some
|
||||
* other reason, e.g., invalid JSON in the POST.
|
||||
* @throws HttpException when an unexpected HTTP error occurs
|
||||
* @throws WebServiceException when some other error occurs. This also
|
||||
* serves as the base class for the above exceptions
|
||||
*
|
||||
* @return array|null The decoded content of a successful response
|
||||
*/
|
||||
private function handleResponse(int $statusCode, ?string $contentType, ?string $responseBody, string $service, string $path) : ?array
|
||||
{
|
||||
if ($statusCode >= 400 && $statusCode <= 499) {
|
||||
$this->handle4xx($statusCode, $contentType, $responseBody, $service, $path);
|
||||
} elseif ($statusCode >= 500) {
|
||||
$this->handle5xx($statusCode, $service, $path);
|
||||
} elseif ($statusCode !== 200 && $statusCode !== 204) {
|
||||
$this->handleUnexpectedStatus($statusCode, $service, $path);
|
||||
}
|
||||
return $this->handleSuccess($statusCode, $responseBody, $service);
|
||||
}
|
||||
/**
|
||||
* @return string describing the JSON error
|
||||
*/
|
||||
private function jsonErrorDescription() : string
|
||||
{
|
||||
$errno = \json_last_error();
|
||||
switch ($errno) {
|
||||
case \JSON_ERROR_DEPTH:
|
||||
return 'The maximum stack depth has been exceeded.';
|
||||
case \JSON_ERROR_STATE_MISMATCH:
|
||||
return 'Invalid or malformed JSON.';
|
||||
case \JSON_ERROR_CTRL_CHAR:
|
||||
return 'Control character error.';
|
||||
case \JSON_ERROR_SYNTAX:
|
||||
return 'Syntax error.';
|
||||
case \JSON_ERROR_UTF8:
|
||||
return 'Malformed UTF-8 characters.';
|
||||
default:
|
||||
return "Other JSON error ({$errno}).";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param string $path the path to use in the URL
|
||||
*
|
||||
* @return string the constructed URL
|
||||
*/
|
||||
private function urlFor(string $path) : string
|
||||
{
|
||||
return ($this->useHttps ? 'https://' : 'http://') . $this->host . $path;
|
||||
}
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string|null $contentType the response content-type
|
||||
* @param string|null $body the response body
|
||||
* @param string $service the service name
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws HttpException
|
||||
* @throws InsufficientFundsException
|
||||
* @throws InvalidRequestException
|
||||
*/
|
||||
private function handle4xx(int $statusCode, ?string $contentType, ?string $body, string $service, string $path) : void
|
||||
{
|
||||
if ($body === null || $body === '') {
|
||||
throw new HttpException("Received a {$statusCode} error for {$service} with no body", $statusCode, $this->urlFor($path));
|
||||
}
|
||||
if ($contentType === null || !\strstr($contentType, 'json')) {
|
||||
throw new HttpException("Received a {$statusCode} error for {$service} with " . 'the following body: ' . $body, $statusCode, $this->urlFor($path));
|
||||
}
|
||||
$message = \json_decode($body, \true);
|
||||
if ($message === null) {
|
||||
throw new HttpException("Received a {$statusCode} error for {$service} but could " . 'not decode the response as JSON: ' . $this->jsonErrorDescription() . ' Body: ' . $body, $statusCode, $this->urlFor($path));
|
||||
}
|
||||
if (!isset($message['code']) || !isset($message['error'])) {
|
||||
throw new HttpException('Error response contains JSON but it does not ' . 'specify code or error keys: ' . $body, $statusCode, $this->urlFor($path));
|
||||
}
|
||||
$this->handleWebServiceError($message['error'], $message['code'], $statusCode, $path);
|
||||
}
|
||||
/**
|
||||
* @param string $message the error message from the web service
|
||||
* @param string $code the error code from the web service
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $path the path used in the request
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidRequestException
|
||||
* @throws InsufficientFundsException
|
||||
*/
|
||||
private function handleWebServiceError(string $message, string $code, int $statusCode, string $path) : void
|
||||
{
|
||||
switch ($code) {
|
||||
case 'IP_ADDRESS_NOT_FOUND':
|
||||
case 'IP_ADDRESS_RESERVED':
|
||||
throw new IpAddressNotFoundException($message, $code, $statusCode, $this->urlFor($path));
|
||||
case 'ACCOUNT_ID_REQUIRED':
|
||||
case 'ACCOUNT_ID_UNKNOWN':
|
||||
case 'AUTHORIZATION_INVALID':
|
||||
case 'LICENSE_KEY_REQUIRED':
|
||||
case 'USER_ID_REQUIRED':
|
||||
case 'USER_ID_UNKNOWN':
|
||||
throw new AuthenticationException($message, $code, $statusCode, $this->urlFor($path));
|
||||
case 'OUT_OF_QUERIES':
|
||||
case 'INSUFFICIENT_FUNDS':
|
||||
throw new InsufficientFundsException($message, $code, $statusCode, $this->urlFor($path));
|
||||
case 'PERMISSION_REQUIRED':
|
||||
throw new PermissionRequiredException($message, $code, $statusCode, $this->urlFor($path));
|
||||
default:
|
||||
throw new InvalidRequestException($message, $code, $statusCode, $this->urlFor($path));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $service the service name
|
||||
* @param string $path the URI path used in the request
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function handle5xx(int $statusCode, string $service, string $path) : void
|
||||
{
|
||||
throw new HttpException("Received a server error ({$statusCode}) for {$service}", $statusCode, $this->urlFor($path));
|
||||
}
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string $service the service name
|
||||
* @param string $path the URI path used in the request
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function handleUnexpectedStatus(int $statusCode, string $service, string $path) : void
|
||||
{
|
||||
throw new HttpException('Received an unexpected HTTP status ' . "({$statusCode}) for {$service}", $statusCode, $this->urlFor($path));
|
||||
}
|
||||
/**
|
||||
* @param int $statusCode the HTTP status code
|
||||
* @param string|null $body the successful request body
|
||||
* @param string $service the service name
|
||||
*
|
||||
* @throws WebServiceException if a response body is included but not
|
||||
* expected, or is not expected but not
|
||||
* included, or is expected and included
|
||||
* but cannot be decoded as JSON
|
||||
*
|
||||
* @return array|null the decoded request body
|
||||
*/
|
||||
private function handleSuccess(int $statusCode, ?string $body, string $service) : ?array
|
||||
{
|
||||
// A 204 should have no response body
|
||||
if ($statusCode === 204) {
|
||||
if ($body !== null && $body !== '') {
|
||||
throw new WebServiceException("Received a 204 response for {$service} along with an " . "unexpected HTTP body: {$body}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// A 200 should have a valid JSON body
|
||||
if ($body === null || $body === '') {
|
||||
throw new WebServiceException("Received a 200 response for {$service} but did not " . 'receive a HTTP body.');
|
||||
}
|
||||
$decodedContent = \json_decode($body, \true);
|
||||
if ($decodedContent === null) {
|
||||
throw new WebServiceException("Received a 200 response for {$service} but could " . 'not decode the response as JSON: ' . $this->jsonErrorDescription() . ' Body: ' . $body);
|
||||
}
|
||||
return $decodedContent;
|
||||
}
|
||||
private function getCaBundle() : ?string
|
||||
{
|
||||
$curlVersion = \curl_version();
|
||||
// On OS X, when the SSL version is "SecureTransport", the system's
|
||||
// keychain will be used.
|
||||
if ($curlVersion['ssl_version'] === 'SecureTransport') {
|
||||
return null;
|
||||
}
|
||||
$cert = CaBundle::getSystemCaRootBundlePath();
|
||||
// Check if the cert is inside a phar. If so, we need to copy the cert
|
||||
// to a temp file so that curl can see it.
|
||||
if (\substr($cert, 0, 7) === 'phar://') {
|
||||
$tempDir = \sys_get_temp_dir();
|
||||
$newCert = \tempnam($tempDir, 'geoip2-');
|
||||
if ($newCert === \false) {
|
||||
throw new \RuntimeException("Unable to create temporary file in {$tempDir}");
|
||||
}
|
||||
if (!\copy($cert, $newCert)) {
|
||||
throw new \RuntimeException("Could not copy {$cert} to {$newCert}: " . \var_export(\error_get_last(), \true));
|
||||
}
|
||||
// We use a shutdown function rather than the destructor as the
|
||||
// destructor isn't called on a fatal error such as an uncaught
|
||||
// exception.
|
||||
\register_shutdown_function(function () use($newCert) {
|
||||
\unlink($newCert);
|
||||
});
|
||||
$cert = $newCert;
|
||||
}
|
||||
if (!\file_exists($cert)) {
|
||||
throw new \RuntimeException("CA cert does not exist at {$cert}");
|
||||
}
|
||||
return $cert;
|
||||
}
|
||||
}
|
||||
108
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/CurlRequest.php
vendored
Normal file
108
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/CurlRequest.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\WebService\Http;
|
||||
|
||||
use Tassos\Vendor\MaxMind\Exception\HttpException;
|
||||
/**
|
||||
* This class is for internal use only. Semantic versioning does not not apply.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CurlRequest implements Request
|
||||
{
|
||||
/**
|
||||
* @var \CurlHandle
|
||||
*/
|
||||
private $ch;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $options;
|
||||
public function __construct(string $url, array $options)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->options = $options;
|
||||
$this->ch = $options['curlHandle'];
|
||||
}
|
||||
/**
|
||||
* @throws HttpException
|
||||
*/
|
||||
public function post(string $body) : array
|
||||
{
|
||||
$curl = $this->createCurl();
|
||||
\curl_setopt($curl, \CURLOPT_POST, \true);
|
||||
\curl_setopt($curl, \CURLOPT_POSTFIELDS, $body);
|
||||
return $this->execute($curl);
|
||||
}
|
||||
public function get() : array
|
||||
{
|
||||
$curl = $this->createCurl();
|
||||
\curl_setopt($curl, \CURLOPT_HTTPGET, \true);
|
||||
return $this->execute($curl);
|
||||
}
|
||||
/**
|
||||
* @return \CurlHandle
|
||||
*/
|
||||
private function createCurl()
|
||||
{
|
||||
\curl_reset($this->ch);
|
||||
$opts = [];
|
||||
$opts[\CURLOPT_URL] = $this->url;
|
||||
if (!empty($this->options['caBundle'])) {
|
||||
$opts[\CURLOPT_CAINFO] = $this->options['caBundle'];
|
||||
}
|
||||
$opts[\CURLOPT_ENCODING] = '';
|
||||
$opts[\CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$opts[\CURLOPT_FOLLOWLOCATION] = \false;
|
||||
$opts[\CURLOPT_SSL_VERIFYPEER] = \true;
|
||||
$opts[\CURLOPT_RETURNTRANSFER] = \true;
|
||||
$opts[\CURLOPT_HTTPHEADER] = $this->options['headers'];
|
||||
$opts[\CURLOPT_USERAGENT] = $this->options['userAgent'];
|
||||
$opts[\CURLOPT_PROXY] = $this->options['proxy'];
|
||||
// The defined()s are here as the *_MS opts are not available on older
|
||||
// cURL versions
|
||||
$connectTimeout = $this->options['connectTimeout'];
|
||||
if (\defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||
$opts[\CURLOPT_CONNECTTIMEOUT_MS] = \ceil($connectTimeout * 1000);
|
||||
} else {
|
||||
$opts[\CURLOPT_CONNECTTIMEOUT] = \ceil($connectTimeout);
|
||||
}
|
||||
$timeout = $this->options['timeout'];
|
||||
if (\defined('CURLOPT_TIMEOUT_MS')) {
|
||||
$opts[\CURLOPT_TIMEOUT_MS] = \ceil($timeout * 1000);
|
||||
} else {
|
||||
$opts[\CURLOPT_TIMEOUT] = \ceil($timeout);
|
||||
}
|
||||
\curl_setopt_array($this->ch, $opts);
|
||||
return $this->ch;
|
||||
}
|
||||
/**
|
||||
* @param \CurlHandle $curl
|
||||
*
|
||||
* @throws HttpException
|
||||
*/
|
||||
private function execute($curl) : array
|
||||
{
|
||||
$body = \curl_exec($curl);
|
||||
if ($errno = \curl_errno($curl)) {
|
||||
$errorMessage = \curl_error($curl);
|
||||
throw new HttpException("cURL error ({$errno}): {$errorMessage}", 0, $this->url);
|
||||
}
|
||||
$statusCode = \curl_getinfo($curl, \CURLINFO_HTTP_CODE);
|
||||
$contentType = \curl_getinfo($curl, \CURLINFO_CONTENT_TYPE);
|
||||
return [
|
||||
$statusCode,
|
||||
// The PHP docs say "Content-Type: of the requested document. NULL
|
||||
// indicates server did not send valid Content-Type: header" for
|
||||
// CURLINFO_CONTENT_TYPE. However, it will return FALSE if no header
|
||||
// is set. To keep our types simple, we return null in this case.
|
||||
$contentType === \false ? null : $contentType,
|
||||
$body,
|
||||
];
|
||||
}
|
||||
}
|
||||
16
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/Request.php
vendored
Normal file
16
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/Request.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\WebService\Http;
|
||||
|
||||
/**
|
||||
* Interface Request.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Request
|
||||
{
|
||||
public function __construct(string $url, array $options);
|
||||
public function post(string $body) : array;
|
||||
public function get() : array;
|
||||
}
|
||||
42
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/RequestFactory.php
vendored
Normal file
42
plugins/system/tgeoip/vendor/maxmind/web-service-common/src/WebService/Http/RequestFactory.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Tassos\Vendor\MaxMind\WebService\Http;
|
||||
|
||||
/**
|
||||
* Class RequestFactory.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RequestFactory
|
||||
{
|
||||
/**
|
||||
* Keep the cURL resource here, so that if there are multiple API requests
|
||||
* done the connection is kept alive, SSL resumption can be used
|
||||
* etcetera.
|
||||
*
|
||||
* @var \CurlHandle|null
|
||||
*/
|
||||
private $ch;
|
||||
public function __destruct()
|
||||
{
|
||||
if (!empty($this->ch)) {
|
||||
\curl_close($this->ch);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return \CurlHandle
|
||||
*/
|
||||
private function getCurlHandle()
|
||||
{
|
||||
if (empty($this->ch)) {
|
||||
$this->ch = \curl_init();
|
||||
}
|
||||
return $this->ch;
|
||||
}
|
||||
public function request(string $url, array $options) : Request
|
||||
{
|
||||
$options['curlHandle'] = $this->getCurlHandle();
|
||||
return new CurlRequest($url, $options);
|
||||
}
|
||||
}
|
||||
37
plugins/system/tgeoip/vendor/scoper-autoload.php
vendored
Normal file
37
plugins/system/tgeoip/vendor/scoper-autoload.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
// scoper-autoload.php @generated by PhpScoper
|
||||
|
||||
// Backup the autoloaded Composer files
|
||||
if (isset($GLOBALS['__composer_autoload_files'])) {
|
||||
$existingComposerAutoloadFiles = $GLOBALS['__composer_autoload_files'];
|
||||
}
|
||||
|
||||
$loader = require_once __DIR__.'/autoload.php';
|
||||
// Ensure InstalledVersions is available
|
||||
$installedVersionsPath = __DIR__.'/composer/InstalledVersions.php';
|
||||
if (file_exists($installedVersionsPath)) require_once $installedVersionsPath;
|
||||
|
||||
// Restore the backup
|
||||
if (isset($existingComposerAutoloadFiles)) {
|
||||
$GLOBALS['__composer_autoload_files'] = $existingComposerAutoloadFiles;
|
||||
} else {
|
||||
unset($GLOBALS['__composer_autoload_files']);
|
||||
}
|
||||
|
||||
// Class aliases. For more information see:
|
||||
// https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md#class-aliases
|
||||
if (!function_exists('humbug_phpscoper_expose_class')) {
|
||||
function humbug_phpscoper_expose_class(string $exposed, string $prefixed): void {
|
||||
if (!class_exists($exposed, false) && !interface_exists($exposed, false) && !trait_exists($exposed, false)) {
|
||||
spl_autoload_call($prefixed);
|
||||
}
|
||||
}
|
||||
}
|
||||
humbug_phpscoper_expose_class('ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d', 'Tassos\Vendor\ComposerAutoloaderInit087ac1c88c9dd7b872309175c7c60d7d');
|
||||
|
||||
// Function aliases. For more information see:
|
||||
// https://github.com/humbug/php-scoper/blob/master/docs/further-reading.md#function-aliases
|
||||
if (!function_exists('mmdb_autoload')) { function mmdb_autoload() { return \Tassos\Vendor\mmdb_autoload(...func_get_args()); } }
|
||||
|
||||
return $loader;
|
||||
4
plugins/system/tgeoip/vendor/splitbrain/php-archive/apigen.neon
vendored
Normal file
4
plugins/system/tgeoip/vendor/splitbrain/php-archive/apigen.neon
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
tree: Yes
|
||||
deprecated: Yes
|
||||
accessLevels: [public]
|
||||
todo: Yes
|
||||
63
plugins/system/tgeoip/vendor/splitbrain/php-archive/generate-api.sh
vendored
Normal file
63
plugins/system/tgeoip/vendor/splitbrain/php-archive/generate-api.sh
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
# where's the source files?
|
||||
SRC='src'
|
||||
|
||||
# for what branch to trigger
|
||||
BRANCH='master'
|
||||
|
||||
# github repo
|
||||
REPO='splitbrain/php-archive'
|
||||
|
||||
# ---- About -------------------------------------------------------
|
||||
#
|
||||
# This script use apigen to generate the documentation for the
|
||||
# repository configured above. When run locally, the documentation
|
||||
# will be placed in the 'docs' folder.
|
||||
# However this script can also be run from travis. This requires
|
||||
# the setup of a secret token as described at http://bit.ly/1MNbPn0
|
||||
#
|
||||
# Additional configuration can be done within an apigen.neon file
|
||||
#
|
||||
# ---- no modifications below ---------------------------------------
|
||||
|
||||
# when on travis, build outside of repository, otherwise locally
|
||||
if [ -z "$TRAVIS" ]; then
|
||||
DST='docs'
|
||||
else
|
||||
DST='../gh-pages'
|
||||
if [ "$TRAVIS_PHP_VERSION" != '7.2' ]; then exit; fi
|
||||
if [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then exit; fi
|
||||
if [ "$TRAVIS_PULL_REQUEST" != 'false' ]; then exit; fi
|
||||
if [ -z "$GH_TOKEN" ]; then
|
||||
echo "GH_TOKEN not set! See: http://bit.ly/1MNbPn0"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get ApiGen.phar
|
||||
wget http://www.apigen.org/apigen.phar -O apigen.phar
|
||||
|
||||
# Generate SDK Docs
|
||||
php apigen.phar generate --template-theme="bootstrap" -s $SRC -d $DST
|
||||
|
||||
|
||||
### if we're not on travis, we're done
|
||||
if [ -z "$TRAVIS" ]; then exit; fi
|
||||
|
||||
# go to the generated docs
|
||||
cd $DST || exit
|
||||
|
||||
# Set identity
|
||||
git config --global user.email "travis@travis-ci.org"
|
||||
git config --global user.name "Travis"
|
||||
|
||||
# Add branch
|
||||
git init
|
||||
git remote add origin https://${GH_TOKEN}@github.com/${REPO}.git > /dev/null
|
||||
git checkout -B gh-pages
|
||||
|
||||
# Push generated files
|
||||
git add .
|
||||
git commit -m "Docs updated by Travis"
|
||||
git push origin gh-pages -fq > /dev/null
|
||||
21
plugins/system/tgeoip/vendor/splitbrain/php-archive/phpunit.xml
vendored
Normal file
21
plugins/system/tgeoip/vendor/splitbrain/php-archive/phpunit.xml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Test Suite">
|
||||
<directory suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="false">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
122
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Archive.php
vendored
Normal file
122
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Archive.php
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
abstract class Archive
|
||||
{
|
||||
const COMPRESS_AUTO = -1;
|
||||
const COMPRESS_NONE = 0;
|
||||
const COMPRESS_GZIP = 1;
|
||||
const COMPRESS_BZIP = 2;
|
||||
/** @var callable */
|
||||
protected $callback;
|
||||
/**
|
||||
* Set the compression level and type
|
||||
*
|
||||
* @param int $level Compression level (0 to 9)
|
||||
* @param int $type Type of compression to use (use COMPRESS_* constants)
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public abstract function setCompression($level = 9, $type = Archive::COMPRESS_AUTO);
|
||||
/**
|
||||
* Open an existing archive file for reading
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public abstract function open($file);
|
||||
/**
|
||||
* Read the contents of an archive
|
||||
*
|
||||
* This function lists the files stored in the archive, and returns an indexed array of FileInfo objects
|
||||
*
|
||||
* The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams.
|
||||
* Reopen the file with open() again if you want to do additional operations
|
||||
*
|
||||
* @return FileInfo[]
|
||||
*/
|
||||
public abstract function contents();
|
||||
/**
|
||||
* Extract an existing archive
|
||||
*
|
||||
* The $strip parameter allows you to strip a certain number of path components from the filenames
|
||||
* found in the archive file, similar to the --strip-components feature of GNU tar. This is triggered when
|
||||
* an integer is passed as $strip.
|
||||
* Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix,
|
||||
* the prefix will be stripped. It is recommended to give prefixes with a trailing slash.
|
||||
*
|
||||
* By default this will extract all files found in the archive. You can restrict the output using the $include
|
||||
* and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If
|
||||
* $include is set, only files that match this expression will be extracted. Files that match the $exclude
|
||||
* expression will never be extracted. Both parameters can be used in combination. Expressions are matched against
|
||||
* stripped filenames as described above.
|
||||
*
|
||||
* The archive is closed afterwards. Reopen the file with open() again if you want to do additional operations
|
||||
*
|
||||
* @param string $outdir the target directory for extracting
|
||||
* @param int|string $strip either the number of path components or a fixed prefix to strip
|
||||
* @param string $exclude a regular expression of files to exclude
|
||||
* @param string $include a regular expression of files to include
|
||||
* @throws ArchiveIOException
|
||||
* @return array
|
||||
*/
|
||||
public abstract function extract($outdir, $strip = '', $exclude = '', $include = '');
|
||||
/**
|
||||
* Create a new archive file
|
||||
*
|
||||
* If $file is empty, the archive file will be created in memory
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public abstract function create($file = '');
|
||||
/**
|
||||
* Add a file to the current archive using an existing file in the filesystem
|
||||
*
|
||||
* @param string $file path to the original file
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public abstract function addFile($file, $fileinfo = '');
|
||||
/**
|
||||
* Add a file to the current archive using the given $data as content
|
||||
*
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data
|
||||
* @param string $data binary content of the file to add
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public abstract function addData($fileinfo, $data);
|
||||
/**
|
||||
* Close the archive, close all file handles
|
||||
*
|
||||
* After a call to this function no more data can be added to the archive, for
|
||||
* read access no reading is allowed anymore
|
||||
*/
|
||||
public abstract function close();
|
||||
/**
|
||||
* Returns the created in-memory archive data
|
||||
*
|
||||
* This implicitly calls close() on the Archive
|
||||
*/
|
||||
public abstract function getArchive();
|
||||
/**
|
||||
* Save the created in-memory archive data
|
||||
*
|
||||
* Note: It is more memory effective to specify the filename in the create() function and
|
||||
* let the library work on the new file directly.
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public abstract function save($file);
|
||||
/**
|
||||
* Set a callback function to be called whenever a file is added or extracted.
|
||||
*
|
||||
* The callback is called with a FileInfo object as parameter. You can use this to show progress
|
||||
* info during an operation.
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function setCallback($callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
}
|
||||
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveCorruptedException.php
vendored
Normal file
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveCorruptedException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* The archive is unreadable
|
||||
*/
|
||||
class ArchiveCorruptedException extends \Exception
|
||||
{
|
||||
}
|
||||
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveIOException.php
vendored
Normal file
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveIOException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* Read/Write Errors
|
||||
*/
|
||||
class ArchiveIOException extends \Exception
|
||||
{
|
||||
}
|
||||
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveIllegalCompressionException.php
vendored
Normal file
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/ArchiveIllegalCompressionException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* Bad or unsupported compression settings requested
|
||||
*/
|
||||
class ArchiveIllegalCompressionException extends \Exception
|
||||
{
|
||||
}
|
||||
323
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfo.php
vendored
Normal file
323
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfo.php
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* Class FileInfo
|
||||
*
|
||||
* stores meta data about a file in an Archive
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @package splitbrain\PHPArchive
|
||||
* @license MIT
|
||||
*/
|
||||
class FileInfo
|
||||
{
|
||||
protected $isdir = \false;
|
||||
protected $path = '';
|
||||
protected $size = 0;
|
||||
protected $csize = 0;
|
||||
protected $mtime = 0;
|
||||
protected $mode = 0664;
|
||||
protected $owner = '';
|
||||
protected $group = '';
|
||||
protected $uid = 0;
|
||||
protected $gid = 0;
|
||||
protected $comment = '';
|
||||
/**
|
||||
* initialize dynamic defaults
|
||||
*
|
||||
* @param string $path The path of the file, can also be set later through setPath()
|
||||
*/
|
||||
public function __construct($path = '')
|
||||
{
|
||||
$this->mtime = \time();
|
||||
$this->setPath($path);
|
||||
}
|
||||
/**
|
||||
* Handle calls to deprecated methods
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if ($name === 'match') {
|
||||
\trigger_error('FileInfo::match() is deprecated, use FileInfo::matchExpression() instead.', \E_USER_NOTICE);
|
||||
return \call_user_func_array([$this, $name], $arguments);
|
||||
}
|
||||
\trigger_error('Call to undefined method FileInfo::' . $name . '()', \E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Factory to build FileInfo from existing file or directory
|
||||
*
|
||||
* @param string $path path to a file on the local file system
|
||||
* @param string $as optional path to use inside the archive
|
||||
* @throws FileInfoException
|
||||
* @return FileInfo
|
||||
*/
|
||||
public static function fromPath($path, $as = '')
|
||||
{
|
||||
\clearstatcache(\false, $path);
|
||||
if (!\file_exists($path)) {
|
||||
throw new FileInfoException("{$path} does not exist");
|
||||
}
|
||||
$stat = \stat($path);
|
||||
$file = new FileInfo();
|
||||
$file->setPath($path);
|
||||
$file->setIsdir(\is_dir($path));
|
||||
$file->setMode(\fileperms($path));
|
||||
$file->setOwner(\fileowner($path));
|
||||
$file->setGroup(\filegroup($path));
|
||||
$file->setSize(\filesize($path));
|
||||
$file->setUid($stat['uid']);
|
||||
$file->setGid($stat['gid']);
|
||||
$file->setMtime($stat['mtime']);
|
||||
if ($as) {
|
||||
$file->setPath($as);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
/**
|
||||
* @return int the filesize. always 0 for directories
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
if ($this->isdir) {
|
||||
return 0;
|
||||
}
|
||||
return $this->size;
|
||||
}
|
||||
/**
|
||||
* @param int $size
|
||||
*/
|
||||
public function setSize($size)
|
||||
{
|
||||
$this->size = $size;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCompressedSize()
|
||||
{
|
||||
return $this->csize;
|
||||
}
|
||||
/**
|
||||
* @param int $csize
|
||||
*/
|
||||
public function setCompressedSize($csize)
|
||||
{
|
||||
$this->csize = $csize;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMtime()
|
||||
{
|
||||
return $this->mtime;
|
||||
}
|
||||
/**
|
||||
* @param int $mtime
|
||||
*/
|
||||
public function setMtime($mtime)
|
||||
{
|
||||
$this->mtime = $mtime;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getGid()
|
||||
{
|
||||
return $this->gid;
|
||||
}
|
||||
/**
|
||||
* @param int $gid
|
||||
*/
|
||||
public function setGid($gid)
|
||||
{
|
||||
$this->gid = $gid;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUid()
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
/**
|
||||
* @param int $uid
|
||||
*/
|
||||
public function setUid($uid)
|
||||
{
|
||||
$this->uid = $uid;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getComment()
|
||||
{
|
||||
return $this->comment;
|
||||
}
|
||||
/**
|
||||
* @param string $comment
|
||||
*/
|
||||
public function setComment($comment)
|
||||
{
|
||||
$this->comment = $comment;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGroup()
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
/**
|
||||
* @param string $group
|
||||
*/
|
||||
public function setGroup($group)
|
||||
{
|
||||
$this->group = $group;
|
||||
}
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getIsdir()
|
||||
{
|
||||
return $this->isdir;
|
||||
}
|
||||
/**
|
||||
* @param boolean $isdir
|
||||
*/
|
||||
public function setIsdir($isdir)
|
||||
{
|
||||
// default mode for directories
|
||||
if ($isdir && $this->mode === 0664) {
|
||||
$this->mode = 0775;
|
||||
}
|
||||
$this->isdir = $isdir;
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMode()
|
||||
{
|
||||
return $this->mode;
|
||||
}
|
||||
/**
|
||||
* @param int $mode
|
||||
*/
|
||||
public function setMode($mode)
|
||||
{
|
||||
$this->mode = $mode;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOwner()
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
/**
|
||||
* @param string $owner
|
||||
*/
|
||||
public function setOwner($owner)
|
||||
{
|
||||
$this->owner = $owner;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
/**
|
||||
* @param string $path
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->path = $this->cleanPath($path);
|
||||
}
|
||||
/**
|
||||
* Cleans up a path and removes relative parts, also strips leading slashes
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function cleanPath($path)
|
||||
{
|
||||
$path = \str_replace('\\', '/', $path);
|
||||
$path = \explode('/', $path);
|
||||
$newpath = array();
|
||||
foreach ($path as $p) {
|
||||
if ($p === '' || $p === '.') {
|
||||
continue;
|
||||
}
|
||||
if ($p === '..') {
|
||||
\array_pop($newpath);
|
||||
continue;
|
||||
}
|
||||
\array_push($newpath, $p);
|
||||
}
|
||||
return \trim(\implode('/', $newpath), '/');
|
||||
}
|
||||
/**
|
||||
* Strip given prefix or number of path segments from the filename
|
||||
*
|
||||
* The $strip parameter allows you to strip a certain number of path components from the filenames
|
||||
* found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when
|
||||
* an integer is passed as $strip.
|
||||
* Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix,
|
||||
* the prefix will be stripped. It is recommended to give prefixes with a trailing slash.
|
||||
*
|
||||
* @param int|string $strip
|
||||
*/
|
||||
public function strip($strip)
|
||||
{
|
||||
$filename = $this->getPath();
|
||||
$striplen = \strlen($strip);
|
||||
if (\is_int($strip)) {
|
||||
// if $strip is an integer we strip this many path components
|
||||
$parts = \explode('/', $filename);
|
||||
if (!$this->getIsdir()) {
|
||||
$base = \array_pop($parts);
|
||||
// keep filename itself
|
||||
} else {
|
||||
$base = '';
|
||||
}
|
||||
$filename = \join('/', \array_slice($parts, $strip));
|
||||
if ($base) {
|
||||
$filename .= "/{$base}";
|
||||
}
|
||||
} else {
|
||||
// if strip is a string, we strip a prefix here
|
||||
if (\substr($filename, 0, $striplen) == $strip) {
|
||||
$filename = \substr($filename, $striplen);
|
||||
}
|
||||
}
|
||||
$this->setPath($filename);
|
||||
}
|
||||
/**
|
||||
* Does the file match the given include and exclude expressions?
|
||||
*
|
||||
* Exclude rules take precedence over include rules
|
||||
*
|
||||
* @param string $include Regular expression of files to include
|
||||
* @param string $exclude Regular expression of files to exclude
|
||||
* @return bool
|
||||
*/
|
||||
public function matchExpression($include = '', $exclude = '')
|
||||
{
|
||||
$extract = \true;
|
||||
if ($include && !\preg_match($include, $this->getPath())) {
|
||||
$extract = \false;
|
||||
}
|
||||
if ($exclude && \preg_match($exclude, $this->getPath())) {
|
||||
$extract = \false;
|
||||
}
|
||||
return $extract;
|
||||
}
|
||||
}
|
||||
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfoException.php
vendored
Normal file
10
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/FileInfoException.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* File meta data problems
|
||||
*/
|
||||
class FileInfoException extends \Exception
|
||||
{
|
||||
}
|
||||
640
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Tar.php
vendored
Normal file
640
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Tar.php
vendored
Normal file
@ -0,0 +1,640 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* Class Tar
|
||||
*
|
||||
* Creates or extracts Tar archives. Supports gz and bzip compression
|
||||
*
|
||||
* Long pathnames (>100 chars) are supported in POSIX ustar and GNU longlink formats.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @package splitbrain\PHPArchive
|
||||
* @license MIT
|
||||
*/
|
||||
class Tar extends Archive
|
||||
{
|
||||
protected $file = '';
|
||||
protected $comptype = Archive::COMPRESS_AUTO;
|
||||
protected $complevel = 9;
|
||||
protected $fh;
|
||||
protected $memory = '';
|
||||
protected $closed = \true;
|
||||
protected $writeaccess = \false;
|
||||
/**
|
||||
* Sets the compression to use
|
||||
*
|
||||
* @param int $level Compression level (0 to 9)
|
||||
* @param int $type Type of compression to use (use COMPRESS_* constants)
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public function setCompression($level = 9, $type = Archive::COMPRESS_AUTO)
|
||||
{
|
||||
$this->compressioncheck($type);
|
||||
if ($level < -1 || $level > 9) {
|
||||
throw new ArchiveIllegalCompressionException('Compression level should be between -1 and 9');
|
||||
}
|
||||
$this->comptype = $type;
|
||||
$this->complevel = $level;
|
||||
if ($level == 0) {
|
||||
$this->comptype = Archive::COMPRESS_NONE;
|
||||
}
|
||||
if ($type == Archive::COMPRESS_NONE) {
|
||||
$this->complevel = 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Open an existing TAR file for reading
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public function open($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
// update compression to mach file
|
||||
if ($this->comptype == Tar::COMPRESS_AUTO) {
|
||||
$this->setCompression($this->complevel, $this->filetype($file));
|
||||
}
|
||||
// open file handles
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
$this->fh = @\gzopen($this->file, 'rb');
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
$this->fh = @\bzopen($this->file, 'r');
|
||||
} else {
|
||||
$this->fh = @\fopen($this->file, 'rb');
|
||||
}
|
||||
if (!$this->fh) {
|
||||
throw new ArchiveIOException('Could not open file for reading: ' . $this->file);
|
||||
}
|
||||
$this->closed = \false;
|
||||
}
|
||||
/**
|
||||
* Read the contents of a TAR archive
|
||||
*
|
||||
* This function lists the files stored in the archive
|
||||
*
|
||||
* The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams.
|
||||
* Reopen the file with open() again if you want to do additional operations
|
||||
*
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveCorruptedException
|
||||
* @returns FileInfo[]
|
||||
*/
|
||||
public function contents()
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->yieldContents() as $fileinfo) {
|
||||
$result[] = $fileinfo;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* Read the contents of a TAR archive and return each entry using yield
|
||||
* for memory efficiency.
|
||||
*
|
||||
* @see contents()
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveCorruptedException
|
||||
* @returns FileInfo[]
|
||||
*/
|
||||
public function yieldContents()
|
||||
{
|
||||
if ($this->closed || !$this->file) {
|
||||
throw new ArchiveIOException('Can not read from a closed archive');
|
||||
}
|
||||
while ($read = $this->readbytes(512)) {
|
||||
$header = $this->parseHeader($read);
|
||||
if (!\is_array($header)) {
|
||||
continue;
|
||||
}
|
||||
$this->skipbytes(\ceil($header['size'] / 512) * 512);
|
||||
(yield $this->header2fileinfo($header));
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
/**
|
||||
* Extract an existing TAR archive
|
||||
*
|
||||
* The $strip parameter allows you to strip a certain number of path components from the filenames
|
||||
* found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when
|
||||
* an integer is passed as $strip.
|
||||
* Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix,
|
||||
* the prefix will be stripped. It is recommended to give prefixes with a trailing slash.
|
||||
*
|
||||
* By default this will extract all files found in the archive. You can restrict the output using the $include
|
||||
* and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If
|
||||
* $include is set only files that match this expression will be extracted. Files that match the $exclude
|
||||
* expression will never be extracted. Both parameters can be used in combination. Expressions are matched against
|
||||
* stripped filenames as described above.
|
||||
*
|
||||
* The archive is closed afer reading the contents, because rewinding is not possible in bzip2 streams.
|
||||
* Reopen the file with open() again if you want to do additional operations
|
||||
*
|
||||
* @param string $outdir the target directory for extracting
|
||||
* @param int|string $strip either the number of path components or a fixed prefix to strip
|
||||
* @param string $exclude a regular expression of files to exclude
|
||||
* @param string $include a regular expression of files to include
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveCorruptedException
|
||||
* @return FileInfo[]
|
||||
*/
|
||||
public function extract($outdir, $strip = '', $exclude = '', $include = '')
|
||||
{
|
||||
if ($this->closed || !$this->file) {
|
||||
throw new ArchiveIOException('Can not read from a closed archive');
|
||||
}
|
||||
$outdir = \rtrim($outdir, '/');
|
||||
@\mkdir($outdir, 0777, \true);
|
||||
if (!\is_dir($outdir)) {
|
||||
throw new ArchiveIOException("Could not create directory '{$outdir}'");
|
||||
}
|
||||
$extracted = array();
|
||||
while ($dat = $this->readbytes(512)) {
|
||||
// read the file header
|
||||
$header = $this->parseHeader($dat);
|
||||
if (!\is_array($header)) {
|
||||
continue;
|
||||
}
|
||||
$fileinfo = $this->header2fileinfo($header);
|
||||
// apply strip rules
|
||||
$fileinfo->strip($strip);
|
||||
// skip unwanted files
|
||||
if (!\strlen($fileinfo->getPath()) || !$fileinfo->matchExpression($include, $exclude)) {
|
||||
$this->skipbytes(\ceil($header['size'] / 512) * 512);
|
||||
continue;
|
||||
}
|
||||
// create output directory
|
||||
$output = $outdir . '/' . $fileinfo->getPath();
|
||||
$directory = $fileinfo->getIsdir() ? $output : \dirname($output);
|
||||
if (!\file_exists($directory)) {
|
||||
\mkdir($directory, 0777, \true);
|
||||
}
|
||||
// extract data
|
||||
if (!$fileinfo->getIsdir()) {
|
||||
$fp = @\fopen($output, "wb");
|
||||
if (!$fp) {
|
||||
throw new ArchiveIOException('Could not open file for writing: ' . $output);
|
||||
}
|
||||
$size = \floor($header['size'] / 512);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
\fwrite($fp, $this->readbytes(512), 512);
|
||||
}
|
||||
if ($header['size'] % 512 != 0) {
|
||||
\fwrite($fp, $this->readbytes(512), $header['size'] % 512);
|
||||
}
|
||||
\fclose($fp);
|
||||
@\touch($output, $fileinfo->getMtime());
|
||||
@\chmod($output, $fileinfo->getMode());
|
||||
} else {
|
||||
$this->skipbytes(\ceil($header['size'] / 512) * 512);
|
||||
// the size is usually 0 for directories
|
||||
}
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
$extracted[] = $fileinfo;
|
||||
}
|
||||
$this->close();
|
||||
return $extracted;
|
||||
}
|
||||
/**
|
||||
* Create a new TAR file
|
||||
*
|
||||
* If $file is empty, the tar file will be created in memory
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public function create($file = '')
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->memory = '';
|
||||
$this->fh = 0;
|
||||
if ($this->file) {
|
||||
// determine compression
|
||||
if ($this->comptype == Archive::COMPRESS_AUTO) {
|
||||
$this->setCompression($this->complevel, $this->filetype($file));
|
||||
}
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
$this->fh = @\gzopen($this->file, 'wb' . $this->complevel);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
$this->fh = @\bzopen($this->file, 'w');
|
||||
} else {
|
||||
$this->fh = @\fopen($this->file, 'wb');
|
||||
}
|
||||
if (!$this->fh) {
|
||||
throw new ArchiveIOException('Could not open file for writing: ' . $this->file);
|
||||
}
|
||||
}
|
||||
$this->writeaccess = \true;
|
||||
$this->closed = \false;
|
||||
}
|
||||
/**
|
||||
* Add a file to the current TAR archive using an existing file in the filesystem
|
||||
*
|
||||
* @param string $file path to the original file
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original
|
||||
* @throws ArchiveCorruptedException when the file changes while reading it, the archive will be corrupt and should be deleted
|
||||
* @throws ArchiveIOException there was trouble reading the given file, it was not added
|
||||
* @throws FileInfoException trouble reading file info, it was not added
|
||||
*/
|
||||
public function addFile($file, $fileinfo = '')
|
||||
{
|
||||
if (\is_string($fileinfo)) {
|
||||
$fileinfo = FileInfo::fromPath($file, $fileinfo);
|
||||
}
|
||||
if ($this->closed) {
|
||||
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
|
||||
}
|
||||
// create file header
|
||||
$this->writeFileHeader($fileinfo);
|
||||
// write data, but only if we have data to write.
|
||||
// note: on Windows fopen() on a directory will fail, so we prevent
|
||||
// errors on Windows by testing if we have data to write.
|
||||
if (!$fileinfo->getIsdir() && $fileinfo->getSize() > 0) {
|
||||
$read = 0;
|
||||
$fp = @\fopen($file, 'rb');
|
||||
if (!$fp) {
|
||||
throw new ArchiveIOException('Could not open file for reading: ' . $file);
|
||||
}
|
||||
while (!\feof($fp)) {
|
||||
$data = \fread($fp, 512);
|
||||
$read += \strlen($data);
|
||||
if ($data === \false) {
|
||||
break;
|
||||
}
|
||||
if ($data === '') {
|
||||
break;
|
||||
}
|
||||
$packed = \pack("a512", $data);
|
||||
$this->writebytes($packed);
|
||||
}
|
||||
\fclose($fp);
|
||||
if ($read != $fileinfo->getSize()) {
|
||||
$this->close();
|
||||
throw new ArchiveCorruptedException("The size of {$file} changed while reading, archive corrupted. read {$read} expected " . $fileinfo->getSize());
|
||||
}
|
||||
}
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add a file to the current TAR archive using the given $data as content
|
||||
*
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data
|
||||
* @param string $data binary content of the file to add
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function addData($fileinfo, $data)
|
||||
{
|
||||
if (\is_string($fileinfo)) {
|
||||
$fileinfo = new FileInfo($fileinfo);
|
||||
}
|
||||
if ($this->closed) {
|
||||
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
|
||||
}
|
||||
$len = \strlen($data);
|
||||
$fileinfo->setSize($len);
|
||||
$this->writeFileHeader($fileinfo);
|
||||
for ($s = 0; $s < $len; $s += 512) {
|
||||
$this->writebytes(\pack("a512", \substr($data, $s, 512)));
|
||||
}
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add the closing footer to the archive if in write mode, close all file handles
|
||||
*
|
||||
* After a call to this function no more data can be added to the archive, for
|
||||
* read access no reading is allowed anymore
|
||||
*
|
||||
* "Physically, an archive consists of a series of file entries terminated by an end-of-archive entry, which
|
||||
* consists of two 512 blocks of zero bytes"
|
||||
*
|
||||
* @link http://www.gnu.org/software/tar/manual/html_chapter/tar_8.html#SEC134
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
// we did this already
|
||||
// write footer
|
||||
if ($this->writeaccess) {
|
||||
$this->writebytes(\pack("a512", ""));
|
||||
$this->writebytes(\pack("a512", ""));
|
||||
}
|
||||
// close file handles
|
||||
if ($this->file) {
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
\gzclose($this->fh);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
\bzclose($this->fh);
|
||||
} else {
|
||||
\fclose($this->fh);
|
||||
}
|
||||
$this->file = '';
|
||||
$this->fh = 0;
|
||||
}
|
||||
$this->writeaccess = \false;
|
||||
$this->closed = \true;
|
||||
}
|
||||
/**
|
||||
* Returns the created in-memory archive data
|
||||
*
|
||||
* This implicitly calls close() on the Archive
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function getArchive()
|
||||
{
|
||||
$this->close();
|
||||
if ($this->comptype === Archive::COMPRESS_AUTO) {
|
||||
$this->comptype = Archive::COMPRESS_NONE;
|
||||
}
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
return \gzencode($this->memory, $this->complevel);
|
||||
}
|
||||
if ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
return \bzcompress($this->memory);
|
||||
}
|
||||
return $this->memory;
|
||||
}
|
||||
/**
|
||||
* Save the created in-memory archive data
|
||||
*
|
||||
* Note: It more memory effective to specify the filename in the create() function and
|
||||
* let the library work on the new file directly.
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public function save($file)
|
||||
{
|
||||
if ($this->comptype === Archive::COMPRESS_AUTO) {
|
||||
$this->setCompression($this->complevel, $this->filetype($file));
|
||||
}
|
||||
if (!@\file_put_contents($file, $this->getArchive())) {
|
||||
throw new ArchiveIOException('Could not write to file: ' . $file);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read from the open file pointer
|
||||
*
|
||||
* @param int $length bytes to read
|
||||
* @return string
|
||||
*/
|
||||
protected function readbytes($length)
|
||||
{
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
return @\gzread($this->fh, $length);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
return @\bzread($this->fh, $length);
|
||||
} else {
|
||||
return @\fread($this->fh, $length);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Write to the open filepointer or memory
|
||||
*
|
||||
* @param string $data
|
||||
* @throws ArchiveIOException
|
||||
* @return int number of bytes written
|
||||
*/
|
||||
protected function writebytes($data)
|
||||
{
|
||||
if (!$this->file) {
|
||||
$this->memory .= $data;
|
||||
$written = \strlen($data);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
$written = @\gzwrite($this->fh, $data);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
$written = @\bzwrite($this->fh, $data);
|
||||
} else {
|
||||
$written = @\fwrite($this->fh, $data);
|
||||
}
|
||||
if ($written === \false) {
|
||||
throw new ArchiveIOException('Failed to write to archive stream');
|
||||
}
|
||||
return $written;
|
||||
}
|
||||
/**
|
||||
* Skip forward in the open file pointer
|
||||
*
|
||||
* This is basically a wrapper around seek() (and a workaround for bzip2)
|
||||
*
|
||||
* @param int $bytes seek to this position
|
||||
*/
|
||||
protected function skipbytes($bytes)
|
||||
{
|
||||
if ($this->comptype === Archive::COMPRESS_GZIP) {
|
||||
@\gzseek($this->fh, $bytes, \SEEK_CUR);
|
||||
} elseif ($this->comptype === Archive::COMPRESS_BZIP) {
|
||||
// there is no seek in bzip2, we simply read on
|
||||
// bzread allows to read a max of 8kb at once
|
||||
while ($bytes) {
|
||||
$toread = \min(8192, $bytes);
|
||||
@\bzread($this->fh, $toread);
|
||||
$bytes -= $toread;
|
||||
}
|
||||
} else {
|
||||
@\fseek($this->fh, $bytes, \SEEK_CUR);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Write the given file meta data as header
|
||||
*
|
||||
* @param FileInfo $fileinfo
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
protected function writeFileHeader(FileInfo $fileinfo)
|
||||
{
|
||||
$this->writeRawFileHeader($fileinfo->getPath(), $fileinfo->getUid(), $fileinfo->getGid(), $fileinfo->getMode(), $fileinfo->getSize(), $fileinfo->getMtime(), $fileinfo->getIsdir() ? '5' : '0');
|
||||
}
|
||||
/**
|
||||
* Write a file header to the stream
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $uid
|
||||
* @param int $gid
|
||||
* @param int $perm
|
||||
* @param int $size
|
||||
* @param int $mtime
|
||||
* @param string $typeflag Set to '5' for directories
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
protected function writeRawFileHeader($name, $uid, $gid, $perm, $size, $mtime, $typeflag = '')
|
||||
{
|
||||
// handle filename length restrictions
|
||||
$prefix = '';
|
||||
$namelen = \strlen($name);
|
||||
if ($namelen > 100) {
|
||||
$file = \basename($name);
|
||||
$dir = \dirname($name);
|
||||
if (\strlen($file) > 100 || \strlen($dir) > 155) {
|
||||
// we're still too large, let's use GNU longlink
|
||||
$this->writeRawFileHeader('././@LongLink', 0, 0, 0, $namelen, 0, 'L');
|
||||
for ($s = 0; $s < $namelen; $s += 512) {
|
||||
$this->writebytes(\pack("a512", \substr($name, $s, 512)));
|
||||
}
|
||||
$name = \substr($name, 0, 100);
|
||||
// cut off name
|
||||
} else {
|
||||
// we're fine when splitting, use POSIX ustar
|
||||
$prefix = $dir;
|
||||
$name = $file;
|
||||
}
|
||||
}
|
||||
// values are needed in octal
|
||||
$uid = \sprintf("%6s ", \decoct($uid));
|
||||
$gid = \sprintf("%6s ", \decoct($gid));
|
||||
$perm = \sprintf("%6s ", \decoct($perm));
|
||||
$size = \sprintf("%11s ", \decoct($size));
|
||||
$mtime = \sprintf("%11s", \decoct($mtime));
|
||||
$data_first = \pack("a100a8a8a8a12A12", $name, $perm, $uid, $gid, $size, $mtime);
|
||||
$data_last = \pack("a1a100a6a2a32a32a8a8a155a12", $typeflag, '', 'ustar', '', '', '', '', '', $prefix, "");
|
||||
for ($i = 0, $chks = 0; $i < 148; $i++) {
|
||||
$chks += \ord($data_first[$i]);
|
||||
}
|
||||
for ($i = 156, $chks += 256, $j = 0; $i < 512; $i++, $j++) {
|
||||
$chks += \ord($data_last[$j]);
|
||||
}
|
||||
$this->writebytes($data_first);
|
||||
$chks = \pack("a8", \sprintf("%6s ", \decoct($chks)));
|
||||
$this->writebytes($chks . $data_last);
|
||||
}
|
||||
/**
|
||||
* Decode the given tar file header
|
||||
*
|
||||
* @param string $block a 512 byte block containing the header data
|
||||
* @return array|false returns false when this was a null block
|
||||
* @throws ArchiveCorruptedException
|
||||
*/
|
||||
protected function parseHeader($block)
|
||||
{
|
||||
if (!$block || \strlen($block) != 512) {
|
||||
throw new ArchiveCorruptedException('Unexpected length of header');
|
||||
}
|
||||
// null byte blocks are ignored
|
||||
if (\trim($block) === '') {
|
||||
return \false;
|
||||
}
|
||||
for ($i = 0, $chks = 0; $i < 148; $i++) {
|
||||
$chks += \ord($block[$i]);
|
||||
}
|
||||
for ($i = 156, $chks += 256; $i < 512; $i++) {
|
||||
$chks += \ord($block[$i]);
|
||||
}
|
||||
$header = @\unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $block);
|
||||
if (!$header) {
|
||||
throw new ArchiveCorruptedException('Failed to parse header');
|
||||
}
|
||||
$return['checksum'] = \OctDec(\trim($header['checksum']));
|
||||
if ($return['checksum'] != $chks) {
|
||||
throw new ArchiveCorruptedException('Header does not match its checksum');
|
||||
}
|
||||
$return['filename'] = \trim($header['filename']);
|
||||
$return['perm'] = \OctDec(\trim($header['perm']));
|
||||
$return['uid'] = \OctDec(\trim($header['uid']));
|
||||
$return['gid'] = \OctDec(\trim($header['gid']));
|
||||
$return['size'] = \OctDec(\trim($header['size']));
|
||||
$return['mtime'] = \OctDec(\trim($header['mtime']));
|
||||
$return['typeflag'] = $header['typeflag'];
|
||||
$return['link'] = \trim($header['link']);
|
||||
$return['uname'] = \trim($header['uname']);
|
||||
$return['gname'] = \trim($header['gname']);
|
||||
// Handle ustar Posix compliant path prefixes
|
||||
if (\trim($header['prefix'])) {
|
||||
$return['filename'] = \trim($header['prefix']) . '/' . $return['filename'];
|
||||
}
|
||||
// Handle Long-Link entries from GNU Tar
|
||||
if ($return['typeflag'] == 'L') {
|
||||
// following data block(s) is the filename
|
||||
$filename = \trim($this->readbytes(\ceil($return['size'] / 512) * 512));
|
||||
// next block is the real header
|
||||
$block = $this->readbytes(512);
|
||||
$return = $this->parseHeader($block);
|
||||
// overwrite the filename
|
||||
$return['filename'] = $filename;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
/**
|
||||
* Creates a FileInfo object from the given parsed header
|
||||
*
|
||||
* @param $header
|
||||
* @return FileInfo
|
||||
*/
|
||||
protected function header2fileinfo($header)
|
||||
{
|
||||
$fileinfo = new FileInfo();
|
||||
$fileinfo->setPath($header['filename']);
|
||||
$fileinfo->setMode($header['perm']);
|
||||
$fileinfo->setUid($header['uid']);
|
||||
$fileinfo->setGid($header['gid']);
|
||||
$fileinfo->setSize($header['size']);
|
||||
$fileinfo->setMtime($header['mtime']);
|
||||
$fileinfo->setOwner($header['uname']);
|
||||
$fileinfo->setGroup($header['gname']);
|
||||
$fileinfo->setIsdir((bool) $header['typeflag']);
|
||||
return $fileinfo;
|
||||
}
|
||||
/**
|
||||
* Checks if the given compression type is available and throws an exception if not
|
||||
*
|
||||
* @param $comptype
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
protected function compressioncheck($comptype)
|
||||
{
|
||||
if ($comptype === Archive::COMPRESS_GZIP && !\function_exists('gzopen')) {
|
||||
throw new ArchiveIllegalCompressionException('No gzip support available');
|
||||
}
|
||||
if ($comptype === Archive::COMPRESS_BZIP && !\function_exists('bzopen')) {
|
||||
throw new ArchiveIllegalCompressionException('No bzip2 support available');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Guesses the wanted compression from the given file
|
||||
*
|
||||
* Uses magic bytes for existing files, the file extension otherwise
|
||||
*
|
||||
* You don't need to call this yourself. It's used when you pass Archive::COMPRESS_AUTO somewhere
|
||||
*
|
||||
* @param string $file
|
||||
* @return int
|
||||
*/
|
||||
public function filetype($file)
|
||||
{
|
||||
// for existing files, try to read the magic bytes
|
||||
if (\file_exists($file) && \is_readable($file) && \filesize($file) > 5) {
|
||||
$fh = @\fopen($file, 'rb');
|
||||
if (!$fh) {
|
||||
return \false;
|
||||
}
|
||||
$magic = \fread($fh, 5);
|
||||
\fclose($fh);
|
||||
if (\strpos($magic, "BZ") === 0) {
|
||||
return Archive::COMPRESS_BZIP;
|
||||
}
|
||||
if (\strpos($magic, "\x1f\x8b") === 0) {
|
||||
return Archive::COMPRESS_GZIP;
|
||||
}
|
||||
}
|
||||
// otherwise rely on file name
|
||||
$file = \strtolower($file);
|
||||
if (\substr($file, -3) == '.gz' || \substr($file, -4) == '.tgz') {
|
||||
return Archive::COMPRESS_GZIP;
|
||||
} elseif (\substr($file, -4) == '.bz2' || \substr($file, -4) == '.tbz') {
|
||||
return Archive::COMPRESS_BZIP;
|
||||
}
|
||||
return Archive::COMPRESS_NONE;
|
||||
}
|
||||
}
|
||||
898
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Zip.php
vendored
Normal file
898
plugins/system/tgeoip/vendor/splitbrain/php-archive/src/Zip.php
vendored
Normal file
@ -0,0 +1,898 @@
|
||||
<?php
|
||||
|
||||
namespace Tassos\Vendor\splitbrain\PHPArchive;
|
||||
|
||||
/**
|
||||
* Class Zip
|
||||
*
|
||||
* Creates or extracts Zip archives
|
||||
*
|
||||
* for specs see http://www.pkware.com/appnote
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @package splitbrain\PHPArchive
|
||||
* @license MIT
|
||||
*/
|
||||
class Zip extends Archive
|
||||
{
|
||||
const LOCAL_FILE_HEADER_CRC_OFFSET = 14;
|
||||
protected $file = '';
|
||||
protected $fh;
|
||||
protected $memory = '';
|
||||
protected $closed = \true;
|
||||
protected $writeaccess = \false;
|
||||
protected $ctrl_dir;
|
||||
protected $complevel = 9;
|
||||
/**
|
||||
* Set the compression level.
|
||||
*
|
||||
* Compression Type is ignored for ZIP
|
||||
*
|
||||
* You can call this function before adding each file to set differen compression levels
|
||||
* for each file.
|
||||
*
|
||||
* @param int $level Compression level (0 to 9)
|
||||
* @param int $type Type of compression to use ignored for ZIP
|
||||
* @throws ArchiveIllegalCompressionException
|
||||
*/
|
||||
public function setCompression($level = 9, $type = Archive::COMPRESS_AUTO)
|
||||
{
|
||||
if ($level < -1 || $level > 9) {
|
||||
throw new ArchiveIllegalCompressionException('Compression level should be between -1 and 9');
|
||||
}
|
||||
$this->complevel = $level;
|
||||
}
|
||||
/**
|
||||
* Open an existing ZIP file for reading
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function open($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->fh = @\fopen($this->file, 'rb');
|
||||
if (!$this->fh) {
|
||||
throw new ArchiveIOException('Could not open file for reading: ' . $this->file);
|
||||
}
|
||||
$this->closed = \false;
|
||||
}
|
||||
/**
|
||||
* Read the contents of a ZIP archive
|
||||
*
|
||||
* This function lists the files stored in the archive, and returns an indexed array of FileInfo objects
|
||||
*
|
||||
* The archive is closed afer reading the contents, for API compatibility with TAR files
|
||||
* Reopen the file with open() again if you want to do additional operations
|
||||
*
|
||||
* @throws ArchiveIOException
|
||||
* @return FileInfo[]
|
||||
*/
|
||||
public function contents()
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->yieldContents() as $fileinfo) {
|
||||
$result[] = $fileinfo;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* Read the contents of a ZIP archive and return each entry using yield
|
||||
* for memory efficiency.
|
||||
*
|
||||
* @see contents()
|
||||
* @throws ArchiveIOException
|
||||
* @return FileInfo[]
|
||||
*/
|
||||
public function yieldContents()
|
||||
{
|
||||
if ($this->closed || !$this->file) {
|
||||
throw new ArchiveIOException('Can not read from a closed archive');
|
||||
}
|
||||
$centd = $this->readCentralDir();
|
||||
@\rewind($this->fh);
|
||||
@\fseek($this->fh, $centd['offset']);
|
||||
for ($i = 0; $i < $centd['entries']; $i++) {
|
||||
(yield $this->header2fileinfo($this->readCentralFileHeader()));
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
/**
|
||||
* Extract an existing ZIP archive
|
||||
*
|
||||
* The $strip parameter allows you to strip a certain number of path components from the filenames
|
||||
* found in the tar file, similar to the --strip-components feature of GNU tar. This is triggered when
|
||||
* an integer is passed as $strip.
|
||||
* Alternatively a fixed string prefix may be passed in $strip. If the filename matches this prefix,
|
||||
* the prefix will be stripped. It is recommended to give prefixes with a trailing slash.
|
||||
*
|
||||
* By default this will extract all files found in the archive. You can restrict the output using the $include
|
||||
* and $exclude parameter. Both expect a full regular expression (including delimiters and modifiers). If
|
||||
* $include is set only files that match this expression will be extracted. Files that match the $exclude
|
||||
* expression will never be extracted. Both parameters can be used in combination. Expressions are matched against
|
||||
* stripped filenames as described above.
|
||||
*
|
||||
* @param string $outdir the target directory for extracting
|
||||
* @param int|string $strip either the number of path components or a fixed prefix to strip
|
||||
* @param string $exclude a regular expression of files to exclude
|
||||
* @param string $include a regular expression of files to include
|
||||
* @throws ArchiveIOException
|
||||
* @return FileInfo[]
|
||||
*/
|
||||
public function extract($outdir, $strip = '', $exclude = '', $include = '')
|
||||
{
|
||||
if ($this->closed || !$this->file) {
|
||||
throw new ArchiveIOException('Can not read from a closed archive');
|
||||
}
|
||||
$outdir = \rtrim($outdir, '/');
|
||||
@\mkdir($outdir, 0777, \true);
|
||||
$extracted = array();
|
||||
$cdir = $this->readCentralDir();
|
||||
$pos_entry = $cdir['offset'];
|
||||
// begin of the central file directory
|
||||
for ($i = 0; $i < $cdir['entries']; $i++) {
|
||||
// read file header
|
||||
@\fseek($this->fh, $pos_entry);
|
||||
$header = $this->readCentralFileHeader();
|
||||
$header['index'] = $i;
|
||||
$pos_entry = \ftell($this->fh);
|
||||
// position of the next file in central file directory
|
||||
\fseek($this->fh, $header['offset']);
|
||||
// seek to beginning of file header
|
||||
$header = $this->readFileHeader($header);
|
||||
$fileinfo = $this->header2fileinfo($header);
|
||||
// apply strip rules
|
||||
$fileinfo->strip($strip);
|
||||
// skip unwanted files
|
||||
if (!\strlen($fileinfo->getPath()) || !$fileinfo->matchExpression($include, $exclude)) {
|
||||
continue;
|
||||
}
|
||||
$extracted[] = $fileinfo;
|
||||
// create output directory
|
||||
$output = $outdir . '/' . $fileinfo->getPath();
|
||||
$directory = $header['folder'] ? $output : \dirname($output);
|
||||
@\mkdir($directory, 0777, \true);
|
||||
// nothing more to do for directories
|
||||
if ($fileinfo->getIsdir()) {
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// compressed files are written to temporary .gz file first
|
||||
if ($header['compression'] == 0) {
|
||||
$extractto = $output;
|
||||
} else {
|
||||
$extractto = $output . '.gz';
|
||||
}
|
||||
// open file for writing
|
||||
$fp = @\fopen($extractto, "wb");
|
||||
if (!$fp) {
|
||||
throw new ArchiveIOException('Could not open file for writing: ' . $extractto);
|
||||
}
|
||||
// prepend compression header
|
||||
if ($header['compression'] != 0) {
|
||||
$binary_data = \pack('va1a1Va1a1', 0x8b1f, \chr($header['compression']), \chr(0x0), \time(), \chr(0x0), \chr(3));
|
||||
\fwrite($fp, $binary_data, 10);
|
||||
}
|
||||
// read the file and store it on disk
|
||||
$size = $header['compressed_size'];
|
||||
while ($size != 0) {
|
||||
$read_size = $size < 2048 ? $size : 2048;
|
||||
$buffer = \fread($this->fh, $read_size);
|
||||
$binary_data = \pack('a' . $read_size, $buffer);
|
||||
\fwrite($fp, $binary_data, $read_size);
|
||||
$size -= $read_size;
|
||||
}
|
||||
// finalize compressed file
|
||||
if ($header['compression'] != 0) {
|
||||
$binary_data = \pack('VV', $header['crc'], $header['size']);
|
||||
\fwrite($fp, $binary_data, 8);
|
||||
}
|
||||
// close file
|
||||
\fclose($fp);
|
||||
// unpack compressed file
|
||||
if ($header['compression'] != 0) {
|
||||
$gzp = @\gzopen($extractto, 'rb');
|
||||
if (!$gzp) {
|
||||
@\unlink($extractto);
|
||||
throw new ArchiveIOException('Failed file extracting. gzip support missing?');
|
||||
}
|
||||
$fp = @\fopen($output, 'wb');
|
||||
if (!$fp) {
|
||||
throw new ArchiveIOException('Could not open file for writing: ' . $extractto);
|
||||
}
|
||||
$size = $header['size'];
|
||||
while ($size != 0) {
|
||||
$read_size = $size < 2048 ? $size : 2048;
|
||||
$buffer = \gzread($gzp, $read_size);
|
||||
$binary_data = \pack('a' . $read_size, $buffer);
|
||||
@\fwrite($fp, $binary_data, $read_size);
|
||||
$size -= $read_size;
|
||||
}
|
||||
\fclose($fp);
|
||||
\gzclose($gzp);
|
||||
\unlink($extractto);
|
||||
// remove temporary gz file
|
||||
}
|
||||
@\touch($output, $fileinfo->getMtime());
|
||||
//FIXME what about permissions?
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
$this->close();
|
||||
return $extracted;
|
||||
}
|
||||
/**
|
||||
* Create a new ZIP file
|
||||
*
|
||||
* If $file is empty, the zip file will be created in memory
|
||||
*
|
||||
* @param string $file
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function create($file = '')
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->memory = '';
|
||||
$this->fh = 0;
|
||||
if ($this->file) {
|
||||
$this->fh = @\fopen($this->file, 'wb');
|
||||
if (!$this->fh) {
|
||||
throw new ArchiveIOException('Could not open file for writing: ' . $this->file);
|
||||
}
|
||||
}
|
||||
$this->writeaccess = \true;
|
||||
$this->closed = \false;
|
||||
$this->ctrl_dir = array();
|
||||
}
|
||||
/**
|
||||
* Add a file to the current ZIP archive using an existing file in the filesystem
|
||||
*
|
||||
* @param string $file path to the original file
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
/**
|
||||
* Add a file to the current archive using an existing file in the filesystem
|
||||
*
|
||||
* @param string $file path to the original file
|
||||
* @param string|FileInfo $fileinfo either the name to use in archive (string) or a FileInfo oject with all meta data, empty to take from original
|
||||
* @throws ArchiveIOException
|
||||
* @throws FileInfoException
|
||||
*/
|
||||
public function addFile($file, $fileinfo = '')
|
||||
{
|
||||
if (\is_string($fileinfo)) {
|
||||
$fileinfo = FileInfo::fromPath($file, $fileinfo);
|
||||
}
|
||||
if ($this->closed) {
|
||||
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
|
||||
}
|
||||
$fp = @\fopen($file, 'rb');
|
||||
if ($fp === \false) {
|
||||
throw new ArchiveIOException('Could not open file for reading: ' . $file);
|
||||
}
|
||||
$offset = $this->dataOffset();
|
||||
$name = $fileinfo->getPath();
|
||||
$time = $fileinfo->getMtime();
|
||||
// write local file header (temporary CRC and size)
|
||||
$this->writebytes($this->makeLocalFileHeader($time, 0, 0, 0, $name, (bool) $this->complevel));
|
||||
// we store no encryption header
|
||||
// prepare info, compress and write data to archive
|
||||
$deflate_context = \deflate_init(\ZLIB_ENCODING_DEFLATE, ['level' => $this->complevel]);
|
||||
$crc_context = \hash_init('crc32b');
|
||||
$size = $csize = 0;
|
||||
while (!\feof($fp)) {
|
||||
$block = \fread($fp, 512);
|
||||
if ($this->complevel) {
|
||||
$is_first_block = $size === 0;
|
||||
$is_last_block = \feof($fp);
|
||||
if ($is_last_block) {
|
||||
$c_block = \deflate_add($deflate_context, $block, \ZLIB_FINISH);
|
||||
// get rid of the compression footer
|
||||
$c_block = \substr($c_block, 0, -4);
|
||||
} else {
|
||||
$c_block = \deflate_add($deflate_context, $block, \ZLIB_NO_FLUSH);
|
||||
}
|
||||
// get rid of the compression header
|
||||
if ($is_first_block) {
|
||||
$c_block = \substr($c_block, 2);
|
||||
}
|
||||
$csize += \strlen($c_block);
|
||||
$this->writebytes($c_block);
|
||||
} else {
|
||||
$this->writebytes($block);
|
||||
}
|
||||
$size += \strlen($block);
|
||||
\hash_update($crc_context, $block);
|
||||
}
|
||||
\fclose($fp);
|
||||
// update the local file header with the computed CRC and size
|
||||
$crc = \hexdec(\hash_final($crc_context));
|
||||
$csize = $this->complevel ? $csize : $size;
|
||||
$this->writebytesAt($this->makeCrcAndSize($crc, $size, $csize), $offset + self::LOCAL_FILE_HEADER_CRC_OFFSET);
|
||||
// we store no data descriptor
|
||||
// add info to central file directory
|
||||
$this->ctrl_dir[] = $this->makeCentralFileRecord($offset, $time, $crc, $size, $csize, $name, (bool) $this->complevel);
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add a file to the current Zip archive using the given $data as content
|
||||
*
|
||||
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data
|
||||
* @param string $data binary content of the file to add
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function addData($fileinfo, $data)
|
||||
{
|
||||
if (\is_string($fileinfo)) {
|
||||
$fileinfo = new FileInfo($fileinfo);
|
||||
}
|
||||
if ($this->closed) {
|
||||
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
|
||||
}
|
||||
// prepare info and compress data
|
||||
$size = \strlen($data);
|
||||
$crc = \crc32($data);
|
||||
if ($this->complevel) {
|
||||
$data = \gzcompress($data, $this->complevel);
|
||||
$data = \substr($data, 2, -4);
|
||||
// strip compression headers
|
||||
}
|
||||
$csize = \strlen($data);
|
||||
$offset = $this->dataOffset();
|
||||
$name = $fileinfo->getPath();
|
||||
$time = $fileinfo->getMtime();
|
||||
// write local file header
|
||||
$this->writebytes($this->makeLocalFileHeader($time, $crc, $size, $csize, $name, (bool) $this->complevel));
|
||||
// we store no encryption header
|
||||
// write data
|
||||
$this->writebytes($data);
|
||||
// we store no data descriptor
|
||||
// add info to central file directory
|
||||
$this->ctrl_dir[] = $this->makeCentralFileRecord($offset, $time, $crc, $size, $csize, $name, (bool) $this->complevel);
|
||||
if (\is_callable($this->callback)) {
|
||||
\call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add the closing footer to the archive if in write mode, close all file handles
|
||||
*
|
||||
* After a call to this function no more data can be added to the archive, for
|
||||
* read access no reading is allowed anymore
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
// we did this already
|
||||
if ($this->writeaccess) {
|
||||
// write central directory
|
||||
$offset = $this->dataOffset();
|
||||
$ctrldir = \join('', $this->ctrl_dir);
|
||||
$this->writebytes($ctrldir);
|
||||
// write end of central directory record
|
||||
$this->writebytes("PK\x05\x06");
|
||||
// end of central dir signature
|
||||
$this->writebytes(\pack('v', 0));
|
||||
// number of this disk
|
||||
$this->writebytes(\pack('v', 0));
|
||||
// number of the disk with the start of the central directory
|
||||
$this->writebytes(\pack('v', \count($this->ctrl_dir)));
|
||||
// total number of entries in the central directory on this disk
|
||||
$this->writebytes(\pack('v', \count($this->ctrl_dir)));
|
||||
// total number of entries in the central directory
|
||||
$this->writebytes(\pack('V', \strlen($ctrldir)));
|
||||
// size of the central directory
|
||||
$this->writebytes(\pack('V', $offset));
|
||||
// offset of start of central directory with respect to the starting disk number
|
||||
$this->writebytes(\pack('v', 0));
|
||||
// .ZIP file comment length
|
||||
$this->ctrl_dir = array();
|
||||
}
|
||||
// close file handles
|
||||
if ($this->file) {
|
||||
\fclose($this->fh);
|
||||
$this->file = '';
|
||||
$this->fh = 0;
|
||||
}
|
||||
$this->writeaccess = \false;
|
||||
$this->closed = \true;
|
||||
}
|
||||
/**
|
||||
* Returns the created in-memory archive data
|
||||
*
|
||||
* This implicitly calls close() on the Archive
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function getArchive()
|
||||
{
|
||||
$this->close();
|
||||
return $this->memory;
|
||||
}
|
||||
/**
|
||||
* Save the created in-memory archive data
|
||||
*
|
||||
* Note: It's more memory effective to specify the filename in the create() function and
|
||||
* let the library work on the new file directly.
|
||||
*
|
||||
* @param $file
|
||||
* @throws ArchiveIOException
|
||||
*/
|
||||
public function save($file)
|
||||
{
|
||||
if (!@\file_put_contents($file, $this->getArchive())) {
|
||||
throw new ArchiveIOException('Could not write to file: ' . $file);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read the central directory
|
||||
*
|
||||
* This key-value list contains general information about the ZIP file
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function readCentralDir()
|
||||
{
|
||||
$size = \filesize($this->file);
|
||||
if ($size < 277) {
|
||||
$maximum_size = $size;
|
||||
} else {
|
||||
$maximum_size = 277;
|
||||
}
|
||||
@\fseek($this->fh, $size - $maximum_size);
|
||||
$pos = \ftell($this->fh);
|
||||
$bytes = 0x0;
|
||||
while ($pos < $size) {
|
||||
$byte = @\fread($this->fh, 1);
|
||||
$bytes = $bytes << 8 & 0xffffffff | \ord($byte);
|
||||
if ($bytes == 0x504b0506) {
|
||||
break;
|
||||
}
|
||||
$pos++;
|
||||
}
|
||||
$data = \unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', \fread($this->fh, 18));
|
||||
if ($data['comment_size'] != 0) {
|
||||
$centd['comment'] = \fread($this->fh, $data['comment_size']);
|
||||
} else {
|
||||
$centd['comment'] = '';
|
||||
}
|
||||
$centd['entries'] = $data['entries'];
|
||||
$centd['disk_entries'] = $data['disk_entries'];
|
||||
$centd['offset'] = $data['offset'];
|
||||
$centd['disk_start'] = $data['disk_start'];
|
||||
$centd['size'] = $data['size'];
|
||||
$centd['disk'] = $data['disk'];
|
||||
return $centd;
|
||||
}
|
||||
/**
|
||||
* Read the next central file header
|
||||
*
|
||||
* Assumes the current file pointer is pointing at the right position
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function readCentralFileHeader()
|
||||
{
|
||||
$binary_data = \fread($this->fh, 46);
|
||||
$header = \unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data);
|
||||
if ($header['filename_len'] != 0) {
|
||||
$header['filename'] = \fread($this->fh, $header['filename_len']);
|
||||
} else {
|
||||
$header['filename'] = '';
|
||||
}
|
||||
if ($header['extra_len'] != 0) {
|
||||
$header['extra'] = \fread($this->fh, $header['extra_len']);
|
||||
$header['extradata'] = $this->parseExtra($header['extra']);
|
||||
} else {
|
||||
$header['extra'] = '';
|
||||
$header['extradata'] = array();
|
||||
}
|
||||
if ($header['comment_len'] != 0) {
|
||||
$header['comment'] = \fread($this->fh, $header['comment_len']);
|
||||
} else {
|
||||
$header['comment'] = '';
|
||||
}
|
||||
$header['mtime'] = $this->makeUnixTime($header['mdate'], $header['mtime']);
|
||||
$header['stored_filename'] = $header['filename'];
|
||||
$header['status'] = 'ok';
|
||||
if (\substr($header['filename'], -1) == '/') {
|
||||
$header['external'] = 0x41ff0010;
|
||||
}
|
||||
$header['folder'] = $header['external'] == 0x41ff0010 || $header['external'] == 16 ? 1 : 0;
|
||||
return $header;
|
||||
}
|
||||
/**
|
||||
* Reads the local file header
|
||||
*
|
||||
* This header precedes each individual file inside the zip file. Assumes the current file pointer is pointing at
|
||||
* the right position already. Enhances the given central header with the data found at the local header.
|
||||
*
|
||||
* @param array $header the central file header read previously (see above)
|
||||
* @return array
|
||||
*/
|
||||
protected function readFileHeader($header)
|
||||
{
|
||||
$binary_data = \fread($this->fh, 30);
|
||||
$data = \unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
|
||||
$header['filename'] = \fread($this->fh, $data['filename_len']);
|
||||
if ($data['extra_len'] != 0) {
|
||||
$header['extra'] = \fread($this->fh, $data['extra_len']);
|
||||
$header['extradata'] = \array_merge($header['extradata'], $this->parseExtra($header['extra']));
|
||||
} else {
|
||||
$header['extra'] = '';
|
||||
$header['extradata'] = array();
|
||||
}
|
||||
$header['compression'] = $data['compression'];
|
||||
foreach (array('size', 'compressed_size', 'crc') as $hd) {
|
||||
// On ODT files, these headers are 0. Keep the previous value.
|
||||
if ($data[$hd] != 0) {
|
||||
$header[$hd] = $data[$hd];
|
||||
}
|
||||
}
|
||||
$header['flag'] = $data['flag'];
|
||||
$header['mtime'] = $this->makeUnixTime($data['mdate'], $data['mtime']);
|
||||
$header['stored_filename'] = $header['filename'];
|
||||
$header['status'] = "ok";
|
||||
$header['folder'] = $header['external'] == 0x41ff0010 || $header['external'] == 16 ? 1 : 0;
|
||||
return $header;
|
||||
}
|
||||
/**
|
||||
* Parse the extra headers into fields
|
||||
*
|
||||
* @param string $header
|
||||
* @return array
|
||||
*/
|
||||
protected function parseExtra($header)
|
||||
{
|
||||
$extra = array();
|
||||
// parse all extra fields as raw values
|
||||
while (\strlen($header) !== 0) {
|
||||
$set = \unpack('vid/vlen', $header);
|
||||
$header = \substr($header, 4);
|
||||
$value = \substr($header, 0, $set['len']);
|
||||
$header = \substr($header, $set['len']);
|
||||
$extra[$set['id']] = $value;
|
||||
}
|
||||
// handle known ones
|
||||
if (isset($extra[0x6375])) {
|
||||
$extra['utf8comment'] = \substr($extra[0x7075], 5);
|
||||
// strip version and crc
|
||||
}
|
||||
if (isset($extra[0x7075])) {
|
||||
$extra['utf8path'] = \substr($extra[0x7075], 5);
|
||||
// strip version and crc
|
||||
}
|
||||
return $extra;
|
||||
}
|
||||
/**
|
||||
* Create fileinfo object from header data
|
||||
*
|
||||
* @param $header
|
||||
* @return FileInfo
|
||||
*/
|
||||
protected function header2fileinfo($header)
|
||||
{
|
||||
$fileinfo = new FileInfo();
|
||||
$fileinfo->setSize($header['size']);
|
||||
$fileinfo->setCompressedSize($header['compressed_size']);
|
||||
$fileinfo->setMtime($header['mtime']);
|
||||
$fileinfo->setComment($header['comment']);
|
||||
$fileinfo->setIsdir($header['external'] == 0x41ff0010 || $header['external'] == 16);
|
||||
if (isset($header['extradata']['utf8path'])) {
|
||||
$fileinfo->setPath($header['extradata']['utf8path']);
|
||||
} else {
|
||||
$fileinfo->setPath($this->cpToUtf8($header['filename']));
|
||||
}
|
||||
if (isset($header['extradata']['utf8comment'])) {
|
||||
$fileinfo->setComment($header['extradata']['utf8comment']);
|
||||
} else {
|
||||
$fileinfo->setComment($this->cpToUtf8($header['comment']));
|
||||
}
|
||||
return $fileinfo;
|
||||
}
|
||||
/**
|
||||
* Convert the given CP437 encoded string to UTF-8
|
||||
*
|
||||
* Tries iconv with the correct encoding first, falls back to mbstring with CP850 which is
|
||||
* similar enough. CP437 seems not to be available in mbstring. Lastly falls back to keeping the
|
||||
* string as is, which is still better than nothing.
|
||||
*
|
||||
* On some systems iconv is available, but the codepage is not. We also check for that.
|
||||
*
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
protected function cpToUtf8($string)
|
||||
{
|
||||
if (\function_exists('iconv') && @\iconv_strlen('', 'CP437') !== \false) {
|
||||
return \iconv('CP437', 'UTF-8', $string);
|
||||
} elseif (\function_exists('mb_convert_encoding')) {
|
||||
return \mb_convert_encoding($string, 'UTF-8', 'CP850');
|
||||
} else {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert the given UTF-8 encoded string to CP437
|
||||
*
|
||||
* Same caveats as for cpToUtf8() apply
|
||||
*
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
protected function utf8ToCp($string)
|
||||
{
|
||||
// try iconv first
|
||||
if (\function_exists('iconv')) {
|
||||
$conv = @\iconv('UTF-8', 'CP437//IGNORE', $string);
|
||||
if ($conv) {
|
||||
return $conv;
|
||||
}
|
||||
// it worked
|
||||
}
|
||||
// still here? iconv failed to convert the string. Try another method
|
||||
// see http://php.net/manual/en/function.iconv.php#108643
|
||||
if (\function_exists('mb_convert_encoding')) {
|
||||
return \mb_convert_encoding($string, 'CP850', 'UTF-8');
|
||||
} else {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Write to the open filepointer or memory
|
||||
*
|
||||
* @param string $data
|
||||
* @throws ArchiveIOException
|
||||
* @return int number of bytes written
|
||||
*/
|
||||
protected function writebytes($data)
|
||||
{
|
||||
if (!$this->file) {
|
||||
$this->memory .= $data;
|
||||
$written = \strlen($data);
|
||||
} else {
|
||||
$written = @\fwrite($this->fh, $data);
|
||||
}
|
||||
if ($written === \false) {
|
||||
throw new ArchiveIOException('Failed to write to archive stream');
|
||||
}
|
||||
return $written;
|
||||
}
|
||||
/**
|
||||
* Write to the open filepointer or memory at the specified offset
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $offset
|
||||
* @throws ArchiveIOException
|
||||
* @return int number of bytes written
|
||||
*/
|
||||
protected function writebytesAt($data, $offset)
|
||||
{
|
||||
if (!$this->file) {
|
||||
$this->memory .= \substr_replace($this->memory, $data, $offset);
|
||||
$written = \strlen($data);
|
||||
} else {
|
||||
@\fseek($this->fh, $offset);
|
||||
$written = @\fwrite($this->fh, $data);
|
||||
@\fseek($this->fh, 0, \SEEK_END);
|
||||
}
|
||||
if ($written === \false) {
|
||||
throw new ArchiveIOException('Failed to write to archive stream');
|
||||
}
|
||||
return $written;
|
||||
}
|
||||
/**
|
||||
* Current data pointer position
|
||||
*
|
||||
* @fixme might need a -1
|
||||
* @return int
|
||||
*/
|
||||
protected function dataOffset()
|
||||
{
|
||||
if ($this->file) {
|
||||
return \ftell($this->fh);
|
||||
} else {
|
||||
return \strlen($this->memory);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a DOS timestamp from a UNIX timestamp
|
||||
*
|
||||
* DOS timestamps start at 1980-01-01, earlier UNIX stamps will be set to this date
|
||||
*
|
||||
* @param $time
|
||||
* @return int
|
||||
*/
|
||||
protected function makeDosTime($time)
|
||||
{
|
||||
$timearray = \getdate($time);
|
||||
if ($timearray['year'] < 1980) {
|
||||
$timearray['year'] = 1980;
|
||||
$timearray['mon'] = 1;
|
||||
$timearray['mday'] = 1;
|
||||
$timearray['hours'] = 0;
|
||||
$timearray['minutes'] = 0;
|
||||
$timearray['seconds'] = 0;
|
||||
}
|
||||
return $timearray['year'] - 1980 << 25 | $timearray['mon'] << 21 | $timearray['mday'] << 16 | $timearray['hours'] << 11 | $timearray['minutes'] << 5 | $timearray['seconds'] >> 1;
|
||||
}
|
||||
/**
|
||||
* Create a UNIX timestamp from a DOS timestamp
|
||||
*
|
||||
* @param $mdate
|
||||
* @param $mtime
|
||||
* @return int
|
||||
*/
|
||||
protected function makeUnixTime($mdate = null, $mtime = null)
|
||||
{
|
||||
if ($mdate && $mtime) {
|
||||
$year = (($mdate & 0xfe00) >> 9) + 1980;
|
||||
$month = ($mdate & 0x1e0) >> 5;
|
||||
$day = $mdate & 0x1f;
|
||||
$hour = ($mtime & 0xf800) >> 11;
|
||||
$minute = ($mtime & 0x7e0) >> 5;
|
||||
$seconde = ($mtime & 0x1f) << 1;
|
||||
$mtime = \mktime($hour, $minute, $seconde, $month, $day, $year);
|
||||
} else {
|
||||
$mtime = \time();
|
||||
}
|
||||
return $mtime;
|
||||
}
|
||||
/**
|
||||
* Returns a local file header for the given data
|
||||
*
|
||||
* @param int $offset location of the local header
|
||||
* @param int $ts unix timestamp
|
||||
* @param int $crc CRC32 checksum of the uncompressed data
|
||||
* @param int $len length of the uncompressed data
|
||||
* @param int $clen length of the compressed data
|
||||
* @param string $name file name
|
||||
* @param boolean|null $comp if compression is used, if null it's determined from $len != $clen
|
||||
* @return string
|
||||
*/
|
||||
protected function makeCentralFileRecord($offset, $ts, $crc, $len, $clen, $name, $comp = null)
|
||||
{
|
||||
if (\is_null($comp)) {
|
||||
$comp = $len != $clen;
|
||||
}
|
||||
$comp = $comp ? 8 : 0;
|
||||
$dtime = \dechex($this->makeDosTime($ts));
|
||||
list($name, $extra) = $this->encodeFilename($name);
|
||||
$header = "PK\x01\x02";
|
||||
// central file header signature
|
||||
$header .= \pack('v', 14);
|
||||
// version made by - VFAT
|
||||
$header .= \pack('v', 20);
|
||||
// version needed to extract - 2.0
|
||||
$header .= \pack('v', 0);
|
||||
// general purpose flag - no flags set
|
||||
$header .= \pack('v', $comp);
|
||||
// compression method - deflate|none
|
||||
$header .= \pack('H*', $dtime[6] . $dtime[7] . $dtime[4] . $dtime[5] . $dtime[2] . $dtime[3] . $dtime[0] . $dtime[1]);
|
||||
// last mod file time and date
|
||||
$header .= \pack('V', $crc);
|
||||
// crc-32
|
||||
$header .= \pack('V', $clen);
|
||||
// compressed size
|
||||
$header .= \pack('V', $len);
|
||||
// uncompressed size
|
||||
$header .= \pack('v', \strlen($name));
|
||||
// file name length
|
||||
$header .= \pack('v', \strlen($extra));
|
||||
// extra field length
|
||||
$header .= \pack('v', 0);
|
||||
// file comment length
|
||||
$header .= \pack('v', 0);
|
||||
// disk number start
|
||||
$header .= \pack('v', 0);
|
||||
// internal file attributes
|
||||
$header .= \pack('V', 0);
|
||||
// external file attributes @todo was 0x32!?
|
||||
$header .= \pack('V', $offset);
|
||||
// relative offset of local header
|
||||
$header .= $name;
|
||||
// file name
|
||||
$header .= $extra;
|
||||
// extra (utf-8 filename)
|
||||
return $header;
|
||||
}
|
||||
/**
|
||||
* Returns a local file header for the given data
|
||||
*
|
||||
* @param int $ts unix timestamp
|
||||
* @param int $crc CRC32 checksum of the uncompressed data
|
||||
* @param int $len length of the uncompressed data
|
||||
* @param int $clen length of the compressed data
|
||||
* @param string $name file name
|
||||
* @param boolean|null $comp if compression is used, if null it's determined from $len != $clen
|
||||
* @return string
|
||||
*/
|
||||
protected function makeLocalFileHeader($ts, $crc, $len, $clen, $name, $comp = null)
|
||||
{
|
||||
if (\is_null($comp)) {
|
||||
$comp = $len != $clen;
|
||||
}
|
||||
$comp = $comp ? 8 : 0;
|
||||
$dtime = \dechex($this->makeDosTime($ts));
|
||||
list($name, $extra) = $this->encodeFilename($name);
|
||||
$header = "PK\x03\x04";
|
||||
// local file header signature
|
||||
$header .= \pack('v', 20);
|
||||
// version needed to extract - 2.0
|
||||
$header .= \pack('v', 0);
|
||||
// general purpose flag - no flags set
|
||||
$header .= \pack('v', $comp);
|
||||
// compression method - deflate|none
|
||||
$header .= \pack('H*', $dtime[6] . $dtime[7] . $dtime[4] . $dtime[5] . $dtime[2] . $dtime[3] . $dtime[0] . $dtime[1]);
|
||||
// last mod file time and date
|
||||
$header .= \pack('V', $crc);
|
||||
// crc-32
|
||||
$header .= \pack('V', $clen);
|
||||
// compressed size
|
||||
$header .= \pack('V', $len);
|
||||
// uncompressed size
|
||||
$header .= \pack('v', \strlen($name));
|
||||
// file name length
|
||||
$header .= \pack('v', \strlen($extra));
|
||||
// extra field length
|
||||
$header .= $name;
|
||||
// file name
|
||||
$header .= $extra;
|
||||
// extra (utf-8 filename)
|
||||
return $header;
|
||||
}
|
||||
/**
|
||||
* Returns only a part of the local file header containing the CRC, size and compressed size.
|
||||
* Used to update these fields for an already written header.
|
||||
*
|
||||
* @param int $crc CRC32 checksum of the uncompressed data
|
||||
* @param int $len length of the uncompressed data
|
||||
* @param int $clen length of the compressed data
|
||||
* @return string
|
||||
*/
|
||||
protected function makeCrcAndSize($crc, $len, $clen)
|
||||
{
|
||||
$header = \pack('V', $crc);
|
||||
// crc-32
|
||||
$header .= \pack('V', $clen);
|
||||
// compressed size
|
||||
$header .= \pack('V', $len);
|
||||
// uncompressed size
|
||||
return $header;
|
||||
}
|
||||
/**
|
||||
* Returns an allowed filename and an extra field header
|
||||
*
|
||||
* When encoding stuff outside the 7bit ASCII range it needs to be placed in a separate
|
||||
* extra field
|
||||
*
|
||||
* @param $original
|
||||
* @return array($filename, $extra)
|
||||
*/
|
||||
protected function encodeFilename($original)
|
||||
{
|
||||
$cp437 = $this->utf8ToCp($original);
|
||||
if ($cp437 === $original) {
|
||||
return array($original, '');
|
||||
}
|
||||
$extra = \pack(
|
||||
'vvCV',
|
||||
0x7075,
|
||||
// tag
|
||||
\strlen($original) + 5,
|
||||
// length of file + version + crc
|
||||
1,
|
||||
// version
|
||||
\crc32($original)
|
||||
);
|
||||
$extra .= $original;
|
||||
return array($cp437, $extra);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user