init web ems all
This commit is contained in:
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/IncompatibleWithGoogleAuthenticatorException.php
vendored
Executable file
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/IncompatibleWithGoogleAuthenticatorException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class IncompatibleWithGoogleAuthenticatorException extends Exception
|
||||
{
|
||||
protected $message = 'This secret key is not compatible with Google Authenticator.';
|
||||
}
|
||||
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/InsecureCallException.php
vendored
Executable file
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/InsecureCallException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InsecureCallException extends Exception
|
||||
{
|
||||
protected $message = 'It\'s not secure to send secret keys to Google Apis, you have to explicitly allow it by calling $google2fa->setAllowInsecureCallToGoogleApis(true).';
|
||||
}
|
||||
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/InvalidCharactersException.php
vendored
Executable file
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/InvalidCharactersException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidCharactersException extends Exception
|
||||
{
|
||||
protected $message = 'Invalid characters in the base32 string.';
|
||||
}
|
||||
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/SecretKeyTooShortException.php
vendored
Executable file
10
phpMyAdmin/vendor/pragmarx/google2fa/src/Exceptions/SecretKeyTooShortException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SecretKeyTooShortException extends Exception
|
||||
{
|
||||
protected $message = 'Secret key is too short. Must be at least 16 base32 characters';
|
||||
}
|
||||
354
phpMyAdmin/vendor/pragmarx/google2fa/src/Google2FA.php
vendored
Executable file
354
phpMyAdmin/vendor/pragmarx/google2fa/src/Google2FA.php
vendored
Executable file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA;
|
||||
|
||||
use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException;
|
||||
use PragmaRX\Google2FA\Support\Base32;
|
||||
use PragmaRX\Google2FA\Support\Constants;
|
||||
use PragmaRX\Google2FA\Support\QRCode;
|
||||
|
||||
class Google2FA
|
||||
{
|
||||
use QRCode, Base32;
|
||||
|
||||
/**
|
||||
* Length of the Token generated.
|
||||
*/
|
||||
protected $oneTimePasswordLength = 6;
|
||||
|
||||
/**
|
||||
* Interval between key regeneration.
|
||||
*/
|
||||
protected $keyRegeneration = 30;
|
||||
|
||||
/**
|
||||
* Secret.
|
||||
*/
|
||||
protected $secret;
|
||||
|
||||
/**
|
||||
* Window.
|
||||
*/
|
||||
protected $window = 1; // Keys will be valid for 60 seconds
|
||||
|
||||
/**
|
||||
* Find a valid One Time Password.
|
||||
*
|
||||
* @param $secret
|
||||
* @param $key
|
||||
* @param $window
|
||||
* @param $startingTimestamp
|
||||
* @param $timestamp
|
||||
* @param string $oldTimestamp
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function findValidOTP($secret, $key, $window, $startingTimestamp, $timestamp, $oldTimestamp = Constants::ARGUMENT_NOT_SET)
|
||||
{
|
||||
for (; $startingTimestamp <= $timestamp + $this->getWindow($window); $startingTimestamp++) {
|
||||
if (hash_equals($this->oathHotp($secret, $startingTimestamp), $key)) {
|
||||
return
|
||||
$oldTimestamp === Constants::ARGUMENT_NOT_SET
|
||||
? true
|
||||
: $startingTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a digit secret key in base32 format.
|
||||
*
|
||||
* @param int $length
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateSecretKey($length = 16, $prefix = '')
|
||||
{
|
||||
return $this->generateBase32RandomKey($length, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current one time password for a key.
|
||||
*
|
||||
* @param $secret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentOtp($secret)
|
||||
{
|
||||
return $this->oathHotp($secret, $this->getTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key regeneration.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getKeyRegeneration()
|
||||
{
|
||||
return $this->keyRegeneration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OTP length.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOneTimePasswordLength()
|
||||
{
|
||||
return $this->oneTimePasswordLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret.
|
||||
*
|
||||
* @param string|null $secret
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSecret($secret = null)
|
||||
{
|
||||
return
|
||||
is_null($secret)
|
||||
? $this->secret
|
||||
: $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Unix Timestamp divided by the $keyRegeneration
|
||||
* period.
|
||||
*
|
||||
* @return int
|
||||
**/
|
||||
public function getTimestamp()
|
||||
{
|
||||
return (int) floor(microtime(true) / $this->keyRegeneration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OTP window.
|
||||
*
|
||||
* @param null|int $window
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getWindow($window = null)
|
||||
{
|
||||
return
|
||||
is_null($window)
|
||||
? $this->window
|
||||
: $window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a window based starting timestamp.
|
||||
*
|
||||
* @param $window
|
||||
* @param $timestamp
|
||||
* @param $oldTimestamp
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function makeStartingTimestamp($window, $timestamp, $oldTimestamp)
|
||||
{
|
||||
return $oldTimestamp === Constants::ARGUMENT_NOT_SET
|
||||
? $timestamp - $this->getWindow($window)
|
||||
: max($timestamp - $this->getWindow($window), $oldTimestamp + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/use a starting timestamp for key verification.
|
||||
*
|
||||
* @param string|int|null $timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function makeTimestamp($timestamp = null)
|
||||
{
|
||||
if (is_null($timestamp)) {
|
||||
return $this->getTimestamp();
|
||||
}
|
||||
|
||||
return (int) $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the secret key and the timestamp and returns the one time
|
||||
* password.
|
||||
*
|
||||
* @param string $secret - Secret key in binary form.
|
||||
* @param int $counter - Timestamp as returned by getTimestamp.
|
||||
*
|
||||
* @throws SecretKeyTooShortException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function oathHotp($secret, $counter)
|
||||
{
|
||||
$secret = $this->base32Decode($this->getSecret($secret));
|
||||
|
||||
if (strlen($secret) < 8) {
|
||||
throw new SecretKeyTooShortException();
|
||||
}
|
||||
|
||||
// Counter must be 64-bit int
|
||||
$bin_counter = pack('N*', 0, $counter);
|
||||
|
||||
$hash = hash_hmac('sha1', $bin_counter, $secret, true);
|
||||
|
||||
return str_pad($this->oathTruncate($hash), $this->getOneTimePasswordLength(), '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the OTP from the SHA1 hash.
|
||||
*
|
||||
* @param string $hash
|
||||
*
|
||||
* @return int
|
||||
**/
|
||||
public function oathTruncate($hash)
|
||||
{
|
||||
$offset = ord($hash[19]) & 0xf;
|
||||
|
||||
$temp = unpack('N', substr($hash, $offset, 4));
|
||||
|
||||
return substr($temp[1] & 0x7fffffff, -$this->getOneTimePasswordLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove invalid chars from a base 32 string.
|
||||
*
|
||||
* @param $string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function removeInvalidChars($string)
|
||||
{
|
||||
return preg_replace('/[^'.Constants::VALID_FOR_B32.']/', '', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the enforce Google Authenticator compatibility property.
|
||||
*
|
||||
* @param mixed $enforceGoogleAuthenticatorCompatibility
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEnforceGoogleAuthenticatorCompatibility($enforceGoogleAuthenticatorCompatibility)
|
||||
{
|
||||
$this->enforceGoogleAuthenticatorCompatibility = $enforceGoogleAuthenticatorCompatibility;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set key regeneration.
|
||||
*
|
||||
* @param mixed $keyRegeneration
|
||||
*/
|
||||
public function setKeyRegeneration($keyRegeneration)
|
||||
{
|
||||
$this->keyRegeneration = $keyRegeneration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set OTP length.
|
||||
*
|
||||
* @param mixed $oneTimePasswordLength
|
||||
*/
|
||||
public function setOneTimePasswordLength($oneTimePasswordLength)
|
||||
{
|
||||
$this->oneTimePasswordLength = $oneTimePasswordLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set secret.
|
||||
*
|
||||
* @param mixed $secret
|
||||
*/
|
||||
public function setSecret($secret)
|
||||
{
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OTP window.
|
||||
*
|
||||
* @param mixed $window
|
||||
*/
|
||||
public function setWindow($window)
|
||||
{
|
||||
$this->window = $window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a user inputted key against the current timestamp. Checks $window
|
||||
* keys either side of the timestamp.
|
||||
*
|
||||
* @param string $key - User specified key
|
||||
* @param null|string $secret
|
||||
* @param null|int $window
|
||||
* @param null|int $timestamp
|
||||
* @param null|string|int $oldTimestamp
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function verify($key, $secret = null, $window = null, $timestamp = null, $oldTimestamp = Constants::ARGUMENT_NOT_SET)
|
||||
{
|
||||
return $this->verifyKey(
|
||||
$secret,
|
||||
$key,
|
||||
$window,
|
||||
$timestamp,
|
||||
$oldTimestamp
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a user inputted key against the current timestamp. Checks $window
|
||||
* keys either side of the timestamp.
|
||||
*
|
||||
* @param string $secret
|
||||
* @param string $key - User specified key
|
||||
* @param null|int $window
|
||||
* @param null|int $timestamp
|
||||
* @param null|string|int $oldTimestamp
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
public function verifyKey($secret, $key, $window = null, $timestamp = null, $oldTimestamp = Constants::ARGUMENT_NOT_SET)
|
||||
{
|
||||
$timestamp = $this->makeTimestamp($timestamp);
|
||||
|
||||
return $this->findValidOTP(
|
||||
$secret,
|
||||
$key,
|
||||
$window,
|
||||
$this->makeStartingTimestamp($window, $timestamp, $oldTimestamp),
|
||||
$timestamp,
|
||||
$oldTimestamp
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a user inputted key against the current timestamp. Checks $window
|
||||
* keys either side of the timestamp, but ensures that the given key is newer than
|
||||
* the given oldTimestamp. Useful if you need to ensure that a single key cannot
|
||||
* be used twice.
|
||||
*
|
||||
* @param string $secret
|
||||
* @param string $key - User specified key
|
||||
* @param int $oldTimestamp - The timestamp from the last verified key
|
||||
* @param int|null $window
|
||||
* @param int|null $timestamp
|
||||
*
|
||||
* @return bool|int - false (not verified) or the timestamp of the verified key
|
||||
**/
|
||||
public function verifyKeyNewer($secret, $key, $oldTimestamp, $window = null, $timestamp = null)
|
||||
{
|
||||
return $this->verifyKey($secret, $key, $window, $timestamp, $oldTimestamp);
|
||||
}
|
||||
}
|
||||
135
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Base32.php
vendored
Executable file
135
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Base32.php
vendored
Executable file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Support;
|
||||
|
||||
use ParagonIE\ConstantTime\Base32 as ParagonieBase32;
|
||||
use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException;
|
||||
use PragmaRX\Google2FA\Exceptions\InvalidCharactersException;
|
||||
|
||||
trait Base32
|
||||
{
|
||||
/**
|
||||
* Enforce Google Authenticator compatibility.
|
||||
*/
|
||||
protected $enforceGoogleAuthenticatorCompatibility = true;
|
||||
|
||||
/**
|
||||
* Generate a digit secret key in base32 format.
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateBase32RandomKey($length = 16, $prefix = '')
|
||||
{
|
||||
$secret = $prefix ? $this->toBase32($prefix) : '';
|
||||
|
||||
$secret = $this->strPadBase32($secret, $length);
|
||||
|
||||
$this->validateSecret($secret);
|
||||
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base32 string into a binary string.
|
||||
*
|
||||
* @param string $b32
|
||||
*
|
||||
* @throws InvalidCharactersException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function base32Decode($b32)
|
||||
{
|
||||
$b32 = strtoupper($b32);
|
||||
|
||||
$this->validateSecret($b32);
|
||||
|
||||
return ParagonieBase32::decodeUpper($b32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad string with random base 32 chars.
|
||||
*
|
||||
* @param $string
|
||||
* @param $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function strPadBase32($string, $length)
|
||||
{
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$string .= substr(Constants::VALID_FOR_B32_SCRAMBLED, $this->getRandomNumber(), 1);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string to Base32.
|
||||
*
|
||||
* @param $string
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function toBase32($string)
|
||||
{
|
||||
$encoded = ParagonieBase32::encodeUpper($string);
|
||||
|
||||
return str_replace('=', '', $encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random number.
|
||||
*
|
||||
* @param $from
|
||||
* @param $to
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getRandomNumber($from = 0, $to = 31)
|
||||
{
|
||||
return random_int($from, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the secret.
|
||||
*
|
||||
* @param $b32
|
||||
*/
|
||||
protected function validateSecret($b32)
|
||||
{
|
||||
$this->checkForValidCharacters($b32);
|
||||
|
||||
$this->checkGoogleAuthenticatorCompatibility($b32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the secret key is compatible with Google Authenticator.
|
||||
*
|
||||
* @param $b32
|
||||
*
|
||||
* @throws IncompatibleWithGoogleAuthenticatorException
|
||||
*/
|
||||
protected function checkGoogleAuthenticatorCompatibility($b32)
|
||||
{
|
||||
if ($this->enforceGoogleAuthenticatorCompatibility && ((strlen($b32) & (strlen($b32) - 1)) !== 0)) {
|
||||
throw new IncompatibleWithGoogleAuthenticatorException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all secret key characters are valid.
|
||||
*
|
||||
* @param $b32
|
||||
*
|
||||
* @throws InvalidCharactersException
|
||||
*/
|
||||
protected function checkForValidCharacters($b32)
|
||||
{
|
||||
if (preg_replace('/[^'.Constants::VALID_FOR_B32.']/', '', $b32) !== $b32) {
|
||||
throw new InvalidCharactersException();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Constants.php
vendored
Executable file
21
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Constants.php
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Support;
|
||||
|
||||
class Constants
|
||||
{
|
||||
/**
|
||||
* Characters valid for Base 32.
|
||||
*/
|
||||
const VALID_FOR_B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
|
||||
/**
|
||||
* Characters valid for Base 32, scrambled.
|
||||
*/
|
||||
const VALID_FOR_B32_SCRAMBLED = '234567QWERTYUIOPASDFGHJKLZXCVBNM';
|
||||
|
||||
/**
|
||||
* Argument not set constant.
|
||||
*/
|
||||
const ARGUMENT_NOT_SET = '__not_set__';
|
||||
}
|
||||
91
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/QRCode.php
vendored
Executable file
91
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/QRCode.php
vendored
Executable file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Support;
|
||||
|
||||
use BaconQrCode\Renderer\Image\Png;
|
||||
use BaconQrCode\Writer as BaconQrCodeWriter;
|
||||
use PragmaRX\Google2FA\Exceptions\InsecureCallException;
|
||||
|
||||
trait QRCode
|
||||
{
|
||||
/**
|
||||
* Sending your secret key to Google API is a security issue. Developer must explicitly allow it.
|
||||
*/
|
||||
protected $allowInsecureCallToGoogleApis = false;
|
||||
|
||||
/**
|
||||
* Creates a Google QR code url.
|
||||
*
|
||||
* @param string $company
|
||||
* @param string $holder
|
||||
* @param string $secret
|
||||
* @param int $size
|
||||
*
|
||||
* @throws InsecureCallException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQRCodeGoogleUrl($company, $holder, $secret, $size = 200)
|
||||
{
|
||||
if (!$this->allowInsecureCallToGoogleApis) {
|
||||
throw new InsecureCallException('It\'s not secure to send secret keys to Google Apis, you have to explicitly allow it by calling $google2fa->setAllowInsecureCallToGoogleApis(true).');
|
||||
}
|
||||
|
||||
$url = $this->getQRCodeUrl($company, $holder, $secret);
|
||||
|
||||
return Url::generateGoogleQRCodeUrl('https://chart.googleapis.com/', 'chart', 'chs='.$size.'x'.$size.'&chld=M|0&cht=qr&chl=', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a QR code data url to display inline.
|
||||
*
|
||||
* @param string $company
|
||||
* @param string $holder
|
||||
* @param string $secret
|
||||
* @param int $size
|
||||
* @param string $encoding Default to UTF-8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQRCodeInline($company, $holder, $secret, $size = 200, $encoding = 'utf-8')
|
||||
{
|
||||
$url = $this->getQRCodeUrl($company, $holder, $secret);
|
||||
|
||||
$renderer = new Png();
|
||||
$renderer->setWidth($size);
|
||||
$renderer->setHeight($size);
|
||||
|
||||
$bacon = new BaconQrCodeWriter($renderer);
|
||||
$data = $bacon->writeString($url, $encoding);
|
||||
|
||||
return 'data:image/png;base64,'.base64_encode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a QR code url.
|
||||
*
|
||||
* @param $company
|
||||
* @param $holder
|
||||
* @param $secret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQRCodeUrl($company, $holder, $secret)
|
||||
{
|
||||
return 'otpauth://totp/'.rawurlencode($company).':'.rawurlencode($holder).'?secret='.$secret.'&issuer='.rawurlencode($company).'';
|
||||
}
|
||||
|
||||
/**
|
||||
* AllowInsecureCallToGoogleApis setter.
|
||||
*
|
||||
* @param mixed $allowInsecureCallToGoogleApis
|
||||
*
|
||||
* @return QRCode
|
||||
*/
|
||||
public function setAllowInsecureCallToGoogleApis($allowInsecureCallToGoogleApis)
|
||||
{
|
||||
$this->allowInsecureCallToGoogleApis = $allowInsecureCallToGoogleApis;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
16
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Url.php
vendored
Executable file
16
phpMyAdmin/vendor/pragmarx/google2fa/src/Support/Url.php
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PragmaRX\Google2FA\Support;
|
||||
|
||||
class Url
|
||||
{
|
||||
public static function generateGoogleQRCodeUrl($domain, $page, $queryParameters, $qrCodeUrl)
|
||||
{
|
||||
$url = $domain.
|
||||
rawurlencode($page).
|
||||
'?'.$queryParameters.
|
||||
urlencode($qrCodeUrl);
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user