Subversion Repositories php_utils

Compare Revisions

Regard whitespace Rev 4 → Rev 5

/trunk/last_weekday_date.phps
File deleted
/trunk/gmp_supplement.inc.phps
File deleted
/trunk/vtor_get_contents.inc.phps
File deleted
/trunk/htmlentities_compat.inc.php
File deleted
/trunk/decode_jwt_token.inc.phps
File deleted
/trunk/color_utils.inc.php
File deleted
/trunk/ipv4_functions.inc.phps
File deleted
/trunk/ipv6_functions.inc.phps
File deleted
/trunk/x_509_utils.inc.phps
File deleted
/trunk/easterdates.phps
File deleted
\ No newline at end of file
/trunk/VtsBrowserDownload.class.php
File deleted
/trunk/oid_utils.inc.phps
File deleted
/trunk/ipresolution.inc.phps
File deleted
/trunk/SecureMailer.class.phps
File deleted
/trunk/googlecache.inc.phps
File deleted
/trunk/anti_xss.php
File deleted
/trunk/ip_functions.inc.phps
File deleted
/trunk/marschallHash.phps
File deleted
/trunk/cookie_test.phps
File deleted
\ No newline at end of file
/trunk/functions_diff.inc.phps
File deleted
/trunk/simplexml_supplement.inc.phps
File deleted
/trunk/grep_funcs.inc.phps
File deleted
/trunk/xml_utils.inc.phps
File deleted
/trunk/SecureMailer.class.php
0,0 → 1,133
<?php
 
/*
* Secure Mailer PHP Class
* Copyright 2009-2013 Daniel Marschall, ViaThinkSoft
* QB_SECURE_MAIL_PARAM (C) Erich Kachel
* Version 2013-04-14
*
* Licensed 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.
*/
 
// TODO: getHeaders() as single string , attachments , remove headers etc, headers as array in/out, do you also need addRawHeader()?
 
class SecureMailer {
private $headers = '';
 
// TODO: This should rather be private, but it won't work
const endl = "\n"; // GMX doesn't like CRLF! wtf?! (tested in Postfix in Linux)
 
private function QB_SECURE_MAIL_PARAM($param_ = '', $level_ = 2) {
// Prevents eMail header injections
// Source: http://www.erich-kachel.de/?p=26 (modified)
 
/* replace until done */
$filtered = null;
while (is_null($filtered) || ($param_ != $filtered)) {
if (!is_null($filtered)) {
$param_ = $filtered;
}
 
$filtered = preg_replace("/(Content-Transfer-Encoding:|MIME-Version:|content-type:|Subject:|to:|cc:|bcc:|from:|reply-to:)/ims", '', $param_);
}
 
unset($filtered);
 
if ($level_ >= 2) {
/* replace until done */
while (!isset($filtered) || ($param_ != $filtered)) {
if (isset($filtered)) {
$param_ = $filtered;
}
 
$filtered = preg_replace("/(%0A|\\\\r|%0D|\\\\n|%00|\\\\0|%09|\\\\t|%01|%02|%03|%04|%05|%06|%07|%08|%09|%0B|%0C|%0E|%0F|%10|%11|%12|%13)/ims", '', $param_);
}
}
 
return $param_;
}
 
private function getHeaders() {
return $this->headers;
}
 
private static function mail_base64_encode($text) {
// Why 72? Seen here: http://linux.dsplabs.com.au/munpack-mime-base64-multi-part-attachment-php-perl-decode-email-pdf-p82/
return wordwrap(base64_encode($text), 72, self::endl, true);
}
 
private function headerLine($name, $value) {
// Change 2011-02-09
// LF is OK! CRLF does lead to CR+CRLF on some systems!
// http://bugs.php.net/bug.php?id=15841
// The mail() function is not talking to an SMTP server, so RFC2822 does not apply here. mail() is talking to a command line program on the local system, and it is reasonable to expect that program to require system-native line breaks.
return $this->QB_SECURE_MAIL_PARAM($name).': '.$this->QB_SECURE_MAIL_PARAM($value)."\n";
}
 
public function addHeader($name, $value) {
$this->headers .= $this->headerLine($name, $value);
}
 
public static function utf8Subject($subject) {
return '=?UTF-8?B?'.base64_encode(utf8_encode($subject)).'?=';
}
 
private function _sendMail($recipient, $subject, $message, $add_headers='') {
return @mail(
$this->QB_SECURE_MAIL_PARAM($recipient),
$this->QB_SECURE_MAIL_PARAM($subject),
$this->QB_SECURE_MAIL_PARAM($message, 1),
$this->getHeaders().$add_headers
);
}
 
public function sendMail($recipient, $subject, $message) {
return $this->_sendMail($recipient, $subject, $message, '');
}
 
// TODO: generate plain from html (strip tags), optional
public function sendMailHTMLandPlainMultipart($to, $subject, $msg_html, $msg_plain) {
$boundary = uniqid('np');
 
$msg_html = $this->QB_SECURE_MAIL_PARAM($msg_html, 1);
$msg_plain = $this->QB_SECURE_MAIL_PARAM($msg_plain, 1);
 
$add_headers = $this->headerLine('MIME-Version', '1.0');
$add_headers .= $this->headerLine('Content-Type', 'multipart/alternative; boundary="'.$boundary.'"');
 
$message = "This is a MIME encoded message.";
$message .= self::endl;
$message .= self::endl;
$message .= "--" . $boundary . self::endl;
$message .= "Content-type: text/plain; charset=utf-8".self::endl;
$message .= "Content-Transfer-Encoding: base64".self::endl;
$message .= self::endl;
$message .= $this->mail_base64_encode($msg_plain); // better than wordwrap&quoted-printable because of long lines (e.g. links)
$message .= self::endl;
$message .= self::endl;
$message .= "--" . $boundary . self::endl;
$message .= "Content-type: text/html; charset=utf-8".self::endl;
$message .= "Content-Transfer-Encoding: base64".self::endl;
$message .= self::endl;
$message .= $this->mail_base64_encode($msg_html);
$message .= self::endl;
$message .= self::endl."--" . $boundary . "--";
 
return @mail(
$this->QB_SECURE_MAIL_PARAM($to),
$this->QB_SECURE_MAIL_PARAM($subject),
$message,
$this->getHeaders().$add_headers
);
}
}
/trunk/cookie_test.php
0,0 → 1,21
<?php
 
if ($cookie_state == 1)
{
if (isset($_COOKIE['TestCookie']))
{
setcookie('TestCookie', 'test', time());
echo 'Cookies werden unterstützt!';
}
else
{
echo 'Es werden keine Cookies unterstützt!';
}
}
else
{
setcookie('TestCookie', 'test');
header('Location: '.$_SERVER['PHP_SELF'].'?cookie_state=1');
}
 
?>
/trunk/decode_jwt_token.inc.php
0,0 → 1,122
<?php
 
/*
* JWT Decoder for PHP
* Copyright 2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-15
*
* Licensed 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.
*/
 
function decode_idtoken($id_token, $verification_certs=null, $allowed_algorithms = array()) {
// Parts taken and simplified from https://github.com/firebase/php-jwt , licensed by BSD-3-clause
// Here is a great page for encode and decode tokens for testing: https://jwt.io/
 
$parts = explode('.', $id_token);
if (count($parts) === 5) return false; // encrypted JWT not yet supported
if (count($parts) !== 3) return false;
list($header_base64, $payload_base64, $signature_base64) = $parts;
 
$header_ary = json_decode(urlsafeB64Decode($header_base64),true);
if ($header_ary['typ'] !== 'JWT') return false;
 
if ($verification_certs) {
$key = isset($header_ary['kid']) ? $verification_certs[$header_ary['kid']] : $verification_certs;
 
$msg = $header_base64.'.'.$payload_base64;
$signature = urlsafeB64Decode($signature_base64);
 
$jwt_algo = $header_ary['alg'];
 
// see https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
// https://datatracker.ietf.org/doc/html/rfc8725#section-3.1
if (!in_array($jwt_algo, $allowed_algorithms)) return false;
 
if ($jwt_algo != 'none') {
$php_algo = 'SHA'.substr($jwt_algo,2,3);
switch (substr($jwt_algo,0,2)) {
case 'ES':
// OpenSSL expects an ASN.1 DER sequence for ES256 signatures
$signature = signatureToDER($signature);
if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT
if (!openssl_verify($msg, $signature, $key, $php_algo)) return false;
break;
case 'RS':
if (!function_exists('openssl_verify')) break; // if OpenSSL is not installed, we just accept the JWT
if (!openssl_verify($msg, $signature, $key, $php_algo)) return false;
break;
case 'HS':
$hash = @hash_hmac($php_algo, $msg, $key, true);
if (!$hash) break; // if the hash algo is not available, we just accept the JWT
if (!hash_equals($signature, $hash)) return false;
break;
case 'PS':
// This feature is new and not yet available in php-jwt
file_put_contents($msg_file = tempnam("/tmp", ""), $msg);
file_put_contents($sig_file = tempnam("/tmp", ""), $signature);
file_put_contents($key_file = tempnam("/tmp", ""), $key);
$ec = -1;
$out = array();
$cmd = "openssl dgst -".strtolower($php_algo)." -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -verify ".escapeshellarg($key_file)." -signature ".escapeshellarg($sig_file)." ".escapeshellarg($msg_file);
$cmd .= (strtoupper(substr(PHP_OS,0,3)) === 'WIN') ? ' 2> NUL' : ' 2> /dev/null';
exec($cmd, $out, $ec);
unlink($msg_file);
unlink($sig_file);
unlink($key_file);
if (($ec !== 0) && (count($out) === 0)) break; // If OpenSSL is not found, we just accept the JWT
if (($ec !== 0) || (strpos(implode("\n",$out),"Verified OK") === false)) return false;
break;
default:
return false;
}
}
}
 
$payload_ary = json_decode(urlsafeB64Decode($payload_base64), true);
 
$leeway = 60; // 1 Minute
if (isset($payload_ary['nbf']) && (time()+$leeway<$payload_ary['nbf'])) return false;
if (isset($payload_ary['exp']) && (time()-$leeway>$payload_ary['exp'])) return false;
 
return $payload_ary;
}
 
function urlsafeB64Decode($input) {
// Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
 
function signatureToDER($sig) {
// Taken from https://github.com/firebase/php-jwt , licensed by BSD-3-clause, modified
 
// Separate the signature into r-value and s-value
list($r, $s) = str_split($sig, (int) (strlen($sig) / 2));
 
// Trim leading zeros
$r = ltrim($r, "\x00");
$s = ltrim($s, "\x00");
 
// Convert r-value and s-value from unsigned big-endian integers to signed two's complement
if (ord($r[0]) > 0x7f) $r = "\x00" . $r;
if (ord($s[0]) > 0x7f) $s = "\x00" . $s;
 
$der_r = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($r)).$r;
$der_s = chr(0x00/*primitive*/ | 0x02/*INTEGER*/).chr(strlen($s)).$s;
$der = chr(0x20/*constructed*/ | 0x10/*SEQUENCE*/).chr(strlen($der_r.$der_s)).$der_r.$der_s;
return $der;
}
/trunk/easterdates.php
0,0 → 1,101
<?php
 
// Neujahr setzen (fester Feiertag am 1. Januar)
// Hl. Drei Könige setzen (fester Feiertag am 6. Januar)
// Rosenmontag berechnen (beweglicher Feiertag; 48 Tage vor Ostern)
// Aschermittwoch berechnen (beweglicher Feiertag; 46 Tage vor Ostern)
// Karfreitag berechnen (beweglicher Feiertag; 2 Tage vor Ostern)
// Ostersonntag
// Ostermontag berechnen (beweglicher Feiertag; 1 Tag nach Ostern)
// Maifeiertag setzen (fester Feiertag am 1. Mai)
// Christi Himmelfahrt berechnen (beweglicher Feiertag; 39 Tage nach Ostern)
// Pfingstsonntag berechnen (beweglicher Feiertag; 49 Tage nach Ostern)
// Pfingstmontag berechnen (beweglicher Feiertag; 50 Tage nach Ostern)
// Fronleichnam berechnen (beweglicher Feiertag; 60 Tage nach Ostern)
// Mariä Himmelfahrt setzen (fester Feiertag am 15. August)
// Tag der deutschen Einheit setzen (fester Feiertag am 3. Oktober)
// Reformationstag setzen (fester Feiertag am 31. Oktober)
// Allerheiligen setzen (fester Feiertag am 1. November)
// Heiligabend setzen (fester 'Feiertag' am 24. Dezember)
// Erster Weihnachtstag setzen (fester 'Feiertag' am 25. Dezember)
// Zweiter Weihnachtstag setzen (fester 'Feiertag' am 26. Dezember)
// Sylvester setzen (fester 'Feiertag' am 31. Dezember)
 
function ostersonntag($year) {
 
$J = date ("Y", mktime(0, 0, 0, 1, 1, $year));
 
$a = $J % 19;
$b = $J % 4;
$c = $J % 7;
$m = number_format (8 * number_format ($J / 100) + 13) / 25 - 2;
$s = number_format ($J / 100 ) - number_format ($J / 400) - 2;
$M = (15 + $s - $m) % 30;
$N = (6 + $s) % 7;
$d = ($M + 19 * $a) % 30;
 
if ($d == 29) {
$D = 28;
} else if ($d == 28 and $a >= 11) {
$D = 27;
} else {
$D = $d;
}
 
$e = (2 * $b + 4 * $c + 6 * $D + $N) % 7;
 
 
$easter = mktime (0, 0, 0, 3, 21, $J) + (($D + $e + 1) * 86400);
 
return $easter;
 
}
 
 
function get_easter_depending_days($year) {
$es = ostersonntag($year);
$sd = 24 * 60 * 60;
$days = array(
'rose_monday' => $es - (48 * $sd),
'shrove_tuesday' => $es - (47 * $sd),
'ash_wednesday' => $es - (46 * $sd),
'palm_sunday' => $es - (7 * $sd),
'good_friday' => $es - (2 * $sd),
'easter_sunday' => $es,
'easter_monday' => $es + (1 * $sd),
'low_sunday' => $es + (7 * $sd),
'ascension_day' => $es + (39 * $sd),
'whit_sunday' => $es + (49 * $sd),
'whit_monday' => $es + (50 * $sd),
'corpus_christi' => $es + (60 * $sd)
);
return($days);
}
 
$days = get_easter_depending_days(date("Y"));
echo "Rosemontag: ",
date ("d.m.Y", $days['rose_monday']), "<br>";
echo "Fastnachtsdienstag: ",
date ("d.m.Y", $days['shrove_tuesday']), "<br>";
echo "Aschermittwoch: ",
date ("d.m.Y", $days['ash_wednesday']), "<br>";
echo "Palmsonntag: ",
date ("d.m.Y", $days['palm_sunday']), "<br>";
echo "Karfreitag: ",
date ("d.m.Y", $days['good_friday']), "<br>";
echo "Ostersonntag: ",
date ("d.m.Y", $days['easter_sunday']), "<br>";
echo "Ostermontag: ",
date ("d.m.Y", $days['easter_monday']), "<br>";
echo "Weißer Sonntag: ",
date ("d.m.Y", $days['low_sunday']), "<br>";
echo "Christi Himmelfahrt: ",
date ("d.m.Y", $days['ascension_day']), "<br>";
echo "Pfingstsonntag: ",
date ("d.m.Y", $days['whit_sunday']), "<br>";
echo "Pfingstmontag: ",
date ("d.m.Y", $days['whit_monday']), "<br>";
echo "Fronleichnam: ",
date ("d.m.Y", $days['corpus_christi']), "<br>";
 
?>
/trunk/functions_diff.inc.php
0,0 → 1,84
<?php
 
/*
* PHP diff functions
* Copyright 2012 Daniel Marschall, ViaThinkSoft
* Revision 2012-11-16
*
* Licensed 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.
*/
 
define('TABS_WS', 6);
 
function output_unified_diff($fileA, $fileB, $num_lines=3) {
$fileA = realpath($fileA);
$fileB = realpath($fileB);
 
ob_start();
system("diff -wbB --ignore-blank-lines -U ".escapeshellarg($num_lines)." ".escapeshellarg($fileA)." ".escapeshellarg($fileB));
$cont = ob_get_contents();
ob_end_clean();
 
$ary = explode("\n", $cont);
foreach ($ary as $n => $a) {
$c = substr($a, 0, 1);
$c2 = substr($a, 0, 2);
$c3 = substr($a, 0, 3);
 
echo '<code>';
if (($c3 == '+++') || ($c3 == '---')) {
echo '<b><font color="gray">'.html_format($a).'</font></b>';
} else if ($c2 == '@@') {
echo '<b><font color="blue">'.html_format($a).'</font></b>';
} else if ($c == '+') {
echo '<font color="green">'.html_format($a).'</font>';
} else if ($c == '-') {
echo '<font color="red">'.html_format($a).'</font>';
} else {
echo html_format($a);
}
echo "</code><br />\n";
}
}
 
function output_diff($fileA, $fileB, $num_lines=3) {
$fileA = realpath($fileA);
$fileB = realpath($fileB);
 
ob_start();
system("diff -wbB --ignore-blank-lines ".escapeshellarg($fileA)." ".escapeshellarg($fileB));
$cont = ob_get_contents();
ob_end_clean();
 
$ary = explode("\n", $cont);
foreach ($ary as $n => $a) {
$c = substr($a, 0, 1);
 
echo '<code>';
if (($c == '>') || ($c == '<')) {
echo '<b><font color="blue">'.html_format($c).'</font></b>'.html_format(substr($a, 1));
} else {
echo '<b><font color="blue">'.html_format($a).'</font></b>';
}
echo "</code><br />\n";
}
}
 
function html_format($x) {
$x = htmlentities($x);
$x = str_replace("\t", str_repeat(' ', TABS_WS), $x);
$x = str_replace(' ', '&nbsp;', $x);
return $x;
}
 
?>
/trunk/gmp_supplement.inc.php
0,0 → 1,829
<?php
 
/*
* PHP GMP-Supplement implemented using BCMath
* Copyright 2020-2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-21
*
* Licensed 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.
*/
 
if (function_exists('bcadd')) {
// ----------------- Implementation of GMP functions using BCMath -----------------
 
if (!function_exists('gmp_init') ) {
define('GMP_ROUND_ZERO', 0);
define('GMP_ROUND_PLUSINF', 1);
define('GMP_ROUND_MINUSINF', 2);
define('GMP_MSW_FIRST', 1);
define('GMP_LSW_FIRST', 2);
define('GMP_LITTLE_ENDIAN', 4);
define('GMP_BIG_ENDIAN', 8);
define('GMP_NATIVE_ENDIAN', 16);
define('GMP_VERSION', '6.0.0');
 
// gmp_abs ( GMP $a ) : GMP
// Absolute value
function gmp_abs($a) {
bcscale(0);
if (bccomp($a, "0") == 1) {
return $a;
} else {
return bcmul($a, "-1");
}
}
 
// gmp_add ( GMP $a , GMP $b ) : GMP
// Add numbers
function gmp_add($a, $b) {
bcscale(0);
 
// bcadd ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
return bcadd($a, $b);
}
 
// gmp_and ( GMP $a , GMP $b ) : GMP
// Bitwise AND
function gmp_and($a, $b) {
bcscale(0);
// Convert $a and $b to a binary string
$ab = bc_dec2bin($a);
$bb = bc_dec2bin($b);
$length = max(strlen($ab), strlen($bb));
$ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
$bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
 
// Do the bitwise binary operation
$cb = '';
for ($i=0; $i<$length; $i++) {
$cb .= (($ab[$i] == 1) and ($bb[$i] == 1)) ? '1' : '0';
}
 
// Convert back to a decimal number
return bc_bin2dec($cb);
}
 
// gmp_binomial ( mixed $n , int $k ) : GMP
// Calculates binomial coefficient
function gmp_binomial($n, $k) {
bcscale(0);
throw new Exception("gmp_binomial() NOT IMPLEMENTED");
}
 
// gmp_clrbit ( GMP $a , int $index ) : void
// Clear bit
function gmp_clrbit(&$a, $index) {
bcscale(0);
gmp_setbit($a, $index, false);
}
 
// gmp_cmp ( GMP $a , GMP $b ) : int
// Compare numbers
function gmp_cmp($a, $b) {
bcscale(0);
 
// bccomp ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : int
return bccomp($a, $b);
}
 
// gmp_com ( GMP $a ) : GMP
// Calculates one's complement
function gmp_com($a) {
bcscale(0);
// Convert $a and $b to a binary string
$ab = bc_dec2bin($a);
 
// Swap every bit
for ($i=0; $i<strlen($ab); $i++) {
$ab[$i] = ($ab[$i] == '1' ? '0' : '1');
}
 
// Convert back to a decimal number
return bc_bin2dec($ab);
}
 
// gmp_div_q ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
// Divide numbers
function gmp_div_q($a, $b, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
bcscale(0);
 
// bcdiv ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
return bcdiv($a, $b);
}
 
// Divide numbers and get quotient and remainder
// gmp_div_qr ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : array
function gmp_div_qr($n, $d, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
bcscale(0);
return array(
gmp_div_q($n, $d, $round),
gmp_div_r($n, $d, $round)
);
}
 
// Remainder of the division of numbers
// gmp_div_r ( GMP $n , GMP $d [, int $round = GMP_ROUND_ZERO ] ) : GMP
function gmp_div_r($n, $d, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
bcscale(0);
// The remainder operator can be used with negative integers. The rule is:
// - Perform the operation as if both operands were positive.
// - If the left operand is negative, then make the result negative.
// - If the left operand is positive, then make the result positive.
// - Ignore the sign of the right operand in all cases.
$r = bcmod($n, $d);
if (bccomp($n, "0") < 0) $r = bcmul($r, "-1");
return $r;
}
 
// gmp_div ( GMP $a , GMP $b [, int $round = GMP_ROUND_ZERO ] ) : GMP
// Divide numbers
function gmp_div($a, $b, $round = GMP_ROUND_ZERO/*$round not implemented*/) {
bcscale(0);
return gmp_div_q($a, $b, $round); // Alias von gmp_div_q
}
 
// gmp_divexact ( GMP $n , GMP $d ) : GMP
// Exact division of numbers
function gmp_divexact($n, $d) {
bcscale(0);
return bcdiv($n, $d);
}
 
// gmp_export ( GMP $gmpnumber [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : string
// Export to a binary string
function gmp_export($gmpnumber, $word_size = 1, $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) {
if ($word_size != 1) throw new Exception("Word size != 1 not implemented");
if ($options != GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) throw new Exception("Different options not implemented");
 
bcscale(0);
$gmpnumber = bcmul($gmpnumber,"1"); // normalize
return gmp_init(bin2hex($gmpnumber), 16);
}
 
// gmp_fact ( mixed $a ) : GMP
// Factorial
function gmp_fact($a) {
bcscale(0);
return bcfact($a);
}
 
// gmp_gcd ( GMP $a , GMP $b ) : GMP
// Calculate GCD
function gmp_gcd($a, $b) {
bcscale(0);
return gmp_gcdext($a, $b)['g'];
}
 
// gmp_gcdext ( GMP $a , GMP $b ) : array
// Calculate GCD and multipliers
function gmp_gcdext($a, $b) {
bcscale(0);
 
// Source: https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Math/BigInteger/Engines/BCMath.php#L285
// modified to make it fit here and to be compatible with gmp_gcdext
 
$s = '1';
$t = '0';
$s_ = '0';
$t_ = '1';
 
while (bccomp($b, '0', 0) != 0) {
$q = bcdiv($a, $b, 0);
 
$temp = $a;
$a = $b;
$b = bcsub($temp, bcmul($b, $q, 0), 0);
 
$temp = $s;
$s = $s_;
$s_ = bcsub($temp, bcmul($s, $q, 0), 0);
 
$temp = $t;
$t = $t_;
$t_ = bcsub($temp, bcmul($t, $q, 0), 0);
}
 
return [
'g' => /*$this->normalize*/($a),
's' => /*$this->normalize*/($s),
't' => /*$this->normalize*/($t)
];
}
 
// gmp_hamdist ( GMP $a , GMP $b ) : int
// Hamming distance
function gmp_hamdist($a, $b) {
bcscale(0);
throw new Exception("gmp_hamdist() NOT IMPLEMENTED");
}
 
// gmp_import ( string $data [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : GMP
// Import from a binary string
function gmp_import($data, $word_size=1, $options=GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) {
bcscale(0);
 
if ($word_size != 1) throw new Exception("Word size != 1 not implemented");
if ($options != GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) throw new Exception("Different options not implemented");
 
return gmp_init(hex2bin(gmp_strval(gmp_init($data), 16)));
}
 
// gmp_init ( mixed $number [, int $base = 0 ] ) : GMP
// Create GMP number
function gmp_init($number, $base=0) {
bcscale(0);
if ($base == 0) {
// If base is 0 (default value), the actual base is determined from the leading characters:
// if the first two characters are 0x or 0X, hexadecimal is assumed,
// otherwise if the first character is "0", octal is assumed,
// otherwise decimal is assumed.
if (strtoupper(substr($number, 0, 2)) == '0X') {
$base = 16;
} else if (strtoupper(substr($number, 0, 1)) == '0') {
$base = 8;
} else {
$base = 10;
}
}
 
if ($base == 10) {
return $number;
} else {
return base_convert_bigint($number, $base, 10);
}
}
 
// gmp_intval ( GMP $gmpnumber ) : int
// Convert GMP number to integer
function gmp_intval($gmpnumber) {
bcscale(0);
return (int)$gmpnumber;
}
 
// gmp_invert ( GMP $a , GMP $b ) : GMP
// Inverse by modulo
function gmp_invert($a, $b) {
bcscale(0);
 
// Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L246
 
while (bccomp($a, '0')==-1) {
$a=bcadd($b, $a);
}
while (bccomp($b, $a)==-1) {
$a=bcmod($a, $b);
}
$c=$a;
$d=$b;
$uc=1;
$vc=0;
$ud=0;
$vd=1;
while (bccomp($c, '0')!=0) {
$temp1=$c;
$q=bcdiv($d, $c, 0);
$c=bcmod($d, $c);
$d=$temp1;
$temp2=$uc;
$temp3=$vc;
$uc=bcsub($ud, bcmul($q, $uc));
$vc=bcsub($vd, bcmul($q, $vc));
$ud=$temp2;
$vd=$temp3;
}
$result='';
if (bccomp($d, '1')==0) {
if (bccomp($ud, '0')==1) {
$result=$ud;
} else {
$result=bcadd($ud, $b);
}
} else {
throw new ErrorException("ERROR: $a and $b are NOT relatively prime.");
}
return $result;
}
 
// gmp_jacobi ( GMP $a , GMP $p ) : int
// Jacobi symbol
function gmp_jacobi($a, $p) {
bcscale(0);
 
// Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L136
 
if ($p>=3 && $p%2==1) {
$a = bcmod($a, $p);
if ($a == '0') return '0';
if ($a == '1') return '1';
$a1 = $a;
$e = 0;
while (bcmod($a1, '2') == '0') {
$a1 = bcdiv($a1, '2');
$e = bcadd($e, '1');
}
$s = (bcmod($e, '2')=='0' || bcmod($p, '8')=='1' || bcmod($p, '8')=='7') ? '1' : '-1';
if ($a1 == '1') return $s;
if (bcmod($p, '4')=='3' && bcmod($a1, '4')=='3') $s = -$s;
return bcmul($s, (string)gmp_jacobi(bcmod($p, $a1), $a1));
} else {
return false;
}
}
 
// gmp_kronecker ( mixed $a , mixed $b ) : int
// Kronecker symbol
function gmp_kronecker($a, $b) {
bcscale(0);
throw new Exception("gmp_kronecker() NOT IMPLEMENTED");
}
 
// gmp_lcm ( mixed $a , mixed $b ) : GMP
// Calculate LCM
function gmp_lcm($a, $b) {
bcscale(0);
 
if ((bccomp($a,'0')==0) && (bccomp($b,'0')==0)) {
return '0';
} else {
return gmp_div(gmp_abs(gmp_mul($a,$b)), gmp_gcd($a,$b));
}
}
 
// gmp_legendre ( GMP $a , GMP $p ) : int
// Legendre symbol
function gmp_legendre($a, $p) {
bcscale(0);
throw new Exception("gmp_legendre() NOT IMPLEMENTED");
}
 
// gmp_mod ( GMP $n , GMP $d ) : GMP
// Modulo operation
function gmp_mod($n, $d) {
bcscale(0);
 
// bcmod ( string $dividend , string $divisor [, int $scale = 0 ] ) : string
return bcmod($n, $d);
}
 
// gmp_mul ( GMP $a , GMP $b ) : GMP
// Multiply numbers
function gmp_mul($a, $b) {
bcscale(0);
 
// bcmul ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
return bcmul($a, $b);
}
 
// gmp_neg ( GMP $a ) : GMP
// Negate number
function gmp_neg($a) {
bcscale(0);
return bcmul($a, "-1");
}
 
// gmp_nextprime ( int $a ) : GMP
// Find next prime number
function gmp_nextprime($a) {
bcscale(0);
 
// Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L692
 
if (bccomp($a, '2') == '-1') {
return '2';
}
$result = gmp_or(bcadd($a, '1'), '1');
while (!gmp_prob_prime($result)) {
$result = bcadd($result, '2');
}
return $result;
}
 
// gmp_or ( GMP $a , GMP $b ) : GMP
// Bitwise OR
function gmp_or($a, $b) {
bcscale(0);
// Convert $a and $b to a binary string
$ab = bc_dec2bin($a);
$bb = bc_dec2bin($b);
$length = max(strlen($ab), strlen($bb));
$ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
$bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
 
// Do the bitwise binary operation
$cb = '';
for ($i=0; $i<$length; $i++) {
$cb .= (($ab[$i] == 1) or ($bb[$i] == 1)) ? '1' : '0';
}
 
// Convert back to a decimal number
return bc_bin2dec($cb);
}
 
// gmp_perfect_power ( mixed $a ) : bool
// Perfect power check
function gmp_perfect_power($a) {
bcscale(0);
throw new Exception("gmp_perfect_power() NOT IMPLEMENTED");
}
 
// gmp_perfect_square ( GMP $a ) : bool
// Perfect square check
function gmp_perfect_square($a) {
bcscale(0);
throw new Exception("gmp_perfect_square() NOT IMPLEMENTED");
}
 
// gmp_popcount ( GMP $a ) : int
// Population count
function gmp_popcount($a) {
bcscale(0);
$ab = bc_dec2bin($a);
return substr_count($ab, '1');
}
 
// gmp_pow ( GMP $base , int $exp ) : GMP
// Raise number into power
function gmp_pow($base, $exp) {
bcscale(0);
 
// bcpow ( string $base , string $exponent [, int $scale = 0 ] ) : string
return bcpow($base, $exp);
}
 
// gmp_powm ( GMP $base , GMP $exp , GMP $mod ) : GMP
// Raise number into power with modulo
function gmp_powm($base, $exp, $mod) {
bcscale(0);
 
// bcpowmod ( string $base , string $exponent , string $modulus [, int $scale = 0 ] ) : string
return bcpowmod($base, $exp, $mod);
}
 
// gmp_prob_prime ( GMP $a [, int $reps = 10 ] ) : int
// Check if number is "probably prime"
function gmp_prob_prime($a, $reps=10) {
bcscale(0);
 
// Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L655
 
$t = 40;
$k = 0;
$m = bcsub($reps, '1');
while (bcmod($m, '2') == '0') {
$k = bcadd($k, '1');
$m = bcdiv($m, '2');
}
for ($i=0; $i<$t; $i++) {
$a = bcrand('1', bcsub($reps, '1'));
if ($m < 0) {
return new ErrorException("Negative exponents ($m) not allowed");
} else {
$b0 = bcpowmod($a, $m, $reps);
}
if ($b0!=1 && $b0!=bcsub($reps, '1')) {
$j = 1;
while ($j<=$k-1 && $b0!=bcsub($reps, '1')) {
$b0 = bcpowmod($b0, '2', $reps);
if ($b0 == 1) {
return false;
}
$j++;
}
if ($b0 != bcsub($reps, '1')) {
return false;
}
}
}
return true;
}
 
// gmp_random_bits ( int $bits ) : GMP
// Random number
function gmp_random_bits($bits) {
bcscale(0);
$min = 0;
$max = bcsub(bcpow('2', $bits), '1');
return bcrand($min, $max);
}
 
// gmp_random_range ( GMP $min , GMP $max ) : GMP
// Random number
function gmp_random_range($min, $max) {
bcscale(0);
return bcrand($min, $max);
}
 
// gmp_random_seed ( mixed $seed ) : void
// Sets the RNG seed
function gmp_random_seed($seed) {
bcscale(0);
bcrand_seed($seed);
}
 
// gmp_random ([ int $limiter = 20 ] ) : GMP
// Random number (deprecated)
function gmp_random($limiter=20) {
bcscale(0);
throw new Exception("gmp_random() is deprecated! Please use gmp_random_bits() or gmp_random_range() instead.");
}
 
// gmp_root ( GMP $a , int $nth ) : GMP
// Take the integer part of nth root
function gmp_root($a, $nth) {
bcscale(0);
throw new Exception("gmp_root() NOT IMPLEMENTED");
}
 
// gmp_rootrem ( GMP $a , int $nth ) : array
// Take the integer part and remainder of nth root
function gmp_rootrem($a, $nth) {
bcscale(0);
throw new Exception("gmp_rootrem() NOT IMPLEMENTED");
}
 
// gmp_scan0 ( GMP $a , int $start ) : int
// Scan for 0
function gmp_scan0($a, $start) {
bcscale(0);
 
$ab = bc_dec2bin($a);
 
if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
if ($start >= strlen($ab)) return $start;
 
for ($i=$start; $i<strlen($ab); $i++) {
if ($ab[strlen($ab)-1-$i] == '0') {
return $i;
}
}
 
return false;
}
 
// gmp_scan1 ( GMP $a , int $start ) : int
// Scan for 1
function gmp_scan1($a, $start) {
bcscale(0);
 
$ab = bc_dec2bin($a);
 
if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero");
if ($start >= strlen($ab)) return -1;
 
for ($i=$start; $i<strlen($ab); $i++) {
if ($ab[strlen($ab)-1-$i] == '1') {
return $i;
}
}
 
return false;
}
 
// gmp_setbit ( GMP $a , int $index [, bool $bit_on = TRUE ] ) : void
// Set bit
function gmp_setbit(&$a, $index, $bit_on=TRUE) {
bcscale(0);
$ab = bc_dec2bin($a);
 
if ($index < 0) throw new Exception("Invalid index");
if ($index >= strlen($ab)) {
$ab = str_pad($ab, $index+1, '0', STR_PAD_LEFT);
}
 
$ab[strlen($ab)-1-$index] = $bit_on ? '1' : '0';
 
$a = bc_bin2dec($ab);
}
 
// gmp_sign ( GMP $a ) : int
// Sign of number
function gmp_sign($a) {
bcscale(0);
return bccomp($a, "0");
}
 
// gmp_sqrt ( GMP $a ) : GMP
// Calculate square root
function gmp_sqrt($a) {
bcscale(0);
 
// bcsqrt ( string $operand [, int $scale = 0 ] ) : string
return bcsqrt($a);
}
 
// gmp_sqrtrem ( GMP $a ) : array
// Square root with remainder
function gmp_sqrtrem($a) {
bcscale(0);
throw new Exception("gmp_sqrtrem() NOT IMPLEMENTED");
}
 
// gmp_strval ( GMP $gmpnumber [, int $base = 10 ] ) : string
// Convert GMP number to string
function gmp_strval($gmpnumber, $base=10) {
bcscale(0);
if ($base == 10) {
return $gmpnumber;
} else {
return base_convert_bigint($gmpnumber, 10, $base);
}
}
 
// gmp_sub ( GMP $a , GMP $b ) : GMP
// Subtract numbers
function gmp_sub($a, $b) {
bcscale(0);
 
// bcsub ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string
return bcsub($a, $b);
}
 
// gmp_testbit ( GMP $a , int $index ) : bool
// Tests if a bit is set
function gmp_testbit($a, $index) {
bcscale(0);
$ab = bc_dec2bin($a);
 
if ($index < 0) throw new Exception("Invalid index");
if ($index >= strlen($ab)) return ('0' == '1');
 
return $ab[strlen($ab)-1-$index] == '1';
}
 
// gmp_xor ( GMP $a , GMP $b ) : GMP
// Bitwise XOR
function gmp_xor($a, $b) {
bcscale(0);
// Convert $a and $b to a binary string
$ab = bc_dec2bin($a);
$bb = bc_dec2bin($b);
$length = max(strlen($ab), strlen($bb));
$ab = str_pad($ab, $length, "0", STR_PAD_LEFT);
$bb = str_pad($bb, $length, "0", STR_PAD_LEFT);
 
// Do the bitwise binary operation
$cb = '';
for ($i=0; $i<$length; $i++) {
$cb .= (($ab[$i] == 1) xor ($bb[$i] == 1)) ? '1' : '0';
}
 
// Convert back to a decimal number
return bc_bin2dec($cb);
}
}
 
// ----------------- Helper functions -----------------
 
function base_convert_bigint($numstring, $frombase, $tobase) {
$frombase_str = '';
for ($i=0; $i<$frombase; $i++) {
$frombase_str .= strtoupper(base_convert((string)$i, 10, 36));
}
 
$tobase_str = '';
for ($i=0; $i<$tobase; $i++) {
$tobase_str .= strtoupper(base_convert((string)$i, 10, 36));
}
 
$length = strlen($numstring);
$result = '';
$number = array();
for ($i = 0; $i < $length; $i++) {
$number[$i] = stripos($frombase_str, $numstring[$i]);
}
do { // Loop until whole number is converted
$divide = 0;
$newlen = 0;
for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers)
$divide = $divide * $frombase + $number[$i];
if ($divide >= $tobase) {
$number[$newlen++] = (int)($divide / $tobase);
$divide = $divide % $tobase;
} else if ($newlen > 0) {
$number[$newlen++] = 0;
}
}
$length = $newlen;
$result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character)
}
while ($newlen != 0);
 
return $result;
}
 
function bc_dec2bin($decimal_i) {
// https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
 
bcscale(0);
 
$binary_i = '';
do {
$binary_i = bcmod($decimal_i,'2') . $binary_i;
$decimal_i = bcdiv($decimal_i,'2');
} while (bccomp($decimal_i,'0'));
 
return $binary_i;
}
 
function bc_bin2dec($binary_i) {
// https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/
 
bcscale(0);
 
$decimal_i = '0';
for ($i = 0; $i < strlen($binary_i); $i++) {
$decimal_i = bcmul($decimal_i,'2');
$decimal_i = bcadd($decimal_i,$binary_i[$i]);
}
 
return $decimal_i;
}
 
// ----------------- New functions -----------------
 
// Newly added: gmp_not / bcnot
function bcnot($a) {
bcscale(0);
// Convert $a to a binary string
$ab = bc_dec2bin($a);
$length = strlen($ab);
 
// Do the bitwise binary operation
$cb = '';
for ($i=0; $i<$length; $i++) {
$cb .= ($ab[$i] == 1) ? '0' : '1';
}
 
// Convert back to a decimal number
return bc_bin2dec($cb);
}
function gmp_not($a) {
bcscale(0);
return bcnot($a);
}
 
// Newly added: bcshiftl / gmp_shiftl
function bcshiftl($num, $bits) {
bcscale(0);
return bcmul($num, bcpow('2', $bits));
}
function gmp_shiftl($num, $bits) {
bcscale(0);
return bcshiftl($num, $bits);
}
 
// Newly added: bcshiftr / gmp_shiftr
function bcshiftr($num, $bits) {
bcscale(0);
return bcdiv($num, bcpow('2', $bits));
}
function gmp_shiftr($num, $bits) {
bcscale(0);
return bcshiftr($num, $bits);
}
 
// Newly added: bcfact (used by gmp_fact)
function bcfact($a) {
bcscale(0);
 
// Source: https://www.php.net/manual/de/book.bc.php#116510
 
if (!filter_var($a, FILTER_VALIDATE_INT) || $a <= 0) {
throw new InvalidArgumentException(sprintf('Argument must be natural number, "%s" given.', $a));
}
 
for ($result = '1'; $a > 0; $a--) {
$result = bcmul($result, $a);
}
 
return $result;
}
 
// Newly added (used by gmp_prob_prime, gmp_random_range and gmp_random_bits)
function bcrand($min, $max = false) {
bcscale(0);
// Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/BCMathUtils.php#L7
// Fixed: https://github.com/CityOfZion/neo-php/issues/16
if (!$max) {
$max = $min;
$min = 0;
}
return bcadd(bcmul(bcdiv((string)mt_rand(), (string)mt_getrandmax(), strlen($max)), bcsub(bcadd($max, '1'), $min)), $min);
}
 
// Newly added (used by gmp_random_seed)
function bcrand_seed($seed) {
bcscale(0);
mt_srand($seed);
}
}
/trunk/googlecache.inc.php
0,0 → 1,39
<?php
 
/*
* Google GetCache
* Copyright 2015 Daniel Marschall, ViaThinkSoft
* Version 2015-06-26
*
* Licensed 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.
*/
 
function google_getcache($url) {
$options = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"Cookie: foo=bar\r\n" . // check function.stream-context-create on php.net
"User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
)
);
 
$context = stream_context_create($options);
 
$url = 'https://www.google.de/search?q='.urlencode($url);
$cont = file_get_contents($url, false, $context);
preg_match_all('@(http://webcache.googleusercontent.com/.+)"@ismU', $cont, $m);
if (!isset($m[1][0])) return false;
$url = urldecode($m[1][0]);
return file_get_contents($url, false, $context);
}
/trunk/grep_funcs.inc.php
0,0 → 1,43
<?php
 
/*
* Grep functions for PHP
* Copyright 2012-2013 Daniel Marschall, ViaThinkSoft
* Version 2013-03-08
*
* Licensed 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.
*/
 
# TODO: if console available, use it
 
// "grep"
function grep(&$array, $substr) {
if (!is_array($array)) return false;
$ret = array();
foreach ($array as &$a) {
if (strpos($a, $substr) !== false) $ret[] = $a;
}
return $ret;
}
 
// "grep -v"
function antigrep(&$array, $substr) {
if (!is_array($array)) return false;
$ret = array();
foreach ($array as &$a) {
if (strpos($a, $substr) === false) $ret[] = $a;
}
return $ret;
}
 
?>
/trunk/ip_functions.inc.php
0,0 → 1,61
<?php
 
/*
* IP functions
* Copyright 2015 Daniel Marschall, ViaThinkSoft
* Version 2015-10-27
*
* Licensed 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.
*/
 
function get_real_ip() {
/* Eindeutige IP Adresse erhalten, auch bei Proxies und (neu:) von SSH connections im CLI modus */
// http://lists.phpbar.de/pipermail/php/Week-of-Mon-20040322/007749.html
// Modificated by VTS
// Version: 2015-10-27
 
// TODO: ipv6
 
if (isset($_SERVER['SSH_CLIENT'])) { $ary = explode(' ', $_SERVER['SSH_CLIENT']); return $ary[0]; }
if (isset($_SERVER['SSH_CONNECTION'])) { $ary = explode(' ', $_SERVER['SSH_CONNECTION']); return $ary[0]; }
 
$client_ip = (isset($_SERVER['HTTP_CLIENT_IP'])) ? $_SERVER['HTTP_CLIENT_IP'] : '';
 
// It is not secure to use these, since they are not validated: http://www.thespanner.co.uk/2007/12/02/faking-the-unexpected/
// $x_forwarded_for = (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '';
$x_forwarded_for = '';
 
$remote_addr = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : '';
 
if (!empty($client_ip)) {
$ip_expl = explode('.', $client_ip);
$referer = explode('.', $remote_addr);
if ($referer[0] != $ip_expl[0]) {
$ip = array_reverse($ip_expl);
$return = implode('.', $ip);
} else {
$return = $client_ip;
}
} else if (!empty($x_forwarded_for)) {
if (strstr($x_forwarded_for, ',')) {
$ip_expl = explode(',', $x_forwarded_for);
$return = end($ip_expl);
} else {
$return = $x_forwarded_for;
}
} else {
$return = $remote_addr;
}
unset ($client_ip, $x_forwarded_for, $remote_addr, $ip_expl);
return $return;
}
/trunk/ipresolution.inc.php
0,0 → 1,105
<?php
 
/*
* IP resolution functions
* Copyright 2012 Daniel Marschall, ViaThinkSoft
* Version 2012-02-02
*
* Licensed 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.
*/
 
/* -- Testcases --
print_r(gethostbynamel6('example.com'));
print_r(gethostbynamel6('ipv6.google.com'));
print_r(gethostbynamel6('google.de'));
print_r(gethostbynamel6('ipv6.google.de'));
print_r(gethostbynamel6('abc'));
print_r(gethostbynamel6('111.111.111.111'));
print_r(gethostbynamel6('2620::2d0:200:0:0:0:10'));
*/
 
function resolveip($host) {
return gethostbynamel6($host);
}
 
# http://www.php.net/manual/en/function.gethostbyname.php#70936
# Modified by ViaThinkSoft
 
# VTS-Modified: try_a default false -> true
function gethostbyname6($host, $try_a = /* false */ true) {
// get AAAA record for $host
// if $try_a is true, if AAAA fails, it tries for A
// the first match found is returned
// otherwise returns false
 
$dns = gethostbynamel6($host, $try_a);
if ($dns == false) {
return false;
} else {
return $dns[0];
}
}
 
# VTS-Modified: try_a default false -> true
function gethostbynamel6($host, $try_a = /* false */ true) {
# Added by VTS
$ipfilter = filter_var($host,FILTER_VALIDATE_IP);
if ($ipfilter != '') return array($ipfilter);
 
// get AAAA records for $host,
// if $try_a is true, if AAAA fails, it tries for A
// results are returned in an array of ips found matching type
// otherwise returns false
 
$dns6 = dns_get_record($host, DNS_AAAA);
if ($try_a == true) {
$dns4 = dns_get_record($host, DNS_A);
$dns = array_merge($dns4, $dns6);
} else {
$dns = $dns6;
}
$ip6 = array();
$ip4 = array();
foreach ($dns as $record) {
if ($record["type"] == "A") {
$ip4[] = $record["ip"];
}
if ($record["type"] == "AAAA") {
$ip6[] = $record["ipv6"];
}
}
 
# VTS-Modified: IP4+IP6 merged
$merged = array_merge($ip4, $ip6);
if (count($merged) < 1) {
return false;
} else {
return $merged;
}
 
if (count($ip6) < 1) {
if ($try_a == true) {
if (count($ip4) < 1) {
return false;
} else {
return $ip4;
}
} else {
return false;
}
} else {
return $ip6;
}
}
 
?>
/trunk/ipv4_functions.inc.php
0,0 → 1,667
<?php
 
/*
* IPv4 functions for PHP
* Copyright 2012-2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-21
*
* Licensed 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.
*/
 
// TODO: oop, exceptions?
 
// Very small self-test:
/*
function ipv4_selftest() {
$iv_b = ipv4_complete('1.2');
$iv_m = 20;
$r = ipv4_cidr2range($iv_b, $iv_m);
echo "$iv_b/$iv_m => $r[0] - $r[1]\n";
 
$rev = ipv4_range2cidr($r[0], $r[1]);
$rev = implode("\n", $rev);
echo "$r[0] - $r[1] => $rev [";
$ok = $rev == "$iv_b/$iv_m";
echo $ok ? 'OK' : 'Mismatch';
echo "]\n";
echo "In-CIDR-Test: ";
echo ipv4_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail';
echo "\n";
}
ipv4_selftest();
*/
 
function ipv4_cidr2range($baseip_or_cidr, $subnet='') {
# (C) 2012 ViaThinkSoft
# Version 1.1
# This function converts an CIDR notation <baseip>/<subnet> into an IPv4 address block array($low_ip, $high_ip)
 
if (strpos($baseip_or_cidr, '/') !== false) {
$tmp = explode('/', $baseip_or_cidr, 2);
$baseip_or_cidr = $tmp[0];
$subnet = $tmp[1];
unset($tmp);
}
 
if (($subnet < 0) || ($subnet > 32)) return false;
 
$maxint32 = 0xFFFFFFFF;
$netmask = $maxint32 << (32-$subnet);
$netmask = $netmask & $maxint32; // crop to 32 bits
$wildcard = $maxint32 ^ $netmask; // ~$netmask;
 
$x = ipv4_incomplete_ip2long($baseip_or_cidr) & $netmask;
$nums = $wildcard;
$low = long2ip($x);
$high = long2ip($x + $nums);
 
return array($low, $high);
}
 
function ipv4_range2cidr($baseip, $topip, $shortening=false) {
# (C) 2012 ViaThinkSoft
# Version 1.0
# This function converts an IPv4 address block into valid CIDR blocks (There may be multiple blocks!)
 
$out = array();
if (ipv4_cmp($baseip, $topip) > 0) return false;
while (ipv4_incomplete_ip2long($baseip)-1 != ipv4_incomplete_ip2long($topip)) {
$i = -1;
do {
$i++;
$range = ipv4_cidr2range($baseip, $i);
$l = $range[0];
$t = $range[1];
} while ((ipv4_cmp($l, $baseip) != 0) || (ipv4_cmp($t, $topip) > 0));
 
# Shortening: Stroke ".0" at the end
if ($shortening) $baseip = ipv4_shortening($baseip);
 
$out[] = "$baseip/$i";
$baseip = ipv4_add($t, 1);
}
return $out;
}
 
function ipv4_shortening($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return preg_replace("|(\\.0{1,3}){0,3}\$|ismU", '', $ip);
}
 
function ipv4_add($baseip, $num) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return long2ip(ipv4_incomplete_ip2long($baseip) + $num);
}
 
function ipv4_sub($baseip, $num) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return long2ip(ipv4_incomplete_ip2long($baseip) - $num);
}
 
function ipv4_cmp($a, $b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$a = ipv4_incomplete_ip2long($a);
$b = ipv4_incomplete_ip2long($b);
 
if ($a == $b) return 0;
if ($a < $b) return -1;
if ($a > $b) return 1;
}
 
function ipv4_in_cidr($haystack, $needle) {
# (C) 2012 ViaThinkSoft
# Version 1.1
 
$x = explode('/', $haystack);
$ha = ipv4_cidr2range($x[0], $x[1]);
 
$x = explode('/', $needle);
if (!isset($x[1])) $x[1] = '32'; // single IP
$ne = ipv4_cidr2range($x[0], $x[1]);
 
$ha_low = ipv4_incomplete_ip2long($ha[0]);
$ha_hig = ipv4_incomplete_ip2long($ha[1]);
$ne_low = ipv4_incomplete_ip2long($ne[0]);
$ne_hig = ipv4_incomplete_ip2long($ne[1]);
 
# HA: low[ ]high
# NE: low[ ]high
 
return ($ne_low >= $ha_low) && ($ne_hig <= $ha_hig);
}
 
function ipv4_complete($short_form) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$short_form = trim($short_form);
if ($short_form == '') return '0.0.0.0';
$c = substr_count($short_form, '.');
if ($c > 3) return false;
if ($c == 3) return $short_form;
$c = substr_count($short_form, '.');
$short_form .= str_repeat('.0', 3-$c);
return $short_form;
}
 
function ipv4_incomplete_ip2long($ip) {
# (C) 2012-2014 ViaThinkSoft
# Version 1.2
 
# return sprintf('%u', ip2long(ipv4_complete($ip)));
return sprintf('%u', ip2long(ipv4_normalize($ip)));
}
 
// IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y !
function ipv4_merge_address_blocks($data, $debug = false, $shortening = false) {
# (C) 2012-2013 ViaThinkSoft
# Version 2.2
 
if ($debug !== false) $STARTZEIT = time();
 
// 1. Convert IPs to numbers
 
$cmp_ary = array();
foreach ($data as $a => &$b) {
$a = ipv4_incomplete_ip2long($a);
$b = ipv4_incomplete_ip2long($b);
 
$cmp_ary[$a] = $b;
unset($a);
unset($b);
}
 
// 2. Sort array
 
ksort($cmp_ary);
 
// 3. Merge the blocks in an intelligent way (and remove redundant blocks)
 
# Merge overlapping blocks
# [ ]
# [ ] -> [ ]
 
# Merge neighbor blocks
# [ ][ ] -> [ ]
 
# Remove redundant blocks
# [ ] -> [ ]
# [ ]
 
$merge_count = 0;
$redundant_deleted_count = 0;
$round_count = 0;
do {
if ($debug !== false) {
$LAUFZEIT = time() - $STARTZEIT;
echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r";
}
 
$round_count++;
 
$clean = true;
 
foreach ($cmp_ary as $a => &$b) {
foreach ($cmp_ary as $x => &$y) {
// x in range [a+1..b+1] ?
if ($x<=$a) continue;
if ($x>$b+1) break;
 
// Merge
$clean = false;
if ($y>$b) {
$merge_count++;
$b = $y;
unset($cmp_ary[$x]);
} else {
$redundant_deleted_count++;
unset($cmp_ary[$x]);
}
}
}
} while (!$clean);
 
if ($debug !== false) {
$LAUFZEIT = time() - $STARTZEIT;
echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n";
}
 
// 4. Convert back to IPs
 
$out_ary = array();
foreach ($cmp_ary as $a => &$b) {
$a = long2ip($a);
$b = long2ip($b);
if ($shortening) {
$a = ipv4_shortening($a);
$b = ipv4_shortening($b);
}
$out_ary[$a] = $b;
}
 
return $out_ary;
}
 
function ipv4_merge_arrays($data_a, $data_b) {
# (C) 2012 ViaThinkSoft
# Version 1.2
 
$normalized_data_a = array();
foreach ($data_a as $from => &$to) {
$normalized_data_a[ipv4_normalize($from)] = ipv4_normalize($to);
}
 
$normalized_data_b = array();
foreach ($data_b as $from => &$to) {
$normalized_data_b[ipv4_normalize($from)] = ipv4_normalize($to);
}
 
$data = array();
 
foreach ($normalized_data_a as $from => &$to) {
if (isset($normalized_data_b[$from])) {
$data[$from] = ipv4_max($to, $normalized_data_b[$from]);
} else {
$data[$from] = $to;
}
}
 
foreach ($normalized_data_b as $from => &$to) {
if (!isset($normalized_data_a[$from])) {
$data[$from] = $to;
}
}
 
return $data;
}
 
function ipv4_valid($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
# return ipv4_incomplete_ip2long($ip) !== false;
return ip2long($ip) !== false;
}
 
function ipv4_normalize($ip) {
# (C) 2012-2013 ViaThinkSoft
# Version 1.1.1
 
# Example:
# 100.010.001.000 -> 100.10.1.0
 
$ip = ipv4_complete($ip);
if (!$ip) return false;
 
# ip2long buggy: 001.0.0.0 is not accepted
## $cry = explode('.', $ip);
## $cry[0] = preg_replace('@^0+@', '', $cry[0]); if ($cry[0] == '') $cry[0] = '0';
## $cry[1] = preg_replace('@^0+@', '', $cry[1]); if ($cry[1] == '') $cry[1] = '0';
## $cry[2] = preg_replace('@^0+@', '', $cry[2]); if ($cry[2] == '') $cry[2] = '0';
## $cry[3] = preg_replace('@^0+@', '', $cry[3]); if ($cry[3] == '') $cry[3] = '0';
## $ip = implode('.', $cry);
## return $ip;
 
return preg_replace('@^0{0,2}([0-9]{1,3})\.0{0,2}([0-9]{1,3})\.0{0,2}([0-9]{1,3})\.0{0,2}([0-9]{1,3})$@', '\\1.\\2.\\3.\\4', $ip);
}
 
function ipv4_expand($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
# Example:
# 100.10.1.0 -> 100.010.001.000
 
$ip = ipv4_complete($ip);
if (!$ip) return false;
 
$cry = explode('.', $ip);
$cry[0] = str_pad($cry[0], 3, '0', STR_PAD_LEFT);
$cry[1] = str_pad($cry[1], 3, '0', STR_PAD_LEFT);
$cry[2] = str_pad($cry[2], 3, '0', STR_PAD_LEFT);
$cry[3] = str_pad($cry[3], 3, '0', STR_PAD_LEFT);
return implode('.', $cry);
}
 
function ipv4_min($ip_a, $ip_b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
if (ipv4_cmp($ip_a, $ip_b) == -1) {
return $ip_a;
} else {
return $ip_b;
}
}
 
function ipv4_max($ip_a, $ip_b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
if (ipv4_cmp($ip_a, $ip_b) == 1) {
return $ip_a;
} else {
return $ip_b;
}
}
 
function ipv4_ipcount($data) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$cnt = 0;
 
foreach ($data as $from => &$to) {
$cnt += ipv4_incomplete_ip2long($to) - ipv4_incomplete_ip2long($from);
}
 
return $cnt;
}
 
function ipv4_read_file($file) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$data = array();
 
$lines = file($file);
foreach ($lines as &$line) {
$rng = ipv4_line2range($line);
$data[$rng[0]] = $rng[1];
}
 
return $data;
}
 
function ipv4_line2range($line) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$line = trim($line);
 
if (strpos($line, '/') !== false) {
$rng = ipv4_cidr2range($line);
} else {
$rng = explode('-', $line);
$rng[0] = trim($rng[0]);
$rng[1] = trim($rng[1]);
$rng[0] = ipv4_normalize($rng[0]);
if (!isset($rng[1])) $rng[1] = $rng[0];
$rng[1] = ipv4_normalize($rng[1]);
}
 
return $rng;
}
 
# --- New 16,12,12
 
define('IPV4_BITS', 32);
 
function ipv4_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) {
$ary = ipv4_cidr_split($ipOrCIDR_Searchterm);
$ip = $ary[0];
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {
return false;
}
 
$ary = ipv4_cidr_split($ipOrCIDR_Candidate);
$ip = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {
return false;
}
 
$x = ipv4_trackdown($ipOrCIDR_Searchterm);
 
if (ipv4_in_cidr($x[0], $ip.'/'.$cidr_bits)) {
$ary = ipv4_cidr_split($x[0]);
$cidr_bits2 = $ary[1];
if ($cidr_bits2 > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
return $cidr_bits2-$cidr_bits;
}
 
$i = 0;
$max = false;
foreach ($x as &$y) {
if (ipv4_in_cidr($ip.'/'.$cidr_bits, $y)) {
$max = $i;
}
$i++;
}
 
return $max;
}
 
function ipv4_cidr_split($ipOrCIDR) {
$ary = explode('/', $ipOrCIDR, 2);
$cidr_bits = isset($ary[1]) ? $ary[1] : IPV4_BITS;
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
$ip = $ary[0];
return array($ip, $cidr_bits);
}
 
function ipv4_equals($ipOrCIDRA, $ipOrCIDRB) {
return ipv4_normalize_range($ipOrCIDRA) == ipv4_normalize_range($ipOrCIDRB);
}
 
function ipv4_cidr_min_ip($ipOrCIDR) {
$ary = ipv4_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV4_BITS-$cidr_bits);
 
return bin2ip($m);
}
 
function ipv4_cidr_max_ip($ipOrCIDR) {
$ary = ipv4_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV4_BITS-$cidr_bits);
 
return bin2ip($m);
}
 
function ipv4_normalize_range($ipOrCIDR) {
$ary = ipv4_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV4_BITS-$cidr_bits);
 
return bin2ip($m) . '/' . $cidr_bits;
}
 
function ipv4_trackdown($ipOrCIDR) {
$ary = ipv4_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV4_BITS) return false; // throw new Exception('CIDR bits > '.IPV4_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$out = array();
$m = ip2bin($ipOrCIDR);
 
for ($i=$cidr_bits; $i>=0; $i--) {
$m = substr($m, 0, $i) . str_repeat('0', IPV4_BITS-$i);
$out[] = bin2ip($m) . '/' . $i;
}
 
return $out;
}
 
# ---
 
if (!function_exists('ip2bin')) {
function ip2bin($ip) {
# Source: http://php.net/manual/en/function.ip2long.php#104163
# modified by VTS
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
$iplong = ip2long($ip);
assert($iplong !== false);
return base_convert((string)$iplong, 10, 2);
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
return false;
}
if (($ip_n = inet_pton($ip)) === false) {
return false;
}
$bits = 15; // 16 x 8 bit = 128bit (ipv6)
$ipbin = ''; # added by vts to avoid warning
while ($bits >= 0) {
$bin = sprintf('%08b', (ord($ip_n[$bits])));
$ipbin = $bin.$ipbin;
$bits--;
}
return $ipbin;
}
}
 
if (!function_exists('bin2ip')) {
function bin2ip($bin) {
# Source: http://php.net/manual/en/function.ip2long.php#104163
# modified by VTS
 
if (strlen($bin) <= 32) { // 32bits (ipv4)
$iplong = base_convert($bin, 2, 10);
return long2ip(intval($iplong));
}
if (strlen($bin) != 128) {
return false;
}
$pad = 128 - strlen($bin);
for ($i = 1; $i <= $pad; $i++) {
$bin = '0'.$bin;
}
$bits = 0;
$ipv6 = ''; # added by vts to avoid warning
while ($bits <= 7) {
$bin_part = substr($bin,($bits*16),16);
$ipv6 .= dechex(bindec($bin_part)) . ':';
$bits++;
}
return inet_ntop(inet_pton(substr($ipv6, 0, -1)));
}
}
 
# --- TEST
 
/*
assert(ipv4_normalize('100.010.001.000') == '100.10.1.0');
assert(ipv4_normalize('100.010.01.000') == '100.10.1.0');
assert(ipv4_normalize('100.10.001.000') == '100.10.1.0');
assert(ipv4_normalize('1.010.001.000') == '1.10.1.0');
assert(ipv4_normalize('1.10.001.000') == '1.10.1.0');
 
assert(ipv4_distance('192.168.0.0/16', '192.168.64.0/18') == -2);
assert(ipv4_distance('192.168.0.0/17', '192.168.64.0/18') == -1);
assert(ipv4_distance('192.168.64.0/18', '192.168.64.0/18') == 0);
assert(ipv4_distance('192.168.64.0/19', '192.168.64.0/18') == 1);
assert(ipv4_distance('192.168.64.0/20', '192.168.64.0/18') == 2);
 
assert(ipv4_distance('192.168.69.202/31', '192.168.69.200/31') === false);
assert(ipv4_distance('192.168.69.201/32', '192.168.69.200/32') === false);
assert(ipv4_distance('192.168.69.201', '192.168.69.200') === false);
*/
 
/*
$test = '192.168.69.123';
$x = ipv4_trackdown($test);
foreach ($x as &$cidr) {
$min = ipv4_cidr_min_ip($cidr);
$max = ipv4_cidr_max_ip($cidr);
echo "$cidr ($min - $max)\n";
}
*/
 
 
 
 
function ipv4_sort($ary) {
$f = array();
foreach ($ary as $c) {
$a = explode('/', $c);
$ip = $a[0];
$bits = isset($a[1]) ? $a[1] : 32;
 
$d = ip2bin($ip);
 
# ord('*') must be smaller than ord('0')
$d = substr($d, 0, $bits).str_repeat('*', 32-$bits);
 
$f[$d] = $c;
}
 
return $f;
}
 
function ipv4_make_tree($ary) {
$ary = ipv4_sort($ary);
 
if (count($ary) == 0) return array();
 
$sub_begin = '';
$sub_begin_ip = '';
foreach ($ary as $n => $d) {
$sub_begin = substr($n, 0, strpos($n, '*'));
$sub_begin_ip = $d;
unset($ary[$n]);
break;
}
 
$sub = array();
$nonsub = array();
foreach ($ary as $n => $d) {
if (substr($n, 0, strlen($sub_begin)) == $sub_begin) {
$sub[$n] = $d;
} else {
$nonsub[$n] = $d;
}
}
 
$out = array();
$out[$sub_begin_ip] = ipv4_make_tree($sub);
 
$a = ipv4_make_tree($nonsub);
 
$out = array_merge($out, $a);
 
return $out;
}
 
/trunk/ipv6_functions.inc.php
0,0 → 1,769
<?php
 
/*
* IPv6 functions for PHP
* Copyright 2012-2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-21
*
* Licensed 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.
*/
 
# This library requires either the GMP extension (or BCMath if gmp_supplement.inc.php is present)
 
// TODO: oop, exceptions?
// TODO: variant without gmp ?
// TODO: IPv6 resolution 'ffff::192.168.69.1' -> 'ffff:0000:0000:0000:0000:0000:c0a8:4501' does not work!
 
if (file_exists(__DIR__ . '/gmp_supplement.inc.php')) include_once __DIR__ . '/gmp_supplement.inc.php';
 
define('GMP_ONE', gmp_init('1'));
 
// Very small self-test:
/*
function ipv6_selftest() {
$iv_b = 'c0ff:ee00::';
$iv_m = 32;
$r = ipv6_cidr2range($iv_b, $iv_m);
echo "$iv_b/$iv_m => $r[0] - $r[1]\n";
 
$rev = ipv6_range2cidr($r[0], $r[1]);
$rev = implode("\n", $rev);
echo "$r[0] - $r[1] => $rev [";
$ok = $rev == "$iv_b/$iv_m";
echo $ok ? 'OK' : 'Mismatch';
echo "]\n";
echo "In-CIDR-Test: ";
echo ipv6_in_cidr("$iv_b/$iv_m", "$iv_b/$iv_m") ? 'OK' : 'Fail';
echo "\n";
}
ipv6_selftest();
*/
 
$cache_ipv6_cidr2range = array();
function ipv6_cidr2range($baseip_or_cidr, $subnet='') {
# (C) 2012 ViaThinkSoft
# Version 1.1
# This function converts an CIDR notation <baseip>/<subnet> into an IPv6 address block array($low_ip, $high_ip)
 
global $cache_ipv6_cidr2range;
$vvv = $baseip_or_cidr.'|'.$subnet;
if (isset($cache_ipv6_cidr2range[$vvv])) return $cache_ipv6_cidr2range[$vvv];
 
if (strpos($baseip_or_cidr, '/') !== false) {
$tmp = explode('/', $baseip_or_cidr, 2);
$baseip_or_cidr = $tmp[0];
$subnet = $tmp[1];
unset($tmp);
}
 
if (($subnet < 0) || ($subnet > 128)) {
$cache_ipv6_cidr2range[$vvv] = false;
return false;
}
 
$maxint128 = gmp_sub(gmp_pow('2', 128), GMP_ONE); # TODO: GMP_TWO ?
$netmask = gmp_shiftl($maxint128, 128-$subnet);
$netmask = gmp_and($netmask, $maxint128); // crop to 128 bit
$wildcard = gmp_xor($maxint128, $netmask);
 
$x = gmp_and(ip2long6($baseip_or_cidr), $netmask);
$nums = $wildcard;
$low = long2ip6($x);
$high = long2ip6(gmp_add($x, $nums));
 
$out = array($low, $high);
$cache_ipv6_cidr2range[$vvv] = $out;
return $out;
}
 
$cache_ipv6_range2cidr = array();
function ipv6_range2cidr($baseip, $topip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
# This function converts an IPv6 address block into valid CIDR blocks (There may be multiple blocks!)
 
global $cache_ipv6_range2cidr;
$vvv = $baseip.'|'.$topip;
if (isset($cache_ipv6_range2cidr[$vvv])) return $cache_ipv6_range2cidr[$vvv];
 
$out = array();
if (ipv6_cmp($baseip, $topip) > 0) {
$cache_ipv6_range2cidr[$vvv] = false;
return false;
}
while (gmp_cmp(gmp_sub(ip2long6($baseip), GMP_ONE), ip2long6($topip)) != 0) {
$i = -1;
do {
$i++;
$range = ipv6_cidr2range($baseip, $i);
$l = $range[0];
$t = $range[1];
} while ((ipv6_cmp($l, $baseip) != 0) || (ipv6_cmp($t, $topip) > 0));
 
$out[] = "$baseip/$i";
$baseip = ipv6_add($t, GMP_ONE);
}
 
$cache_ipv6_range2cidr[$vvv] = $out;
return $out;
}
 
function ipv6_add($baseip, $num) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return long2ip6(gmp_add(ip2long6($baseip), $num));
}
 
function ipv6_sub($baseip, $num) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return long2ip6(gmp_sub(ip2long6($baseip), $num));
}
 
function ipv6_cmp($a, $b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return gmp_cmp(ip2long6($a), ip2long6($b));
}
 
$cache_ipv6_in_cidr = array();
function ipv6_in_cidr($haystack, $needle) {
# (C) 2012 ViaThinkSoft
# Version 1.1
 
global $cache_ipv6_in_cidr;
$vvv = $haystack.'|'.$needle;
if (isset($cache_ipv6_in_cidr[$vvv])) return $cache_ipv6_in_cidr[$vvv];
 
$x = explode('/', $haystack);
$ha = ipv6_cidr2range($x[0], $x[1]);
 
$x = explode('/', $needle);
if (!isset($x[1])) $x[1] = 128; // single IP
$ne = ipv6_cidr2range($x[0], $x[1]);
 
$ha_low = ip2long6($ha[0]);
$ha_hig = ip2long6($ha[1]);
$ne_low = ip2long6($ne[0]);
$ne_hig = ip2long6($ne[1]);
 
# HA: low[ ]high
# NE: low[ ]high
 
$out = (gmp_cmp($ne_low, $ha_low) >= 0) && (gmp_cmp($ne_hig, $ha_hig) <= 0);
$cache_ipv6_in_cidr[$vvv] = $out;
return $out;
}
 
// IMPORTANT! $cmp_ary[x]=y MUST HAVE x<=y !
function ipv6_merge_address_blocks($data, $debug = false) {
# (C) 2012-2013 ViaThinkSoft
# Version 2.2
 
if ($debug !== false) $STARTZEIT = time();
 
// 1. Convert IPs to numbers
 
$cmp_ary = array();
foreach ($data as $a => &$b) {
$a = ip2long6($a);
$b = ip2long6($b);
 
$cmp_ary[gmp_strval($a)] = gmp_strval($b);
unset($a);
unset($b);
}
 
// 2. Sort array
 
ksort($cmp_ary);
 
// 3. Merge the blocks in an intelligent way (and remove redundant blocks)
 
# Merge overlapping blocks
# [ ]
# [ ] -> [ ]
 
# Merge neighbor blocks
# [ ][ ] -> [ ]
 
# Remove redundant blocks
# [ ] -> [ ]
# [ ]
 
$merge_count = 0;
$redundant_deleted_count = 0;
$round_count = 0;
do {
if ($debug !== false) {
$LAUFZEIT = time() - $STARTZEIT;
echo $debug."Merging... $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\r";
}
 
$round_count++;
 
$clean = true;
 
foreach ($cmp_ary as $a => &$b) {
foreach ($cmp_ary as $x => &$y) {
// x in range [a+1..b+1] ?
if (gmp_cmp(gmp_init($x), gmp_init($a)) <= 0) continue;
if (gmp_cmp(gmp_init($x), gmp_add(gmp_init($b), GMP_ONE)) > 0) break;
 
// Merge
$clean = false;
if (gmp_cmp(gmp_init($y), gmp_init($b)) > 0) {
$merge_count++;
$b = $y;
unset($cmp_ary[$x]);
} else {
$redundant_deleted_count++;
unset($cmp_ary[$x]);
}
}
}
} while (!$clean);
 
if ($debug !== false) {
$LAUFZEIT = time() - $STARTZEIT;
echo $debug."Merge completed. $round_count rounds; merged $merge_count blocks; deleted $redundant_deleted_count redundant blocks; time: $LAUFZEIT seconds\n";
}
 
// 4. Convert back to IPs
 
$out_ary = array();
foreach ($cmp_ary as $a => &$b) {
$a = long2ip6(gmp_init($a));
$b = long2ip6(gmp_init($b));
$out_ary[$a] = $b;
}
 
return $out_ary;
}
 
function ipv6_merge_arrays($data_a, $data_b) {
# (C) 2012 ViaThinkSoft
# Version 1.2
 
$normalized_data_a = array();
foreach ($data_a as $from => &$to) {
$normalized_data_a[ipv6_normalize($from)] = ipv6_normalize($to);
}
 
$normalized_data_b = array();
foreach ($data_b as $from => &$to) {
$normalized_data_b[ipv6_normalize($from)] = ipv6_normalize($to);
}
 
$data = array();
 
foreach ($normalized_data_a as $from => &$to) {
if (isset($normalized_data_b[$from])) {
$data[$from] = ipv6_max($to, $normalized_data_b[$from]);
} else {
$data[$from] = $to;
}
}
 
foreach ($normalized_data_b as $from => &$to) {
if (!isset($normalized_data_a[$from])) {
$data[$from] = $to;
}
}
 
return $data;
}
 
function ipv6_valid($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
return ip2long6($ip) !== false;
}
 
function ipv6_normalize($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
# Example:
# 2001:0000:0000::1 -> 2001::1
 
$long = ip2long6($ip);
if ($long == -1 || $long === FALSE) return false;
return long2ip6($long);
}
 
function ipv6_expand($ip) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
# Example:
# 2001::1 -> 2001:0000:0000:0000:0000:0000:0000:0000
 
$long = ip2long6($ip);
if ($long == -1 || $long === FALSE) return false;
return long2ip6($long, false);
}
 
function ipv6_min($ip_a, $ip_b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
if (ipv6_cmp($ip_a, $ip_b) == -1) {
return $ip_a;
} else {
return $ip_b;
}
}
 
function ipv6_max($ip_a, $ip_b) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
if (ipv6_cmp($ip_a, $ip_b) == 1) {
return $ip_a;
} else {
return $ip_b;
}
}
 
function ipv6_ipcount($data) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$cnt = gmp_init('0');
 
foreach ($data as $from => &$to) {
$cnt = gmp_add($cnt, gmp_sub(ip2long6($to), ip2long6($from)));
}
 
return gmp_strval($cnt, 10);
}
 
function ipv6_read_file($file) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$data = array();
 
$lines = file($file);
foreach ($lines as &$line) {
$rng = ipv6_line2range($line);
$data[$rng[0]] = $rng[1];
}
 
return $data;
}
 
function ipv6_line2range($line) {
# (C) 2012 ViaThinkSoft
# Version 1.0
 
$line = trim($line);
 
if (strpos($line, '/') !== false) {
$rng = ipv6_cidr2range($line);
} else {
$rng = explode('-', $line);
$rng[0] = trim($rng[0]);
$rng[1] = trim($rng[1]);
$rng[0] = ipv6_normalize($rng[0]);
if (!isset($rng[1])) $rng[1] = $rng[0];
$rng[1] = ipv6_normalize($rng[1]);
}
 
return $rng;
}
 
# ---
 
if (!function_exists('gmp_shiftl')) {
function gmp_shiftl($x, $n) { // shift left
// http://www.php.net/manual/en/ref.gmp.php#99788
return gmp_mul($x, gmp_pow('2', $n));
}
}
 
if (!function_exists('gmp_shiftr')) {
function gmp_shiftr($x, $n) { // shift right
// http://www.php.net/manual/en/ref.gmp.php#99788
return gmp_div($x, gmp_pow('2', $n));
}
}
 
$cache_ip2long6 = array();
function ip2long6($ipv6) {
// Source:
// http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
// Slightly modified
 
global $cache_ip2long6;
if (isset($cache_ip2long6[$ipv6])) return $cache_ip2long6[$ipv6];
 
if ($ipv6 == '') $ipv6 = '::';
 
if (filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
$cache_ip2long6[$ipv6] = false;
return false;
}
 
$ip_n = @inet_pton($ipv6);
if ($ip_n === false) {
$cache_ip2long6[$ipv6] = false;
return false; // modified
}
$bytes = 16; // 16 bytes x 8 bit/byte = 128bit
$ipv6long = '';
 
while ($bytes > 0) {
$bin = sprintf('%08b',(ord($ip_n[$bytes-1])));
$ipv6long = $bin.$ipv6long;
$bytes--;
}
 
// $out = gmp_strval(gmp_init($ipv6long, 2), 10);
$out = gmp_init($ipv6long, 2);
$cache_ip2long6[$ipv6] = $out;
return $out;
}
 
$cache_long2ip6 = array();
function long2ip6($ipv6long, $compress=true) {
// Source:
// http://www.netz-guru.de/2009/11/07/php-ipv6-ip2long-und-long2ip-funktionen/
// Slightly modified
 
global $cache_long2ip6;
$vvv = ($compress ? 'T' : 'F').$ipv6long;
if (isset($cache_long2ip6[$vvv])) return $cache_long2ip6[$vvv];
 
// $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
$bin = gmp_strval($ipv6long, 2);
if (strlen($bin) < 128) {
$pad = 128 - strlen($bin);
for ($i = 1; $i <= $pad; $i++) {
$bin = '0'.$bin;
}
}
 
$bytes = 0;
$ipv6 = '';
while ($bytes < 8) { // 16 bytes x 8 bit/byte = 128bit
$bin_part = substr($bin,($bytes*16),16);
$part = dechex(bindec($bin_part));
if (!$compress) {
$part = str_pad($part, 4, '0', STR_PAD_LEFT);
}
$ipv6 .= $part.':';
$bytes++;
}
 
if ($compress) {
$out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
} else {
$out = substr($ipv6, 0, strlen($ipv6)-1);
}
$cache_long2ip6[$vvv] = $out;
return $out;
}
 
# --- New 16,12,12
 
define('IPV6_BITS', 128);
 
$global_ipv6_distance = array();
function ipv6_distance($ipOrCIDR_Searchterm, $ipOrCIDR_Candidate) {
global $global_ipv6_distance;
$vvv = $ipOrCIDR_Searchterm.'|'.$ipOrCIDR_Candidate;
if (isset($global_ipv6_distance[$vvv])) return $global_ipv6_distance[$vvv];
 
$ary = ipv6_cidr_split($ipOrCIDR_Searchterm);
$ip = $ary[0];
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
$global_ipv6_distance[$vvv] = false;
return false;
}
 
$ary = ipv6_cidr_split($ipOrCIDR_Candidate);
$ip = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV6_BITS) {
$global_ipv6_distance[$vvv] = false;
return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
}
if (!is_numeric($cidr_bits)) return false;
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
$global_ipv6_distance[$vvv] = false;
return false;
}
 
$x = ipv6_trackdown($ipOrCIDR_Searchterm);
 
if (ipv6_in_cidr($x[0], $ip.'/'.$cidr_bits)) {
$ary = ipv6_cidr_split($x[0]);
$cidr_bits2 = $ary[1];
if ($cidr_bits2 > IPV6_BITS) {
$global_ipv6_distance[$vvv] = false;
return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
}
$out = $cidr_bits2-$cidr_bits;
$global_ipv6_distance[$vvv] = $out;
return $out;
}
 
$i = 0;
$max = false;
foreach ($x as &$y) {
if (ipv6_in_cidr($ip.'/'.$cidr_bits, $y)) {
$max = $i;
}
$i++;
}
 
$global_ipv6_distance[$vvv] = $max;
return $max;
}
 
function ipv6_cidr_split($ipOrCIDR) {
$ary = explode('/', $ipOrCIDR, 2);
$cidr_bits = isset($ary[1]) ? $ary[1] : IPV6_BITS;
if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
if (!is_numeric($cidr_bits)) return false;
$ip = $ary[0];
return array($ip, $cidr_bits);
}
 
function ipv6_equals($ipOrCIDRA, $ipOrCIDRB) {
return ipv6_normalize_range($ipOrCIDRA) == ipv6_normalize_range($ipOrCIDRB);
}
 
function ipv6_cidr_min_ip($ipOrCIDR) {
$ary = ipv6_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
 
return bin2ip($m);
}
 
function ipv6_cidr_max_ip($ipOrCIDR) {
$ary = ipv6_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('1', IPV6_BITS-$cidr_bits);
 
return bin2ip($m);
}
 
function ipv6_normalize_range($ipOrCIDR) {
# 2001:1800::1/21
# --> 2001:1800::/21
 
# 2001:1af8:4100:a061:0001::1337
# --> 2001:1af8:4100:a061:1::1337/128
 
$ary = ipv6_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$m = ip2bin($ipOrCIDR);
$m = substr($m, 0, $cidr_bits) . str_repeat('0', IPV6_BITS-$cidr_bits);
 
return bin2ip($m) . '/' . $cidr_bits;
}
 
function ipv6_trackdown($ipOrCIDR) {
$ary = ipv6_cidr_split($ipOrCIDR);
$ipOrCIDR = $ary[0];
$cidr_bits = $ary[1];
if ($cidr_bits > IPV6_BITS) return false; // throw new Exception('CIDR bits > '.IPV6_BITS);
if (!is_numeric($cidr_bits)) return false;
 
$out = array();
$m = ip2bin($ipOrCIDR);
for ($i=$cidr_bits; $i>=0; $i--) {
$m = substr($m, 0, $i) . str_repeat('0', IPV6_BITS-$i);
$out[] = bin2ip($m) . '/' . $i;
}
 
return $out;
}
 
function ipv6_sort($ary) {
$f = array();
foreach ($ary as $c) {
$a = explode('/', $c);
$ip = $a[0];
$bits = isset($a[1]) ? $a[1] : 128;
 
$d = ip2bin($ip);
 
# ord('*') must be smaller than ord('0')
$d = substr($d, 0, $bits).str_repeat('*', 128-$bits);
 
$f[$d] = $c;
}
 
return $f;
}
 
function ipv6_make_tree($ary) {
$ary = ipv6_sort($ary);
 
if (count($ary) == 0) return array();
 
$sub_begin = '';
$sub_begin_ip = '';
foreach ($ary as $n => $d) {
$sub_begin = substr($n, 0, strpos($n, '*'));
$sub_begin_ip = $d;
unset($ary[$n]);
break;
}
 
$sub = array();
$nonsub = array();
foreach ($ary as $n => $d) {
if (substr($n, 0, strlen($sub_begin)) == $sub_begin) {
$sub[$n] = $d;
} else {
$nonsub[$n] = $d;
}
}
 
$out = array();
$out[$sub_begin_ip] = ipv6_make_tree($sub);
 
$a = ipv6_make_tree($nonsub);
 
$out = array_merge($out, $a);
 
return $out;
}
 
# ---
 
if (!function_exists('ip2bin')) {
$cache_ip2bin = array();
function ip2bin($ip) {
# Source: http://php.net/manual/en/function.ip2long.php#104163
# modified by VTS
 
global $cache_ip2bin;
if (isset($cache_ip2bin[$ip])) return $cache_ip2bin[$ip];
 
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
$iplong = ip2long($ip);
assert($iplong !== false);
$out = base_convert((string)$iplong, 10, 2);
$cache_ip2bin[$ip] = $out;
return $out;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) {
$cache_ip2bin[$ip] = false;
return false;
}
if (($ip_n = inet_pton($ip)) === false) {
$cache_ip2bin[$ip] = false;
return false;
}
$bits = 15; // 16 x 8 bit = 128bit (ipv6)
$ipbin = ''; # added by vts to avoid warning
while ($bits >= 0) {
$bin = sprintf('%08b', (ord($ip_n[$bits])));
$ipbin = $bin.$ipbin;
$bits--;
}
 
$cache_ip2bin[$ip] = $ipbin;
return $ipbin;
}
}
 
if (!function_exists('bin2ip')) {
$cache_bin2ip = array();
function bin2ip($bin) {
# Source: http://php.net/manual/en/function.ip2long.php#104163
# modified by VTS
 
global $cache_bin2ip;
if (isset($cache_bin2ip[$bin])) return $cache_bin2ip[$bin];
 
if (strlen($bin) <= 32) { // 32bits (ipv4)
$iplong = base_convert($bin, 2, 10);
$out = long2ip(intval($iplong));
$cache_bin2ip[$bin] = $out;
return $out;
}
if (strlen($bin) != 128) {
$cache_bin2ip[$bin] = false;
return false;
}
$pad = 128 - strlen($bin);
for ($i = 1; $i <= $pad; $i++) {
$bin = '0'.$bin;
}
$bits = 0;
$ipv6 = ''; # added by vts to avoid warning
while ($bits <= 7) {
$bin_part = substr($bin,($bits*16),16);
$ipv6 .= dechex(bindec($bin_part)) . ':';
$bits++;
}
 
$out = inet_ntop(inet_pton(substr($ipv6, 0, -1)));
$cache_bin2ip[$bin] = $out;
return $out;
}
}
 
# --- TEST
 
/*
assert(ipv6_normalize('2001:0000:0000::1') == '2001::1');
 
assert(ipv6_distance('2001:1ae0::/27', '2001:1af8::/29') == -2);
assert(ipv6_distance('2001:1af0::/28', '2001:1af8::/29') == -1);
assert(ipv6_distance('2001:1af8::/29', '2001:1af8::/29') == 0);
assert(ipv6_distance('2001:1af8::/30', '2001:1af8::/29') == 1);
assert(ipv6_distance('2001:1af8::/31', '2001:1af8::/29') == 2);
 
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/127', '2001:1af8:4100:a061:0001::1335/127') === false);
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336/128', '2001:1af8:4100:a061:0001::1337/128') === false);
assert(ipv6_distance('2001:1af8:4100:a061:0001::1336', '2001:1af8:4100:a061:0001::1337') === false);
*/
 
/*
$test = '2001:1af8:4100:a061:0001::1337';
$x = ipv6_trackdown($test);
foreach ($x as &$cidr) {
$min = ipv6_cidr_min_ip($cidr);
$max = ipv6_cidr_max_ip($cidr);
echo "$cidr ($min - $max)\n";
}
*/
/trunk/last_weekday_date.php
0,0 → 1,11
<?php
 
function get_last_weekday_date($dow) {
for ($i=0; $i<=6; $i++) {
$d = ftime()-$i*86400;
$e = date('N', $d);
if ($e == $dow) {
return date('d.m.Y', $d);
}
}
}
/trunk/marschallHash.php
0,0 → 1,145
<?php
 
// Mastercopy of this file:
// http://www.viathinksoft.de/~daniel-marschall/code/php/marschallHash.phps
 
function MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
 
// --------------------------------------------------------------------------------
// MarschallHash: Uncrackable hash with multiple SHA1 iterations in base64 encoding
// This function is pretty slow because of the iterations, but therefore secure
// against offline attacks or rainbowtables. Also, the slowlyness of this hash
// makes the creation of rainbow tables much harder.
//
// (C)Copyright 2011 Daniel Marschall, ViaThinkSoft. All rights reserved.
// www.daniel-marschall.de / www.viathinksoft.de
//
// Notation of the hash name:
// - MHA-1987 (resp. MHA-xxxx where xxxx stands for $iterations)
// - MHAb-1987 is used for the binary output variant
// - MD5MHA-1987 is used if $password is a (unsalted!) md5-hash
// - MD5MHAb-1987 is used for the binary output variant.
//
// Default parameters:
// iteratedSalt = '' --> you should change this value to something
// user-specific (e.g. username) or something random
// iterations = 1987 --> you should ONLY change this value to a lower
// one if you have performance issues
// binary_output = no --> base64 encoding is chosen by default
//
// Format:
// - MHA has the same length as SHA1.
// - MHA in base64 format has a constant length of 27 bytes and is case sensitive.
// - MHA in binary format has a constant length of 20 bytes.
//
// Comparison 1:
// x = ''
// SHA1(x) = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' (Len = 40)
// SHA256(x) = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' (Len = 64)
// MD5(x) = 'd41d8cd98f00b204e9800998ecf8427e' (Len = 32)
// MHA(x) = 'UOLv7DgK5/4S7994FeSWZkHDJoQ' (Len = 27)
//
// Comparison 2:
// x = 'The quick brown fox jumps over the lazy dog'
// SHA1(x) = '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' (Len = 40)
// SHA256(x) = 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592' (Len = 64)
// MD5(x) = '9e107d9d372bb6826bd81d3542a419d6' (Len = 32)
// MHA(x) = 'Bqdd38sigmurBt6kU/0q99GWSnE' (Len = 27)
//
// Mechanism:
// MHA(x, iteratedSalt, iterations) = optionalBase64(rec(iterations, x, iteratedSalt))
// rec[n, x, iteratedSalt] = binarySHA1(iteratedSalt || rec[n-1, x, iteratedSalt] || iteratedSalt)
// rec[0, x, iteratedSalt] = x
//
// Recommended usage:
// - Use the username as iteratedSalt
// MHA($password, $username) == $database_hash
// - If you want to upgrade your existing user-database, e.g. already hashed with md5():
// a) Update your database:
// $database_hash_new = MHA($database_hash_old, $username)
// b) Compare with this variant:
// MHA(md5($password), $username) == $database_hash_new
// or
// MD5_MHA($password, $username) == $database_hash_new
//
// Revision: 2011-07-21 (fixed typo in 2013-03-09)
// --------------------------------------------------------------------------------
 
if ($iterations < 1) {
trigger_error('at function ' . __FUNCTION__ . ': $iterations has to be greater or equal 1', E_USER_ERROR);
return false;
}
 
$m = $password;
 
for ($i=1; $i<=$iterations; $i++) {
$m = sha1($iteratedSalt.$m.$iteratedSalt, true); // SHA1 with binary output
}
 
if (!$binary_output) {
$m = base64_encode($m);
 
// Remove single "=" at the end
# $m = str_replace('=', '', $m);
$m = substr($m, 0, 27);
}
 
return $m;
}
 
// --- The following functions are useable for database migration ---
 
function MD5_TO_MD5_MHA($md5_hash, $iteratedSalt='', $iterations=1987, $binary_output=false) {
// Use this function to migrate a (unsalted!) md5 hash into a MD5MHA hash
//
// Actually, this is just an alias of MHA()
 
return marschallHash($md5_hash, $iteratedSalt, $iterations, $binary_output);
}
 
function MD5_MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
// Use this function if you have a MD5MHA hash instead of a MHA hash
//
// MD5MHA() is equal to MHA(MD5()) where MD5() is unsalted!
 
return MHA(md5($password), $iteratedSalt, $iterations, $binary_output);
}
 
function MD5MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
// Alias of MD5_MHA()
 
return MD5_MHA($password, $iteratedSalt, $iterations, $binary_output);
}
 
function MHA_AddIterations($mha, $iteratedSalt='', $additionalIterations, $binary_output=false) {
// This function converts a MHA with x itertions into a MHA with
// x+additionalIterations iterations, if the iteratedSalt is equal.
// Use this function if you want to upgrade your database to a higher MHA strength.
//
// Example:
// MHA_AddIterations(MHA('test', 'salt', 1987), 'salt', 13) == MHA('test', 'salt', 1987+13);
//
// Of course, you cannot lower the strength of a MHA, so additionalIterations has to be >= 0.
 
// Is it Base64 input?
# if (strlen($mha) == 28) $mha = base64_decode($mha);
if (strlen($mha) == 27) $mha = base64_decode($mha);
 
// Is it now binary input?
// (Que) Will there be problems if the input string looks like multibyte?
if (strlen($mha) != 20) {
trigger_error('at function ' . __FUNCTION__ . ': does not seem to be a MHA', E_USER_ERROR);
return false;
}
 
if ($additionalIterations == 0) return $mha;
 
if ($additionalIterations < 0) {
trigger_error('at function ' . __FUNCTION__ . ': additionalIterations has to be 0 or higher.', E_USER_ERROR);
return false;
}
 
return marschallHash($mha, $iteratedSalt, $additionalIterations, $binary_output);
}
 
?>
/trunk/oid_utils.inc.php
0,0 → 1,915
<?php
 
/*
* OID-Utilities for PHP
* Copyright 2011-2021 Daniel Marschall, ViaThinkSoft
* Version 2021-05-21
*
* Licensed 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.
*/
 
// All functions in this library are compatible with leading zeroes (not recommended) and leading dots
 
// TODO: change some function names, so that they have a uniform naming schema, and rename "oid identifier" into "ASN.1 alphanumeric identifier"
// oid_id_is_valid() => asn1_alpha_id_valid()
 
define('OID_DOT_FORBIDDEN', 0);
define('OID_DOT_OPTIONAL', 1);
define('OID_DOT_REQUIRED', 2);
 
/**
* Checks if an OID has a valid dot notation.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param string $oid<br />
* An OID in dot notation.
* @param boolean $allow_leading_zeroes<br />
* true of leading zeroes are allowed or not.
* @param boolean $allow_leading_dot<br />
* true of leading dots are allowed or not.
* @return boolean true if the dot notation is valid.
**/
function oid_valid_dotnotation($oid, $allow_leading_zeroes=true, $allow_leading_dot=false, $min_len=0) {
$regex = oid_validation_regex($allow_leading_zeroes, $allow_leading_dot, $min_len);
 
$m = array();
return preg_match($regex, $oid, $m) ? true : false;
}
 
/**
* Returns a full regular expression to validate an OID in dot-notation
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param boolean $allow_leading_zeroes<br />
* true of leading zeroes are allowed or not.
* @param boolean $allow_leading_dot<br />
* true of leading dots are allowed or not.
* @return string The regular expression
**/
function oid_validation_regex($allow_leading_zeroes=true, $allow_leading_dot=false, $min_len=0) {
$leading_dot_policy = $allow_leading_dot ? OID_DOT_OPTIONAL : OID_DOT_FORBIDDEN;
 
$part_regex = oid_part_regex($min_len, $allow_leading_zeroes, $leading_dot_policy);
 
return '@^'.$part_regex.'$@';
}
 
/**
* Returns a partial regular expression which matches valid OIDs in dot notation.
* It can be inserted into regular expressions.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param int $min_len<br />
* 0="." and greater will be recognized, but not ""<br />
* 1=".2" and greater will be recognized<br />
* 2=".2.999" and greater will be recognized (default)<br />
* etc.
* @param boolean $allow_leading_zeroes<br />
* true: ".2.0999" will be recognized<br />
* false: ".2.0999" won't be recognized (default)
* @param int $leading_dot_policy<br />
* 0 (OID_DOT_FORBIDDEN): forbidden<br />
* 1 (OID_DOT_OPTIONAL) : optional (default)<br />
* 2 (OID_DOT_REQUIRED) : enforced
* @return string|false A regular expression which matches OIDs in dot notation
**/
function oid_part_regex($min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL) {
switch ($leading_dot_policy) {
case 0: // forbidden
$lead_dot = '';
break;
case 1: // optional
$lead_dot = '\\.{0,1}';
break;
case 2: // enforced
$lead_dot = '\\.';
break;
default:
assert(false);
return false;
}
 
$lead_zero = $allow_leading_zeroes ? '0*' : '';
$zero_till_thirtynine = '(([0-9])|([1-3][0-9]))'; // second arc is limited to 0..39 if root arc is 0..1
$singledot_option = ($min_len == 0) && ($leading_dot_policy != OID_DOT_FORBIDDEN) ? '|\\.' : '';
$only_root_option = ($min_len <= 1) ? '|('.$lead_dot.$lead_zero.'[0-2])' : '';
 
$regex = '
(
(
(
('.$lead_dot.$lead_zero.'[0-1])
\\.'.$lead_zero.$zero_till_thirtynine.'
(\\.'.$lead_zero.'(0|[1-9][0-9]*)){'.max(0, $min_len-2).',}
)|(
('.$lead_dot.$lead_zero.'[2])
(\\.'.$lead_zero.'(0|[1-9][0-9]*)){'.max(0, $min_len-1).',}
)
'.$only_root_option.'
'.$singledot_option.'
)
)';
 
// Remove the indentations which are used to maintain this large regular expression in a human friendly way
$regex = str_replace("\n", '', $regex);
$regex = str_replace("\r", '', $regex);
$regex = str_replace("\t", '', $regex);
$regex = str_replace(' ', '', $regex);
 
return $regex;
}
 
/**
* Searches all OIDs in $text and outputs them as array.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param string $text<br />
* The text to be parsed
* @param int $min_len<br />
* 0="." and greater will be recognized, but not ""<br />
* 1=".2" and greater will be recognized<br />
* 2=".2.999" and greater will be recognized (default)<br />
* etc.
* @param boolean $allow_leading_zeroes<br />
* true: ".2.0999" will be recognized<br />
* false: ".2.0999" won't be recognized (default)
* @param int $leading_dot_policy<br />
* 0 (OID_DOT_FORBIDDEN): forbidden<br />
* 1 (OID_DOT_OPTIONAL) : optional (default)<br />
* 2 (OID_DOT_REQUIRED) : enforced
* @param boolean $requires_whitespace_delimiters<br />
* true: "2.999" will be recognized, as well as " 2.999 " (default)<br />
* false: "2.999!" will be reconigzed, as well as "2.999.c" (this might be used in in documentations with templates)
* @return string[] An array of OIDs in dot notation
**/
function parse_oids($text, $min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL, $requires_whitespace_delimiters=true) {
$regex = oid_detection_regex($min_len, $allow_leading_zeroes, $leading_dot_policy, $requires_whitespace_delimiters);
 
$matches = array();
preg_match_all($regex, $text, $matches);
return $matches[1];
}
 
/**
* Returns a full regular expression for detecting OIDs in dot notation inside a text.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param int $min_len<br />
* 0="." and greater will be recognized, but not ""<br />
* 1=".2" and greater will be recognized<br />
* 2=".2.999" and greater will be recognized (default)<br />
* etc.
* @param boolean $allow_leading_zeroes<br />
* true: ".2.0999" will be recognized<br />
* false: ".2.0999" won't be recognized (default)
* @param int $leading_dot_policy<br />
* 0 (OID_DOT_FORBIDDEN): forbidden<br />
* 1 (OID_DOT_OPTIONAL) : optional (default)<br />
* 2 (OID_DOT_REQUIRED) : enforced
* @param boolean $requires_whitespace_delimiters<br />
* true: "2.999" will be recognized, as well as " 2.999 " (default)<br />
* false: "2.999!" will be reconigzed, as well as "2.999.c" (this might be used in in documentations with templates)
* @return string The regular expression
**/
function oid_detection_regex($min_len=2, $allow_leading_zeroes=false, $leading_dot_policy=OID_DOT_OPTIONAL, $requires_whitespace_delimiters=true) {
if ($requires_whitespace_delimiters) {
// A fully qualified regular expression which can be used by preg_match()
$begin_condition = '(?<=^|\\s)';
$end_condition = '(?=\\s|$)';
} else {
// A partial expression which can be used inside another regular expression
$begin_condition = '(?<![\d])';
$end_condition = '(?![\d])';
}
 
$part_regex = oid_part_regex($min_len, $allow_leading_zeroes, $leading_dot_policy);
 
return '@'.$begin_condition.$part_regex.$end_condition.'@';
}
 
/**
* Returns the parent of an OID in dot notation or the OID itself, if it is the root.<br />
* Leading dots and leading zeroes are tolerated.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-16
* @param string $oid<br />
* An OID in dot notation.
* @return string|false The parent OID in dot notation.
**/
function oid_up($oid) {
$oid = sanitizeOID($oid, 'auto');
if ($oid === false) return false;
 
$p = strrpos($oid, '.');
if ($p === false) return $oid;
if ($p == 0) return '.';
 
return substr($oid, 0, $p);
}
 
/**
* Outputs the depth of an OID.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param string $oid An OID in dot notation (with or without leading dot)
* @return int The depth of the OID, e.g. 2.999 and .2.999 has the length 2.
**/
function oid_len($oid) {
if ($oid == '') return 0;
if ($oid[0] == '.') $oid = substr($oid, 1);
return substr_count($oid, '.')+1;
}
function oid_depth($oid) {
return oid_len($oid);
}
 
/**
* Lists all parents of an OID.
* This function tolerates leading dots. The parent of '.' stays '.'.
* The OID will not be checked for validity!
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-17
* @param string $oid<br />
* An OID in dot notation.
* @return string[] An array with all parent OIDs.
**/
function oid_parents($oid) {
$parents = array();
 
while (oid_len($oid) > 1) {
$oid = oid_up($oid);
$parents[] = $oid;
}
 
if (substr($oid, 0, 1) == '.') $parents[] = '.';
 
return $parents;
}
 
/*
assert(oid_parents('.1.2.999') == array('.1.2', '.1', '.'));
assert(oid_parents('1.2.999') == array('1.2', '1'));
assert(oid_parents('.') == array('.'));
assert(oid_parents('') == array());
*/
 
/**
* Sorts an array containing OIDs in dot notation.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-09
* @param string[] $ary<br />
* An array of OIDs in dot notation.<br />
* This array will be changed by this method.
* @param boolean $output_with_leading_dot<br />
* true: The array will be normalized to OIDs with a leading dot.
* false: The array will be normalized to OIDs without a leading dot. (default)
**/
function oidSort(&$ary, $output_with_leading_dot=false) {
$out = array();
 
$none = $output_with_leading_dot ? '.' : '';
 
$d = array();
$oid = null;
foreach ($ary as &$oid) {
if (($oid == '') || ($oid == '.')) {
$out[] = $none;
} else {
$oid = sanitizeOID($oid, 'auto'); // strike leading zeroes
$bry = explode('.', $oid, 2);
$firstarc = $bry[0];
$rest = (isset($bry[1])) ? $bry[1] : '';
$d[$firstarc][] = $rest;
}
}
unset($oid);
ksort($d);
 
$data = null;
foreach ($d as $firstarc => &$data) {
oidSort($data);
foreach ($data as &$rest) {
$out[] = ($output_with_leading_dot ? '.' : '')."$firstarc" . (($rest != $none) ? ".$rest" : '');
}
}
unset($data);
 
$ary = $out;
}
 
/**
* Checks if two OIDs in dot-notation are equal
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-05-27
* @param string $oidA<br />
* First OID
* @param string $oidB<br />
* Second OID
* @return boolean|null True if the OIDs are equal, null if one of the OIDs are invalid
**/
function oid_dotnotation_equal($oidA, $oidB) {
$oidA = sanitizeOID($oidA, false);
if ($oidA === false) return null;
 
$oidB = sanitizeOID($oidB, false);
if ($oidB === false) return null;
 
return $oidA === $oidB;
}
 
/**
* Removes leading zeroes from an OID in dot notation.
* @author Daniel Marschall, ViaThinkSoft
* @version 2015-08-17
* @param string $oid<br />
* An OID in dot notation.
* @param boolean $leading_dot<br />
* true: The OID is valid, if it contains a leading dot.<br />
* false (default): The OID is valid, if it does not contain a leading dot.
* 'auto: Allow both
* @return string|false The OID without leading dots, or <code>false</code> if the OID is syntactically wrong.
**/
$oid_sanitize_cache = array();
function sanitizeOID($oid, $leading_dot=false) {
if ($leading_dot) $leading_dot = substr($oid,0,1) == '.';
 
// We are using a cache, since this function is used very often by OIDplus
global $oid_sanitize_cache;
$v = ($leading_dot ? 'T' : 'F').$oid;
if (isset($oid_sanitize_cache[$v])) return $oid_sanitize_cache[$v];
 
if ($leading_dot) {
if ($oid == '.') return '';
} else {
if ($oid == '') return '';
}
 
$out = '';
$ary = explode('.', $oid);
foreach ($ary as $n => &$a) {
if (($leading_dot) && ($n == 0)) {
if ($a != '') return false;
continue;
}
 
if (!ctype_digit($a)) return false; // does contain something other than digits
 
// strike leading zeroes
$a = preg_replace("@^0+@", '', $a);
if ($a == '') $a = 0;
 
if (($leading_dot) || ($n != 0)) $out .= '.';
$out .= $a;
}
unset($a);
unset($ary);
 
$oid_sanitize_cache[$v] = $out;
return $out;
}
 
/**
* Shows the top arc of an OID.
* This function tolerates leading dots.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-16
* @param string $oid<br />
* An OID in dot notation.
* @return string|false The top arc of the OID or empty string if it is already the root ('.')
**/
function oid_toparc($oid) {
$leadingdot = substr($oid,0,1) == '.';
 
$oid = sanitizeOID($oid, $leadingdot);
if ($oid === false) return false;
 
if (!$leadingdot) $oid = '.'.$oid;
 
$p = strrpos($oid, '.');
if ($p === false) return false;
$r = substr($oid, $p+1);
 
if ($leadingdot) {
# if ($r == '') return '.';
return $r;
} else {
return substr($r, 1);
}
}
 
/**
* Calculates the distance between two OIDs.
* This function tolerates leading dots and leading zeroes.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-20
* @param string $a<br />
* An OID.
* @param string $b<br />
* An OID.
* @return int|false false if both OIDs do not have a child-parent or parent-child relation, e.g. oid_distance('2.999.1.2.3', '2.999.4.5') = false, or if one of the OIDs is syntactially invalid<br />
* >0 if $a is more specific than $b , e.g. oid_distance('2.999.1.2', '2.999') = 2<br />
* <0 if $a is more common than $b , e.g. oid_distance('2.999', '2.999.1.2') = -2
**/
function oid_distance($a, $b) {
if (substr($a,0,1) == '.') $a = substr($a,1);
if (substr($b,0,1) == '.') $b = substr($b,1);
 
$a = sanitizeOID($a, false);
if ($a === false) return false;
$b = sanitizeOID($b, false);
if ($b === false) return false;
 
$ary = explode('.', $a);
$bry = explode('.', $b);
 
$min_len = min(count($ary), count($bry));
 
for ($i=0; $i<$min_len; $i++) {
if ($ary[$i] != $bry[$i]) return false;
}
 
return count($ary) - count($bry);
}
/*
assert(oid_distance('2.999.1.2.3', '2.999.4.5') === false);
assert(oid_distance('2.999.1.2', '2.999') === 2);
assert(oid_distance('2.999', '2.999.1.2') === -2);
*/
 
/**
* Adds a leading dot to an OID.
* Leading zeroes are tolerated.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-20
* @param string $oid<br />
* An OID.
* @return string|false The OID with a leading dot or false if the OID is syntactially wrong.
**/
function oid_add_leading_dot($oid) {
$oid = sanitizeOID($oid, 'auto');
if ($oid === false) return false;
 
if ($oid[0] != '.') $oid = '.'.$oid;
return $oid;
}
 
/**
* Removes a leading dot to an OID.
* Leading zeroes are tolerated.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-20
* @param string $oid<br />
* An OID.
* @return string|false The OID without a leading dot or false if the OID is syntactially wrong.
**/
function oid_remove_leading_dot($oid) {
$oid = sanitizeOID($oid, 'auto');
if ($oid === false) return false;
 
if (substr($oid,0,1) == '.') $oid = substr($oid, 1);
return $oid;
}
 
/**
* Find the common ancestor of two or more OIDs
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-05-27
* @param string[] $oids<br />
* An array of multiple OIDs, e.g. 2.999.1 and 2.999.2.3.4
* @return string|false The common ancestor, e.g. 2.999, or false if there is no common ancestor.
**/
function oid_common_ancestor(array $oids) {
$shared = array();
 
if (!is_array($oids)) return false;
if (count($oids) === 0) return false;
 
foreach ($oids as &$oid) {
$oid = sanitizeOID($oid, false);
if ($oid === false) return false;
$oid = explode('.', $oid);
}
 
$max_ok = strlen($oids[0]);
for ($i=1; $i<count($oids); $i++) {
for ($j=0; $j<min(strlen($oids[$i]),strlen($oids[0])); $j++) {
if ($oids[$i][$j] != $oids[0][$j]) {
if ($j < $max_ok) $max_ok = $j;
break;
}
}
if ($j < $max_ok) $max_ok = $j;
}
 
$out = array();
for ($i=0; $i<$max_ok; $i++) {
$out[] = $oids[0][$i];
}
return implode('.', $out);
}
/*
assert(oid_shared_ancestor(array('2.999.4.5.3', '2.999.4.5')) === "2.999.4.5");
assert(oid_shared_ancestor(array('2.999.4.5', '2.999.4.5.3')) === "2.999.4.5");
assert(oid_shared_ancestor(array('2.999.1.2.3', '2.999.4.5')) === "2.999");
*/
 
 
# === OID-IRI NOTATION FUNCTIONS ===
 
if (!function_exists('mb_ord')) {
# http://stackoverflow.com/a/24755772/3544341
function mb_ord($char, $encoding = 'UTF-8') {
if ($encoding === 'UCS-4BE') {
list(, $ord) = (strlen($char) === 4) ? @unpack('N', $char) : @unpack('n', $char);
return $ord;
} else {
return mb_ord(mb_convert_encoding($char, 'UCS-4BE', $encoding), 'UCS-4BE');
}
}
}
 
function iri_char_valid($c, $firstchar, $lastchar) {
// see Rec. ITU-T X.660, clause 7.5
 
if (($firstchar || $lastchar) && ($c == '-')) return false;
 
if ($c == '-') return true;
if ($c == '.') return true;
if ($c == '_') return true;
if ($c == '~') return true;
if (($c >= '0') && ($c <= '9') && (!$firstchar)) return true;
if (($c >= 'A') && ($c <= 'Z')) return true;
if (($c >= 'a') && ($c <= 'z')) return true;
 
$v = mb_ord($c);
 
if (($v >= 0x000000A0) && ($v <= 0x0000DFFE)) return true;
if (($v >= 0x0000F900) && ($v <= 0x0000FDCF)) return true;
if (($v >= 0x0000FDF0) && ($v <= 0x0000FFEF)) return true;
if (($v >= 0x00010000) && ($v <= 0x0001FFFD)) return true;
if (($v >= 0x00020000) && ($v <= 0x0002FFFD)) return true;
if (($v >= 0x00030000) && ($v <= 0x0003FFFD)) return true;
if (($v >= 0x00040000) && ($v <= 0x0004FFFD)) return true;
if (($v >= 0x00050000) && ($v <= 0x0005FFFD)) return true;
if (($v >= 0x00060000) && ($v <= 0x0006FFFD)) return true;
if (($v >= 0x00070000) && ($v <= 0x0007FFFD)) return true;
if (($v >= 0x00080000) && ($v <= 0x0008FFFD)) return true;
if (($v >= 0x00090000) && ($v <= 0x0009FFFD)) return true;
if (($v >= 0x000A0000) && ($v <= 0x000AFFFD)) return true;
if (($v >= 0x000B0000) && ($v <= 0x000BFFFD)) return true;
if (($v >= 0x000C0000) && ($v <= 0x000CFFFD)) return true;
if (($v >= 0x000D0000) && ($v <= 0x000DFFFD)) return true;
if (($v >= 0x000E1000) && ($v <= 0x000EFFFD)) return true;
 
// Note: Rec. ITU-T X.660, clause 7.5.3 would also forbid ranges which are marked in ISO/IEC 10646 as "(This position shall not be used)"
// But tool implementers should be tolerate them, since these limitations can be removed in future.
 
return false;
}
 
function iri_arc_valid($arc, $allow_numeric=true) {
if ($arc == '') return false;
 
$m = array();
if ($allow_numeric && preg_match('@^(\\d+)$@', $arc, $m)) return true; # numeric arc
 
// Question: Should we strip RTL/LTR characters?
 
if (mb_substr($arc, 2, 2) == '--') return false; // see Rec. ITU-T X.660, clause 7.5.4
 
$array = array();
preg_match_all('/./u', $arc, $array, PREG_SET_ORDER);
$len = count($array);
foreach ($array as $i => $char) {
if (!iri_char_valid($char[0], $i==0, $i==$len-1)) return false;
}
 
return true;
}
 
/**
* Checks if an IRI identifier is valid or not.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-17
* @param string $iri<br />
* An OID in OID-IRI notation, e.g. /Example/test
* @return boolean true if the IRI identifier is valid.
**/
function iri_valid($iri) {
if ($iri == '/') return true; // OK?
 
if (substr($iri, 0, 1) != '/') return false;
 
$ary = explode('/', $iri);
array_shift($ary);
foreach ($ary as $a) {
if (!iri_arc_valid($a)) return false;
}
 
return true;
}
 
/*
assert(iri_arc_valid('ABCDEF'));
assert(!iri_arc_valid('-ABCDEF'));
assert(!iri_arc_valid('ABCDEF-'));
assert(!iri_arc_valid(' ABCDEF'));
assert(!iri_arc_valid('2 ABCDEF'));
assert(!iri_arc_valid(''));
 
assert(!iri_valid(''));
assert(iri_valid('/'));
assert(iri_valid('/hello/world'));
assert(iri_valid('/123/world'));
assert(!iri_valid('/hello/0world'));
assert(!iri_valid('/hello/xo--test'));
assert(!iri_valid('/hello/-super-/sd'));
*/
 
/**
* Returns an associative array in the form 'ASN.1' => '/2/1' .
* @author Daniel Marschall, ViaThinkSoft
* @version 2018-01-05
* @see http://itu.int/go/X660
* @return array<string,string> An associative array in the form 'ASN.1' => '/2/1' .
**/
function iri_get_long_arcs() {
$iri_long_arcs = array();
$iri_long_arcs['ASN.1'] = '/2/1';
$iri_long_arcs['Country'] = '/2/16';
$iri_long_arcs['International-Organizations'] = '/2/23';
$iri_long_arcs['UUID'] = '/2/25';
$iri_long_arcs['Tag-Based'] = '/2/27';
$iri_long_arcs['BIP'] = '/2/41';
$iri_long_arcs['Telebiometrics'] = '/2/42';
$iri_long_arcs['Cybersecurity'] = '/2/48';
$iri_long_arcs['Alerting'] = '/2/49';
$iri_long_arcs['OIDResolutionSystem'] = '/2/50';
$iri_long_arcs['GS1'] = '/2/51';
$iri_long_arcs['Example'] = '/2/999'; // English
$iri_long_arcs['Exemple'] = '/2/999'; // French
$iri_long_arcs['Ejemplo'] = '/2/999'; // Spanish
$iri_long_arcs["\u{0627}\u{0644}\u{0645}\u{062B}\u{0627}\u{0644}"] = '/2/999'; // Arabic
$iri_long_arcs["\u{8303}\u{4F8B}"] = '/2/999'; // Chinese
$iri_long_arcs["\u{041F}\u{0440}\u{0438}\u{043C}\u{0435}\u{0440}"] = '/2/999'; // Russian
$iri_long_arcs["\u{C608}\u{C81C}"] = '/2/999'; // Korean
$iri_long_arcs["\u{4F8B}"] = '/2/999'; // Japanese
$iri_long_arcs['Beispiel'] = '/2/999'; // German
return $iri_long_arcs;
}
 
/**
* Tries to shorten/simplify an IRI by applying "long arcs", e.g. /2/999/123 -> /Example/123 .
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-05-22
* @param string $iri<br />
* An OID in OID-IRI notation, e.g. /Example/test
* @return string|false The modified IRI.
**/
function iri_add_longarcs($iri) {
$iri_long_arcs = iri_get_long_arcs();
 
if (!iri_valid($iri)) return false;
 
$ary = explode('/', $iri);
 
$ary_number_iri = $ary;
if ($ary_number_iri[1] == 'Joint-ISO-ITU-T') $ary_number_iri[1] = '2';
 
$number_iri = implode('/', $ary_number_iri);
 
foreach ($iri_long_arcs as $cur_longarc => $cur_iri) {
assert(iri_valid($cur_iri));
if (strpos($number_iri.'/', $cur_iri.'/') === 0) {
$cnt = substr_count($cur_iri, '/');
for ($i=1; $i<$cnt; $i++) {
array_shift($ary);
}
$ary[0] = '';
$ary[1] = $cur_longarc;
$iri = implode('/', $ary);
break;
}
}
 
return $iri;
}
/*
assert(iri_add_longarcs('/2/999/123') === '/Example/123');
*/
 
# === FUNCTIONS FOR OIDS IN ASN.1 NOTATION ===
 
/**
* Checks if an ASN.1 identifier is valid.
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-05-22
* @param string $id<br />
* An ASN.1 identifier, e.g. "example". Not "example(99)" or "99" and not a path like "{ 2 999 }"
* Note: Use asn1_path_valid() for validating a whole ASN.1 notation path.
* @return boolean true, if the identifier is valid: It begins with an lowercase letter and contains only 0-9, a-z, A-Z and "-"
**/
function oid_id_is_valid($id) {
// see Rec. ITU-T X.660 | ISO/IEC 9834-1, clause 7.7
// and Rec. ITU-T X.680 | ISO/IEC 8824-1, clause 12.3
if (substr($id,-1,1) == '-') return false;
if (strstr($id,'--')) return false;
return preg_match('/^([a-z][a-zA-Z0-9-]*)$/', $id) != 0;
}
 
/**
* Checks if the ASN.1 notation of an OID is valid.
* This function does not tolerate leading zeros.
* This function will fail (return false) if there are unresolved symbols, e.g. {iso test} is not valid while { iso 123 } is valid.
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-17
* @param string $asn1<br />
* An OID in ASN.1 notation.
* @return boolean true if the identifier is valid.
**/
function asn1_path_valid($asn1) {
return asn1_to_dot($asn1) != false;
}
 
/**
* Returns an array of standardized ASN.1 alphanumeric identifiers which do not require a numeric identifier, e.g. { 2 example }
* The array has the form '0.0.a' -> '0.0.1'
* @author Daniel Marschall, ViaThinkSoft
* @version 2019-03-25
* @see http://www.oid-info.com/name-forms.htm
* @return array<string,string> Associative array of standardized ASN.1 alphanumeric identifiers
**/
function asn1_get_standardized_array() {
 
// Taken from oid-info.com
// http://www.oid-info.com/name-forms.htm
$standardized = array();
$standardized['itu-t'] = '0';
$standardized['ccitt'] = '0';
$standardized['iso'] = '1';
$standardized['joint-iso-itu-t'] = '2';
$standardized['joint-iso-ccitt'] = '2';
$standardized['0.recommendation'] = '0.0';
$standardized['0.0.a'] = '0.0.1';
$standardized['0.0.b'] = '0.0.2';
$standardized['0.0.c'] = '0.0.3';
$standardized['0.0.d'] = '0.0.4';
$standardized['0.0.e'] = '0.0.5';
$standardized['0.0.f'] = '0.0.6';
$standardized['0.0.g'] = '0.0.7';
$standardized['0.0.h'] = '0.0.8';
$standardized['0.0.i'] = '0.0.9';
$standardized['0.0.j'] = '0.0.10';
$standardized['0.0.k'] = '0.0.11';
$standardized['0.0.l'] = '0.0.12';
$standardized['0.0.m'] = '0.0.13';
$standardized['0.0.n'] = '0.0.14';
$standardized['0.0.o'] = '0.0.15';
$standardized['0.0.p'] = '0.0.16';
$standardized['0.0.q'] = '0.0.17';
$standardized['0.0.r'] = '0.0.18';
$standardized['0.0.s'] = '0.0.19';
$standardized['0.0.t'] = '0.0.20';
$standardized['0.0.u'] = '0.0.21';
$standardized['0.0.v'] = '0.0.22';
$standardized['0.0.w'] = '0.0.23'; // actually, this OID does not exist
$standardized['0.0.x'] = '0.0.24';
$standardized['0.0.y'] = '0.0.25';
$standardized['0.0.z'] = '0.0.26';
$standardized['0.question'] = '0.1';
$standardized['0.administration'] = '0.2';
$standardized['0.network-operator'] = '0.3';
$standardized['0.identified-organization'] = '0.4';
$standardized['1.standard'] = '1.0';
$standardized['1.registration-authority'] = '1.1';
$standardized['1.member-body'] = '1.2';
$standardized['1.identified-organization'] = '1.3';
return $standardized;
}
 
/**
* Converts an OID in ASN.1 notation into an OID in dot notation and tries to resolve well-known identifiers.<br />
* e.g. {joint-iso-itu-t(2) example(999) 1 2 3} --> 2.999.1.2.3<br />
* e.g. {iso 3} --> 1.3
* This function does not tolerate leading zeros.
* This function will fail (return false) if there are unresolved symbols, e.g. {iso test} will not be resolved to 1.test
* @author Daniel Marschall, ViaThinkSoft
* @version 2014-12-17
* @param string $asn<br />
* An OID in ASN.1 notation.
* @return string|false An OID in dot notation without leading dot or false if the path is invalid.
**/
function asn1_to_dot($asn) {
$standardized = asn1_get_standardized_array();
 
// Clean up
$count = -1;
$asn = preg_replace('@^\\{(.+)\\}$@', '\\1', $asn, -1, $count);
if ($count == 0) return false; // { and } are required. The ASN.1 path will NOT be trimmed by this function
 
// If identifier is set, apply it (no check if it overrides a standardized identifier)
$asn = preg_replace('|\s*([a-z][a-zA-Z0-9-]*)\s*\((\d+)\)|', ' \\2', $asn);
$asn = trim($asn);
 
// Set dots
$asn = preg_replace('|\s+|', '.', $asn);
 
// Apply standardized identifiers (case sensitive)
$asn .= '.';
foreach ($standardized as $s => $r) {
$asn = preg_replace("@^".preg_quote($s,"@")."@", $r, $asn);
}
$asn = substr($asn, 0, strlen($asn)-1);
 
// Check if all numbers are OK
// -> every arc must be resolved
// -> numeric arcs must not have a leading zero
// -> invalid stuff will be recognized, e.g. a "(1)" without an identifier in front of it
$ary = explode('.', $asn);
foreach ($ary as $a) {
$m = array();
if (!preg_match('@^(0|([1-9]\\d*))$@', $a, $m)) return false;
}
 
return $asn;
}
 
/*
assert(asn1_to_dot('{2 999 (1)}') == false);
assert(asn1_to_dot('{2 999 test}') == false);
assert(asn1_to_dot('{2 999 1}') == '2.999.1');
assert(asn1_to_dot(' {2 999 1} ') == false);
assert(asn1_to_dot('2 999 1') == false);
assert(asn1_to_dot('{2 999 01}') == false);
assert(asn1_to_dot('{ 0 question 123 }') == '0.1.123');
assert(asn1_to_dot('{ iso }') == '1');
assert(asn1_to_dot('{ iso(1) }') == '1');
assert(asn1_to_dot('{ iso(2) }') == '2');
assert(asn1_to_dot('{ iso 3 }') == '1.3');
*/
 
/**
* Gets the last numeric identifier of an ASN.1 notation OID.
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-06-11
* @param string $asn1id<br />
* An ASN.1 identifier string, e.g. { 2 example(999) test(1) }
* @return int|false The last numeric identifier arc, e.g. "1" or false if the ID is invalid
**/
function asn1_last_identifier($asn1id) {
$asn1id = preg_replace('@\(\s*\d+\s*\)@', '', $asn1id);
$asn1id = trim(str_replace(array('{', '}', "\t"), ' ', $asn1id));
$ary = explode(' ', $asn1id);
$asn1id = $ary[count($ary)-1];
return preg_match('#[^0-9]#',$asn1id) ? (int)$asn1id : false;
}
 
/**
* "Soft corrects" an invalid ASN.1 identifier.<br />
* Attention, by "soft correcting" the ID, it is not authoritative anymore, and might not be able to be resolved by ORS.
* @author Daniel Marschall, ViaThinkSoft
* @version 2020-05-22
* @param string $id<br />
* An ASN.1 identifier.
* @param boolean $append_id_prefix<br />
* true (default): If the identifier doesn't start with a-Z, the problem will be solved by prepending "id-" to the identifier.<br />
* false: If the identifier doesn't start with a-Z, then the problem cannot be solved (method returns empty string).
* @return string The "soft corrected" ASN.1 identifier.<br />
* Invalid characters will be removed.<br />
* Uncorrectable start elements (0-9 or "-") will be either removed or solved by prepending "id-" (see <code>$append_id_prefix</code>)<br />
* If the identifier begins with an upper case letter, the letter will be converted into lower case.
**/
function oid_soft_correct_id($id, $append_id_prefix = true) {
// Convert "_" to "-"
$id = str_replace('_', '-', $id);
 
// Convert "--" to "-"
$id = str_replace('--', '-', $id);
 
// Remove invalid characters
$id = preg_replace('/[^a-zA-Z0-9-]+/', '', $id);
 
// Remove uncorrectable start elements (0-9 or "-")
if ($append_id_prefix) {
$id = preg_replace('/^([^a-zA-Z]+)/', 'id-$1', $id);
} else {
$id = preg_replace('/^([^a-zA-Z]+)/', '', $id);
}
 
// "Correct" upper case beginning letter by converting it to lower case
if (preg_match('/^[A-Z]/', $id)) {
$id = strtolower($id[0]) . substr($id, 1);
}
 
return $id;
}
/trunk/simplexml_supplement.inc.php
0,0 → 1,329
<?php
 
/*
* PHP SimpleXML-Supplement
* Copyright 2020 - 2021 Daniel Marschall, ViaThinkSoft
* Revision 2021-05-25
*
* Licensed 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.
*/
 
 
// ======== ATTENTION, PLEASE READ ========
// This supplement script was created to support rare PHP installations that
// do not contain SimpleXML, for example at PHP you need to explicitly
// install the package "php-xml" if you want to have SimpleXML (In the PHP
// documentation, it is written that SimpleXML is available to all, which is
// not true).
//
// Beware that the supplement behaves differently than the real SimpleXML!
// (If you know how to improve this, please feel free to send me a patch)
//
// Just a few differences towards the original SimpleXML
// - print_r() looks different
// - The supplement requires that an XML string begins with "<!DOCTYPE" or "<?xml",
// otherwise, the first element will not be stripped away
// - The supplement is slow because of regular expressions
// - Many functions like "asXML" are not implemented
// - There might be other incompatibilities
//
// So, if you want to use the SimpleXML supplement, then please carefully
// test it with your application if it works.
// ========================================
 
if (!function_exists('simplexml_load_string')) {
 
// We cannot store the number 0, 1, 2, ... as items in the SimpleXMLElement, because PHP 7.0 had a bug
// that prevented /get_object_vars() from working correctly
// https://stackoverflow.com/questions/46000541/get-object-vars-returning-different-results-depending-on-php-version
// https://stackoverflow.com/questions/4914376/failed-to-get-dynamic-instance-variables-via-phps-reflection/4914405#comment76610293_4914405
define('SIMPLEXML_SUPPLEMENT_MAGIC', '_SIMPLEXML_SUPPLEMENT_IDX_');
 
function _simplexml_supplement_isnumeric($x) {
return substr($x,0,strlen(SIMPLEXML_SUPPLEMENT_MAGIC)) === SIMPLEXML_SUPPLEMENT_MAGIC;
}
function _simplexml_supplement_getnumber($x) {
return (int)substr($x,strlen(SIMPLEXML_SUPPLEMENT_MAGIC));
}
function _simplexml_supplement_addnumberprefix($x) {
return SIMPLEXML_SUPPLEMENT_MAGIC.$x;
}
 
// We may not store the fields "position" and "attrs" in the SimpleXMLElement object,
// otherweise the typecast SimpleXMLElement=>array will include them
$_simplexml_supplement_properties = array();
 
function simplexml_load_file($file): SimpleXMLElement {
return simplexml_load_string(file_get_contents($file));
}
 
function simplexml_load_string($testxml): SimpleXMLElement {
$out = new SimpleXMLElement(); /** @phpstan-ignore-line */
 
$testxml = preg_replace('@<!\\-\\-.+\\-\\->@','',$testxml); // remove comments
$testxml = preg_replace('@<([^>\\s]+)\\s*/>@smU','<\\1></\\1>',$testxml); // <x/> => <x></x>
 
if ((stripos($testxml, '<?xml') !== false) || (stripos($testxml, '<!doctype') !== false)) {
$testxml = preg_replace('@<\\?.+\\?>@','',$testxml);
$testxml = preg_replace('@<!doctype.+>@i','',$testxml);
$m = array();
preg_match('@<(\\S+?)[^>]*>(.*)</\\1>@smU',$testxml,$m); // find root element
$root_element = $m[1];
} else {
$root_element = null;
}
 
$m = array();
preg_match_all('@<(\\S+?)([^>]*)>(.*)</\\1>@smU', $testxml, $m, PREG_SET_ORDER);
foreach ($m as $n) {
$name = $n[1];
$other = $n[2];
$val = $n[3];
 
$val = str_replace('<![CDATA[', '', $val);
$val = str_replace(']]>', '', $val);
$val = trim($val);
 
$new = $out->addChild($name, $val);
 
$m2 = array();
preg_match_all('@(\S+)=\\"([^\\"]+)\\"@smU', $other, $m2, PREG_SET_ORDER);
foreach ($m2 as $n2) {
$att_name = $n2[1];
$att_val = $n2[2];
$new->addAttribute($att_name, $att_val);
}
}
 
if (!is_null($root_element)) {
$out = $out->$root_element;
}
 
return $out;
}
 
class SimpleXMLElement implements ArrayAccess, Iterator {
 
function __destruct() {
global $_simplexml_supplement_properties;
unset($_simplexml_supplement_properties[spl_object_hash($this)]);
}
 
public function addAttribute($name, $val) {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)]['attrs'][$name] = $val;
}
 
public function attributes() {
global $_simplexml_supplement_properties;
return $_simplexml_supplement_properties[spl_object_hash($this)]['attrs'];
}
 
public function isSupplement() {
return true;
}
 
public function __construct($val=null) {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)] = array(
"position" => 0,
"attrs" => array()
);
if (!is_null($val)) {
$this->{_simplexml_supplement_addnumberprefix(0)} = $val;
}
}
 
public function isArray() {
$vars = get_object_vars($this);
$max = -1;
foreach ($vars as $x => $dummy) {
if (!_simplexml_supplement_isnumeric($x)) {
$max = -1;
break;
} else {
$num = _simplexml_supplement_getnumber($x);
if ($num > $max) $max = $num;
}
}
return $max > 0;
}
 
public function addToArray($val) {
$vars = get_object_vars($this);
$max = -1;
foreach ($vars as $x => $dummy) {
if (!_simplexml_supplement_isnumeric($x)) {
$max = -1;
break;
} else {
$num = _simplexml_supplement_getnumber($x);
if ($num > $max) $max = $num;
}
}
$max++;
$this->{_simplexml_supplement_addnumberprefix($max)} = $val;
}
 
public function __toString() {
$data = get_object_vars($this);
if (is_array($data)) {
if (isset($data[_simplexml_supplement_addnumberprefix(0)])) {
return $data[_simplexml_supplement_addnumberprefix(0)];
} else {
return '';
}
} else {
return $data;
}
}
 
public function offsetExists($offset) {
return isset($this->$offset);
}
 
public function offsetGet($offset) {
return $this->$offset;
}
 
public function offsetSet($offset, $value) {
$this->$offset = $value;
}
 
public function offsetUnset($offset) {
unset($this->$offset);
}
 
public function __get($name) {
// Output nothing
return new SimpleXMLElement(); /** @phpstan-ignore-line */
}
 
public function addChild($name, $val=null) {
global $_simplexml_supplement_properties;
 
if ($val == null) $val = new SimpleXMLElement(); /** @phpstan-ignore-line */
 
if ((substr(trim($val),0,1) === '<') || (trim($val) == '')) {
$val = simplexml_load_string($val);
}
 
$data = get_object_vars($this);
 
if (!isset($data[$name])) {
if ($val instanceof SimpleXMLElement) {
$this->$name = $val;
} else {
$this->$name = new SimpleXMLElement($val);
}
} else {
if (!($val instanceof SimpleXMLElement)) {
$val = new SimpleXMLElement($val);
}
 
if ($data[$name]->isArray()) {
$data[$name]->addToArray($val);
} else {
$tmp = new SimpleXMLElement(); /** @phpstan-ignore-line */
$tmp->addToArray($data[$name]);
$tmp->addToArray($val);
$this->$name = $tmp;
$_simplexml_supplement_properties[spl_object_hash($this)]['attrs'] = array();
}
return $val;
}
 
return $this->$name;
}
 
public function rewind() {
global $_simplexml_supplement_properties;
$_simplexml_supplement_properties[spl_object_hash($this)]['position'] = 0;
}
 
public function current() {
global $_simplexml_supplement_properties;
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
if ($dummy2 instanceof SimpleXMLElement) {
return $dummy2;
} else {
return new SimpleXMLElement($dummy2);
}
}
$cnt++;
}
} else {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) {
if ($dummy instanceof SimpleXMLElement) {
return $dummy;
} else {
return new SimpleXMLElement($dummy);
}
}
$cnt++;
}
}
 
 
}
 
public function key() {
global $_simplexml_supplement_properties;
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x/*sic*/;
$cnt++;
}
} else {
if ($cnt == $_simplexml_supplement_properties[spl_object_hash($this)]['position']) return $x;
$cnt++;
}
}
}
 
public function next() {
global $_simplexml_supplement_properties;
++$_simplexml_supplement_properties[spl_object_hash($this)]['position'];
}
 
public function valid() {
global $_simplexml_supplement_properties;
 
$vars = get_object_vars($this);
$cnt = 0;
foreach ($vars as $x => $dummy) {
if (($dummy instanceof SimpleXMLElement) && !_simplexml_supplement_isnumeric($x) && $dummy->isArray()) {
$vars2 = get_object_vars($dummy);
foreach ($vars2 as $x2 => $dummy2) {
$cnt++;
}
} else {
$cnt++;
}
}
 
return $_simplexml_supplement_properties[spl_object_hash($this)]['position'] < $cnt;
}
 
}
}
/trunk/vtor_get_contents.inc.php
0,0 → 1,12
<?php
 
define('UA_NAME', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)');
 
function file_get_contents2($url) {
$out = array();
exec("vtor -- wget -q -U ".escapeshellarg(UA_NAME)." -O - ".escapeshellarg($url), $out, $code);
if ($code != 0) return false;
return implode("\n", $out);
}
 
?>
/trunk/x_509_utils.inc.php
0,0 → 1,403
<?php
 
/*
* X.509 Utilities for PHP
* Copyright 2011-2014 Daniel Marschall, ViaThinkSoft
* Version 2014-11-17
*
* Licensed 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.
*/
 
# define('OPENSSL_EXEC', 'openssl');
# define('OPENSSL_EXEC', 'torify openssl');
define('OPENSSL_EXEC', 'vtor -cr 1 -- openssl');
 
# ToDo: For every function 2 modes: certFile, certPEM
 
function x_509_matching_issuer($cert, $issuer) {
exec(OPENSSL_EXEC.' verify -purpose any -CApath /dev/null -CAfile '.escapeshellarg($issuer).' '.escapeshellarg($cert), $out, $code);
$out = implode("\n", $out);
# Ab 1.0 wird hier ein Errorcode zurückgeliefert
# if ($code != 0) return false;
 
# TODO
# error 20 at 0 depth lookup:unable to get local issuer certificate
$chain0_ok = strpos($out, "error 2 at 1 depth lookup:unable to get issuer certificate") !== false;
$all_ok = substr($out, -2) == 'OK';
 
$ok = $chain0_ok | $all_ok;
 
return $ok;
}
 
function x_509_is_crl_file($infile) { # Only PEM files
$cx = file($infile);
return trim($cx[0]) == '-----BEGIN X509 CRL-----';
}
 
function x_509_chain($infile, $CApath) {
$chain = array();
$chain[] = $infile;
 
while (true) {
$out = array();
exec(OPENSSL_EXEC.' x509 -issuer_hash -in '.escapeshellarg($infile).' -noout', $out, $code);
if ($code != 0) return false;
$hash = $out[0];
unset($out);
 
# $ary = glob($CApath . $hash . '.*');
# $aryr = glob($CApath . $hash . '.r*');
 
$ary = array();
$aryr = array();
$all_trusted = glob($CApath . '*.pem');
foreach ($all_trusted as &$a) {
if (x_509_is_crl_file($a)) {
$out = array();
exec(OPENSSL_EXEC.' crl -hash -noout -in '.escapeshellarg($a), $out, $code);
if ($code != 0) return false;
$this_hash = trim($out[0]);
unset($out);
# echo "CRL $a : $this_hash == $hash<br>\n";
if ($this_hash == $hash) {
$aryr[] = $a;
}
if ($code != 0) return false;
} else {
$out = array();
exec(OPENSSL_EXEC.' x509 -subject_hash -noout -in '.escapeshellarg($a), $out, $code);
if ($code != 0) return false;
$this_hash = trim($out[0]);
unset($out);
# echo "CERT $a : $this_hash == $hash<br>\n";
if ($this_hash == $hash) {
$ary[] = $a;
}
}
}
 
$found = false;
# echo "Searching issuer for $infile... (Hash = $hash)<br>\n";
foreach ($ary as &$a) {
if (in_array($a, $aryr)) continue;
 
# echo "Check $a...<br>\n";
if (x_509_matching_issuer($infile, $a)) {
# echo "Found! New file is $a<br>\n";
$found = true;
$infile = $a;
 
if (in_array($a, $chain)) {
# echo "Finished.\n";
return $chain;
}
 
$chain[] = $a;
break;
}
}
if (!$found) {
# echo "No issuer found!\n";
return false;
}
}
}
 
function x_509_get_ocsp_uris($infile) {
exec(OPENSSL_EXEC.' x509 -ocsp_uri -in '.escapeshellarg($infile).' -noout', $out, $code);
if ($code != 0) return false;
return $out;
}
 
 
function x_509_ocsp_check_chain($infile, $CApath) {
return '(Skipped)'; # TODO: we need caching, otherwise the page is too slow
 
$x = x_509_chain($infile, $CApath);
 
if ($x === false) {
return 'Error: Could not complete chain!';
}
 
# echo 'Chain: ';
# print_r($x);
 
$found_ocsp = false;
$diag_nonce_err = false;
$diag_verify_err = false;
$diag_revoked = false;
$diag_unknown = false;
 
foreach ($x as $n => &$y) {
if (isset($x[$n+1])) {
$issuer = $x[$n+1];
} else {
$issuer = $y; // Root
}
 
$uris = x_509_get_ocsp_uris($y);
 
foreach ($uris as &$uri) {
$found_ocsp = true;
 
$out = array();
$xx = parse_url($uri);
$host = $xx['host'];
# $cmd = OPENSSL_EXEC." ocsp -issuer ".escapeshellarg($issuer)." -cert ".escapeshellarg($y)." -url ".escapeshellarg($uri)." -CApath ".escapeshellarg($CApath)." -VAfile ".escapeshellarg($issuer)." -nonce -header 'HOST' ".escapeshellarg($host)." -header 'User-Agent' 'Mozilla/5.0 (Windows NT 6.1; rv23.0) Gecko/20100101 Firefox/23.0' 2>&1" /* -text */;
# TODO: trusted.pem nicht hartcoden
$cmd = OPENSSL_EXEC." ocsp -issuer ".escapeshellarg($issuer)." -cert ".escapeshellarg($y)." -url ".escapeshellarg($uri)." -CAfile ".escapeshellarg($CApath.'/../trusted.pem')." -VAfile ".escapeshellarg($issuer)." -nonce -header 'HOST' ".escapeshellarg($host)." -header 'User-Agent' 'Mozilla/5.0 (Windows NT 6.1; rv23.0) Gecko/20100101 Firefox/23.0' 2>&1" /* -text */;
#echo $cmd;
exec($cmd, $out, $code);
if ($code != 0) {
if (($out[0] == 'Error querying OCSP responsder') ||
($out[0] == 'Error querying OCSP responder')) {
# TODO: openssl has a typo 'Error querying OCSP responsder'
# TODO: why does this error occour for comodo CA?
return "Error querying OCSP responder (Code $code)";
}
# print_r($out);
return 'Error: OpenSSL-Exec failure ('.$code.')!';
}
 
$outc = implode("\n", $out);
if (strpos($outc, "Response verify OK") === false) $diag_verify_err = true;
if (strpos($outc, "WARNING: no nonce in response") !== false) $diag_nonce_err = true;
# We are currently not watching for other warnings (ToDo)
 
if (strpos($outc, "$y: unknown") !== false) {
$diag_unknown = true;
} else if (strpos($outc, "$y: revoked") !== false) {
$diag_revoked = true;
} else if (strpos($outc, "$y: good") === false) {
#echo "C = $outc<br>\n";
#Ã TODO:
# COMODO sagt
# C = Responder Error: unauthorized
# STARTCOM sagt
# C = Responder Error: malformedrequest
return "Error: Unexpected OCSP state! ($outc)";
}
 
# print_r($out);
unset($out);
}
}
 
# echo "Found OCSP = ".($found_ocsp ? 1 : 0)."\n";
# echo "Diag Nonce Error = ".($diag_nonce_err ? 1 : 0)."\n";
# echo "Diag Verify Error = ".($diag_verify_err ? 1 : 0)."\n";
# echo "Diag Revoked Error = ".($diag_revoked ? 1 : 0)."\n";
# echo "Diag Unknown Error = ".($diag_unknown ? 1 : 0)."\n";
 
if (!$found_ocsp) {
return 'No OCSP responders found in chain.';
}
 
if ($diag_verify_err) {
return 'Error: OCSP Verification failure!';
}
 
if ($diag_revoked) {
return 'Error: Some certs are revoked!';
}
 
if ($diag_unknown) {
return 'Warning: Some certs have unknown state!';
}
 
if ($diag_nonce_err) {
return 'OK, but NONCE missing';
}
 
return 'OK';
}
 
function _opensslVerify($cert, $mode = 0, $crl_mode = 0) {
# mode
# 0 = cert is a file
# 1 = cert is pem string
 
# crl_mode
# 0 = no crl check
# 1 = 1 crl check
# 2 = all crl check
 
$params = '';
if ($crl_mode == 0) {
$params = '';
} else if ($crl_mode == 1) {
$params = '-crl_check ';
} else if ($crl_mode == 2) {
$params = '-crl_check_all ';
} else {
return false;
}
 
if ($mode == 0) {
# $cmd = OPENSSL_EXEC.' verify '.$params.' -CApath '.escapeshellarg(__DIR__.'/../ca/trusted/').' '.escapeshellarg($cert);
$cmd = OPENSSL_EXEC.' verify '.$params.' -CAfile '.escapeshellarg(__DIR__.'/../ca/trusted.pem').' '.escapeshellarg($cert);
} else if ($mode == 1) {
# $cmd = 'echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' verify '.$params.' -CApath '.escapeshellarg(__DIR__.'/../ca/trusted/');
$cmd = 'echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' verify '.$params.' -CAfile '.escapeshellarg(__DIR__.'/../ca/trusted.pem');
} else {
return false;
}
$out = array();
exec($cmd, $out, $code);
 
if ($code != 0) return false;
 
return $out;
}
 
function opensslVerify($cert, $mode = 0) {
# 0 = cert is a file
# 1 = cert is pem string
 
$out = _opensslVerify($cert, $mode, 0);
if ($out === false) return 'Internal error';
$outtext = implode("\n", $out);
 
$out_crl = _opensslVerify($cert, $mode, 2);
if ($out_crl === false) return 'Internal error';
$outtext_crl = implode("\n", $out_crl);
 
if (strpos($outtext, "unable to get local issuer certificate") !== false) {
return 'CA unknown';
} else if (strpos($outtext, "certificate signature failure") !== false) {
return 'Fraudulent!';
}
 
$stat_expired = (strpos($outtext, "certificate has expired") !== false);
$stat_revoked = (strpos($outtext_crl, "certificate revoked") !== false);
 
# (ToDo) We are currently not looking for warnings
# $stat_crl_expired = (strpos($outtext_crl, "CRL has expired") !== false);
 
if ($stat_expired && $stat_revoked) {
return 'Expired & Revoked';
} else if ($stat_revoked) {
return 'Revoked';
} else if ($stat_expired) {
return 'Expired';
}
 
if (strpos($out[0], ': OK') !== false) {
return 'Verified';
}
 
return 'Unknown error';
}
 
function getTextdump($cert, $mode = 0, $format = 0) {
# mode
# 0 = cert is a file
# 1 = cert is pem string
 
# format
# 0 = normal
# 1 = nameopt
 
if ($format == 0) {
$params = '';
} else if ($format == 1) {
$params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq, lname"';
} else {
return false;
}
 
if ($mode == 0) {
exec(OPENSSL_EXEC.' x509 -noout -text'.$params.' -in '.escapeshellarg($cert), $out, $code);
} else if ($mode == 1) {
exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout -text'.$params, $out, $code);
} else {
return false;
}
 
if ($code != 0) return false;
 
$text = implode("\n", $out);
 
$text = str_replace("\n\n", "\n", $text); # TODO: repeat until no \n\n exist anymore
 
return $text;
}
 
function getAttributes($cert, $mode = 0, $issuer = false, $longnames = false) {
# mode
# 0 = cert is a file
# 1 = cert is pem string
 
if ($longnames) {
$params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq, lname"';
} else {
$params = ' -nameopt "esc_ctrl, esc_msb, sep_multiline, space_eq"';
}
 
if ($issuer) {
$params .= ' -issuer';
} else {
$params .= ' -subject';
}
 
if ($mode == 0) {
exec(OPENSSL_EXEC.' x509 -noout'.$params.' -in '.escapeshellarg($cert), $out, $code);
} else if ($mode == 1) {
exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout'.$params, $out, $code);
} else {
return false;
}
 
$attributes = array();
foreach ($out as $n => &$o) {
if ($n == 0) continue;
preg_match("| (.*) = (.*)$|ismU", $o, $m);
if (!isset($attributes[$m[1]])) $attributes[$m[1]] = array();
$attributes[$m[1]][] = $m[2];
}
 
return $attributes;
}
 
function openssl_get_sig_base64($cert, $mode = 0) {
# mode
# 0 = cert is a file
# 1 = cert is pem string
 
$out = array();
if ($mode == 0) {
exec(OPENSSL_EXEC.' x509 -noout'.$params.' -in '.escapeshellarg($cert), $out, $code);
} else if ($mode == 1) {
exec('echo '.escapeshellarg($cert).' | '.OPENSSL_EXEC.' x509 -noout'.$params, $out, $code);
} else {
return false;
}
$dump = implode("\n", $out);
 
/*
 
Signature Algorithm: sha1WithRSAEncryption
65:f0:6f:f0:1d:66:a4:fe:d1:38:85:6f:5e:06:7b:f3:a7:08:
...
1a:13:37
 
*/
 
$regex = "@\n {4}Signature Algorithm: (\S+)\n(( {8}([a-f0-9][a-f0-9]:){18}\n)* {8}([a-f0-9][a-f0-9](:[a-f0-9][a-f0-9]){0,17}\n))@sm";
preg_match_all($regex, "$dump\n", $m);
if (!isset($m[2][0])) return false;
$x = preg_replace("@[^a-z0-9]@", "", $m[2][0]);
$x = hex2bin($x);
return base64_encode($x);
}
/trunk/xml_utils.inc.php
0,0 → 1,190
<?php
 
/*
* XML Encoding Utilities
* Copyright 2011-2020 Daniel Marschall, ViaThinkSoft
* Version 1.7.2 (2020-12-06)
*
* Licensed 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.
*/
 
// http://www.viathinksoft.de/?page=codelib&showid=89
 
// Unicode-proof htmlentities.
// Returns 'normal' chars as chars and weirdos as numeric html entites.
// Source: http://www.php.net/manual/en/function.htmlentities.php#107985 ; modified
// Modified by Daniel Marschall, ViaThinkSoft
function htmlentities_numeric($str, $allow_html=false, $encode_linebreaks=false) {
// Convert $str to UTF-8 if it is not already
if (mb_detect_encoding($str, "auto", true) != 'UTF-8') {
# $str = mb_convert_encoding($str, 'UTF-8', 'Windows-1252');
# $str = mb_convert_encoding($str, 'UTF-8', 'auto');
$str = mb_convert_encoding($str, 'UTF-8');
}
 
// get rid of existing entities else double-escape
// DM 24.08.2016 Removed because of OIDplus 1.0 XML export
//$str = html_entity_decode(stripslashes($str),ENT_QUOTES,'UTF-8');
 
$ar = preg_split('/(?<!^)(?!$)/u', $str); // return array of every multi-byte character
$str2 = '';
foreach ($ar as $c) {
$o = ord($c);
if (
(strlen($c) > 1) || /* multi-byte [unicode] */
($o < 32 || $o > 126) || /* <- control / latin weirdos -> */
($o > 33 && $o < 40) || /* quotes + ampersand */
($o > 59 && $o < 63) /* html */
) {
// convert to numeric entity
$c = mb_encode_numericentity($c, array(0x0, 0xffff, 0, 0xffff), 'UTF-8');
 
if ($allow_html) {
if ($c == '&#60;') $c = '<';
if ($c == '&#62;') $c = '>';
if ($c == '&#61;') $c = '=';
if ($c == '&#34;') $c = '"';
if ($c == '&#39;') $c = '\'';
if ($c == '&#38;') $c = '&'; // DM 24.08.2016 Re-added because OIDplus 1.0 XML export
}
 
if (!$encode_linebreaks) {
if ($allow_html) {
if ($c == "&#10;") $c = "<br />";
if ($c == "&#13;") $c = "<br />";
} else {
if ($c == "&#10;") $c = "\n";
if ($c == "&#13;") $c = "\r";
}
}
}
$str2 .= $c;
}
return $str2;
}
 
function ordUTF8($c, $index = 0, &$bytes = null) {
// http://de.php.net/manual/en/function.ord.php#78032
 
$len = strlen($c);
$bytes = 0;
 
if ($index >= $len) {
return false;
}
 
$h = ord($c[$index]);
 
if ($h <= 0x7F) {
$bytes = 1;
return $h;
} else if ($h < 0xC2) {
return false;
} else if ($h <= 0xDF && $index < $len - 1) {
$bytes = 2;
return ($h & 0x1F) << 6 | (ord($c[$index + 1]) & 0x3F);
} else if ($h <= 0xEF && $index < $len - 2) {
$bytes = 3;
return ($h & 0x0F) << 12 | (ord($c[$index + 1]) & 0x3F) << 6
| (ord($c[$index + 2]) & 0x3F);
} else if ($h <= 0xF4 && $index < $len - 3) {
$bytes = 4;
return ($h & 0x0F) << 18 | (ord($c[$index + 1]) & 0x3F) << 12
| (ord($c[$index + 2]) & 0x3F) << 6
| (ord($c[$index + 3]) & 0x3F);
} else {
return false;
}
}
 
function utf16_to_utf8($str) {
// http://betamode.de/2008/09/08/php-utf-16-zu-utf-8-konvertieren/
// http://www.moddular.org/log/utf16-to-utf8
 
$c0 = ord($str[0]);
$c1 = ord($str[1]);
if ($c0 == 0xFE && $c1 == 0xFF) {
$be = true;
} else if ($c0 == 0xFF && $c1 == 0xFE) {
$be = false;
} else {
return $str;
}
$str = substr($str, 2);
$len = strlen($str);
$dec = '';
for ($i = 0; $i < $len; $i += 2) {
$c = ($be) ? ord($str[$i]) << 8 | ord($str[$i + 1]) :
ord($str[$i + 1]) << 8 | ord($str[$i]);
if ($c >= 0x0001 && $c <= 0x007F) {
$dec .= chr($c);
} else if ($c > 0x07FF) {
$dec .= chr(0xE0 | (($c >> 12) & 0x0F));
$dec .= chr(0x80 | (($c >> 6) & 0x3F));
$dec .= chr(0x80 | (($c >> 0) & 0x3F));
} else {
$dec .= chr(0xC0 | (($c >> 6) & 0x1F));
$dec .= chr(0x80 | (($c >> 0) & 0x3F));
}
}
return $dec;
}
 
function html_named_to_numeric_entities($str) {
if (!mb_detect_encoding($str, 'UTF-8', true)) $str = utf8_encode($str);
return mb_htmlentities(decodeNamedEntities($str));
}
 
if (!function_exists('decodeNamedEntities')) {
function decodeNamedEntities($string) {
// https://stackoverflow.com/questions/20406599/how-to-encode-for-entity-igrave-not-defined-error-in-xml-feed
static $entities = NULL;
if (NULL === $entities) {
$entities = array_flip(
array_diff(
get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML401, 'UTF-8'),
get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_XML1, 'UTF-8')
)
);
}
return str_replace(array_keys($entities), $entities, $string);
}
}
 
if (!function_exists('mb_convert_encoding')) {
// https://riptutorial.com/php/example/15633/converting-unicode-characters-to-their-numeric-value-and-or-html-entities-using-php
function mb_convert_encoding($str, $to_encoding, $from_encoding = NULL) {
return iconv(($from_encoding === NULL) ? mb_internal_encoding() : $from_encoding, $to_encoding, $str);
}
}
 
if (!function_exists('mb_ord')) {
// https://riptutorial.com/php/example/15633/converting-unicode-characters-to-their-numeric-value-and-or-html-entities-using-php
function mb_ord($char, $encoding = 'UTF-8') {
if ($encoding === 'UCS-4BE') {
list(, $ord) = (strlen($char) === 4) ? @unpack('N', $char) : @unpack('n', $char);
return $ord;
} else {
return mb_ord(mb_convert_encoding($char, 'UCS-4BE', $encoding), 'UCS-4BE');
}
}
}
 
if (!function_exists('mb_htmlentities')) {
// https://riptutorial.com/php/example/15633/converting-unicode-characters-to-their-numeric-value-and-or-html-entities-using-php
function mb_htmlentities($string, $hex = true, $encoding = 'UTF-8') {
return preg_replace_callback('/[\x{80}-\x{10FFFF}]/u', function ($match) use ($hex) {
return sprintf($hex ? '&#x%X;' : '&#%d;', mb_ord($match[0]));
}, $string);
}
}